Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / jigsaw / src / org / w3c / jigsaw / http / httpd.java
diff --git a/JMCR-Stable/real-world application/jigsaw/src/org/w3c/jigsaw/http/httpd.java b/JMCR-Stable/real-world application/jigsaw/src/org/w3c/jigsaw/http/httpd.java
new file mode 100644 (file)
index 0000000..8d6b029
--- /dev/null
@@ -0,0 +1,2348 @@
+// httpd.java\r
+// $Id: httpd.java,v 1.2 2010/06/15 17:52:58 smhuang Exp $\r
+// (c) COPYRIGHT MIT and INRIA, 1996.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+\r
+package org.w3c.jigsaw.http ;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.PrintStream;\r
+import java.util.Date;\r
+import java.util.Enumeration;\r
+import java.util.Hashtable;\r
+import java.util.Properties;\r
+import java.util.Vector;\r
+import java.net.InetAddress;\r
+import java.net.MalformedURLException;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+import java.net.URL;\r
+import java.net.UnknownHostException;\r
+\r
+import org.w3c.tools.resources.AbstractContainer;\r
+import org.w3c.tools.resources.DummyResourceReference;\r
+import org.w3c.tools.resources.FilterInterface;\r
+import org.w3c.tools.resources.FramedResource;\r
+import org.w3c.tools.resources.InvalidResourceException;\r
+import org.w3c.tools.resources.LookupResult;\r
+import org.w3c.tools.resources.LookupState;\r
+import org.w3c.tools.resources.ProtocolException;\r
+import org.w3c.tools.resources.ReplyInterface;\r
+import org.w3c.tools.resources.RequestInterface;\r
+import org.w3c.tools.resources.Resource;\r
+import org.w3c.tools.resources.ResourceContext;\r
+import org.w3c.tools.resources.ResourceException;\r
+import org.w3c.tools.resources.ResourceFilter;\r
+import org.w3c.tools.resources.ResourceReference;\r
+import org.w3c.tools.resources.ResourceSpace;\r
+import org.w3c.tools.resources.ServerInterface;\r
+import org.w3c.tools.resources.store.ResourceStoreManager;\r
+import org.w3c.tools.resources.indexer.IndexerModule;\r
+import org.w3c.tools.resources.indexer.IndexersCatalog;\r
+import org.w3c.tools.resources.indexer.ResourceIndexer;\r
+import org.w3c.tools.timers.EventManager;\r
+import org.w3c.jigsaw.auth.RealmsCatalog;\r
+import org.w3c.jigsaw.resources.CheckpointResource;\r
+import org.w3c.jigsaw.daemon.ServerHandler;\r
+import org.w3c.jigsaw.daemon.ServerHandlerInitException;\r
+import org.w3c.jigsaw.daemon.ServerHandlerManager;\r
+import org.w3c.www.http.HTTP;\r
+import org.w3c.www.http.HeaderValue;\r
+import org.w3c.www.http.HttpEntityMessage;\r
+import org.w3c.www.http.HttpFactory;\r
+import org.w3c.www.http.HttpMessage;\r
+import org.w3c.www.http.HttpReplyMessage;\r
+import org.w3c.www.http.HttpRequestMessage;\r
+import org.w3c.www.http.HttpTokenList;\r
+import org.w3c.jigsaw.config.PropertySet;\r
+import org.w3c.util.IO;\r
+import org.w3c.util.ObservableProperties;\r
+import org.w3c.util.PropertyMonitoring;\r
+import org.w3c.util.Status;\r
+import org.w3c.tools.resources.ProtocolException;\r
+import org.w3c.tools.resources.upgrade.Upgrader;\r
+import org.w3c.www.mime.MimeParserFactory;\r
+import org.w3c.www.mime.MimeType;\r
+\r
+/**\r
+ * <p>The server main class. This class can be used either through its\r
+ * main method, to run a full httpd server, or simply by importing it\r
+ * into your app. This latter possibility allows you to export some of\r
+ * your application state through http.\r
+ *\r
+ * <p>The server itself uses this to report about memory consumption,\r
+ * running threads, etc.\r
+ */\r
+\r
+public class httpd implements \r
+      ServerInterface, Runnable, PropertyMonitoring, Cloneable, Status\r
+{\r
+    /**\r
+     * The current displayed version of Jigsaw.\r
+     */\r
+    public static final String version = "2.2.6";\r
+    /**\r
+     * The current internal version counter of Jigsaw.\r
+     * This counter is bumped anytime the configuration needs upgrade.\r
+     */\r
+    public static final int verscount = 4;\r
+\r
+    /**\r
+     * debug flag\r
+     */\r
+    public static final boolean debug = true;\r
+\r
+    public static final\r
+    String VERSCOUNT_P = "org.w3c.jigsaw.version.counter";\r
+    /**\r
+     * Name of the server software property.\r
+     * The server software is the string that gets emited by Jigsaw \r
+     * on each reply, to tell the client what server emited the reply.\r
+     * <p>This property defaults to <strong>Jigsaw/1.0a</strong>.\r
+     */\r
+    public static final \r
+    String SERVER_SOFTWARE_P = "org.w3c.jigsaw.server";\r
+    /**\r
+     * If the Host property is not set (see below), you can select if you\r
+     * want to use FQDN (broken on some jdk implementation) or just the IP\r
+     * address as the default host name, it usually defaults to "false"\r
+     * means, use FQDN.\r
+     */\r
+    public static final String DEFHOSTIP_P = "org.w3c.jigsaw.defhostip" ;\r
+    /**\r
+     * Name of the server host property.\r
+     * The host property should be set to the name of the host running\r
+     * this server.\r
+     * <p>This property defaults to the local host name, although if you want\r
+     * directory listing to work propertly, you might need to provide the \r
+     * full host name (including its domain).\r
+     */\r
+    public static final String HOST_P            = "org.w3c.jigsaw.host" ;\r
+    /**\r
+     * Name of the property giving the server root directory.\r
+     * <p>The server root directory is used to deduce a bunch of defaults\r
+     * properties, when they don't have any specific values.\r
+     * <p>This property has no defaults.\r
+     */\r
+    public static final String ROOT_P = "org.w3c.jigsaw.root" ;\r
+    /**\r
+     * Name of the property giving the server's config directory.\r
+     */\r
+    public static final String CONFIG_P = "org.w3c.jigsaw.config";\r
+    /**\r
+     * Name of the property giving the server space directory.\r
+     * The server space directory should contain an index file, built\r
+     * with the indexer.\r
+     * <p>This property defaults to <org.w3c.jigsaw.root>/WWW.\r
+     */\r
+    public static final String SPACE_P            = "org.w3c.jigsaw.space" ;\r
+    /**\r
+     * Name of the server port property.\r
+     * At initializatiojn time, the server will bind its accepting socket\r
+     * to the host its runs on, and to the provided port.\r
+     * <p>This property defaults to <code>8888</code>.\r
+     */\r
+    public static final String PORT_P            = "org.w3c.jigsaw.port" ;\r
+    /**\r
+     * Name of the server's trace property.\r
+     * When set to true, the server will emit some traces indicating \r
+     * its current state by using the logger <em>trace</em> methods.\r
+     * This property should be set to <string>true</strong> if you want\r
+     * clients to emit traces.\r
+     * <p>This property defaults to <strong>false</strong>.\r
+     */\r
+    public static final String TRACE_P           = "org.w3c.jigsaw.trace" ;\r
+    /**\r
+     * Name of the server's keep alive flag.\r
+     * This property is used to determine wether this server should keep\r
+     * its connection alive. Keeping connection alive requires this flag\r
+     * to set to <strong>true</strong>, and clients to be compliant to the\r
+     * keep alive feature as described in HTTP/1.1 specification.\r
+     * <p>This property defaults to <strong>true</strong>.\r
+     */\r
+    public static final String KEEP_ALIVE_P      = "org.w3c.jigsaw.keepAlive" ;\r
+    /**\r
+     * Name of the server's connection time out property.\r
+     * This property gives, in milliseconds, the timeout to use for\r
+     * connections that remains idel, waiting for an incoming request.\r
+     * <p>This property defaults to <code>10000</code> milliseconds.\r
+     */\r
+    public static final String KEEP_TIMEOUT_P=\r
+                                           "org.w3c.jigsaw.keep_alive.timeout";\r
+    /**\r
+     * Name of the server's request time out property.\r
+     * The request time out property value indicates, in milliseconds, the\r
+     * allowed duration of a request. Any request whose duration exceeds\r
+     * this time out value will be aborted.\r
+     * <p>This property defaults to <code>60000</code>.\r
+     */\r
+    public static final String REQUEST_TIMEOUT_P=\r
+                                              "org.w3c.jigsaw.request.timeout";\r
+    /**\r
+     * Name of the client thread priority property.\r
+     * Every client threads will run at the given priority, which should be\r
+     * in the range of valid threads priority.\r
+     * <p>This property defaults to <code>Thread.NORM_PRIORITY</code>.\r
+     */\r
+    public static final String CLIENT_PRIORITY_P=\r
+                                              "org.w3c.jigsaw.client.priority";\r
+    /**\r
+     * Nam eof the property giving the client output buffer size.\r
+     * Each clients, when not using a shuffler, has to allocate its own\r
+     * output buffer, Output buffer size may increase/decrease significantly\r
+     * the Jigsaw performances, so change it with care.\r
+     * <p>This property defaults to <code>8192</code>.\r
+     */\r
+    public static final String CLIENT_BUFSIZE_P=\r
+                                               "org.w3c.jigsaw.client.bufsize";\r
+    /**\r
+   * Name of the property indicating wether client should be debuged.\r
+   * When debuged, clients emit some traces, through the server logger\r
+   * about their current state.\r
+   * <p>This property defaults to <strong>false</strong>.\r
+   */\r
+    public static final String CLIENT_DEBUG_P="org.w3c.jigsaw.client.debug" ;\r
+    /**\r
+     * Name of  property that indicates if some security manager is required.\r
+     * You usually don't want to run a security manager for the server, \r
+     * except in the unlikely (right now) case that you want the server to\r
+     * be able to host agents.\r
+     * <p>This property defaults to <string>false</strong>.\r
+     */\r
+    public static final String USE_SM_P = "org.w3c.http.useSecurityManager" ;\r
+    /**\r
+     * Name of property indicating the logger class to use.\r
+     * The Jigsaw server allows you to implement your own logger. The only\r
+     * logger provided with the core server is the \r
+     * <code>org.w3c.jigsaw.core.CommonLogger</code>, which implements the\r
+     * common log format.\r
+     * <p>Property defaults to <code>org.w3c.jigsaw.core.CommonLogger</code>\r
+     */\r
+    public static final String LOGGER_P = "org.w3c.jigsaw.logger" ;\r
+    /**\r
+     * Name of property indicating the "lenient" mode of HTTP parsing.\r
+     * <p>Property defaults to <code>true</code>\r
+     */\r
+    public static final String LENIENT_P = "org.w3c.jigsaw.http.lenient" ;\r
+    /**\r
+     * Name of the property indicating the client factory class.\r
+     */\r
+    public static final String CLIENT_FACTORY_P = \r
+       "org.w3c.jigsaw.http.ClientFactory";\r
+    /**\r
+     * Name of the property giving the shuffler path.\r
+     * This property should be set if you are to use the shuffler. The \r
+     * data shuffler is an external process to whiuch Jigsaw delegates \r
+     * the task of writing back document content to clients. Use this\r
+     * when you think your server isn't fast enough.\r
+     * <p>This should be an absloute path.\r
+     * <p>This property has no defaults.\r
+     */\r
+    public static final String SHUFFLER_PATH_P="org.w3c.jigsaw.shuffler.path";\r
+\r
+    /**\r
+     * Name of the property giving the name of the root resource.\r
+     * Upon startup, or restart, the server will look in its root store\r
+     * a resource whose name is given by this resource, and install it as\r
+     * its root resource.\r
+     * <p>This property defaults to <code>root</code>.\r
+     */\r
+    public static final String ROOT_NAME_P = "org.w3c.jigsaw.root.name" ;\r
+    public static final String ROOT_CLASS_P = "org.w3c.jigsaw.root.class" ;\r
+\r
+    /**\r
+     * Max number of store loaded in memory.\r
+     */ \r
+    public static final String MAX_LOADED_STORE_P="org.w3c.jigsaw.loadedstore";\r
+    public static final int MAX_LOADED_STORE = 128;\r
+    int max_loaded_store = -1;\r
+\r
+    /**\r
+     * Max number of store loaded in memory.\r
+     */ \r
+    public static final String STORE_SIZE_LIMIT_P="org.w3c.jigsaw.storesize";\r
+    public static final int STORE_SIZE_LIMIT = -1;\r
+    int store_size_limit = -1;\r
+    \r
+    /**\r
+     * Name of the property giving the path of the property file.\r
+     * this should be used internally (for restart) only.\r
+     * <p>This property defaults to <code>config/httpd.props</code>.\r
+     */\r
+    public static final String PROPS_P = "org.w3c.jigsaw.propfile" ;\r
+    /**\r
+     * Name of the property indicating if the file-system is case sensitive.\r
+     * This property determines wether Jigsaw will list all files to check \r
+     * for case sensitivity, before creating a resource for that file.\r
+     * <p>For obvious security reasons, this property defaults to \r
+     * <strong>true</strong>.\r
+     */\r
+    public static final \r
+    String FS_SENSITIVITY = "org.w3c.jigsaw.checkSensitivity";\r
+    /**\r
+     * Name of the property indicating the URL of Jigsaw's help.\r
+     * This URL should point to the URL path of Jigsaw's documentation\r
+     * as served by that server.\r
+     */\r
+    public static String DOCURL_P = "org.w3c.jigsaw.docurl";\r
+\r
+    /**\r
+     * Name of the property indicating the startup classes to load\r
+     */\r
+    public static String STARTUP_P = "org.w3c.jigsaw.startup";\r
+\r
+    /**\r
+     * Name of the property indicating the trash directory.\r
+     */\r
+    public static String TRASHDIR_P = "org.w3c.jigsaw.trashdir";\r
+\r
+    /**\r
+     * Name of the property indicating the URL of Jigsaw's chekpointer.\r
+     */\r
+    public static \r
+    String CHECKURL_P = "org.w3c.jigsaw.checkpointer";\r
+\r
+    /**\r
+     * Name of the property indicating the public methods allowed on that \r
+     * server.\r
+     * This property should provide a <code>|</code> separated list of\r
+     * methods available on that server.\r
+     * <p>This property defaults to: <strong>GET | HEAD | PUT | POST \r
+     * | OPTIONS | DELETE | LINK | UNLINK | TRACE</code>.\r
+     */\r
+    public static \r
+    String PUBLIC_P = "org.w3c.jigsaw.publicMethods";\r
+    /**\r
+     * Name of the property that indicates the root resource for edit.\r
+     * The edit root resource is the one that will show up by default\r
+     * when accessing the admin server from JigAdmin.\r
+     */\r
+    public static \r
+    String EDIT_ROOT_P = "org.w3c.jigsaw.edit.root";\r
+\r
+    /**\r
+     * Name of the serializer class used to store resources.\r
+     */\r
+    public static String SERIALIZER_CLASS_P = "org.w3c.jigsaw.serializer";\r
+\r
+    /**\r
+     * UNIX - Name of the property that indicates the server user.\r
+     * When set, the server will try to turn itself to the given user name\r
+     * after initialization. If this fail, the server will abort.\r
+     * <p>This property has no default value.\r
+     */\r
+    public static\r
+    String SERVER_USER_P = "org.w3c.jigsaw.unix.user";\r
+    /**\r
+     * UNIX - Name of the property that indicates the server group.\r
+     * When set, the server will try to turn itself to the given group name\r
+     * after initialization. If this fail, the server will abort.\r
+     * <p>This property has no default value.\r
+     */\r
+    public static\r
+    String SERVER_GROUP_P = "org.w3c.jigsaw.unix.group";\r
+\r
+    /**\r
+     * Should we show the URL that triggered an error in the error message\r
+     * or not?\r
+     * Displaying it can lead to so-called "cross-scripting" hacks\r
+     */\r
+    public static\r
+    String DISPLAY_URL_ON_ERROR_P = "org.w3c.jigsaw.error.url";\r
+   \r
+    /**\r
+     * The list of currently running servers.\r
+     */\r
+    private static Hashtable servers = new Hashtable() ;\r
+\r
+    /* FIXME */ public Thread thread    = null ;\r
+\r
+    private String         software  = "Jigsaw/2.2.6";\r
+    private ServerSocket   socket    = null ;\r
+    private Logger         logger    = null ;\r
+    private Shuffler       shuffler  = null ;\r
+    public  EventManager   timer     = null ;\r
+    ClientFactory          factory   = null ;\r
+\r
+    // FIXME This is a temporary hack to take care of clones\r
+    protected int[] instances = {1};    // object containing the nb of clones\r
+\r
+    /**\r
+     * The (optional) server handler manager that created us.\r
+     */\r
+    private ServerHandlerManager shm = null;\r
+    /**\r
+     * The server identifier can be any String.\r
+     * This identifier is used by the configuration applets, to show all the \r
+     * running servers in the process, and to edit their properties, etc.\r
+     */\r
+    private String identifier = null ;\r
+\r
+    /**\r
+     * This server statistics object.\r
+     */\r
+    private httpdStatistics statistics = null ;\r
+    /**\r
+     * This server set of properties.\r
+     */\r
+    protected ObservableProperties props    = null ;\r
+    /** \r
+     * Should the server run the server in trace mode ?\r
+     */\r
+    private boolean tracep = false ;\r
+    /**\r
+     * Should the server try to keep connections alive ?\r
+     */\r
+    private boolean keep = true ; \r
+    /**\r
+     * What logger class should the server use to log accesses.\r
+     */\r
+    private String logger_class = null ;\r
+    /**\r
+     * Should we display URL on error?\r
+     */\r
+    private boolean uri_error = false;\r
+    /**\r
+     * Are we lenient in HTTP mode?\r
+     */\r
+    private boolean lenient = true;\r
+    /**\r
+     * What client factory class should the server use.\r
+     */\r
+    private String \r
+    factory_class = "org.w3c.jigsaw.http.socket.SocketClientFactory";\r
+    /**\r
+     * The coordinate of the shuffler, or <strong>null</strong> is none is to \r
+     * be used. \r
+     */\r
+    private String shuffler_path = null ;\r
+    /**\r
+     * The server's root directory.\r
+     */\r
+    private File root_dir = null ;\r
+    /**\r
+     * The directory containing the server exported documents.\r
+     */\r
+    private File space_dir = null ;\r
+    /**\r
+     * FIXME check\r
+     * The server host name.\r
+     */\r
+    protected String host = null ;\r
+    /**\r
+     * FIXME check\r
+     * The server port.\r
+     */\r
+    protected int port = 8001 ;\r
+    /**\r
+     * This server client debug flag.\r
+     */\r
+    private boolean client_debug = false ;\r
+    /**\r
+     * This server's request time slice, in milliseconds.\r
+     */\r
+    private int request_time_out = 1200000 ;\r
+    /**\r
+     * This server's connection allowed idle time in milliseconds.\r
+     */\r
+    private int connection_time_out = 1200000 ;\r
+    /**\r
+     * This server's client thread priority.\r
+     */\r
+    private int client_priority = Thread.NORM_PRIORITY ;\r
+    /**\r
+     * This server's clients buffer size.\r
+     */\r
+    private int client_bufsize = 4096 ;\r
+    /**\r
+     * Is the file system case-sensitive ?\r
+     */\r
+    private boolean sensitivity = true;\r
+    /**\r
+     * This server root entity.\r
+     */\r
+    public FramedResource root = null ;\r
+    /**\r
+     * the old root ResourceReference\r
+     */\r
+    private ResourceReference root_reference = null;\r
+    /**\r
+     * FIXME check for clones\r
+     * This server URL.\r
+     */\r
+    protected URL url = null ;\r
+    /**\r
+     * Finishing (killing) the server.\r
+     */\r
+    private boolean finishing = false ;\r
+    /**\r
+     * Finishing, but restart straight up.\r
+     */\r
+    private boolean restarting = false ;\r
+    /**\r
+     * The indexer attached to this server.\r
+     */\r
+    private ResourceIndexer indexer = null ;\r
+    /**\r
+     * The realm catalog\r
+     */\r
+    private RealmsCatalog realms = null ;\r
+    /**\r
+     * The resource store manager for this server.\r
+     */\r
+    private ResourceStoreManager manager = null ;\r
+    /**\r
+     * The root resource's identifier.\r
+     */\r
+    private String root_name = null ;\r
+    private String root_class = null;\r
+    /**\r
+     * The full URL of Jigsaw's documentation as served by that server.\r
+     */\r
+    private String docurl = null;\r
+\r
+    /**\r
+     * The trash directory\r
+     */\r
+    private String trashdir = null;\r
+\r
+    /**\r
+     * The full URL of Jigsaw's chekpointer.\r
+     */\r
+    private String checkurl = null;\r
+\r
+    /**\r
+     * The list of public methods for that server.\r
+     */\r
+    private  String publicMethods[] = { "GET"\r
+                                       , "HEAD"\r
+                                       , "PUT"\r
+                                       , "POST"\r
+                                       , "LINK"\r
+                                       , "UNLINK"\r
+                                       , "DELETE"\r
+                                       , "OPTIONS" \r
+                                       , "TRACE"\r
+    } ;\r
+    /**\r
+     * The <code>Public</code> header value, computed out of the\r
+     * <code>publicMethods</code> variable.\r
+     */\r
+    private HttpTokenList publicHeader = null;\r
+    /**\r
+     * The edit root for that server.\r
+     */\r
+    private ResourceReference editroot = null;\r
+    /**\r
+     * the sets of properties of this server\r
+     */\r
+    private Vector         propSet     = new Vector(8);\r
+    /**\r
+     * the catalog of indexers of this server \r
+     */\r
+    private IndexersCatalog indexers = null;\r
+    /**\r
+     * the resource context of this server \r
+     */\r
+    private ResourceContext context = null;\r
+    /**\r
+     * the config resource of this server\r
+     */\r
+    private AbstractContainer configResource = null;\r
+    /**\r
+     * and its dummy resource reference\r
+     */\r
+    private ResourceReference rr_configResource = null;\r
+\r
+    // is this server a clone?\r
+    private boolean isAClone = false;\r
+    // our master server ID, if we are a clone\r
+    private String masterID = null;\r
+\r
+    /**\r
+     * The property monitoring implementation.\r
+     * @param name The name of the property that has changed.\r
+     * @return A boolean, <strong>true</strong> if the changed was taken into\r
+     *    account, <strong>false</strong> otherwise.\r
+     */\r
+    public boolean propertyChanged (String name) {\r
+       // Is this a property we are interested in ?\r
+       if ( name.equals(SERVER_SOFTWARE_P) ) {\r
+           software = props.getString(name, software);\r
+           return true;\r
+       } else if ( name.equals(TRACE_P) ) {\r
+           tracep = props.getBoolean(name, tracep) ;\r
+           errlog (name + " changed to " + tracep) ;\r
+           return true ;\r
+       } else if ( name.equals(LENIENT_P) ) {\r
+           lenient = props.getBoolean(name, lenient) ;\r
+           errlog (name + " changed to " + lenient) ;\r
+           return true ;\r
+       } else if ( name.equals(DISPLAY_URL_ON_ERROR_P) ) {\r
+           uri_error = props.getBoolean(name, uri_error);\r
+           errlog (name + " changed to " + uri_error);\r
+           return true;\r
+       } else if ( name.equals(KEEP_ALIVE_P) ) {\r
+           keep = props.getBoolean (name, keep) ;\r
+           errlog (name + " changed to " + keep) ;\r
+           return true ;\r
+       } else if ( name.equals(LOGGER_P) ) {\r
+           String tmp_logger_class;\r
+           Logger tmp_logger;\r
+           tmp_logger_class = props.getString(name, logger_class);\r
+           // for now the removal of the logger should be done by hand\r
+           if (!tmp_logger_class.equals(logger_class)) {\r
+               try {\r
+                   tmp_logger = (Logger) \r
+                       Class.forName(tmp_logger_class).newInstance() ;\r
+                   //Added by Jeff Huang\r
+                   //TODO: FIXIT\r
+               } catch (Exception ex) {\r
+                   errlog (name + " change failed (bad logger class)") ;\r
+                   return false;\r
+               }\r
+               synchronized (this) {\r
+                   if (logger != null) {\r
+                       logger.shutdown();\r
+                   }\r
+                   tmp_logger.initialize(this);\r
+                   logger = tmp_logger;\r
+                   logger_class = tmp_logger_class;\r
+               }\r
+           }\r
+           return true;\r
+       } else if ( name.equals(ROOT_NAME_P) ) {\r
+           String newname = props.getString(name, null);\r
+           if ( changeRoot(newname) != null ) {\r
+               errlog("new root resource ["+newname+"]");\r
+               return true;\r
+           } else {\r
+               errlog("failed to change root to ["+newname+"].");\r
+               return false;\r
+           }\r
+       } else if ( name.equals(SPACE_P) ) {\r
+           errlog (name + " change failed (server running)") ;\r
+           return false ;\r
+       } else if ( name.equals(HOST_P) ) {\r
+           errlog (name + " change failed (server running)") ;\r
+           return false ;\r
+       } else if ( name.equals(PORT_P) ) {\r
+           // we will restart the server\r
+           errlog (name + " switching port : " + props.getInteger(name, 80)) ;\r
+           int newport = props.getInteger(name, 80);\r
+           if (port != newport) {\r
+               int oldport = port;\r
+               port = newport;\r
+               checkpoint();\r
+               ServerSocket newsocket = null;\r
+               try {\r
+                   newsocket = factory.createServerSocket();\r
+                   socket.close();\r
+                   socket = newsocket;\r
+               } catch (Exception ex) {\r
+                   try { newsocket.close(); } catch (Exception e) {};\r
+                   port = oldport;\r
+                   // an error occured, return false\r
+                   return false;\r
+               }\r
+           }\r
+           return true ;\r
+       } else if ( name.equals(CLIENT_DEBUG_P) ) {\r
+           client_debug = props.getBoolean(name, client_debug) ;\r
+           errlog (name + " changed to " + client_debug) ;\r
+           return true ;\r
+       } else if ( name.equals(REQUEST_TIMEOUT_P) ){\r
+           request_time_out = props.getInteger(name, request_time_out);\r
+           errlog (name + " changed to " + request_time_out) ;\r
+           return true ;\r
+       } else if ( name.equals(KEEP_TIMEOUT_P) ) {\r
+           connection_time_out = props.getInteger(name\r
+                                                  , connection_time_out);\r
+           errlog (name + " changed to " + connection_time_out) ;\r
+           return true ;\r
+       } else if ( name.equals(CLIENT_PRIORITY_P) ){\r
+           client_priority = props.getInteger (name, client_priority) ;\r
+           errlog (name + " changed to " + client_priority) ;\r
+           return true ;\r
+       } else if ( name.equals(CLIENT_BUFSIZE_P) ){\r
+           client_bufsize = props.getInteger (name, client_bufsize) ;\r
+           errlog (name + " changed to " + client_bufsize) ;\r
+           return true ;\r
+       } else if ( name.equals(DOCURL_P) ) {\r
+           String propval = props.getString(name, docurl);\r
+           try {\r
+               URL u  = new URL(getURL(), propval);\r
+               docurl = u.toExternalForm();\r
+           } catch (Exception ex) {\r
+               return false;\r
+           }\r
+           return true;\r
+       } else if (name.equals(TRASHDIR_P)) {\r
+           trashdir = props.getString(name, trashdir);\r
+           File dir = new File(trashdir);\r
+           if (! dir.exists())\r
+               dir.mkdirs();\r
+           errlog(name + " changed to "+ trashdir);\r
+           return true;\r
+       } else if ( name.equals(CHECKURL_P) ) {\r
+           checkurl = props.getString(name, checkurl);\r
+           errlog(name + " changed to "+ checkurl);\r
+           return true;\r
+       } else if ( name.equals(PUBLIC_P) ) {\r
+           publicMethods = props.getStringArray(name, publicMethods);\r
+           publicHeader  = null;\r
+           return true;\r
+       } else if ( name.equals(SERVER_USER_P) ) {\r
+           String user = props.getString(SERVER_USER_P, null);\r
+           errlog("new user: "+user);\r
+           return false;\r
+       } else if (name.equals(SERVER_GROUP_P) ) {\r
+           String group = props.getString(SERVER_GROUP_P, null);\r
+           errlog("new group: "+group);\r
+           return false;\r
+       } else {\r
+           // We  don't care about this one\r
+           return true ;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Initialize this server indexer.\r
+     */\r
+    private void initializeIndexer() {\r
+       ResourceContext c  = getDefaultContext();\r
+       IndexersCatalog ic = getIndexersCatalog();\r
+       IndexerModule   m  = new IndexerModule(ic);\r
+       // Register the default indexer:\r
+       m.registerIndexer(c, "default");\r
+       // Register the indexer module:\r
+       c.registerModule(IndexerModule.NAME, m);\r
+    }\r
+\r
+    /**\r
+     * Initialize the resource store manager for this server.\r
+     */\r
+    private void initializeResourceSpace(String server_name,\r
+                                        String root_class,\r
+                                        String root_name,\r
+                                        String serializer,\r
+                                        int max_loaded_store) \r
+    {\r
+       Hashtable defs = new Hashtable(11) ;\r
+       defs.put("url", "/");\r
+       defs.put("directory", space_dir) ;\r
+       defs.put("context", getDefaultContext()) ;\r
+       \r
+       this.manager = new ResourceStoreManager(server_name,\r
+                                               this.getStoreDirectory(),\r
+                                               root_class,\r
+                                               root_name,\r
+                                               serializer,\r
+                                               max_loaded_store,\r
+                                               store_size_limit,\r
+                                               defs);\r
+    }\r
+\r
+    /**\r
+     * Lookup the root store for some resource.\r
+     * @param name The name of the resource to lookup in the root store.\r
+     * @return The loaded resource, or <strong>null</strong>.\r
+     */\r
+    public ResourceReference loadResource(String name) {\r
+       Hashtable defs = new Hashtable(11) ;\r
+       defs.put("url", "/"+name);\r
+       defs.put("directory", space_dir) ;\r
+       ResourceContext context = new ResourceContext(getDefaultContext());\r
+       defs.put("context", context) ;\r
+       ResourceReference rr = manager.loadRootResource(name, defs);\r
+       if (rr != null)\r
+           context.setResourceReference(rr);\r
+       return rr;\r
+    }\r
+\r
+    /**\r
+     * start the automatic checkpoint\r
+     */\r
+    public void startCheckpoint() {\r
+       if (checkurl == null) {\r
+           errlog("checkpointer URL unknown.");\r
+           checkpoint();\r
+           return;\r
+       }\r
+       try {\r
+           LookupState   ls  = new LookupState(checkurl);\r
+           LookupResult  lr  = new LookupResult(root.getResourceReference());\r
+           if (root.lookup(ls,lr)) {\r
+               ResourceReference  target = lr.getTarget();\r
+               if (target != null) {\r
+                   try {\r
+                       Resource res = target.lock();\r
+                       if (res instanceof CheckpointResource) {\r
+                           ((CheckpointResource) res).activate();\r
+                           errlog("Chekpointer started at: "+new Date()+".");\r
+                       } else {\r
+                           errlog("The chekpointer url ("+checkurl+\r
+                                  ") doesn't point to a CheckpointResource");\r
+                           checkpoint();\r
+                       }\r
+                   } catch (InvalidResourceException ex) {\r
+                       errlog("Invalid Checkpointer : "+ex.getMessage());\r
+                       checkpoint();\r
+                   } finally {\r
+                       target.unlock();\r
+                   }\r
+               } else {\r
+                   errlog("can't find Checkpointer");\r
+                   checkpoint();\r
+               }\r
+           } else {\r
+               errlog("Checkpointer: lookup fail");\r
+               checkpoint();\r
+           }\r
+       } catch (ProtocolException ex) {\r
+           errlog("Checkpointer : "+ex.getMessage());\r
+           checkpoint();\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Checkpoint all cached data, by saving them to disk.\r
+     */\r
+    public void checkpoint() {\r
+       manager.checkpoint();\r
+    }\r
+\r
+    /**\r
+     * Dynamically change the root resource for the server.\r
+     * This is kind a dangerous operation !\r
+     * @param name The name of the new root resource, to be found in the\r
+     * root resource store.\r
+     * @return The new installed root resource, or <strong>null</strong>\r
+     * if we couldn't load the given resource.\r
+     */\r
+    public synchronized ResourceReference loadRoot(String name) {\r
+       ResourceReference newroot = null;\r
+       String editRootName = props.getString(EDIT_ROOT_P, null);\r
+\r
+       // Restore the appropriate root resource:\r
+       Hashtable defs = new Hashtable(11) ;\r
+       defs.put("url", "/");\r
+       defs.put("directory", space_dir) ;\r
+       ResourceContext context = null;\r
+       if ((editRootName != null) && (! name.equals(editRootName))) {\r
+           if (editroot == null) {\r
+               Hashtable edefs = new Hashtable(11) ;\r
+               edefs.put("url", "/");\r
+               edefs.put("directory", space_dir) ;\r
+               ResourceContext econtext = \r
+                   new ResourceContext(getDefaultContext());\r
+               edefs.put("context", econtext) ;\r
+               editroot = manager.loadRootResource(editRootName, edefs);\r
+               if (editroot != null)\r
+                   econtext.setResourceReference(editroot);\r
+           }\r
+           context = new ResourceContext(editroot);\r
+       } else {\r
+           context = new ResourceContext(getDefaultContext());\r
+       }\r
+       defs.put("context", context) ;\r
+       ResourceReference rr = manager.loadRootResource(name, defs);\r
+       if (rr != null)\r
+           context.setResourceReference(rr);\r
+       return rr;\r
+    }\r
+\r
+    private synchronized FramedResource changeRoot(String name) {\r
+       ResourceReference newroot = loadRoot(name);\r
+       FramedResource oldroot = this.root;\r
+       String oldroot_name = this.root_name;\r
+       if ( newroot != null ) {\r
+           try {\r
+               this.root      = (FramedResource)newroot.lock();\r
+               this.root_name = name;\r
+               if (root_reference != null)\r
+                   root_reference.unlock();\r
+               root_reference = newroot;\r
+               return root;\r
+           } catch (InvalidResourceException ex) {\r
+               this.root = oldroot;\r
+               this.root_name = oldroot_name;\r
+               return null;\r
+           }\r
+       }\r
+       return null;\r
+    }\r
+\r
+    /**\r
+     * Initialize this server's root resource.\r
+     * @exception ServerHandlerInitException if unable to be initialized.\r
+     */\r
+\r
+    private void initializeRootResource() \r
+       throws ServerHandlerInitException\r
+    {\r
+       // Check for un-found root resource:\r
+       if ( changeRoot(root_name) == null ) {\r
+           String err = ("Unable to restore root resource ["+root_name+"]"\r
+                         +" from store (not found).");\r
+           throw new ServerHandlerInitException(err);\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Initialize the realms catalog for this server.\r
+     */\r
+\r
+    private void initializeRealmsCatalog() {\r
+       this.realms = \r
+           new RealmsCatalog(new ResourceContext(getDefaultContext()));\r
+    }\r
+\r
+    /**\r
+     * Initialize the server logger and the statistics object.\r
+     * @exception ServerHandlerInitException if unable to be initialized.\r
+     */\r
+\r
+    private void initializeLogger() \r
+       throws ServerHandlerInitException\r
+    {\r
+       if ( logger_class != null ) {\r
+           try {\r
+               logger = (Logger) Class.forName(logger_class).newInstance() ;\r
+               \r
+               //Added by Jeff Huang\r
+               logger = new org.w3c.jigsaw.http.CommonLogger();\r
+               logger.initialize (this) ;\r
+           } catch (Exception ex) {\r
+               String err = ("Unable to create logger of class ["+\r
+                             logger_class +"]"+\r
+                             "\r\ndetails: \r\n"+\r
+                             ex.getMessage());\r
+               throw new ServerHandlerInitException(err);\r
+           }\r
+       } else {\r
+           warning (getBanner() + ": no logger specified, not logging.");\r
+       }\r
+       // Initialize the statistics object:\r
+       statistics = new httpdStatistics(this) ;\r
+    }\r
+\r
+    /**\r
+     * Initialize the server socket, create a suitable client factory, start.\r
+     * This method creates the physicall listening socket, and instantiate\r
+     * an appropriate client factory for that socket. It then run the accept\r
+     * thread, ready to accept new incomming connections.\r
+     * @exception ServerHandlerInitException if unable to be initialized.\r
+     */\r
+\r
+    private void initializeServerSocket() \r
+       throws ServerHandlerInitException\r
+    {\r
+       // Create a suitable client factory:\r
+       try {\r
+           Class c = Class.forName(factory_class);\r
+           factory = (ClientFactory) c.newInstance();\r
+           //Added by Jeff Huang\r
+           //TODO: FIXIT\r
+           factory.initialize(this);\r
+       } catch (Exception ex) {\r
+           String err = ("Unable to create a client factory of class "+\r
+                         "\"" + factory_class + "\""+\r
+                         " details: \r\n" + ex.getMessage());\r
+           throw new ServerHandlerInitException(err);\r
+       }\r
+       // If needed, create a server socket instance for that context:\r
+       try {\r
+           socket = factory.createServerSocket();\r
+       } catch (IOException ex) {\r
+           String err = ("Unable to create server socket on port "+port\r
+                         + ": " + ex.getMessage() + ".");\r
+           throw new ServerHandlerInitException(err);\r
+       }\r
+       this.thread   = new Thread (this) ;\r
+       this.thread.setName(identifier) ;\r
+       this.thread.setPriority (Thread.MAX_PRIORITY) ;\r
+    }\r
+\r
+    protected MimeParserFactory getMimeClientFactory(Client client) {\r
+       return new MimeClientFactory(client);\r
+    }\r
+\r
+    /**\r
+     * Initialize our event manager.\r
+     */\r
+\r
+    private void initializeEventManager() {\r
+       this.timer   = new EventManager () ;\r
+       this.timer.setDaemon(true);\r
+       this.timer.start() ;\r
+    }\r
+\r
+    /**\r
+     * startup classes\r
+     */\r
+    protected void loadStartupClasses() {\r
+       String classes[] = props.getStringArray(STARTUP_P, null);\r
+       if (classes != null) {\r
+           for (int i = 0 ; i < classes.length ; i++) {\r
+               try {\r
+                   Class c = Class.forName(classes[i]);\r
+                   httpdPreloadInterface hpi = \r
+                       (httpdPreloadInterface)c.newInstance();\r
+                   //Added by Jeff Huang\r
+                   //TODO: FIXIT\r
+                   hpi.preload(this);\r
+               } catch (ClassNotFoundException cnfex) {\r
+                   errlog("Startup class not found : "+cnfex.getMessage());\r
+               } catch (InstantiationException iex) {\r
+                   errlog("Unable to instanciate : "+iex.getMessage());\r
+               } catch (ClassCastException ccex) {\r
+                   errlog("Startup classes must be instance of "+\r
+                          "httpdPreloadInterface: "+ccex.getMessage());\r
+               } catch (IllegalAccessException iaex) {\r
+                   errlog("IllegalAccess "+iaex.getMessage());\r
+               }\r
+           }\r
+       }\r
+    }\r
+\r
+    /**\r
+     * FIXME protected for now to handle clones\r
+     * Initialize some of the servers instance values from properties.\r
+     * @exception ServerHandlerInitException if unable to be initialized.\r
+     */\r
+\r
+    protected void initializeProperties() \r
+       throws ServerHandlerInitException\r
+    {\r
+       // Compute some default values (host and port)\r
+       String defhost  = null ;\r
+       String rootstr  = null ;\r
+       String spacestr = null ;\r
+       \r
+       boolean ip_host = props.getBoolean(DEFHOSTIP_P, false);\r
+\r
+       try {\r
+           if (ip_host)\r
+               defhost = InetAddress.getLocalHost().getHostAddress() ;\r
+           else \r
+               defhost = InetAddress.getLocalHost().getHostName() ;\r
+       } catch (UnknownHostException e) {\r
+           defhost = null;\r
+       }\r
+       // Second stage: get property values:\r
+       software         = props.getString(SERVER_SOFTWARE_P, software);\r
+       tracep           = props.getBoolean(TRACE_P,tracep) ;\r
+       uri_error        = props.getBoolean(DISPLAY_URL_ON_ERROR_P, false);\r
+       lenient          = props.getBoolean(LENIENT_P, true);\r
+       keep             = props.getBoolean(KEEP_ALIVE_P,keep) ;\r
+       logger_class     = props.getString(LOGGER_P, null) ;\r
+       factory_class    = props.getString(CLIENT_FACTORY_P,factory_class);\r
+       shuffler_path    = props.getString(SHUFFLER_PATH_P, null) ;\r
+       rootstr          = props.getString(ROOT_P, null) ;\r
+       spacestr         = props.getString(SPACE_P, null);\r
+       host             = props.getString(HOST_P, defhost) ;\r
+       port             = props.getInteger(PORT_P, port) ;\r
+       root_name        = props.getString(ROOT_NAME_P, "root") ;\r
+       root_class       = props.getString(ROOT_CLASS_P, null);\r
+       max_loaded_store = props.getInteger(MAX_LOADED_STORE_P,\r
+                                           MAX_LOADED_STORE);\r
+       store_size_limit = props.getInteger(STORE_SIZE_LIMIT_P,\r
+                                           STORE_SIZE_LIMIT);\r
+       sensitivity      = props.getBoolean(FS_SENSITIVITY, true);\r
+       publicMethods    = props.getStringArray(PUBLIC_P, publicMethods);\r
+       // Get client properties:\r
+       client_debug        = props.getBoolean (CLIENT_DEBUG_P, client_debug) ;\r
+       request_time_out    = props.getInteger (REQUEST_TIMEOUT_P\r
+                                               , request_time_out);\r
+       connection_time_out = props.getInteger (KEEP_TIMEOUT_P\r
+                                               , connection_time_out);\r
+       client_priority     = props.getInteger(CLIENT_PRIORITY_P\r
+                                              , client_priority);\r
+       client_bufsize      = props.getInteger(CLIENT_BUFSIZE_P\r
+                                              , client_bufsize);\r
+       // Check that a host name has been given:\r
+       if ( host == null )\r
+           throw new ServerHandlerInitException(this.getClass().getName()\r
+                                                +"[initializeProperties]: "\r
+                                                +"[host] undefined.");\r
+       // Default the root directory to the current directory:\r
+       if ( rootstr == null ) {\r
+           // Try the current directory as root:\r
+           rootstr = System.getProperties().getProperty("user.dir", null) ;\r
+           if ( rootstr == null )\r
+               throw new ServerHandlerInitException(this.getClass().getName()\r
+                                                    +"[initializeProperties]:"\r
+                                                    +"[root] undefined.");\r
+       }\r
+       root_dir = new File(rootstr) ;\r
+       // Default the space directory to root/WWW\r
+       if ( spacestr == null ) \r
+           space_dir = new File(root_dir, "WWW") ;\r
+       else\r
+           space_dir = new File(spacestr) ;\r
+       // Help URL:\r
+       String propval = props.getString(DOCURL_P, null);\r
+       if ( propval != null ) {\r
+           try {\r
+               URL u  = new URL(getURL(), propval);\r
+               docurl = u.toExternalForm();\r
+           } catch (Exception ex) {\r
+           }\r
+       }\r
+       // Trash Dir\r
+       trashdir = props.getString(TRASHDIR_P, trashdir);\r
+       // checpointer url\r
+       checkurl = props.getString(CHECKURL_P, checkurl);\r
+    }\r
+\r
+    /**\r
+     * Register a property set to the server.\r
+     * @param propSet The property set to register.\r
+     */    \r
+    public synchronized void registerPropertySet(PropertySet set) {\r
+       // Add this set to our known property set:\r
+       propSet.addElement(set);\r
+    }\r
+\r
+    /** \r
+     * Enumerate all the registered property sets\r
+     * @return an enumeration of </code>PropertySet</code>\r
+     */\r
+    public Enumeration enumeratePropertySet() {\r
+       return propSet.elements(); \r
+    }\r
+\r
+    /** \r
+     * Get a property set matching a specific name \r
+     * @return a Resource, the property set found\r
+     */   \r
+    public Resource getPropertySet(String name) {\r
+       for (int i = 0 ; i < propSet.size() ; i++) { \r
+           PropertySet set = (PropertySet) propSet.elementAt(i);\r
+           if ( set.getIdentifier().equals(name) )\r
+               return set;\r
+       }\r
+       return null; \r
+    } \r
+\r
+    protected void initializePropertySets() {\r
+       registerPropertySet(new GeneralProp("general", this)); \r
+       registerPropertySet(new ConnectionProp("connection", this)); \r
+       registerPropertySet(new LoggingProp("logging", this)); \r
+    } \r
+\r
+    /**\r
+     * Get this server statistics.\r
+     */\r
+\r
+    public httpdStatistics getStatistics() {\r
+       return statistics ;\r
+    }\r
+\r
+    /**\r
+     * Get this server properties.\r
+     */\r
+\r
+    public ObservableProperties getProperties() {\r
+       return props ;\r
+    }\r
+\r
+    /**\r
+     * Is the underlying file-system case sensitive ?\r
+     * @return A boolean, <strong>true</strong> if file system is case \r
+     * sensitive, <strong>false</strong> otherwise.\r
+     */\r
+    public boolean checkFileSystemSensitivity() {\r
+       return sensitivity;\r
+    }\r
+\r
+    /**\r
+     * Get the full URL of Jigsaw's documentation.\r
+     * @return A String encoded URL.\r
+     */\r
+    public String getDocumentationURL() {\r
+       return docurl;\r
+    }\r
+\r
+    /**\r
+     * Get the tracsh directory\r
+     */\r
+    public String getTrashDirectory() {\r
+       return trashdir;\r
+    }\r
+\r
+    /**\r
+     * Get the client's debug flags from the properties.\r
+     */\r
+    public final boolean getClientDebug() {\r
+       return client_debug ;\r
+    }\r
+\r
+    /**\r
+     * Does this server wants clients to try keeping connections alive ?\r
+     */\r
+    public final boolean getClientKeepConnection() {\r
+       return keep ;\r
+    }\r
+\r
+    /**\r
+     * Get the request allowed time slice from the properties.\r
+     */\r
+    public final int getRequestTimeOut() {\r
+       return request_time_out ;\r
+    }\r
+\r
+    /**\r
+     * Get the connection allowed idle time from the properties.\r
+     */\r
+    public final int getConnectionTimeOut() {\r
+       return connection_time_out ;\r
+    }\r
+\r
+    /**\r
+     * Get the client's threads priority from the properties.\r
+     */\r
+    public final int getClientThreadPriority() {\r
+       return client_priority ;\r
+    }\r
+\r
+    /**\r
+     * Get the client's buffer size.\r
+     */\r
+    public final int getClientBufferSize() {\r
+       return client_bufsize ;\r
+    }\r
+\r
+    /**\r
+     * Get this server host name.\r
+     */\r
+    public String getHost () {\r
+       return host ;\r
+    }\r
+\r
+    /**\r
+     * Get this server port number.\r
+     */\r
+    public int getPort () {\r
+       return port ;\r
+    }\r
+\r
+    /**\r
+     * Get the server current root resource.\r
+     */\r
+    public FramedResource getRoot() {\r
+       return root ;\r
+    }\r
+\r
+    /**\r
+     * get the resource reference of the root resource of the server\r
+     */\r
+    public ResourceReference getRootReference() {\r
+       return root_reference;\r
+    }\r
+\r
+    /**\r
+     * Get the logger for that server.\r
+     * @return A Logger compatible instance, or <strong>null</strong> if \r
+     * no logger specified.\r
+     */\r
+    public Logger getLogger() {\r
+       return logger;\r
+    }\r
+\r
+    /**\r
+     * Get the server's edit root resource.\r
+     * The edit root is the one that shows up by default when using JigAdmin\r
+     * It is named "root" in the interface.\r
+     * @return An HTTPResource.\r
+     */\r
+\r
+    public synchronized ResourceReference getEditRoot() {\r
+       if ( editroot == null ) {\r
+           // Check for the appropriate property:\r
+           String name = props.getString(EDIT_ROOT_P, null);\r
+           if ( name != null ) {\r
+               editroot = loadRoot(name);\r
+           }\r
+           if ( editroot == null ) {\r
+               editroot = getRootReference();\r
+           }\r
+       }\r
+       return editroot;\r
+    }\r
+\r
+    /**\r
+     * Get the server URL.\r
+     */\r
+    public URL getURL() {\r
+       if ( url == null ) {\r
+           try {\r
+               if ( port != 80 ) \r
+                   url = new URL("http", host, port, "/");\r
+               else\r
+                   url = new URL("http", host, "/");\r
+           } catch (MalformedURLException ex) {\r
+               throw new RuntimeException("unable to build server's URL");\r
+           }\r
+       }               \r
+       return url ;\r
+    }\r
+\r
+    /**\r
+     * Get the server software string.\r
+     */\r
+    public String getSoftware () {\r
+       return software;\r
+    }\r
+\r
+    /**\r
+     * Get the server local port\r
+     */\r
+    public int getLocalPort() {\r
+       return socket.getLocalPort() ;\r
+    }\r
+\r
+    /**\r
+     * Get this server identifier.\r
+     */\r
+    public String getIdentifier() {\r
+       return identifier ;\r
+    }\r
+\r
+    /**\r
+     * Get the server inet address\r
+     * @return The INET address this server is listening to.\r
+     */\r
+    public InetAddress getInetAddress() {\r
+       return socket.getInetAddress() ;\r
+    }\r
+\r
+    /**\r
+     * Get this server root directory.\r
+     */\r
+    public File getRootDirectory() {\r
+       return root_dir ;\r
+    }\r
+\r
+    /**\r
+     * Get this server space diretory\r
+     */\r
+    public File getSpaceDir() {\r
+       return space_dir;\r
+    }\r
+\r
+    /**\r
+     * Get this server config directory.\r
+     */\r
+    public File getConfigDirectory() {\r
+       File file = props.getFile(CONFIG_P, null);\r
+       return (file == null) ? new File(getRootDirectory(), "config") : file;\r
+    }\r
+\r
+    /**\r
+     * Get this server authentication directory.\r
+     */\r
+    public File getAuthDirectory() {\r
+       return new File(getConfigDirectory(), "auth");\r
+    }\r
+\r
+    /**\r
+     * Get this server store directory.\r
+     */\r
+    public File getStoreDirectory() {\r
+       return new File(getConfigDirectory(), "stores");\r
+    }\r
+\r
+    /**\r
+     * Get this server index directory\r
+     */\r
+    public File getIndexerDirectory() {\r
+       return new File(getConfigDirectory(), "indexers");\r
+    }\r
+\r
+    /**\r
+     * Get temp directory\r
+     */\r
+    public File getTempDirectory() {\r
+       return new File(getRootDirectory(), "tmp");\r
+    }\r
+\r
+    /**\r
+     * Clean the temp dir.\r
+     */\r
+    protected void cleanTempDirectory() {\r
+       org.w3c.util.IO.clean(getTempDirectory());\r
+    }\r
+\r
+    /**\r
+     * get the indexer catalog of this server \r
+     */\r
+    public IndexersCatalog getIndexersCatalog() {\r
+       if ( indexers == null )\r
+           indexers = new IndexersCatalog(\r
+               new ResourceContext(getDefaultContext()));\r
+       return indexers;\r
+    }\r
+\r
+    /**\r
+     * Get this server realm catalog.\r
+     */\r
+    public RealmsCatalog getRealmsCatalog() {\r
+       return realms ;\r
+    }\r
+\r
+    /**\r
+     * Get this server resourcestore manager.\r
+     */\r
+    public ResourceStoreManager getResourceStoreManager() {\r
+       return manager ;\r
+    }\r
+\r
+    /**\r
+     * Get this server resource space\r
+     */\r
+    public ResourceSpace getResourceSpace() {\r
+       return manager ;\r
+    }\r
+\r
+    /**\r
+     * Get the default resource context for that server.\r
+     */\r
+    public ResourceContext getDefaultContext() {\r
+       return context;\r
+    }\r
+\r
+    /**\r
+     * Get the lenient value, tru if we are lenient in HTTP parsing\r
+     */\r
+    public boolean isLenient() {\r
+       return lenient;\r
+    }\r
+\r
+    /**\r
+     * Cleanup the resources associated with this server context.\r
+     * This method should only be called by the server thread itself, when\r
+     * it is requested to perform the cleanup.\r
+     * @param restart If <strong>true</strong> the server is restarted \r
+     *     (reinitialized) straight away.\r
+     */\r
+\r
+    protected synchronized void cleanup(boolean restart) {\r
+       // Close the accepting socket:\r
+       try {\r
+           socket.close() ;\r
+           socket = null ;\r
+       } catch (IOException ex) {\r
+           errlog ("[cleanup]: IOException while closing server socket.");\r
+       }\r
+       // FIXME temporary hack for clones\r
+       synchronized( instances ) {\r
+           // remove one instance\r
+           instances[0]--;         // @wplatzer\r
+           if ( factory != null )\r
+               factory.shutdown(true) ;\r
+           factory = null ;\r
+           if ( manager != null && instances[0] == 0) // FIXME (shm)\r
+               manager.shutdown() ;\r
+           manager = null ;\r
+           if ( shuffler != null )\r
+               shuffler.shutdown() ;\r
+           shuffler = null ;\r
+           // Unregister to property monitoring\r
+           props.unregisterObserver (this) ;\r
+           errlog ("shutdown completed at: "+new Date()+".") ;\r
+           // Finally close the log\r
+           if ( logger != null && instances[0] == 0) // FIXME shm\r
+               logger.shutdown() ;\r
+           logger = null ;\r
+           // Release any other pointers:\r
+           timer.stopEventManager() ;\r
+           System.out.println (getIdentifier()+": " + getURL() + " done.") ;\r
+           System.out.flush() ;\r
+       // Keep the data neede to reinit (in case needed)\r
+           File init_propfile = props.getFile(PROPS_P, null);\r
+           ObservableProperties init_props = props ;\r
+           String init_identifier = identifier ;\r
+           // Release pointed data:\r
+           identifier = null ;\r
+           manager    = null ;\r
+           factory    = null ;\r
+           shuffler   = null ;\r
+           // FIXME clones props      = null ;\r
+           indexer    = null ;\r
+           root       = null ;\r
+           realms     = null ;\r
+           logger     = null ;\r
+           socket     = null ;\r
+           timer      = null ;\r
+           thread     = null ;\r
+           url        = null ;\r
+           restarting = false ;\r
+           finishing  = false ;\r
+           if ( restart ) {\r
+               try {\r
+                   instances[0]++; //FIXME clones\r
+                   initialize(shm, init_identifier, init_props) ;\r
+                   start();\r
+               } catch (Exception ex) {\r
+                   // We really can't do more than this here:\r
+                   System.out.println("*** server restart failed.") ;\r
+                   ex.printStackTrace() ;\r
+               }\r
+           }\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Shutdown the server properly.\r
+     * This methods shutdown the server, and clean-up all its associated \r
+     * resources. If the current thread is not the server thread, it unblocks\r
+     * the server thread from its accept() call, and forces it to perform\r
+     * the rest of the shutdown operation itself.\r
+     * @see httpd#cleanup\r
+     */\r
+\r
+    public synchronized void shutdown () {\r
+       checkpoint();\r
+       errlog ("shutdown inited...(save done)") ;\r
+       finishing = true ;\r
+       try {\r
+           Socket unlock = new Socket(host, port) ;\r
+           unlock.close() ;\r
+       } catch (IOException ex) {\r
+           errlog("[shutdown]: IOException while unblocking server thread.");\r
+       }\r
+       shm.removeServerHandler(this);\r
+       cleanTempDirectory();\r
+    }\r
+\r
+    /**\r
+     * Restart the server properly.\r
+     * This methods restarts the server. It cleans-up all its associated \r
+     * resources, and reinitialize it from scratch. If the current thread is\r
+     * not the server thread, it unblocks\r
+     * the server thread from its accept() call, and forces it to perform\r
+     * the rest of the restart operation itself.\r
+     * @param reload_properties Should we reload the properties from the\r
+     *    property file, or should we just reinitialize from the current set\r
+     *    of properties.\r
+     * @see httpd#cleanup\r
+     */\r
+\r
+    public synchronized void restart () {\r
+       errlog ("[restart]: inited !") ;\r
+       finishing    = true ;\r
+       restarting   = true ;\r
+       try {\r
+           Socket unlock = new Socket(host, port) ;\r
+           unlock.close() ;\r
+       } catch (IOException ex) {\r
+           errlog ("[restart]: IOException while unblocking server thread.");\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Turn debugging on/off for this instance of httpd server.\r
+     * @param A boolean, true turns debugging on, flase turns it off.\r
+     */\r
+\r
+    public void debug (boolean onoff) {\r
+       tracep = onoff ;\r
+    }\r
+\r
+    /**\r
+     * Emit a server trace. Traces are used solely for debugging purposes. You\r
+     * should either use <b>log</b> or <b>error</b> to report informations.\r
+     * @param client The client object which wants to report the trace.\r
+     * @param msg The trace message.\r
+     * @see httpd#log\r
+     */\r
+\r
+    public void trace (Client client, String msg) {\r
+       if ( tracep && (logger != null) )\r
+           logger.trace (client, msg) ;\r
+    }\r
+\r
+    /**\r
+     * Emit a server trace, on behalf of the server itself.\r
+     * @param msg The trace the server wants to emit.\r
+     */\r
+    public void trace (String msg) {\r
+       if ( tracep && (logger != null))\r
+           logger.trace (msg) ;\r
+    }\r
+\r
+    /**\r
+     * Emit a log entry.\r
+     * @param client The client whose request is to be logged.\r
+     * @param request The request that has been handled.\r
+     * @param reply The emitted reply.\r
+     * @param nbytes The number of bytes emitted back to the client.\r
+     * @param duration The time it took to process the request.\r
+     */\r
+    public void log (Client client\r
+                    , Request request, Reply reply\r
+                    , int nbytes\r
+                    , long duration) {\r
+       if ( logger != null )\r
+           logger.log (request, reply, nbytes, duration) ;\r
+       statistics.updateStatistics(client, request, reply, nbytes, duration) ;\r
+    }\r
+\r
+    /**\r
+     * Emit a log message.\r
+     * @param msg The message to log.\r
+     */\r
+    public void log(String msg) {\r
+       logger.log(msg);\r
+    }\r
+\r
+    /**\r
+     * Emit a server error on behalf of some client object.\r
+     * @param client The client.\r
+     * @param msg The error message.\r
+     */\r
+    public void errlog (Client client, String msg) {\r
+       if ( logger != null )\r
+           logger.errlog(client, msg) ;\r
+    }\r
+\r
+    /**\r
+     * Emit an error on behalf of the server.\r
+     * @param msg The error message.\r
+     */\r
+\r
+    public void errlog (String msg) {\r
+       if ( logger != null )\r
+           logger.errlog ("["+identifier+"] "+msg) ;\r
+    }\r
+\r
+    /**\r
+     * The prefered form for reporting errors.\r
+     * @param from The object that emited the error.\r
+     * @param msg The error message.\r
+     */\r
+    public void errlog(Object from, String msg) {\r
+       if ( logger != null )\r
+           logger.errlog("["+from.getClass().getName()+"]: "+msg);\r
+    }\r
+\r
+    /**\r
+     * Another nice way of reporting errors from an HTTPResource.\r
+     * @param from The resource that trigered the error.\r
+     * @param msg The error message.\r
+     */\r
+    public void errlog(Resource from, String msg) {\r
+       if ( logger != null )\r
+           logger.errlog(from.getClass().getName()+"@"+from.unsafeGetURLPath()\r
+                         + ": " + msg);\r
+    }\r
+\r
+    /**\r
+     * Emit a fatal error.\r
+     * @param e Any exception that caused the error.\r
+     * @param msg Any additional message.\r
+     */\r
+    public void fatal (Exception e, String msg) {\r
+       System.out.println ("*** Fatal Error, aborting") ;\r
+       System.out.println (this.getClass().getName() + ": " + msg) ;\r
+       e.printStackTrace() ;\r
+       throw new RuntimeException (msg) ;\r
+    }\r
+\r
+    /**\r
+     * Emit a fatal error.\r
+     * @param msg Any error message\r
+     */\r
+    public void fatal(String msg) {\r
+       System.out.println("*** Fatal error, aborting") ;\r
+       System.out.println(this.getClass().getName() + ": " + msg) ;\r
+       throw new RuntimeException(msg) ;\r
+    }\r
+       \r
+    /**\r
+     * Emit a warning.\r
+     * Warnings are emited, typically if the configuration is inconsistent,\r
+     * and the server can continue its work.\r
+     * @param msg The warning message.\r
+     */\r
+    public void warning (String msg) {\r
+       System.out.println ("*** Warning : " + msg) ;\r
+    }\r
+\r
+    /**\r
+     * Emit a warning.\r
+     * @param e Any exception.\r
+     * @param msg Any message.\r
+     */\r
+    public void warning (Exception e, String msg) {\r
+       System.out.println ("*** Warning: " + msg) ;\r
+       e.printStackTrace() ;\r
+    }\r
+\r
+    /**\r
+     * Get a shuffler for this server's client.\r
+     * Whenever possible, we use a shuffler program to speed up communication\r
+     * with the client. This methods return whatever the server deems \r
+     * appropriate for this client shuffler.\r
+     * @return A Shuffler instance, or <strong>null</strong>.\r
+     * @see org.w3c.jigsaw.http.Shuffler\r
+     */\r
+    public synchronized Shuffler getShuffler (Client client) {\r
+       return shuffler ;\r
+    }\r
+\r
+    protected String getBanner() {\r
+       return "Jigsaw["+version+"]";\r
+    }\r
+\r
+    public void run () {\r
+       // Emit some traces before starting up:\r
+       System.out.println(getBanner()+": serving at "+getURL());\r
+       System.out.flush() ;\r
+       errlog("started at: "+new Date()+".");\r
+       // Enter the evil loop:\r
+       while ( ( ! finishing) && ( socket != null ) ) {\r
+           Socket ns = null ;\r
+           try {\r
+               try {\r
+                               Thread.sleep(100);\r
+                       } catch (InterruptedException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       }\r
+               ns = socket.accept() ;\r
+               ns.setTcpNoDelay(true);\r
+           } catch (IOException e) {\r
+               if (debug)\r
+                   e.printStackTrace() ;\r
+               errlog ("failed to accept incoming connection on "+socket) ;\r
+               // just in case, as it may have been created.\r
+               try { ns.close(); } catch (Exception ex) {};\r
+               ns = null;\r
+           }\r
+           if ( (socket != null) && (ns != null) && (factory != null) ) \r
+               factory.handleConnection (ns) ;\r
+       }\r
+       // Our socket has been closed, perform associated cleanup.\r
+       cleanup(restarting) ;\r
+    }\r
+\r
+    /**\r
+     * Perform the given request on behalf of this server.\r
+     * @param request The request to perform.\r
+     * @return A non-null Reply instance.\r
+     * @exception ProtocolException If some error occurs during processing the\r
+     * request.\r
+     * @exception ResourceException If a resource got a fatal error.\r
+     */\r
+\r
+    public ReplyInterface perform(RequestInterface req)\r
+       throws ProtocolException, ResourceException\r
+    {\r
+       Request request = (Request) req;\r
+       // This may be a server-wide request, this is an ugly hack in HTTP spec\r
+       if ( request.getURL() == Request.THE_SERVER ) {\r
+           if ( request.getMethod().equals("OPTIONS") ) {\r
+               HttpTokenList pub = null;\r
+               synchronized(this) {\r
+                   if ( publicHeader == null )\r
+                       pub = HttpFactory.makeStringList(publicMethods);\r
+                   publicHeader = pub;\r
+               }\r
+               Reply reply = request.makeReply(HTTP.OK);\r
+               if ( pub != null ) {\r
+                   reply.setHeaderValue(Reply.H_PUBLIC, pub);\r
+               }\r
+               reply.setContentLength(0);\r
+               return reply;\r
+           }\r
+       }\r
+       if (request.getMethod().equals("TRACE")) {\r
+           // check if the resource can be proxied\r
+           boolean doit = true;\r
+           LookupState   ls = new LookupState(request);\r
+           LookupResult  lr = new LookupResult(root.getResourceReference());\r
+           try {\r
+               if ( root.lookup(ls, lr) ) {\r
+                   ResourceReference  target = lr.getTarget();\r
+                   if (target != null) {\r
+                       try {\r
+                           // this is plain ugly and won't work for proxy\r
+                           // not based on this resource\r
+                           // do we need another way to to this?\r
+                           FramedResource fr = (FramedResource) target.lock();\r
+                           Class cff = Class.forName(\r
+                                         "org.w3c.jigsaw.proxy.ForwardFrame");\r
+                           doit = (fr.getFrameReference(cff) == null);\r
+                       } catch (Exception ex) {\r
+                           // fail miserably to the fallback\r
+                       } finally {\r
+                           target.unlock();\r
+                       }\r
+                   }\r
+               }\r
+           } catch (Exception ex) {};\r
+           if (doit) {\r
+               Reply reply = request.makeReply(HTTP.OK);\r
+               reply.setNoCache(); // don't cache this\r
+               reply.setMaxAge(-1); \r
+               // Dump the request as the body\r
+               // Removed unused headers:\r
+               // FIXME should be something else for chuncked stream\r
+               ByteArrayOutputStream ba = new ByteArrayOutputStream();\r
+               try {\r
+                   reply.setContentType(new MimeType("message/http"));\r
+                   request.dump(ba);\r
+                   reply.setContentLength(ba.size());\r
+               } catch (Exception ex) {\r
+                   ex.printStackTrace();\r
+               }\r
+               reply.setStream(new ByteArrayInputStream(ba.toByteArray()));\r
+               return reply;\r
+           }\r
+       }\r
+       // Create a lookup state, and a lookup result:\r
+\r
+       ProtocolException error = null;\r
+       LookupState   ls = null;\r
+       LookupResult  lr = null;\r
+       // Run the lookup algorithm of root resource:\r
+       // catch exception to get error (FIXME)\r
+       try {\r
+           lr = new LookupResult(root.getResourceReference());\r
+           ls = new LookupState(request);\r
+\r
+           if ( root.lookup(ls, lr) ) {\r
+               if (lr.hasReply())\r
+                   return lr.getReply();\r
+           }\r
+       } catch (ProtocolException ex) {\r
+           error = ex;\r
+       } catch (Exception ex) {\r
+           /*\r
+            * We have a problem here, the error can be a configuration\r
+            * or resource/extension problem, and it should be a \r
+            * 5xx error, or it is a client side error and it should be\r
+            * a 4xx error, ex, try with "Authorization:" and it fails.\r
+            * For now we will reply with a 400, but with a FIXME\r
+            */\r
+           Reply err = request.makeReply(HTTP.BAD_REQUEST);\r
+           err.setContent("<html><head><title>Bad Request</title></head>\n"\r
+                          + "<body><p>The server was not able to "\r
+                          + "understand this request</p></body></html>");\r
+           error = new ProtocolException(err);\r
+       }\r
+       // Let the target resource perform the method\r
+       ResourceReference  target = lr.getTarget();\r
+       Reply              reply  = null;\r
+\r
+       ResourceFilter filters[]  = lr.getFilters();\r
+       int            infilter   = 0;\r
+\r
+       if (error == null) {\r
+           //call the ingoing filters:\r
+           try {\r
+               // temporary target resource !!! WARNING\r
+               request.setTargetResource(target);\r
+               if ( filters != null ) {\r
+                   for ( ; infilter < filters.length ; infilter++ ) {\r
+                       if ( filters[infilter] == null )\r
+                           continue;\r
+                       reply = (Reply)filters[infilter].\r
+                                                      ingoingFilter(request,\r
+                                                                    filters,\r
+                                                                    infilter);\r
+                       if ( reply != null ) {\r
+                           return reply;\r
+                       }\r
+                   }\r
+               }\r
+           } catch (ProtocolException ex) {\r
+               error = ex;\r
+           }\r
+           //perform the request:\r
+           if ((error == null) && (target != null)) { \r
+               request.setFilters(filters, infilter);\r
+               request.setTargetResource(target);\r
+               try {\r
+                   FramedResource res = (FramedResource)target.lock();\r
+                   reply = (Reply) res.perform(request);\r
+                   if (reply == null) {\r
+                       reply = request.makeReply(HTTP.NOT_FOUND);\r
+                       if (uri_error) {\r
+                           reply.setContent("<html><head><title>Not Found" +\r
+                                            "</title></head>\n"+\r
+                                            "<body><h1>Invalid" +\r
+                                            " URL</h1><p>The URL <b>"+\r
+                                            request.getURL()+\r
+                                            "</b> that you requested is not" +\r
+                                            " available "+\r
+                                            " for this protocol.</body>\n"\r
+                                            +"</html>");\r
+                       } else {\r
+                           reply.setContent("<html><head><title>Not Found" +\r
+                                            "</title></head>\n"+\r
+                                            "<body><h1>Invalid" +\r
+                                            " URL</h1><p>The URL" +\r
+                                            "</b> that you requested is not" +\r
+                                            " available "+\r
+                                            " for this protocol.</body>\n"\r
+                                            +"</html>");\r
+                       }\r
+                       reply.setContentType(org.w3c.www.mime.MimeType.\r
+                                            TEXT_HTML);\r
+                   }\r
+               } catch (InvalidResourceException ex) {\r
+                   //FIXME\r
+                   reply = request.makeReply(HTTP.NOT_FOUND);\r
+                   if (uri_error) {\r
+                       reply.setContent("<html><head><title>Not"+\r
+                                        " Found</title>"+\r
+                                        "</head><body><b>The URL <b>"+\r
+                                        request.getURL()+\r
+                                        "</b> that you requested is not " +\r
+                                        "available, "+\r
+                                        " probably deleted.</body></html>");\r
+                   } else {\r
+                       reply.setContent("<html><head><title>Not"+\r
+                                        " Found</title>"+\r
+                                        "</head><body><b>The URL"+\r
+                                        " that you requested is not " +\r
+                                        "available, "+\r
+                                        " probably deleted.</body></html>");\r
+                   }\r
+                   reply.setContentType(org.w3c.www.mime.MimeType.TEXT_HTML);\r
+               } finally {\r
+                   target.unlock();\r
+               }\r
+           } else {\r
+               reply = request.makeReply(HTTP.NOT_FOUND);\r
+               if (uri_error) {\r
+                   reply.setContent("<html><head>\n"+\r
+                                    "<title>Not Found</title></head>"+\r
+                                    "<body><h1>Invalid URL</h1><p>The URL"+\r
+                                    " <b>"+ request.getURL()+\r
+                                    "</b> that you requested is not"+\r
+                                    " available "+\r
+                                    " on that server.</body></html>");\r
+               } else {\r
+                   reply.setContent("<html><head>\n"+\r
+                                    "<title>Not Found</title></head>"+\r
+                                    "<body><h1>Invalid URL</h1><p>The URL"+\r
+                                    " that you requested is not"+\r
+                                    " available "+\r
+                                    " on that server.</body></html>");\r
+               }\r
+               reply.setContentType(org.w3c.www.mime.MimeType.TEXT_HTML);\r
+           }\r
+       }\r
+       // call the outgoing filters:\r
+       if ((reply == null) || (reply.getStatus() != HTTP.DONE)) {\r
+           if ( error == null ) {\r
+               for (int i = infilter ; --i >= 0 ; ) {\r
+                   if ( filters[i] == null )\r
+                       continue;\r
+                   Reply fr = (Reply)filters[i].outgoingFilter(request,\r
+                                                               reply,\r
+                                                               filters,\r
+                                                               i);\r
+                   if ( fr != null )\r
+                       return fr;\r
+               }\r
+           } else {\r
+               // Make sure we always invoke appropriate filters:\r
+               if (filters != null) {\r
+                   for (int i = filters.length ; --i >= 0 ; ) {\r
+                       if ( filters[i] == null ) {\r
+                           continue;\r
+                       }\r
+                       Reply fr = (Reply)filters[i].exceptionFilter(request,\r
+                                                                error,\r
+                                                                filters,\r
+                                                                i);\r
+                       if ( fr != null ){\r
+                           return fr;\r
+                       }\r
+                   }\r
+               }\r
+               reply = (Reply)error.getReply() ;\r
+               if (reply == null) {\r
+                   reply = request.makeReply(HTTP.INTERNAL_SERVER_ERROR);\r
+                   if (uri_error) {\r
+                       reply.setContent("<html><head>\n"+\r
+                                        "<title>Server Error</title>"+\r
+                                        "</head><body><h1>Invalid URL</h1>"+\r
+                                        "<p>The URL <b>"+\r
+                                        request.getURL()+\r
+                                        "</b>: isn't available "+\r
+                                        " on that server.</body></html>");\r
+                   } else {\r
+                       reply.setContent("<html><head>\n"+\r
+                                        "<title>Server Error</title>"+\r
+                                        "</head><body><h1>Invalid URL</h1>"+\r
+                                        "<p>The URL"+\r
+                                        " isn't available "+\r
+                                        " on that server.</body></html>");\r
+                   }\r
+                   reply.setContentType(org.w3c.www.mime.MimeType.TEXT_HTML);\r
+               }\r
+           }\r
+       }\r
+       return reply;\r
+    }\r
+\r
+    protected boolean checkUpgrade(String id, ObservableProperties props) {\r
+       // Check for an upgrade:\r
+       int configvers = props.getInteger(httpd.VERSCOUNT_P, 1);\r
+       if (configvers < httpd.verscount) {\r
+           System.err.println("*** Jigsaw needs upgrade from internal "+\r
+                              "version "+configvers+" to "+ httpd.verscount);\r
+           if (httpd.verscount == 4) {\r
+               org.w3c.tools.resources.serialization.Serializer serializer =\r
+                new org.w3c.tools.resources.serialization.xml.XMLSerializer();\r
+               props.put(httpd.SERIALIZER_CLASS_P, \r
+                   "org.w3c.tools.resources.serialization.xml.XMLSerializer");\r
+               File propsfile    = props.getFile(PROPS_P, null);\r
+               Upgrader upgrader = new Upgrader(id,\r
+                                                getConfigDirectory(), \r
+                                                propsfile,\r
+                                                serializer);\r
+               props.put(httpd.VERSCOUNT_P, String.valueOf(httpd.verscount));\r
+               props.put(httpd.SERVER_SOFTWARE_P, "Jigsaw/"+version);\r
+               try {\r
+                   upgrader.upgrade(httpd.verscount);\r
+                   return true;\r
+               } catch (Exception ex) {\r
+                   System.err.println(ex.getMessage());\r
+                   return false;\r
+               }\r
+           }\r
+           return false;\r
+       }\r
+       return true;\r
+    }\r
+\r
+    /**\r
+     * Initialize a new HTTP server.\r
+     * The server wil first be initialized from the available properties,\r
+     * it will than startup, and finally run in its own thread.\r
+     * @param identifier The string identifying this server's occurence.\r
+     * @param props A set of properties to initialize from.\r
+     * @exception ServerHandlerInitException if unable to be initialized.\r
+     */\r
+\r
+    public void initialize (ServerHandlerManager shm,\r
+                           String identifier,\r
+                           ObservableProperties props) \r
+       throws ServerHandlerInitException\r
+    {\r
+       // Check for an optional upgrade of config files:\r
+       // checkUpgrade(shm, identifier, props);\r
+       this.shm = shm;\r
+       // Initialize from properties:\r
+       this.identifier = identifier ;\r
+       this.props = props;\r
+       // with an explicit cast for buggy compilers\r
+       this.props.registerObserver((PropertyMonitoring)this) ;\r
+       initializeProperties() ;\r
+       if (! checkUpgrade(identifier, props))\r
+           throw new ServerHandlerInitException("Upgrade failed.");\r
+       // Create the socket, and run the server:\r
+       initializeServerSocket();\r
+    }\r
+\r
+    /**\r
+     * start the server\r
+     * it will than startup, and finally run in its own thread.\r
+     */\r
+\r
+    public void start () \r
+       throws ServerHandlerInitException\r
+    {\r
+       if (!isAClone) {\r
+           // Create the default resource context (shared by all resources):\r
+           this.context = new ResourceContext(this);\r
+           //FIXME\r
+           // Create the resource store manager\r
+           initializeResourceSpace(identifier,\r
+                                   root_class,\r
+                                   props.getString(EDIT_ROOT_P, root_name),\r
+                                   props.getString(SERIALIZER_CLASS_P, null),\r
+                                   max_loaded_store);\r
+           this.context.setSpace(getResourceSpace());\r
+           // Create the resource indexer object\r
+           initializeIndexer();\r
+           // Resurect this server root entity:\r
+           initializeRootResource();\r
+           // Resurect the realms catalog\r
+           initializeRealmsCatalog() ;\r
+           // Initialize property sets\r
+           initializePropertySets();\r
+           // Create this server event manager \r
+           initializeEventManager();\r
+           // Initialize the logger object:\r
+           initializeLogger();\r
+           // Initialize the shuffler object:\r
+           if ( shuffler_path != null ) {\r
+               try {\r
+                   this.shuffler = new Shuffler (shuffler_path) ;\r
+               } catch (Error e) {\r
+                   warning ("unable to launch shuffler to "+\r
+                            shuffler_path+\r
+                            ": " + e.getMessage()) ;\r
+                   this.shuffler = null ;\r
+               } catch (RuntimeException e) {\r
+                   warning (e, "unable to launch shuffler to "+\r
+                            shuffler_path+\r
+                            ": " + e.getMessage()) ;\r
+                   this.shuffler = null ;\r
+               }\r
+           }\r
+           if ( this.shuffler != null )\r
+               trace ("using shuffler at: " + shuffler_path) ;\r
+           // startup classes\r
+           loadStartupClasses();\r
+           // Yeah, now start:\r
+           this.thread.start();\r
+       } else {\r
+           httpd mainServ = (httpd) shm.lookupServerHandler(masterID);\r
+           this.context = mainServ.getDefaultContext();\r
+           this.realms = mainServ.realms;\r
+           this.manager = mainServ.manager;\r
+           // We basically re-use:\r
+           // - the master indexer\r
+           // - the master realms catalog\r
+           // FIXED no need to use the same logger! - the master logger:\r
+           // Initialize the logger object:\r
+           initializeLogger();\r
+           // Resurect this server root entity:\r
+           initializeRootResource();\r
+           // We use our own event manager\r
+           initializeEventManager();\r
+           // Yeah, now start:\r
+           this.thread.start();\r
+       }\r
+    }\r
+\r
+    /**\r
+     * clone this server\r
+     * @exception ServerHandlerInitException if unable to be initialized.\r
+     */\r
+    public ServerHandler clone(ServerHandlerManager shm\r
+                              , String id\r
+                              , ObservableProperties props) \r
+       throws ServerHandlerInitException\r
+    {\r
+       // Clone this master server:\r
+       httpd server      = null;\r
+       try {\r
+           server = (httpd) clone();\r
+       } catch (CloneNotSupportedException ex) {\r
+           throw new ServerHandlerInitException(this.getClass().getName()\r
+                                                + ": clone not supported !");\r
+       }\r
+       server.shm = shm;\r
+       // Nullify some of the cached instance variables:\r
+       server.url = null;\r
+       // Initialize \r
+       server.masterID = server.identifier;\r
+       server.identifier = id;\r
+       server.props      = props;\r
+       server.props.registerObserver((PropertyMonitoring) server);\r
+       server.initializeProperties();\r
+       server.initializeServerSocket();\r
+       server.isAClone = true;\r
+       return server;\r
+    }\r
+\r
+    /**\r
+     * get this server config resource\r
+     */\r
+    public ResourceReference getConfigResource() {\r
+       if ( rr_configResource == null ) {\r
+           configResource = new ConfigResource(this);\r
+           rr_configResource = new DummyResourceReference(configResource);\r
+       }\r
+       return rr_configResource;\r
+    }\r
+\r
+    /**\r
+     * Give the status of this class as a partial HTML text which will be added\r
+     * into a block level element\r
+     * @return a String, the generated HTML\r
+     */\r
+    public String getHTMLStatus() {\r
+       StringBuffer sb = new StringBuffer();\r
+       if (factory instanceof Status) {\r
+           sb.append(((Status)factory).getHTMLStatus());\r
+       }\r
+       sb.append(manager.getHTMLStatus());\r
+       return sb.toString();\r
+    }\r
+\r
+    /**\r
+     * Create a new server instance in this process.\r
+     * @param identifier The server's identifier.\r
+     * @param props The server properties.\r
+     */\r
+    public httpd() {\r
+       super();\r
+    }\r
+\r
+    /**\r
+     * this server's usage\r
+     */\r
+    public static void usage () {\r
+       PrintStream o = System.out ;\r
+       \r
+       o.println("usage: httpd [OPTIONS]") ;\r
+       o.println("-id <id>          : server identifier.");\r
+       o.println("-port <number>    : listen on the given port number.");\r
+       o.println("-host <host>      : full name of host running the server.");\r
+       o.println("-root <directory> : root directory of server.") ;\r
+       o.println("-space <directory>: space directory exported by server") ;\r
+       o.println("-p     <propfile> : property file to read.");\r
+       o.println("-trace            : turns debugging on.") ;\r
+       o.println("-config           : config directory to use.") ;\r
+       o.println("-maxstores <int>  : Max number of stores in memory.") ;\r
+       System.exit (1) ;\r
+    }\r
+\r
+    /**\r
+     * debugging main\r
+     */\r
+    public static void main (String args[]) {\r
+       Integer cmdport    = null ;\r
+       String  cmdhost    = null ;\r
+       String  cmdroot    = null ;\r
+       String  cmdspace   = null ;\r
+       String  cmdprop    = null ;\r
+       String  cmdid      = "http-server" ;\r
+       String  cmdconfig  = "config";\r
+       Boolean cmdtrace   = null ;\r
+       boolean noupgrade  = false;\r
+       String  maxstores  = null;\r
+\r
+       // Parse command line options:\r
+       for (int i = 0 ; i < args.length ; i++) {\r
+           if ( args[i].equals ("-port") ) {\r
+               try {\r
+                   cmdport = new Integer(args[++i]) ;\r
+               } catch (NumberFormatException ex) {\r
+                   System.out.println ("invalid port number ["+args[i]+"]");\r
+                   System.exit (1) ;\r
+               }\r
+           } else if ( args[i].equals("-id") && (i+1 < args.length)) {\r
+               cmdid = args[++i];\r
+           } else if ( args[i].equals ("-maxstores") && (i+1 < args.length)) {\r
+               maxstores = args[++i];\r
+           } else if ( args[i].equals ("-host") && (i+1 < args.length)) {\r
+               cmdhost = args[++i] ;\r
+           } else if ( args[i].equals ("-root") && (i+1 < args.length)) {\r
+               cmdroot = args[++i] ;\r
+           } else if ( args[i].equals ("-space") && (i+1 < args.length)) {\r
+               cmdspace = args[++i] ;\r
+           } else if ( args[i].equals ("-p") && (i+1 < args.length)) {\r
+               cmdprop = args[++i] ;\r
+           } else if ( args[i].equals ("-trace") ) {\r
+               cmdtrace = Boolean.TRUE;\r
+           } else if ( args[i].equals ("?") || args[i].equals ("-help") ) {\r
+               usage() ;\r
+           } else if (args[i].equals("-config") && (i+1 < args.length)) {\r
+               cmdconfig = args[++i];\r
+           } else if ( args[i].equals("-noupgrade") ) {\r
+               noupgrade = true;\r
+           } else {\r
+               continue;\r
+               // System.out.println ("unknown option: ["+args[i]+"]") ;\r
+               // System.exit (1) ;\r
+           }\r
+       }\r
+       // Get the properties for this server:\r
+       ObservableProperties props = null;\r
+       props = new ObservableProperties(System.getProperties()) ;\r
+       // Get the root and configuration directories:\r
+       File root   = ((cmdroot == null)\r
+                      ? new File(props.getProperty("user.dir", null))\r
+                      : new File(cmdroot));\r
+       File config = new File(root, cmdconfig);\r
+       // Locate the property file:\r
+       if (cmdprop == null) {\r
+           // Try to guess it, cause it is really required:\r
+           File guess = new File (config, cmdid+".props");\r
+           if ( ! guess.exists() )\r
+               // A hack for smooth upgrade from 1.0alpha3 to greater:\r
+               guess = new File(config, "httpd.props");\r
+           cmdprop = guess.getAbsolutePath() ;\r
+       }\r
+       if ( cmdprop != null ) {\r
+           System.out.println ("loading properties from: " + cmdprop) ;\r
+           try {\r
+               File propfile = new File(cmdprop) ;\r
+               props.load(new FileInputStream(propfile)) ;\r
+               props.put (PROPS_P, propfile.getAbsolutePath()) ;\r
+           } catch (FileNotFoundException ex) {\r
+               System.out.println ("Unable to load properties: "+cmdprop);\r
+               System.out.println ("\t"+ex.getMessage()) ;\r
+               System.exit (1) ;\r
+           } catch (IOException ex) {\r
+               System.out.println ("Unable to load properties: "+cmdprop);\r
+               System.out.println ("\t"+ex.getMessage()) ;\r
+               System.exit (1) ;\r
+           }\r
+           System.setProperties (props) ;\r
+       }\r
+       // Check for an upgrade:\r
+       int configvers = props.getInteger(httpd.VERSCOUNT_P, 1);\r
+       if (configvers < httpd.verscount) {\r
+           System.err.println("+ Jigsaw needs upgrade from internal version "+\r
+                              configvers+\r
+                              " to " + httpd.verscount);\r
+           if ( noupgrade ) {\r
+               System.err.println("+ Jigsaw cannot run in that version.");\r
+               System.exit(1);\r
+           }\r
+           // upgrade(configvers, httpd.verscount, args);\r
+           return;\r
+       }\r
+       // Override properties with our command line options:\r
+       if ( cmdport != null ) \r
+           props.put (PORT_P, cmdport.toString()) ;\r
+       if ( cmdhost != null ) \r
+           props.put (HOST_P, cmdhost) ;\r
+       if ( cmdroot != null )\r
+           props.put (ROOT_P, root.getAbsolutePath()) ;\r
+       if ( cmdconfig != null )\r
+           props.put(CONFIG_P, config.getAbsolutePath());\r
+       if ( cmdspace != null )\r
+           props.put (SPACE_P, cmdspace) ;\r
+       if ( cmdtrace != null ) {\r
+           props.put (TRACE_P, "true") ;\r
+           props.put (CLIENT_DEBUG_P, "true") ;\r
+       }\r
+       if (maxstores != null) \r
+           props.put (MAX_LOADED_STORE_P, maxstores);\r
+\r
+       // Install security manager if needed:\r
+       if (Boolean.getBoolean(USE_SM_P)) {\r
+           SecurityManager sm = new httpdSecurityManager() ;\r
+           System.setSecurityManager (sm) ;\r
+       }\r
+       // Run the server:\r
+       try {\r
+           httpd server = new httpd ();\r
+           server.initialize(null, cmdid, props) ;\r
+       } catch (Exception e) {\r
+           System.out.println ("*** [httpd]: fatal error, exiting !") ;\r
+           e.printStackTrace () ;\r
+       }\r
+    }\r
+}\r