--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">\r
+<HTML>\r
+<HEAD>\r
+ <!-- Changed by: Benoit Mahe, 18-Jul-1997 -->\r
+ <META NAME="GENERATOR" CONTENT="Mozilla/3.01Gold (X11; I; SunOS 5.5 sun4u) [Netscape]">\r
+ <TITLE>SSIResource extension tutorial</TITLE>\r
+</HEAD>\r
+<BODY BGCOLOR="#FFFFFF">\r
+<P>\r
+<!-- Changed by: Benoit Mahe, 1-Jul-1997 -->\r
+<P>\r
+<!-- Created by GNNpress --><!-- Changed by: Anselm Baird-Smith, 4-Feb-1997\r
+--><A HREF="http://www.w3.org/pub/WWW/" TARGET="_top_"><IMG SRC="/icons/WWW/w3c_home.gif"\r
+ ALT="W3C" BORDER=0 HEIGHT=48 WIDTH=72></A>\r
+<A HREF="http://www.w3.org/pub/WWW/Jigsaw/" TARGET="_top_"><IMG SRC="/icons/jigsaw.gif" ALT="Jigsaw"\r
+ BORDER=0 HEIGHT=49 WIDTH=212></A>\r
+<H1>\r
+ SSIResource extension tutorial\r
+</H1>\r
+<P>\r
+The SSIResource\r
+(<A HREF="../api/w3c.jigsaw.ssi.SSIResource.html">w3c.jigsaw.ssi.SSIResource</A>)\r
+is a Jigsaw resource that provides a flexible way of generating part of the\r
+content of a document from individual pieces. This may sound too general,\r
+and that's because there is little constraint on the way the constituent\r
+pieces are generated. For example, one use of the SSIResource is the traditional\r
+one: the content of any resource can be embedded within any document exported\r
+by the SSIResource, by using the <TT>include</TT> command from the default\r
+command registry. Some other of the default commands allow you to include\r
+the size of the document, the time of day, the hit count, and other general\r
+data.\r
+<P>\r
+One of the goals of this tutorial is to show that the SSIResource is useful\r
+beyond its traditional use, as a powerful way of creating documents with\r
+a dynamically generated content. It is assumed that you are familiar with\r
+the administration of Jigsaw in general.\r
+<H2>\r
+ Commands and registries\r
+</H2>\r
+<P>\r
+The SSIResource will scan through the text of the file looking for a special\r
+kind of HTML comment. If it finds something of the form <TT><!--#command\r
+par_1=val_1 par_2=val_2 ... par_n=val_n --></TT>, it will interpret it\r
+as a command. <TT>par_1</TT> ... <TT>par_n </TT>are the names of the parameters,\r
+and <TT>val_1</TT> ... <TT>val_n</TT> are their values. The values can optionaly\r
+be enclosed in single or double quotes; otherwise they are delimited by ASCII\r
+white space. For example, the string <TT><!--#include\r
+virtual="doc.html"--></TT> denotes a call to a command called "include",\r
+with one parameter called "virtual" that has a value of "doc.html".\r
+<P>\r
+Upon finding a command, the SSIResource will look it up in an object\r
+called the <I>command registry</I>. The command registry returns the <I>command\r
+</I>that is registered by that name. Then, it will call the command's\r
+<TT>execute</TT> method with the specified parameters, and with other contextual\r
+data.\r
+<P>\r
+Command registries are objects of class\r
+<A HREF="../api/w3c.jigsaw.ssi.CommandRegistry.html">w3c.jigsaw.ssi.CommandRegistry</A>.\r
+Since this is an abstract class, a concrete implementation of one must be\r
+available for SSIResource to work. One such implementation is supplied with\r
+the distribution: it is\r
+<A HREF="../api/w3c.jigsaw.ssi.DefaultCommandRegistry.html">w3c.jigsaw.ssi.DefaultCommandRegistry</A>,\r
+which includes the bread-and-butter SSI commands. Commands are implementations\r
+of the <A HREF="../api/w3c.jigsaw.ssi.Command.html">w3c.jigsaw.ssi.Command\r
+</A>interface or\r
+<A HREF="../api/w3c.jigsaw.ssi.ControlCommand.html">w3c.jigsaw.ssi.ControlCommand</A>.\r
+The SSIResource declares a <TT>registryClass</TT> attribute, which is set\r
+to the particular command registry to use in parsing a given document.\r
+<P>\r
+Therefore, the way to extend the SSIResource is to create (either from scratch\r
+or by subclassing an existing one) a command registry that knows about the\r
+new commands that are being added. A good way to become familiar with these\r
+classes is too look at the code for\r
+<A HREF="../api/w3c.jigsaw.ssi.DefaultCommandRegistry.html">DefaultCommandRegistry</A>\r
+and its superclass,\r
+<A HREF="../api/w3c.jigsaw.ssi.BasicCommandRegistry.html">BasicCommandRegistry</A>,\r
+and at the code for the default commands (in rough order of complexity):\r
+<A HREF="../api/w3c.jigsaw.ssi.SampleCommand.html">SampleCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.CountCommand.html">CountCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.ConfigCommand.html">ConfigCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.FSizeCommand.html">FSizeCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.FLastModCommand.html">FLastModCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.EchoCommand.html">EchoCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.IncludeCommand.html">IncludeCommand,</A>\r
+<A HREF="../api/w3c.jigsaw.ssi..jdbc.jdbcCommand.html">jdbcCommand,</A>\r
+<A HREF="../api/w3c.jigsaw.ssi.CounterCommand.html">CounterCommand,</A>\r
+<A HREF="../api/w3c.jigsaw.ssi.servlets.ServletCommand.html">ServletCommand</A>.\r
+<P>\r
+SSIResource allows you to create control commands like loop and test. These\r
+commands implements the\r
+<A HREF="../api/w3c.jigsaw.ssi.ControlCommand.html">w3c.jigsaw.ssi.ControlCommand\r
+</A>interface. Here is the code of the default control commands :\r
+<A HREF="../api/w3c.jigsaw.ssi.IfCommand.html">IfCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.ElseCommand.html">ElseCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.EndifCommand.html">EndifCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.LoopCommand.html">LoopCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.ExitloopCommand.html">ExitloopCommand</A>,\r
+<A HREF="../api/w3c.jigsaw.ssi.EndloopCommand.html">EndloopCommand</A>. The\r
+<A HREF="../api/w3c.jigsaw.ssi.Command.html">w3c.jigsaw.ssi.Command\r
+</A>interface has been modified, a new method was added (getValue). This\r
+method is used by some control commands (if) to get some value relative to\r
+the command.\r
+<P>\r
+Let's have a look of what can be done with control commands :\r
+<P>\r
+This shtml page display the content of the users database.\r
+<PRE><html>\r
+ <head>\r
+ <title>Database SSI</title>\r
+ </head>\r
+\r
+ <body>\r
+ <h1>Database SSI</h1>\r
+ <p>This Server Side Include extension allows you to query a database,\r
+ to make some loop and some tests.\r
+ (which I am doing right now) \r
+\r
+<!--#jdbc select="SELECT * FROM users" name="result" driver="COM.imaginary.sql.msql.MsqlDriver" url="jdbc:msql://www43.inria.fr:4333/users" -->\r
+\r
+ <p>The query has run, here is all the results:<p>\r
+ <table border=2>\r
+ <tr><td><b>Name</td><td><b>Login</td>\r
+ <td><b>Email</td><td><b>Age</td></tr> \r
+<!--#loop name="loop1" -->\r
+ <!--#jdbc name="result" next="true" -->\r
+\r
+ <!--#if name="if1" command="jdbc" var="result" equals="empty" -->\r
+ <!--#exitloop name="loop1" -->\r
+ <!--#endif name="if1" -->\r
+ \r
+ <!-- the three lines above can be changed in : -->\r
+ \r
+ <!--#exitloop name="loop1" command="jdbc" var="result" equals="empty" -->\r
+\r
+ <tr><td>\r
+ <!--#jdbc name="result" column="1" -->\r
+ </td><td>\r
+ <!--#jdbc name="result" column="2" -->\r
+ </td><td>\r
+ <!--#jdbc name="result" column="3" -->\r
+ </td><td>\r
+ <!--#jdbc name="result" column="4" -->\r
+ </td></tr>\r
+<!--#endloop name="loop1" -->\r
+ </table>\r
+ <hr>\r
+ </body>\r
+</html>\r
+</PRE>\r
+<H2>\r
+ IfCommand : the source code\r
+</H2>\r
+<P>\r
+This command implements the <I>classic</I> if statement. This command can\r
+only be used with\r
+<A HREF="../api/w3c.jigsaw.ssi.EndifCommand.html">EndifCommand</A> and\r
+(optionnaly) with\r
+<A HREF="../api/w3c.jigsaw.ssi.ElseCommand.html">ElseCommand</A>.\r
+<PRE>\r
+package w3c.jigsaw.ssi;\r
+\r
+import java.util.*;\r
+\r
+import <A HREF="../api/Package-w3c.jigsaw.http.html">w3c.jigsaw.http.*</A> ;\r
+import <A HREF="../api/Package-w3c.www.http.html">w3c.www.http.*</A> ;\r
+import <A HREF="../api/Package-w3c.jigsaw.resources.html">w3c.jigsaw.resources.*</A> ;\r
+import <A HREF="../api/Package-w3c.util.html">w3c.util.*</A> ;\r
+\r
+<B></B>\r
+<B>/**</B>\r
+<B> * Implementation of the SSI <CODE>if</CODE> command. </B>\r
+<B> * @author Benoit Mahe :<I>bmahe@sophia.inria.fr</I></B>\r
+<B> */ </B>\r
+\r
+public class IfCommand implements <A HREF="../api/w3c.jigsaw.ssi.ControlCommand.html">ControlCommand</A> {\r
+ private final static String NAME = "if";\r
+ private final static boolean debug = true;\r
+<B> </B>\r
+<B> // The parameters accepted by the if command </B>\r
+ private static final String keys[] = { \r
+ "name",\r
+ "command",\r
+ "var",\r
+ "equals"\r
+ };\r
+<B></B>\r
+<B> // Used to store the position of each if command </B>\r
+ protected static Hashtable ifstore = null;\r
+\r
+ static {\r
+ ifstore = new Hashtable(23);\r
+ }\r
+<B></B>\r
+<B> /**</B>\r
+<B> * Returns the (String) value of the given variable.</B>\r
+<B> * @return a String instance.</B>\r
+<B> */ </B>\r
+ public String getValue(Dictionary variables, String var) {\r
+ return null;\r
+ }\r
+\r
+ protected static int getPosition(String name) \r
+ throws ControlCommandException \r
+ {\r
+ Integer pos = (Integer)ifstore.get(name);\r
+ if (pos == null)\r
+ throw new ControlCommandException(NAME,"Position unknown.");\r
+ else return pos.intValue();\r
+ }\r
+\r
+<B></B>\r
+<B> /**</B>\r
+<B> * register the command position in the structure</B>\r
+<B> * witch store the SSIResource.</B>\r
+<B> */ </B>\r
+ public void setPosition(<A HREF="../api/w3c.jigsaw.ssi.SSIResource.html">SSIResource</A> resource,\r
+ <A HREF="../api/w3c.jigsaw.ssi.CommandRegistry.html">CommandRegistry</A> registry,\r
+ <A HREF="../api/w3c.util.ArrayDictionary.html">ArrayDictionary</A> parameters,\r
+ Dictionary variables,\r
+ int position) \r
+ {\r
+ Object values[] = parameters.getMany(keys);\r
+ String name = (String) values[0];\r
+ if (name != null)\r
+ ifstore.put(resource.getURLPath()+":"+name, new Integer(position));\r
+ }\r
+<B></B>\r
+<B> /**</B>\r
+<B> * Executes this command. Might modify variables.</B>\r
+<B> * Must <EM>not</EM> modify the parameters.</B>\r
+<B> * It may handle conditional requests, <EM>except</EM> that if</B>\r
+<B> * it replies with a status of HTTP.NOT_MODIFIED, it <EM>must</EM></B>\r
+<B> * still reply with a content (the same content that it would have</B>\r
+<B> * returned for an inconditional request). This is because</B>\r
+<B> * further SSI commands down the line may decide thay they have</B>\r
+<B> * been modified, and then a content must be emitted by SSIResource.</B>\r
+<B> * @param request the original HTTP request</B>\r
+<B> * @param parameters The parameters for this command</B>\r
+<B> * @param variables The global variables for the parse </B>\r
+<B> * @return a Reply with the output from the command */ </B>\r
+\r
+ public <A HREF="../api/w3c.jigsaw.http.Reply.html">Reply</A> execute(<A HREF="../api/w3c.jigsaw.ssi.SSIResource.html">SSIResource</A> resource,\r
+ <A HREF="../api/w3c.jigsaw.http.Request.html">Request</A> request,\r
+ <A HREF="../api/w3c.util.ArrayDictionary.html">ArrayDictionary</A> parameters,\r
+ Dictionary variables) \r
+ { <B></B>\r
+<B> // Empty reply </B>\r
+ return resource.createCommandReply(request, HTTP.OK);\r
+ }\r
+\r
+ protected boolean check(<A HREF="../api/w3c.jigsaw.ssi.CommandRegistry.html">CommandRegistry</A> registry,\r
+ <A HREF="../api/w3c.util.ArrayDictionary.html">ArrayDictionary</A> parameters,\r
+ Dictionary variables)\r
+ {\r
+ Object values[] = parameters.getMany(keys);\r
+ String name = (String) values[0];\r
+ String command = (String) values[1];\r
+ String var = (String) values[2];\r
+ String equals = (String) values[3];\r
+ \r
+ if ((command == null) || (var == null) || (equals == null))\r
+ return false;\r
+ Command cmd = registry.lookupCommand(command);\r
+ String value = cmd.getValue(variables,var);\r
+ <B>// here is the test</B>\r
+ return value.equals(equals);\r
+ }\r
+<B></B>\r
+<B> /**</B>\r
+<B> * Give the next position in the structure witch</B>\r
+<B> * store the SSIResource.</B>\r
+<B> */ </B>\r
+ public int jumpTo(<A HREF="../api/w3c.jigsaw.ssi.SSIResource.html">SSIResource</A> resource,\r
+ <A HREF="../api/w3c.jigsaw.ssi.CommandRegistry.html">CommandRegistry</A> registry,\r
+ <A HREF="../api/w3c.util.ArrayDictionary.html">ArrayDictionary</A> parameters,\r
+ Dictionary variables)\r
+ throws ControlCommandException\r
+ {\r
+ Object values[] = parameters.getMany(keys);\r
+ String name = (String) values[0];\r
+ if (name != null) {\r
+ if (check(registry,parameters,variables))\r
+ return getPosition(resource.getURLPath()+":"+name)+1;\r
+ try {\r
+ return (ElseCommand.getPosition(resource.getURLPath()+":"+name)+1);\r
+ } catch (ControlCommandException ex) {\r
+ return (EndifCommand.getPosition(resource.getURLPath()+":"+name)+1);\r
+ }\r
+ }\r
+ throw new ControlCommandException(NAME,"name not initialized."); \r
+ }\r
+<B></B>\r
+<B> /** </B>\r
+<B> * Returns the name of this command. <EM>(Case sensitivity is up to</EM></B>\r
+<B><EM> * the <CODE>lookupCommand</CODE> method in the command registry.)</EM></B>\r
+<B> * @return the name of the command</B>\r
+<B> * @see w3c.jigsaw.ssi.CommandRegistry#lookupCommand</B>\r
+<B> */ </B>\r
+ public String getName() {\r
+ return NAME;\r
+ }\r
+\r
+}\r
+\r
+</PRE>\r
+<P>\r
+With this in mind, let's implement a useful extension of SSIResource.\r
+<H2>\r
+ A server statistics page with SSIResource\r
+</H2>\r
+<P>\r
+There is an existing Jigsaw resource\r
+(<A HREF="../api/w3c.jigsaw.status.Statistics.html">w3c.jigsaw.status.Statistics</A>)\r
+that is used to display the internal statistics of the server. In what follows,\r
+we will mimic the functionality of this resource with an SSI command. There\r
+is an object that supplies all these statistics for us; its class is\r
+<A HREF="../api/w3c.jigsaw.http.httpdStatistics.html">w3c.jigsaw.http.httpdStatistics</A>\r
+and it can be obtained from the server. Our SSI command will query this object\r
+and emit the values. We'd like to be able to use it like this: <!--#stat\r
+data=<type> -->, where <type> specifies the particular statistic\r
+that is going to be inserted, and is one of:\r
+<UL>\r
+ <LI>\r
+ <TT>serverLoad</TT>\r
+ <LI>\r
+ <TT>freeThreads</TT>\r
+ <LI>\r
+ <TT>idleThreads</TT>\r
+ <LI>\r
+ <TT>totalThreads</TT>\r
+ <LI>\r
+ <TT>hitCount</TT>\r
+ <LI>\r
+ <TT>meanReqTime</TT>\r
+ <LI>\r
+ <TT>maxReqTime</TT>\r
+ <LI>\r
+ <TT>maxReqURL</TT>\r
+ <LI>\r
+ <TT>minReqTime</TT>\r
+ <LI>\r
+ <TT>minReqUrl</TT>\r
+ <LI>\r
+ <TT>emittedBytes</TT>\r
+</UL>\r
+<P>\r
+Each of them will correspond to one of the methods in httpdStatistics.\r
+<H3>\r
+ Writing the <TT>stat</TT> command\r
+</H3>\r
+<P>\r
+This command can be written in a very straightforward manner. All we have\r
+to do is:\r
+<OL>\r
+ <LI>\r
+ Obtain the httpdStatistics instance from the server.\r
+ <LI>\r
+ Call in it the appropriate method, according to the data parameter.\r
+ <LI>\r
+ Return a reply with this value as content.\r
+</OL>\r
+<P>\r
+This translates to the following java class, which will be called\r
+StatCommand:<BR>\r
+<TABLE CELLPADDING=2 >\r
+ <CAPTION ALIGN="Bottom">\r
+ </CAPTION>\r
+ <TBODY>\r
+ <TR>\r
+ <TD><PRE>package w3c.jigsaw.tutorials ;\r
+\r
+import java.util.* ;\r
+\r
+import <A HREF="../api/Package-w3c.jigsaw.http.html">w3c.jigsaw.http.*</A> ;\r
+import<A HREF="../api/w3c.www.http.HTTP.html"> w3c.www.http.HTTP</A> ;\r
+import <A HREF="../api/Package-w3c.jigsaw.ssi.html">w3c.jigsaw.ssi.*</A> ;\r
+import <A HREF="../api/Package-w3c.util.html">w3c.util.*</A> ;\r
+\r
+public class StatCommand implements <A HREF="../api/w3c.jigsaw.ssi.Command.html">Command</A> {\r
+ private static final String NAME = "stat" ;\r
+\r
+ public final String getName()\r
+ {\r
+ return NAME ;\r
+ }\r
+\r
+ <B>// Unuseful here</B>\r
+ public String getValue(Dictionary variables, String variable) {\r
+ return null;\r
+ }\r
+ \r
+ public <A HREF="../api/w3c.jigsaw.http.Reply.html">Reply</A> execute(<A HREF="../api/w3c.jigsaw.ssi.SSIResource.html">SSIResource</A> resource,\r
+ <A HREF="../api/w3c.jigsaw.http.Request.html">Request</A> request,\r
+ <A HREF="../api/w3c.util.ArrayDictionary.html">ArrayDictionary</A> parameters,\r
+ Dictionary variables)\r
+ {\r
+ <B>// Obtain the statistics from the server</B>\r
+ <A HREF="../api/w3c.jigsaw.http.httpdStatistics.html">httpdStatistics</A> stats = resource.getServer().getStatistics() ;\r
+\r
+ <B>// Get the parameter specifying the kind of statistic to emit.</B>\r
+ String data = (String) parameters.get("data") ;\r
+\r
+ <B>// If the parameter is not supplied, do nothing</B>\r
+ if(data == null)\r
+ return null ;\r
+\r
+ <B>// Otherwise, compare it against the possible different keywords</B>\r
+<B> // (Since there are no "pointers to methods", this is the simplest way it</B>\r
+<B> // can be written)</B>\r
+ long result = -1 ;\r
+ String urlResult = null ;\r
+ if(data.equalsIgnoreCase("serverload")) {\r
+ result = stats.getServerLoad() ;\r
+ } else if(data.equalsIgnoreCase("freethreads")) {\r
+ result = stats.getFreeThreadCount() ;\r
+ } else if(data.equalsIgnoreCase("idlethreads")) {\r
+ result = stats.getIdleThreadCount() ;\r
+ } else if(data.equalsIgnoreCase("totalthreads")) {\r
+ result = stats.getTotalThreadCount() ;\r
+ } else if(data.equalsIgnoreCase("hitcount")) {\r
+ result = stats.getHitCount() ;\r
+ } else if(data.equalsIgnoreCase("meanreqtime")) {\r
+ result = stats.getMeanRequestTime() ;\r
+ } else if(data.equalsIgnoreCase("maxreqtime")) {\r
+ result = stats.getMaxRequestTime() ;\r
+ } else if(data.equalsIgnoreCase("maxrequrl")) {\r
+ urlResult = stats.getMaxRequestURL().toExternalForm() ;\r
+ } else if(data.equalsIgnoreCase("minreqtime")) {\r
+ result = stats.getMinRequestTime() ;\r
+ } else if(data.equalsIgnoreCase("minrequrl")) {\r
+ urlResult = stats.getMinRequestURL().toExternalForm() ;\r
+ } else if(data.equalsIgnoreCase("emittedbytes")) {\r
+ result = stats.getEmittedBytes() ;\r
+ } else return null ;\r
+\r
+ <B>// Make a reply with the datum and return it</B>\r
+ <A HREF="../api/w3c.jigsaw.http.Reply.html">Reply</A> reply = resource.createCommandReply(request, HTTP.OK) ;\r
+ reply.setContent( urlResult == null\r
+ ? Long.toString(result)\r
+ : urlResult ) ;\r
+ return reply ;\r
+ }\r
+}\r
+</PRE>\r
+ </TD>\r
+ </TR>\r
+ </TBODY>\r
+ <TBODY>\r
+ <TR>\r
+ <TD>Listing 1: The command class</TD>\r
+ </TR>\r
+ </TBODY>\r
+</TABLE>\r
+<P>\r
+The <A HREF="../api/w3c.jigsaw.ssi.Command.html">Command</A> interface defines\r
+three methods. The <TT>getName</TT> method simply returns a String with the\r
+name of the command. The getValue method returns a value relative to the\r
+given parameter (Used by control commands).The <TT>execute</TT> method is\r
+the one that does the work. This method can be thought of as the <TT>get</TT>\r
+method in a resource: it takes, among other things, a\r
+<A HREF="../api/w3c.jigsaw.http.Request.html">Request</A> object, and it\r
+produces a <A HREF="../api/w3c.jigsaw.http.Reply.html">Reply</A> object.\r
+The SSIResource will insert the contents of the replies of each of the commands\r
+(<I>partial replies</I>) into the main, global, content, and it will also\r
+merge the relevant headers of the partial replies into the headers of the\r
+global reply. Besides taking a request, the <TT>execute</TT> method takes\r
+these arguments as well:\r
+<DL>\r
+ <DT>\r
+ <TT><A HREF="../api/w3c.jigsaw.ssi.SSIResource.html">w3c.jigsaw.ssi.SSIResource</A>\r
+ resource</TT>\r
+ <DD>\r
+ This is the SSIResource that is executing the command.\r
+ <DT>\r
+ <TT><A HREF="../api/w3c.util.ArrayDictionary.html">w3c.util.ArrayDictionary</A>\r
+ parameters</TT>\r
+ <DD>\r
+ The parameters that the command is called with. An ArrayDictionary is a subclass\r
+ of java.util.Dictionary. The parameters are stored as strings with the parameter\r
+ names as keys.\r
+ <DT>\r
+ <TT>java.util.Dictionary variables</TT>\r
+ <DD>\r
+ The current set of variables. A command may change its behavior according\r
+ to the values of these variables, and it can also modify the variables. The\r
+ meaning of the variables is\r
+ <A HREF="../Reference/w3c.jigsaw.ssi.SSIResource.html#very-global-variables">almost</A>\r
+ completely command- and command registry-dependent. The\r
+ <A HREF="../api/w3c.jigsaw.ssi.DefaultCommandRegistry.html">DefaultCommandRegistry</A>\r
+ uses the variables to keep state across different command calls in the same\r
+ document, such as the current date and time formats.\r
+</DL>\r
+<P>\r
+The <TT>execute</TT> method can also return <B><TT>null</TT></B>, which is\r
+interpreted as the absence of output. There are some subtle differences between\r
+the <TT>execute</TT> method and a regular resource <TT>get</TT> method. In\r
+particular, care must be taken if dealing with conditional requests. This\r
+example is simple enough that this is not a concern.\r
+<P>\r
+Now that the command itself is finished, we need to make it part of a command\r
+registry, so that it can be actually used in documents.\r
+<H3>\r
+ Writing a command registry\r
+</H3>\r
+<P>\r
+Since we'd like to be able to use the "standard" SSI commands in adition\r
+to our brand-new <TT>stat</TT> command, it's not a bad idea to make our new\r
+registry a subclass DefaultCommandRegistry. The way to do this is very\r
+straightforward: <BR>\r
+<TABLE CELLPADDING=2 >\r
+ <CAPTION ALIGN="Bottom">\r
+ </CAPTION>\r
+ <TBODY>\r
+ <TR>\r
+ <TD><PRE>package w3c.jigsaw.tutorials ;\r
+\r
+import <A HREF="../api/Package-w3c.jigsaw.ssi.html">w3c.jigsaw.ssi.*</A> ;\r
+\r
+public class MyCommandRegistry extends <A HREF="../api/w3c.jigsaw.ssi.DefaultCommandRegistry">DefaultCommandRegistry</A> { \r
+ public MyCommandRegistry()\r
+ {\r
+ registerCommand(new StatCommand()) ;\r
+ }\r
+}\r
+</PRE>\r
+ </TD>\r
+ </TR>\r
+ </TBODY>\r
+ <TBODY>\r
+ <TR>\r
+ <TD>Listing 2: The command registry class</TD>\r
+ </TR>\r
+ </TBODY>\r
+</TABLE>\r
+<P>\r
+The constructor simply calls the <TT>registerCommand</TT> method (defined\r
+in\r
+<A HREF="../api/w3c.jigsaw.ssi.BasicCommandRegistry.java">BasicCommandRegistry</A>),\r
+with a new instance of the command that we're adding.\r
+<P>\r
+We're now ready to use this command in a future document.\r
+<H3>\r
+ Using the new registry\r
+</H3>\r
+<P>\r
+One way of using the newly-created command registry is to change the\r
+<TT>registryClass</TT> attribute defined for the <TT>.shtml</TT> extension\r
+to "<TT>w3c.jigsaw.tutorials.MyCommandRegistry</TT>". After doing that, Jigsaw\r
+will use the new registry when indexing new files with the <TT>.shtml</TT>\r
+extension (or reindexing old files). Then we can create a file that makes\r
+use of the new command, and place it in a Jigsaw-accesible directory. For\r
+example, we could do this:<BR>\r
+<TABLE CELLPADDING=2 >\r
+ <CAPTION ALIGN="Bottom">\r
+ </CAPTION>\r
+ <TBODY>\r
+ <TR>\r
+ <TD><PRE><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">\r
+<html>\r
+ <head>\r
+ <meta http-equiv="Refresh" content="5">\r
+ <title>Server Statistics</title>\r
+ </head>\r
+ <body>\r
+ <ul>\r
+ <li>hits: <B><!--#stat data=hitCount --></B>\r
+ <li>bytes: <B><!--#stat data=emittedBytes --></B>\r
+ </ul>\r
+ <p>Request processing times:\r
+ <table border>\r
+ <tr>\r
+ <th align="center"> min\r
+ <th align="center"> avg\r
+ <th align="center"> max\r
+ </tr>\r
+ <tr>\r
+ <th align="center"> <B><!--#stat data=minReqTime --></B>\r
+ <th align="center"> <B><!--#stat data=meanReqTime --></B>\r
+ <th align="center"> <B><!--#stat data=maxReqTime --></B>\r
+ </tr> \r
+ </table>\r
+ <p>Thread counts:\r
+ <table border>\r
+ <tr>\r
+ <th align="center"> free\r
+ <th align="center"> idle\r
+ <th align="center"> total\r
+ </tr>\r
+ <tr>\r
+ <th align="center"> <B><!--#stat data=freeThreads --></B>\r
+ <th align="center"> <B><!--#stat data=idleThreads --></B>\r
+ <th align="center"> <B><!--#stat data=totalThreads --></B>\r
+ </table>\r
+ <p>Current load: <B><!--#stat data=serverLoad --></B>\r
+ </body>\r
+</html>\r
+</PRE>\r
+ </TD>\r
+ </TR>\r
+ </TBODY>\r
+ <TBODY>\r
+ <TR>\r
+ <TD>Listing 3: A possible use of the new command</TD>\r
+ </TR>\r
+ </TBODY>\r
+</TABLE>\r
+<P>\r
+The above document will produce exactly the same output that the\r
+<A HREF="../api/w3c.jigsaw.status.Statistics.html">Statistics</A> resource\r
+would emit.\r
+<H2>\r
+ What have we gained?\r
+</H2>\r
+<P>\r
+At this point we can compare two different approaches to generating HTML\r
+dynamically. The first one involves writing a new, specialized, resource.\r
+The approach illustrated in this tutorial consists of writing an SSI\r
+command and serving the document with the SSIResource. Doing it this way\r
+has these advantages:\r
+<UL>\r
+ <LI>\r
+ The structure of the served document is not hard-coded. Instead, the Java\r
+ code only expresses the minimal needed functionality (in this case, that\r
+ of emitting server statistics), while the markup of the document can be modified\r
+ without having to recompile.\r
+ <LI>\r
+ Different functionalities can be mixed in a more orthogonal way. That is,\r
+ it is inefficient at best to create a resource that combines the function\r
+ of two existing resources. By distilling the functionality into SSI commands,\r
+ it is straightforward to make a registry that has all the needed commands.\r
+</UL>\r
+<P>\r
+One disadvantage of the SSI approach is the extra overhead incurred at serve-time\r
+of constructing the content from the pieces supplied by the commands. The\r
+SSIResource tries to avoid this overhead as much as possible. The most important\r
+optimization in this respect is the fact that the parsing of the document\r
+(i.e., scanning the text for commands, and reading the parameters) is done\r
+only when the file is modified. Even then, each command needs to check its\r
+parameters, which <I>does</I> add to serve-time overhead.\r
+<P>\r
+ <HR>\r
+<P>\r
+<I><A HREF="mailto:anto@w3.org">Antonio Ramírez<BR>\r
+</A>$Id: SSIResource.html,v 1.1 2010/06/15 12:28:36 smhuang Exp $</I>\r
+</BODY></HTML>\r