--- /dev/null
+// 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