0001: // httpd.java
0002: // $Id: httpd.java,v 1.142 2007/04/10 13:21:01 ylafon Exp $
0003: // (c) COPYRIGHT MIT and INRIA, 1996.
0004: // Please first read the full copyright statement in file COPYRIGHT.html
0005:
0006: package org.w3c.jigsaw.http;
0007:
0008: import java.io.ByteArrayInputStream;
0009: import java.io.ByteArrayOutputStream;
0010: import java.io.File;
0011: import java.io.FileInputStream;
0012: import java.io.FileNotFoundException;
0013: import java.io.IOException;
0014: import java.io.InputStream;
0015: import java.io.PrintStream;
0016:
0017: import java.util.Date;
0018: import java.util.Enumeration;
0019: import java.util.Hashtable;
0020: import java.util.Properties;
0021: import java.util.Vector;
0022:
0023: import java.net.InetAddress;
0024: import java.net.MalformedURLException;
0025: import java.net.ServerSocket;
0026: import java.net.Socket;
0027: import java.net.URL;
0028: import java.net.UnknownHostException;
0029:
0030: import org.w3c.tools.resources.AbstractContainer;
0031: import org.w3c.tools.resources.DummyResourceReference;
0032: import org.w3c.tools.resources.FilterInterface;
0033: import org.w3c.tools.resources.FramedResource;
0034: import org.w3c.tools.resources.InvalidResourceException;
0035: import org.w3c.tools.resources.LookupResult;
0036: import org.w3c.tools.resources.LookupState;
0037: import org.w3c.tools.resources.ProtocolException;
0038: import org.w3c.tools.resources.ReplyInterface;
0039: import org.w3c.tools.resources.RequestInterface;
0040: import org.w3c.tools.resources.Resource;
0041: import org.w3c.tools.resources.ResourceContext;
0042: import org.w3c.tools.resources.ResourceException;
0043: import org.w3c.tools.resources.ResourceFilter;
0044: import org.w3c.tools.resources.ResourceReference;
0045: import org.w3c.tools.resources.ResourceSpace;
0046: import org.w3c.tools.resources.ServerInterface;
0047:
0048: import org.w3c.tools.resources.store.ResourceStoreManager;
0049:
0050: import org.w3c.tools.resources.indexer.IndexerModule;
0051: import org.w3c.tools.resources.indexer.IndexersCatalog;
0052: import org.w3c.tools.resources.indexer.ResourceIndexer;
0053:
0054: import org.w3c.tools.timers.EventManager;
0055:
0056: import org.w3c.jigsaw.auth.RealmsCatalog;
0057:
0058: import org.w3c.jigsaw.resources.CheckpointResource;
0059:
0060: import org.w3c.jigsaw.daemon.ServerHandler;
0061: import org.w3c.jigsaw.daemon.ServerHandlerInitException;
0062: import org.w3c.jigsaw.daemon.ServerHandlerManager;
0063:
0064: import org.w3c.www.http.HTTP;
0065: import org.w3c.www.http.HeaderValue;
0066: import org.w3c.www.http.HttpEntityMessage;
0067: import org.w3c.www.http.HttpFactory;
0068: import org.w3c.www.http.HttpMessage;
0069: import org.w3c.www.http.HttpReplyMessage;
0070: import org.w3c.www.http.HttpRequestMessage;
0071: import org.w3c.www.http.HttpTokenList;
0072:
0073: import org.w3c.jigsaw.config.PropertySet;
0074:
0075: import org.w3c.util.IO;
0076: import org.w3c.util.ObservableProperties;
0077: import org.w3c.util.PropertyMonitoring;
0078: import org.w3c.util.Status;
0079:
0080: import org.w3c.tools.resources.ProtocolException;
0081: import org.w3c.tools.resources.upgrade.Upgrader;
0082:
0083: import org.w3c.www.mime.MimeParserFactory;
0084: import org.w3c.www.mime.MimeType;
0085:
0086: /**
0087: * <p>The server main class. This class can be used either through its
0088: * main method, to run a full httpd server, or simply by importing it
0089: * into your app. This latter possibility allows you to export some of
0090: * your application state through http.
0091: *
0092: * <p>The server itself uses this to report about memory consumption,
0093: * running threads, etc.
0094: */
0095:
0096: public class httpd implements ServerInterface, Runnable,
0097: PropertyMonitoring, Cloneable, Status {
0098: /**
0099: * The current displayed version of Jigsaw.
0100: */
0101: public static final String version = "2.2.6";
0102: /**
0103: * The current internal version counter of Jigsaw.
0104: * This counter is bumped anytime the configuration needs upgrade.
0105: */
0106: public static final int verscount = 4;
0107:
0108: /**
0109: * debug flag
0110: */
0111: public static final boolean debug = true;
0112:
0113: public static final String VERSCOUNT_P = "org.w3c.jigsaw.version.counter";
0114: /**
0115: * Name of the server software property.
0116: * The server software is the string that gets emited by Jigsaw
0117: * on each reply, to tell the client what server emited the reply.
0118: * <p>This property defaults to <strong>Jigsaw/1.0a</strong>.
0119: */
0120: public static final String SERVER_SOFTWARE_P = "org.w3c.jigsaw.server";
0121: /**
0122: * If the Host property is not set (see below), you can select if you
0123: * want to use FQDN (broken on some jdk implementation) or just the IP
0124: * address as the default host name, it usually defaults to "false"
0125: * means, use FQDN.
0126: */
0127: public static final String DEFHOSTIP_P = "org.w3c.jigsaw.defhostip";
0128: /**
0129: * Name of the server host property.
0130: * The host property should be set to the name of the host running
0131: * this server.
0132: * <p>This property defaults to the local host name, although if you want
0133: * directory listing to work propertly, you might need to provide the
0134: * full host name (including its domain).
0135: */
0136: public static final String HOST_P = "org.w3c.jigsaw.host";
0137: /**
0138: * Name of the property giving the server root directory.
0139: * <p>The server root directory is used to deduce a bunch of defaults
0140: * properties, when they don't have any specific values.
0141: * <p>This property has no defaults.
0142: */
0143: public static final String ROOT_P = "org.w3c.jigsaw.root";
0144: /**
0145: * Name of the property giving the server's config directory.
0146: */
0147: public static final String CONFIG_P = "org.w3c.jigsaw.config";
0148: /**
0149: * Name of the property giving the server space directory.
0150: * The server space directory should contain an index file, built
0151: * with the indexer.
0152: * <p>This property defaults to <org.w3c.jigsaw.root>/WWW.
0153: */
0154: public static final String SPACE_P = "org.w3c.jigsaw.space";
0155: /**
0156: * Name of the server port property.
0157: * At initializatiojn time, the server will bind its accepting socket
0158: * to the host its runs on, and to the provided port.
0159: * <p>This property defaults to <code>8888</code>.
0160: */
0161: public static final String PORT_P = "org.w3c.jigsaw.port";
0162: /**
0163: * Name of the server's trace property.
0164: * When set to true, the server will emit some traces indicating
0165: * its current state by using the logger <em>trace</em> methods.
0166: * This property should be set to <string>true</strong> if you want
0167: * clients to emit traces.
0168: * <p>This property defaults to <strong>false</strong>.
0169: */
0170: public static final String TRACE_P = "org.w3c.jigsaw.trace";
0171: /**
0172: * Name of the server's keep alive flag.
0173: * This property is used to determine wether this server should keep
0174: * its connection alive. Keeping connection alive requires this flag
0175: * to set to <strong>true</strong>, and clients to be compliant to the
0176: * keep alive feature as described in HTTP/1.1 specification.
0177: * <p>This property defaults to <strong>true</strong>.
0178: */
0179: public static final String KEEP_ALIVE_P = "org.w3c.jigsaw.keepAlive";
0180: /**
0181: * Name of the server's connection time out property.
0182: * This property gives, in milliseconds, the timeout to use for
0183: * connections that remains idel, waiting for an incoming request.
0184: * <p>This property defaults to <code>10000</code> milliseconds.
0185: */
0186: public static final String KEEP_TIMEOUT_P = "org.w3c.jigsaw.keep_alive.timeout";
0187: /**
0188: * Name of the server's request time out property.
0189: * The request time out property value indicates, in milliseconds, the
0190: * allowed duration of a request. Any request whose duration exceeds
0191: * this time out value will be aborted.
0192: * <p>This property defaults to <code>60000</code>.
0193: */
0194: public static final String REQUEST_TIMEOUT_P = "org.w3c.jigsaw.request.timeout";
0195: /**
0196: * Name of the client thread priority property.
0197: * Every client threads will run at the given priority, which should be
0198: * in the range of valid threads priority.
0199: * <p>This property defaults to <code>Thread.NORM_PRIORITY</code>.
0200: */
0201: public static final String CLIENT_PRIORITY_P = "org.w3c.jigsaw.client.priority";
0202: /**
0203: * Nam eof the property giving the client output buffer size.
0204: * Each clients, when not using a shuffler, has to allocate its own
0205: * output buffer, Output buffer size may increase/decrease significantly
0206: * the Jigsaw performances, so change it with care.
0207: * <p>This property defaults to <code>8192</code>.
0208: */
0209: public static final String CLIENT_BUFSIZE_P = "org.w3c.jigsaw.client.bufsize";
0210: /**
0211: * Name of the property indicating wether client should be debuged.
0212: * When debuged, clients emit some traces, through the server logger
0213: * about their current state.
0214: * <p>This property defaults to <strong>false</strong>.
0215: */
0216: public static final String CLIENT_DEBUG_P = "org.w3c.jigsaw.client.debug";
0217: /**
0218: * Name of property that indicates if some security manager is required.
0219: * You usually don't want to run a security manager for the server,
0220: * except in the unlikely (right now) case that you want the server to
0221: * be able to host agents.
0222: * <p>This property defaults to <string>false</strong>.
0223: */
0224: public static final String USE_SM_P = "org.w3c.http.useSecurityManager";
0225: /**
0226: * Name of property indicating the logger class to use.
0227: * The Jigsaw server allows you to implement your own logger. The only
0228: * logger provided with the core server is the
0229: * <code>org.w3c.jigsaw.core.CommonLogger</code>, which implements the
0230: * common log format.
0231: * <p>Property defaults to <code>org.w3c.jigsaw.core.CommonLogger</code>
0232: */
0233: public static final String LOGGER_P = "org.w3c.jigsaw.logger";
0234: /**
0235: * Name of property indicating the "lenient" mode of HTTP parsing.
0236: * <p>Property defaults to <code>true</code>
0237: */
0238: public static final String LENIENT_P = "org.w3c.jigsaw.http.lenient";
0239: /**
0240: * Name of the property indicating the client factory class.
0241: */
0242: public static final String CLIENT_FACTORY_P = "org.w3c.jigsaw.http.ClientFactory";
0243: /**
0244: * Name of the property giving the shuffler path.
0245: * This property should be set if you are to use the shuffler. The
0246: * data shuffler is an external process to whiuch Jigsaw delegates
0247: * the task of writing back document content to clients. Use this
0248: * when you think your server isn't fast enough.
0249: * <p>This should be an absloute path.
0250: * <p>This property has no defaults.
0251: */
0252: public static final String SHUFFLER_PATH_P = "org.w3c.jigsaw.shuffler.path";
0253:
0254: /**
0255: * Name of the property giving the name of the root resource.
0256: * Upon startup, or restart, the server will look in its root store
0257: * a resource whose name is given by this resource, and install it as
0258: * its root resource.
0259: * <p>This property defaults to <code>root</code>.
0260: */
0261: public static final String ROOT_NAME_P = "org.w3c.jigsaw.root.name";
0262: public static final String ROOT_CLASS_P = "org.w3c.jigsaw.root.class";
0263:
0264: /**
0265: * Max number of store loaded in memory.
0266: */
0267: public static final String MAX_LOADED_STORE_P = "org.w3c.jigsaw.loadedstore";
0268: public static final int MAX_LOADED_STORE = 128;
0269: int max_loaded_store = -1;
0270:
0271: /**
0272: * Max number of store loaded in memory.
0273: */
0274: public static final String STORE_SIZE_LIMIT_P = "org.w3c.jigsaw.storesize";
0275: public static final int STORE_SIZE_LIMIT = -1;
0276: int store_size_limit = -1;
0277:
0278: /**
0279: * Name of the property giving the path of the property file.
0280: * this should be used internally (for restart) only.
0281: * <p>This property defaults to <code>config/httpd.props</code>.
0282: */
0283: public static final String PROPS_P = "org.w3c.jigsaw.propfile";
0284: /**
0285: * Name of the property indicating if the file-system is case sensitive.
0286: * This property determines wether Jigsaw will list all files to check
0287: * for case sensitivity, before creating a resource for that file.
0288: * <p>For obvious security reasons, this property defaults to
0289: * <strong>true</strong>.
0290: */
0291: public static final String FS_SENSITIVITY = "org.w3c.jigsaw.checkSensitivity";
0292: /**
0293: * Name of the property indicating the URL of Jigsaw's help.
0294: * This URL should point to the URL path of Jigsaw's documentation
0295: * as served by that server.
0296: */
0297: public static String DOCURL_P = "org.w3c.jigsaw.docurl";
0298:
0299: /**
0300: * Name of the property indicating the startup classes to load
0301: */
0302: public static String STARTUP_P = "org.w3c.jigsaw.startup";
0303:
0304: /**
0305: * Name of the property indicating the trash directory.
0306: */
0307: public static String TRASHDIR_P = "org.w3c.jigsaw.trashdir";
0308:
0309: /**
0310: * Name of the property indicating the URL of Jigsaw's chekpointer.
0311: */
0312: public static String CHECKURL_P = "org.w3c.jigsaw.checkpointer";
0313:
0314: /**
0315: * Name of the property indicating the public methods allowed on that
0316: * server.
0317: * This property should provide a <code>|</code> separated list of
0318: * methods available on that server.
0319: * <p>This property defaults to: <strong>GET | HEAD | PUT | POST
0320: * | OPTIONS | DELETE | LINK | UNLINK | TRACE</code>.
0321: */
0322: public static String PUBLIC_P = "org.w3c.jigsaw.publicMethods";
0323: /**
0324: * Name of the property that indicates the root resource for edit.
0325: * The edit root resource is the one that will show up by default
0326: * when accessing the admin server from JigAdmin.
0327: */
0328: public static String EDIT_ROOT_P = "org.w3c.jigsaw.edit.root";
0329:
0330: /**
0331: * Name of the serializer class used to store resources.
0332: */
0333: public static String SERIALIZER_CLASS_P = "org.w3c.jigsaw.serializer";
0334:
0335: /**
0336: * UNIX - Name of the property that indicates the server user.
0337: * When set, the server will try to turn itself to the given user name
0338: * after initialization. If this fail, the server will abort.
0339: * <p>This property has no default value.
0340: */
0341: public static String SERVER_USER_P = "org.w3c.jigsaw.unix.user";
0342: /**
0343: * UNIX - Name of the property that indicates the server group.
0344: * When set, the server will try to turn itself to the given group name
0345: * after initialization. If this fail, the server will abort.
0346: * <p>This property has no default value.
0347: */
0348: public static String SERVER_GROUP_P = "org.w3c.jigsaw.unix.group";
0349:
0350: /**
0351: * Should we show the URL that triggered an error in the error message
0352: * or not?
0353: * Displaying it can lead to so-called "cross-scripting" hacks
0354: */
0355: public static String DISPLAY_URL_ON_ERROR_P = "org.w3c.jigsaw.error.url";
0356:
0357: /**
0358: * The list of currently running servers.
0359: */
0360: private static Hashtable servers = new Hashtable();
0361:
0362: /* FIXME */public Thread thread = null;
0363:
0364: private String software = "Jigsaw/2.2.6";
0365: private ServerSocket socket = null;
0366: private Logger logger = null;
0367: private Shuffler shuffler = null;
0368: public EventManager timer = null;
0369: ClientFactory factory = null;
0370:
0371: // FIXME This is a temporary hack to take care of clones
0372: protected int[] instances = { 1 }; // object containing the nb of clones
0373:
0374: /**
0375: * The (optional) server handler manager that created us.
0376: */
0377: private ServerHandlerManager shm = null;
0378: /**
0379: * The server identifier can be any String.
0380: * This identifier is used by the configuration applets, to show all the
0381: * running servers in the process, and to edit their properties, etc.
0382: */
0383: private String identifier = null;
0384:
0385: /**
0386: * This server statistics object.
0387: */
0388: private httpdStatistics statistics = null;
0389: /**
0390: * This server set of properties.
0391: */
0392: protected ObservableProperties props = null;
0393: /**
0394: * Should the server run the server in trace mode ?
0395: */
0396: private boolean tracep = false;
0397: /**
0398: * Should the server try to keep connections alive ?
0399: */
0400: private boolean keep = true;
0401: /**
0402: * What logger class should the server use to log accesses.
0403: */
0404: private String logger_class = null;
0405: /**
0406: * Should we display URL on error?
0407: */
0408: private boolean uri_error = false;
0409: /**
0410: * Are we lenient in HTTP mode?
0411: */
0412: private boolean lenient = true;
0413: /**
0414: * What client factory class should the server use.
0415: */
0416: private String factory_class = "org.w3c.jigsaw.http.socket.SocketClientFactory";
0417: /**
0418: * The coordinate of the shuffler, or <strong>null</strong> is none is to
0419: * be used.
0420: */
0421: private String shuffler_path = null;
0422: /**
0423: * The server's root directory.
0424: */
0425: private File root_dir = null;
0426: /**
0427: * The directory containing the server exported documents.
0428: */
0429: private File space_dir = null;
0430: /**
0431: * FIXME check
0432: * The server host name.
0433: */
0434: protected String host = null;
0435: /**
0436: * FIXME check
0437: * The server port.
0438: */
0439: protected int port = 8001;
0440: /**
0441: * This server client debug flag.
0442: */
0443: private boolean client_debug = false;
0444: /**
0445: * This server's request time slice, in milliseconds.
0446: */
0447: private int request_time_out = 1200000;
0448: /**
0449: * This server's connection allowed idle time in milliseconds.
0450: */
0451: private int connection_time_out = 1200000;
0452: /**
0453: * This server's client thread priority.
0454: */
0455: private int client_priority = Thread.NORM_PRIORITY;
0456: /**
0457: * This server's clients buffer size.
0458: */
0459: private int client_bufsize = 4096;
0460: /**
0461: * Is the file system case-sensitive ?
0462: */
0463: private boolean sensitivity = true;
0464: /**
0465: * This server root entity.
0466: */
0467: public FramedResource root = null;
0468: /**
0469: * the old root ResourceReference
0470: */
0471: private ResourceReference root_reference = null;
0472: /**
0473: * FIXME check for clones
0474: * This server URL.
0475: */
0476: protected URL url = null;
0477: /**
0478: * Finishing (killing) the server.
0479: */
0480: private boolean finishing = false;
0481: /**
0482: * Finishing, but restart straight up.
0483: */
0484: private boolean restarting = false;
0485: /**
0486: * The indexer attached to this server.
0487: */
0488: private ResourceIndexer indexer = null;
0489: /**
0490: * The realm catalog
0491: */
0492: private RealmsCatalog realms = null;
0493: /**
0494: * The resource store manager for this server.
0495: */
0496: private ResourceStoreManager manager = null;
0497: /**
0498: * The root resource's identifier.
0499: */
0500: private String root_name = null;
0501: private String root_class = null;
0502: /**
0503: * The full URL of Jigsaw's documentation as served by that server.
0504: */
0505: private String docurl = null;
0506:
0507: /**
0508: * The trash directory
0509: */
0510: private String trashdir = null;
0511:
0512: /**
0513: * The full URL of Jigsaw's chekpointer.
0514: */
0515: private String checkurl = null;
0516:
0517: /**
0518: * The list of public methods for that server.
0519: */
0520: private String publicMethods[] = { "GET", "HEAD", "PUT", "POST",
0521: "LINK", "UNLINK", "DELETE", "OPTIONS", "TRACE" };
0522: /**
0523: * The <code>Public</code> header value, computed out of the
0524: * <code>publicMethods</code> variable.
0525: */
0526: private HttpTokenList publicHeader = null;
0527: /**
0528: * The edit root for that server.
0529: */
0530: private ResourceReference editroot = null;
0531: /**
0532: * the sets of properties of this server
0533: */
0534: private Vector propSet = new Vector(8);
0535: /**
0536: * the catalog of indexers of this server
0537: */
0538: private IndexersCatalog indexers = null;
0539: /**
0540: * the resource context of this server
0541: */
0542: private ResourceContext context = null;
0543: /**
0544: * the config resource of this server
0545: */
0546: private AbstractContainer configResource = null;
0547: /**
0548: * and its dummy resource reference
0549: */
0550: private ResourceReference rr_configResource = null;
0551:
0552: // is this server a clone?
0553: private boolean isAClone = false;
0554: // our master server ID, if we are a clone
0555: private String masterID = null;
0556:
0557: /**
0558: * The property monitoring implementation.
0559: * @param name The name of the property that has changed.
0560: * @return A boolean, <strong>true</strong> if the changed was taken into
0561: * account, <strong>false</strong> otherwise.
0562: */
0563: public boolean propertyChanged(String name) {
0564: // Is this a property we are interested in ?
0565: if (name.equals(SERVER_SOFTWARE_P)) {
0566: software = props.getString(name, software);
0567: return true;
0568: } else if (name.equals(TRACE_P)) {
0569: tracep = props.getBoolean(name, tracep);
0570: errlog(name + " changed to " + tracep);
0571: return true;
0572: } else if (name.equals(LENIENT_P)) {
0573: lenient = props.getBoolean(name, lenient);
0574: errlog(name + " changed to " + lenient);
0575: return true;
0576: } else if (name.equals(DISPLAY_URL_ON_ERROR_P)) {
0577: uri_error = props.getBoolean(name, uri_error);
0578: errlog(name + " changed to " + uri_error);
0579: return true;
0580: } else if (name.equals(KEEP_ALIVE_P)) {
0581: keep = props.getBoolean(name, keep);
0582: errlog(name + " changed to " + keep);
0583: return true;
0584: } else if (name.equals(LOGGER_P)) {
0585: String tmp_logger_class;
0586: Logger tmp_logger;
0587: tmp_logger_class = props.getString(name, logger_class);
0588: // for now the removal of the logger should be done by hand
0589: if (!tmp_logger_class.equals(logger_class)) {
0590: try {
0591: tmp_logger = (Logger) Class.forName(
0592: tmp_logger_class).newInstance();
0593: } catch (Exception ex) {
0594: errlog(name + " change failed (bad logger class)");
0595: return false;
0596: }
0597: synchronized (this ) {
0598: if (logger != null) {
0599: logger.shutdown();
0600: }
0601: tmp_logger.initialize(this );
0602: logger = tmp_logger;
0603: logger_class = tmp_logger_class;
0604: }
0605: }
0606: return true;
0607: } else if (name.equals(ROOT_NAME_P)) {
0608: String newname = props.getString(name, null);
0609: if (changeRoot(newname) != null) {
0610: errlog("new root resource [" + newname + "]");
0611: return true;
0612: } else {
0613: errlog("failed to change root to [" + newname + "].");
0614: return false;
0615: }
0616: } else if (name.equals(SPACE_P)) {
0617: errlog(name + " change failed (server running)");
0618: return false;
0619: } else if (name.equals(HOST_P)) {
0620: errlog(name + " change failed (server running)");
0621: return false;
0622: } else if (name.equals(PORT_P)) {
0623: // we will restart the server
0624: errlog(name + " switching port : "
0625: + props.getInteger(name, 80));
0626: int newport = props.getInteger(name, 80);
0627: if (port != newport) {
0628: int oldport = port;
0629: port = newport;
0630: checkpoint();
0631: ServerSocket newsocket = null;
0632: try {
0633: newsocket = factory.createServerSocket();
0634: socket.close();
0635: socket = newsocket;
0636: } catch (Exception ex) {
0637: try {
0638: newsocket.close();
0639: } catch (Exception e) {
0640: }
0641: ;
0642: port = oldport;
0643: // an error occured, return false
0644: return false;
0645: }
0646: }
0647: return true;
0648: } else if (name.equals(CLIENT_DEBUG_P)) {
0649: client_debug = props.getBoolean(name, client_debug);
0650: errlog(name + " changed to " + client_debug);
0651: return true;
0652: } else if (name.equals(REQUEST_TIMEOUT_P)) {
0653: request_time_out = props.getInteger(name, request_time_out);
0654: errlog(name + " changed to " + request_time_out);
0655: return true;
0656: } else if (name.equals(KEEP_TIMEOUT_P)) {
0657: connection_time_out = props.getInteger(name,
0658: connection_time_out);
0659: errlog(name + " changed to " + connection_time_out);
0660: return true;
0661: } else if (name.equals(CLIENT_PRIORITY_P)) {
0662: client_priority = props.getInteger(name, client_priority);
0663: errlog(name + " changed to " + client_priority);
0664: return true;
0665: } else if (name.equals(CLIENT_BUFSIZE_P)) {
0666: client_bufsize = props.getInteger(name, client_bufsize);
0667: errlog(name + " changed to " + client_bufsize);
0668: return true;
0669: } else if (name.equals(DOCURL_P)) {
0670: String propval = props.getString(name, docurl);
0671: try {
0672: URL u = new URL(getURL(), propval);
0673: docurl = u.toExternalForm();
0674: } catch (Exception ex) {
0675: return false;
0676: }
0677: return true;
0678: } else if (name.equals(TRASHDIR_P)) {
0679: trashdir = props.getString(name, trashdir);
0680: File dir = new File(trashdir);
0681: if (!dir.exists())
0682: dir.mkdirs();
0683: errlog(name + " changed to " + trashdir);
0684: return true;
0685: } else if (name.equals(CHECKURL_P)) {
0686: checkurl = props.getString(name, checkurl);
0687: errlog(name + " changed to " + checkurl);
0688: return true;
0689: } else if (name.equals(PUBLIC_P)) {
0690: publicMethods = props.getStringArray(name, publicMethods);
0691: publicHeader = null;
0692: return true;
0693: } else if (name.equals(SERVER_USER_P)) {
0694: String user = props.getString(SERVER_USER_P, null);
0695: errlog("new user: " + user);
0696: return false;
0697: } else if (name.equals(SERVER_GROUP_P)) {
0698: String group = props.getString(SERVER_GROUP_P, null);
0699: errlog("new group: " + group);
0700: return false;
0701: } else {
0702: // We don't care about this one
0703: return true;
0704: }
0705: }
0706:
0707: /**
0708: * Initialize this server indexer.
0709: */
0710: private void initializeIndexer() {
0711: ResourceContext c = getDefaultContext();
0712: IndexersCatalog ic = getIndexersCatalog();
0713: IndexerModule m = new IndexerModule(ic);
0714: // Register the default indexer:
0715: m.registerIndexer(c, "default");
0716: // Register the indexer module:
0717: c.registerModule(IndexerModule.NAME, m);
0718: }
0719:
0720: /**
0721: * Initialize the resource store manager for this server.
0722: */
0723: private void initializeResourceSpace(String server_name,
0724: String root_class, String root_name, String serializer,
0725: int max_loaded_store) {
0726: Hashtable defs = new Hashtable(11);
0727: defs.put("url", "/");
0728: defs.put("directory", space_dir);
0729: defs.put("context", getDefaultContext());
0730: this .manager = new ResourceStoreManager(server_name, this
0731: .getStoreDirectory(), root_class, root_name,
0732: serializer, max_loaded_store, store_size_limit, defs);
0733: }
0734:
0735: /**
0736: * Lookup the root store for some resource.
0737: * @param name The name of the resource to lookup in the root store.
0738: * @return The loaded resource, or <strong>null</strong>.
0739: */
0740: public ResourceReference loadResource(String name) {
0741: Hashtable defs = new Hashtable(11);
0742: defs.put("url", "/" + name);
0743: defs.put("directory", space_dir);
0744: ResourceContext context = new ResourceContext(
0745: getDefaultContext());
0746: defs.put("context", context);
0747: ResourceReference rr = manager.loadRootResource(name, defs);
0748: if (rr != null)
0749: context.setResourceReference(rr);
0750: return rr;
0751: }
0752:
0753: /**
0754: * start the automatic checkpoint
0755: */
0756: public void startCheckpoint() {
0757: if (checkurl == null) {
0758: errlog("checkpointer URL unknown.");
0759: checkpoint();
0760: return;
0761: }
0762: try {
0763: LookupState ls = new LookupState(checkurl);
0764: LookupResult lr = new LookupResult(root
0765: .getResourceReference());
0766: if (root.lookup(ls, lr)) {
0767: ResourceReference target = lr.getTarget();
0768: if (target != null) {
0769: try {
0770: Resource res = target.lock();
0771: if (res instanceof CheckpointResource) {
0772: ((CheckpointResource) res).activate();
0773: errlog("Chekpointer started at: "
0774: + new Date() + ".");
0775: } else {
0776: errlog("The chekpointer url ("
0777: + checkurl
0778: + ") doesn't point to a CheckpointResource");
0779: checkpoint();
0780: }
0781: } catch (InvalidResourceException ex) {
0782: errlog("Invalid Checkpointer : "
0783: + ex.getMessage());
0784: checkpoint();
0785: } finally {
0786: target.unlock();
0787: }
0788: } else {
0789: errlog("can't find Checkpointer");
0790: checkpoint();
0791: }
0792: } else {
0793: errlog("Checkpointer: lookup fail");
0794: checkpoint();
0795: }
0796: } catch (ProtocolException ex) {
0797: errlog("Checkpointer : " + ex.getMessage());
0798: checkpoint();
0799: }
0800: }
0801:
0802: /**
0803: * Checkpoint all cached data, by saving them to disk.
0804: */
0805: public void checkpoint() {
0806: manager.checkpoint();
0807: }
0808:
0809: /**
0810: * Dynamically change the root resource for the server.
0811: * This is kind a dangerous operation !
0812: * @param name The name of the new root resource, to be found in the
0813: * root resource store.
0814: * @return The new installed root resource, or <strong>null</strong>
0815: * if we couldn't load the given resource.
0816: */
0817: public synchronized ResourceReference loadRoot(String name) {
0818: ResourceReference newroot = null;
0819: String editRootName = props.getString(EDIT_ROOT_P, null);
0820:
0821: // Restore the appropriate root resource:
0822: Hashtable defs = new Hashtable(11);
0823: defs.put("url", "/");
0824: defs.put("directory", space_dir);
0825: ResourceContext context = null;
0826: if ((editRootName != null) && (!name.equals(editRootName))) {
0827: if (editroot == null) {
0828: Hashtable edefs = new Hashtable(11);
0829: edefs.put("url", "/");
0830: edefs.put("directory", space_dir);
0831: ResourceContext econtext = new ResourceContext(
0832: getDefaultContext());
0833: edefs.put("context", econtext);
0834: editroot = manager
0835: .loadRootResource(editRootName, edefs);
0836: if (editroot != null)
0837: econtext.setResourceReference(editroot);
0838: }
0839: context = new ResourceContext(editroot);
0840: } else {
0841: context = new ResourceContext(getDefaultContext());
0842: }
0843: defs.put("context", context);
0844: ResourceReference rr = manager.loadRootResource(name, defs);
0845: if (rr != null)
0846: context.setResourceReference(rr);
0847: return rr;
0848: }
0849:
0850: private synchronized FramedResource changeRoot(String name) {
0851: ResourceReference newroot = loadRoot(name);
0852: FramedResource oldroot = this .root;
0853: String oldroot_name = this .root_name;
0854: if (newroot != null) {
0855: try {
0856: this .root = (FramedResource) newroot.lock();
0857: this .root_name = name;
0858: if (root_reference != null)
0859: root_reference.unlock();
0860: root_reference = newroot;
0861: return root;
0862: } catch (InvalidResourceException ex) {
0863: this .root = oldroot;
0864: this .root_name = oldroot_name;
0865: return null;
0866: }
0867: }
0868: return null;
0869: }
0870:
0871: /**
0872: * Initialize this server's root resource.
0873: * @exception ServerHandlerInitException if unable to be initialized.
0874: */
0875:
0876: private void initializeRootResource()
0877: throws ServerHandlerInitException {
0878: // Check for un-found root resource:
0879: if (changeRoot(root_name) == null) {
0880: String err = ("Unable to restore root resource ["
0881: + root_name + "]" + " from store (not found).");
0882: throw new ServerHandlerInitException(err);
0883: }
0884: }
0885:
0886: /**
0887: * Initialize the realms catalog for this server.
0888: */
0889:
0890: private void initializeRealmsCatalog() {
0891: this .realms = new RealmsCatalog(new ResourceContext(
0892: getDefaultContext()));
0893: }
0894:
0895: /**
0896: * Initialize the server logger and the statistics object.
0897: * @exception ServerHandlerInitException if unable to be initialized.
0898: */
0899:
0900: private void initializeLogger() throws ServerHandlerInitException {
0901: if (logger_class != null) {
0902: try {
0903: logger = (Logger) Class.forName(logger_class)
0904: .newInstance();
0905: logger.initialize(this );
0906: } catch (Exception ex) {
0907: String err = ("Unable to create logger of class ["
0908: + logger_class + "]" + "\r\ndetails: \r\n" + ex
0909: .getMessage());
0910: throw new ServerHandlerInitException(err);
0911: }
0912: } else {
0913: warning(getBanner() + ": no logger specified, not logging.");
0914: }
0915: // Initialize the statistics object:
0916: statistics = new httpdStatistics(this );
0917: }
0918:
0919: /**
0920: * Initialize the server socket, create a suitable client factory, start.
0921: * This method creates the physicall listening socket, and instantiate
0922: * an appropriate client factory for that socket. It then run the accept
0923: * thread, ready to accept new incomming connections.
0924: * @exception ServerHandlerInitException if unable to be initialized.
0925: */
0926:
0927: private void initializeServerSocket()
0928: throws ServerHandlerInitException {
0929: // Create a suitable client factory:
0930: try {
0931: Class c = Class.forName(factory_class);
0932: factory = (ClientFactory) c.newInstance();
0933: factory.initialize(this );
0934: } catch (Exception ex) {
0935: String err = ("Unable to create a client factory of class "
0936: + "\"" + factory_class + "\"" + " details: \r\n" + ex
0937: .getMessage());
0938: throw new ServerHandlerInitException(err);
0939: }
0940: // If needed, create a server socket instance for that context:
0941: try {
0942: socket = factory.createServerSocket();
0943: } catch (IOException ex) {
0944: String err = ("Unable to create server socket on port "
0945: + port + ": " + ex.getMessage() + ".");
0946: throw new ServerHandlerInitException(err);
0947: }
0948: this .thread = new Thread(this );
0949: this .thread.setName(identifier);
0950: this .thread.setPriority(Thread.MAX_PRIORITY);
0951: }
0952:
0953: protected MimeParserFactory getMimeClientFactory(Client client) {
0954: return new MimeClientFactory(client);
0955: }
0956:
0957: /**
0958: * Initialize our event manager.
0959: */
0960:
0961: private void initializeEventManager() {
0962: this .timer = new EventManager();
0963: this .timer.setDaemon(true);
0964: this .timer.start();
0965: }
0966:
0967: /**
0968: * startup classes
0969: */
0970: protected void loadStartupClasses() {
0971: String classes[] = props.getStringArray(STARTUP_P, null);
0972: if (classes != null) {
0973: for (int i = 0; i < classes.length; i++) {
0974: try {
0975: Class c = Class.forName(classes[i]);
0976: httpdPreloadInterface hpi = (httpdPreloadInterface) c
0977: .newInstance();
0978: hpi.preload(this );
0979: } catch (ClassNotFoundException cnfex) {
0980: errlog("Startup class not found : "
0981: + cnfex.getMessage());
0982: } catch (InstantiationException iex) {
0983: errlog("Unable to instanciate : "
0984: + iex.getMessage());
0985: } catch (ClassCastException ccex) {
0986: errlog("Startup classes must be instance of "
0987: + "httpdPreloadInterface: "
0988: + ccex.getMessage());
0989: } catch (IllegalAccessException iaex) {
0990: errlog("IllegalAccess " + iaex.getMessage());
0991: }
0992: }
0993: }
0994: }
0995:
0996: /**
0997: * FIXME protected for now to handle clones
0998: * Initialize some of the servers instance values from properties.
0999: * @exception ServerHandlerInitException if unable to be initialized.
1000: */
1001:
1002: protected void initializeProperties()
1003: throws ServerHandlerInitException {
1004: // Compute some default values (host and port)
1005: String defhost = null;
1006: String rootstr = null;
1007: String spacestr = null;
1008:
1009: boolean ip_host = props.getBoolean(DEFHOSTIP_P, false);
1010:
1011: try {
1012: if (ip_host)
1013: defhost = InetAddress.getLocalHost().getHostAddress();
1014: else
1015: defhost = InetAddress.getLocalHost().getHostName();
1016: } catch (UnknownHostException e) {
1017: defhost = null;
1018: }
1019: // Second stage: get property values:
1020: software = props.getString(SERVER_SOFTWARE_P, software);
1021: tracep = props.getBoolean(TRACE_P, tracep);
1022: uri_error = props.getBoolean(DISPLAY_URL_ON_ERROR_P, false);
1023: lenient = props.getBoolean(LENIENT_P, true);
1024: keep = props.getBoolean(KEEP_ALIVE_P, keep);
1025: logger_class = props.getString(LOGGER_P, null);
1026: factory_class = props
1027: .getString(CLIENT_FACTORY_P, factory_class);
1028: shuffler_path = props.getString(SHUFFLER_PATH_P, null);
1029: rootstr = props.getString(ROOT_P, null);
1030: spacestr = props.getString(SPACE_P, null);
1031: host = props.getString(HOST_P, defhost);
1032: port = props.getInteger(PORT_P, port);
1033: root_name = props.getString(ROOT_NAME_P, "root");
1034: root_class = props.getString(ROOT_CLASS_P, null);
1035: max_loaded_store = props.getInteger(MAX_LOADED_STORE_P,
1036: MAX_LOADED_STORE);
1037: store_size_limit = props.getInteger(STORE_SIZE_LIMIT_P,
1038: STORE_SIZE_LIMIT);
1039: sensitivity = props.getBoolean(FS_SENSITIVITY, true);
1040: publicMethods = props.getStringArray(PUBLIC_P, publicMethods);
1041: // Get client properties:
1042: client_debug = props.getBoolean(CLIENT_DEBUG_P, client_debug);
1043: request_time_out = props.getInteger(REQUEST_TIMEOUT_P,
1044: request_time_out);
1045: connection_time_out = props.getInteger(KEEP_TIMEOUT_P,
1046: connection_time_out);
1047: client_priority = props.getInteger(CLIENT_PRIORITY_P,
1048: client_priority);
1049: client_bufsize = props.getInteger(CLIENT_BUFSIZE_P,
1050: client_bufsize);
1051: // Check that a host name has been given:
1052: if (host == null)
1053: throw new ServerHandlerInitException(this .getClass()
1054: .getName()
1055: + "[initializeProperties]: " + "[host] undefined.");
1056: // Default the root directory to the current directory:
1057: if (rootstr == null) {
1058: // Try the current directory as root:
1059: rootstr = System.getProperties().getProperty("user.dir",
1060: null);
1061: if (rootstr == null)
1062: throw new ServerHandlerInitException(this .getClass()
1063: .getName()
1064: + "[initializeProperties]:"
1065: + "[root] undefined.");
1066: }
1067: root_dir = new File(rootstr);
1068: // Default the space directory to root/WWW
1069: if (spacestr == null)
1070: space_dir = new File(root_dir, "WWW");
1071: else
1072: space_dir = new File(spacestr);
1073: // Help URL:
1074: String propval = props.getString(DOCURL_P, null);
1075: if (propval != null) {
1076: try {
1077: URL u = new URL(getURL(), propval);
1078: docurl = u.toExternalForm();
1079: } catch (Exception ex) {
1080: }
1081: }
1082: // Trash Dir
1083: trashdir = props.getString(TRASHDIR_P, trashdir);
1084: // checpointer url
1085: checkurl = props.getString(CHECKURL_P, checkurl);
1086: }
1087:
1088: /**
1089: * Register a property set to the server.
1090: * @param propSet The property set to register.
1091: */
1092: public synchronized void registerPropertySet(PropertySet set) {
1093: // Add this set to our known property set:
1094: propSet.addElement(set);
1095: }
1096:
1097: /**
1098: * Enumerate all the registered property sets
1099: * @return an enumeration of </code>PropertySet</code>
1100: */
1101: public Enumeration enumeratePropertySet() {
1102: return propSet.elements();
1103: }
1104:
1105: /**
1106: * Get a property set matching a specific name
1107: * @return a Resource, the property set found
1108: */
1109: public Resource getPropertySet(String name) {
1110: for (int i = 0; i < propSet.size(); i++) {
1111: PropertySet set = (PropertySet) propSet.elementAt(i);
1112: if (set.getIdentifier().equals(name))
1113: return set;
1114: }
1115: return null;
1116: }
1117:
1118: protected void initializePropertySets() {
1119: registerPropertySet(new GeneralProp("general", this ));
1120: registerPropertySet(new ConnectionProp("connection", this ));
1121: registerPropertySet(new LoggingProp("logging", this ));
1122: }
1123:
1124: /**
1125: * Get this server statistics.
1126: */
1127:
1128: public httpdStatistics getStatistics() {
1129: return statistics;
1130: }
1131:
1132: /**
1133: * Get this server properties.
1134: */
1135:
1136: public ObservableProperties getProperties() {
1137: return props;
1138: }
1139:
1140: /**
1141: * Is the underlying file-system case sensitive ?
1142: * @return A boolean, <strong>true</strong> if file system is case
1143: * sensitive, <strong>false</strong> otherwise.
1144: */
1145: public boolean checkFileSystemSensitivity() {
1146: return sensitivity;
1147: }
1148:
1149: /**
1150: * Get the full URL of Jigsaw's documentation.
1151: * @return A String encoded URL.
1152: */
1153: public String getDocumentationURL() {
1154: return docurl;
1155: }
1156:
1157: /**
1158: * Get the tracsh directory
1159: */
1160: public String getTrashDirectory() {
1161: return trashdir;
1162: }
1163:
1164: /**
1165: * Get the client's debug flags from the properties.
1166: */
1167: public final boolean getClientDebug() {
1168: return client_debug;
1169: }
1170:
1171: /**
1172: * Does this server wants clients to try keeping connections alive ?
1173: */
1174: public final boolean getClientKeepConnection() {
1175: return keep;
1176: }
1177:
1178: /**
1179: * Get the request allowed time slice from the properties.
1180: */
1181: public final int getRequestTimeOut() {
1182: return request_time_out;
1183: }
1184:
1185: /**
1186: * Get the connection allowed idle time from the properties.
1187: */
1188: public final int getConnectionTimeOut() {
1189: return connection_time_out;
1190: }
1191:
1192: /**
1193: * Get the client's threads priority from the properties.
1194: */
1195: public final int getClientThreadPriority() {
1196: return client_priority;
1197: }
1198:
1199: /**
1200: * Get the client's buffer size.
1201: */
1202: public final int getClientBufferSize() {
1203: return client_bufsize;
1204: }
1205:
1206: /**
1207: * Get this server host name.
1208: */
1209: public String getHost() {
1210: return host;
1211: }
1212:
1213: /**
1214: * Get this server port number.
1215: */
1216: public int getPort() {
1217: return port;
1218: }
1219:
1220: /**
1221: * Get the server current root resource.
1222: */
1223: public FramedResource getRoot() {
1224: return root;
1225: }
1226:
1227: /**
1228: * get the resource reference of the root resource of the server
1229: */
1230: public ResourceReference getRootReference() {
1231: return root_reference;
1232: }
1233:
1234: /**
1235: * Get the logger for that server.
1236: * @return A Logger compatible instance, or <strong>null</strong> if
1237: * no logger specified.
1238: */
1239: public Logger getLogger() {
1240: return logger;
1241: }
1242:
1243: /**
1244: * Get the server's edit root resource.
1245: * The edit root is the one that shows up by default when using JigAdmin
1246: * It is named "root" in the interface.
1247: * @return An HTTPResource.
1248: */
1249:
1250: public synchronized ResourceReference getEditRoot() {
1251: if (editroot == null) {
1252: // Check for the appropriate property:
1253: String name = props.getString(EDIT_ROOT_P, null);
1254: if (name != null) {
1255: editroot = loadRoot(name);
1256: }
1257: if (editroot == null) {
1258: editroot = getRootReference();
1259: }
1260: }
1261: return editroot;
1262: }
1263:
1264: /**
1265: * Get the server URL.
1266: */
1267: public URL getURL() {
1268: if (url == null) {
1269: try {
1270: if (port != 80)
1271: url = new URL("http", host, port, "/");
1272: else
1273: url = new URL("http", host, "/");
1274: } catch (MalformedURLException ex) {
1275: throw new RuntimeException(
1276: "unable to build server's URL");
1277: }
1278: }
1279: return url;
1280: }
1281:
1282: /**
1283: * Get the server software string.
1284: */
1285: public String getSoftware() {
1286: return software;
1287: }
1288:
1289: /**
1290: * Get the server local port
1291: */
1292: public int getLocalPort() {
1293: return socket.getLocalPort();
1294: }
1295:
1296: /**
1297: * Get this server identifier.
1298: */
1299: public String getIdentifier() {
1300: return identifier;
1301: }
1302:
1303: /**
1304: * Get the server inet address
1305: * @return The INET address this server is listening to.
1306: */
1307: public InetAddress getInetAddress() {
1308: return socket.getInetAddress();
1309: }
1310:
1311: /**
1312: * Get this server root directory.
1313: */
1314: public File getRootDirectory() {
1315: return root_dir;
1316: }
1317:
1318: /**
1319: * Get this server space diretory
1320: */
1321: public File getSpaceDir() {
1322: return space_dir;
1323: }
1324:
1325: /**
1326: * Get this server config directory.
1327: */
1328: public File getConfigDirectory() {
1329: File file = props.getFile(CONFIG_P, null);
1330: return (file == null) ? new File(getRootDirectory(), "config")
1331: : file;
1332: }
1333:
1334: /**
1335: * Get this server authentication directory.
1336: */
1337: public File getAuthDirectory() {
1338: return new File(getConfigDirectory(), "auth");
1339: }
1340:
1341: /**
1342: * Get this server store directory.
1343: */
1344: public File getStoreDirectory() {
1345: return new File(getConfigDirectory(), "stores");
1346: }
1347:
1348: /**
1349: * Get this server index directory
1350: */
1351: public File getIndexerDirectory() {
1352: return new File(getConfigDirectory(), "indexers");
1353: }
1354:
1355: /**
1356: * Get temp directory
1357: */
1358: public File getTempDirectory() {
1359: return new File(getRootDirectory(), "tmp");
1360: }
1361:
1362: /**
1363: * Clean the temp dir.
1364: */
1365: protected void cleanTempDirectory() {
1366: org.w3c.util.IO.clean(getTempDirectory());
1367: }
1368:
1369: /**
1370: * get the indexer catalog of this server
1371: */
1372: public IndexersCatalog getIndexersCatalog() {
1373: if (indexers == null)
1374: indexers = new IndexersCatalog(new ResourceContext(
1375: getDefaultContext()));
1376: return indexers;
1377: }
1378:
1379: /**
1380: * Get this server realm catalog.
1381: */
1382: public RealmsCatalog getRealmsCatalog() {
1383: return realms;
1384: }
1385:
1386: /**
1387: * Get this server resourcestore manager.
1388: */
1389: public ResourceStoreManager getResourceStoreManager() {
1390: return manager;
1391: }
1392:
1393: /**
1394: * Get this server resource space
1395: */
1396: public ResourceSpace getResourceSpace() {
1397: return manager;
1398: }
1399:
1400: /**
1401: * Get the default resource context for that server.
1402: */
1403: public ResourceContext getDefaultContext() {
1404: return context;
1405: }
1406:
1407: /**
1408: * Get the lenient value, tru if we are lenient in HTTP parsing
1409: */
1410: public boolean isLenient() {
1411: return lenient;
1412: }
1413:
1414: /**
1415: * Cleanup the resources associated with this server context.
1416: * This method should only be called by the server thread itself, when
1417: * it is requested to perform the cleanup.
1418: * @param restart If <strong>true</strong> the server is restarted
1419: * (reinitialized) straight away.
1420: */
1421:
1422: protected synchronized void cleanup(boolean restart) {
1423: // Close the accepting socket:
1424: try {
1425: socket.close();
1426: socket = null;
1427: } catch (IOException ex) {
1428: errlog("[cleanup]: IOException while closing server socket.");
1429: }
1430: // FIXME temporary hack for clones
1431: synchronized (instances) {
1432: // remove one instance
1433: instances[0]--; // @wplatzer
1434: if (factory != null)
1435: factory.shutdown(true);
1436: factory = null;
1437: if (manager != null && instances[0] == 0) // FIXME (shm)
1438: manager.shutdown();
1439: manager = null;
1440: if (shuffler != null)
1441: shuffler.shutdown();
1442: shuffler = null;
1443: // Unregister to property monitoring
1444: props.unregisterObserver(this );
1445: errlog("shutdown completed at: " + new Date() + ".");
1446: // Finally close the log
1447: if (logger != null && instances[0] == 0) // FIXME shm
1448: logger.shutdown();
1449: logger = null;
1450: // Release any other pointers:
1451: timer.stopEventManager();
1452: System.out.println(getIdentifier() + ": " + getURL()
1453: + " done.");
1454: System.out.flush();
1455: // Keep the data neede to reinit (in case needed)
1456: File init_propfile = props.getFile(PROPS_P, null);
1457: ObservableProperties init_props = props;
1458: String init_identifier = identifier;
1459: // Release pointed data:
1460: identifier = null;
1461: manager = null;
1462: factory = null;
1463: shuffler = null;
1464: // FIXME clones props = null ;
1465: indexer = null;
1466: root = null;
1467: realms = null;
1468: logger = null;
1469: socket = null;
1470: timer = null;
1471: thread = null;
1472: url = null;
1473: restarting = false;
1474: finishing = false;
1475: if (restart) {
1476: try {
1477: instances[0]++; //FIXME clones
1478: initialize(shm, init_identifier, init_props);
1479: start();
1480: } catch (Exception ex) {
1481: // We really can't do more than this here:
1482: System.out.println("*** server restart failed.");
1483: ex.printStackTrace();
1484: }
1485: }
1486: }
1487: }
1488:
1489: /**
1490: * Shutdown the server properly.
1491: * This methods shutdown the server, and clean-up all its associated
1492: * resources. If the current thread is not the server thread, it unblocks
1493: * the server thread from its accept() call, and forces it to perform
1494: * the rest of the shutdown operation itself.
1495: * @see httpd#cleanup
1496: */
1497:
1498: public synchronized void shutdown() {
1499: checkpoint();
1500: errlog("shutdown inited...(save done)");
1501: finishing = true;
1502: try {
1503: Socket unlock = new Socket(host, port);
1504: unlock.close();
1505: } catch (IOException ex) {
1506: errlog("[shutdown]: IOException while unblocking server thread.");
1507: }
1508: shm.removeServerHandler(this );
1509: cleanTempDirectory();
1510: }
1511:
1512: /**
1513: * Restart the server properly.
1514: * This methods restarts the server. It cleans-up all its associated
1515: * resources, and reinitialize it from scratch. If the current thread is
1516: * not the server thread, it unblocks
1517: * the server thread from its accept() call, and forces it to perform
1518: * the rest of the restart operation itself.
1519: * @param reload_properties Should we reload the properties from the
1520: * property file, or should we just reinitialize from the current set
1521: * of properties.
1522: * @see httpd#cleanup
1523: */
1524:
1525: public synchronized void restart() {
1526: errlog("[restart]: inited !");
1527: finishing = true;
1528: restarting = true;
1529: try {
1530: Socket unlock = new Socket(host, port);
1531: unlock.close();
1532: } catch (IOException ex) {
1533: errlog("[restart]: IOException while unblocking server thread.");
1534: }
1535: }
1536:
1537: /**
1538: * Turn debugging on/off for this instance of httpd server.
1539: * @param A boolean, true turns debugging on, flase turns it off.
1540: */
1541:
1542: public void debug(boolean onoff) {
1543: tracep = onoff;
1544: }
1545:
1546: /**
1547: * Emit a server trace. Traces are used solely for debugging purposes. You
1548: * should either use <b>log</b> or <b>error</b> to report informations.
1549: * @param client The client object which wants to report the trace.
1550: * @param msg The trace message.
1551: * @see httpd#log
1552: */
1553:
1554: public void trace(Client client, String msg) {
1555: if (tracep && (logger != null))
1556: logger.trace(client, msg);
1557: }
1558:
1559: /**
1560: * Emit a server trace, on behalf of the server itself.
1561: * @param msg The trace the server wants to emit.
1562: */
1563: public void trace(String msg) {
1564: if (tracep && (logger != null))
1565: logger.trace(msg);
1566: }
1567:
1568: /**
1569: * Emit a log entry.
1570: * @param client The client whose request is to be logged.
1571: * @param request The request that has been handled.
1572: * @param reply The emitted reply.
1573: * @param nbytes The number of bytes emitted back to the client.
1574: * @param duration The time it took to process the request.
1575: */
1576: public void log(Client client, Request request, Reply reply,
1577: int nbytes, long duration) {
1578: if (logger != null)
1579: logger.log(request, reply, nbytes, duration);
1580: statistics.updateStatistics(client, request, reply, nbytes,
1581: duration);
1582: }
1583:
1584: /**
1585: * Emit a log message.
1586: * @param msg The message to log.
1587: */
1588: public void log(String msg) {
1589: logger.log(msg);
1590: }
1591:
1592: /**
1593: * Emit a server error on behalf of some client object.
1594: * @param client The client.
1595: * @param msg The error message.
1596: */
1597: public void errlog(Client client, String msg) {
1598: if (logger != null)
1599: logger.errlog(client, msg);
1600: }
1601:
1602: /**
1603: * Emit an error on behalf of the server.
1604: * @param msg The error message.
1605: */
1606:
1607: public void errlog(String msg) {
1608: if (logger != null)
1609: logger.errlog("[" + identifier + "] " + msg);
1610: }
1611:
1612: /**
1613: * The prefered form for reporting errors.
1614: * @param from The object that emited the error.
1615: * @param msg The error message.
1616: */
1617: public void errlog(Object from, String msg) {
1618: if (logger != null)
1619: logger
1620: .errlog("[" + from.getClass().getName() + "]: "
1621: + msg);
1622: }
1623:
1624: /**
1625: * Another nice way of reporting errors from an HTTPResource.
1626: * @param from The resource that trigered the error.
1627: * @param msg The error message.
1628: */
1629: public void errlog(Resource from, String msg) {
1630: if (logger != null)
1631: logger.errlog(from.getClass().getName() + "@"
1632: + from.unsafeGetURLPath() + ": " + msg);
1633: }
1634:
1635: /**
1636: * Emit a fatal error.
1637: * @param e Any exception that caused the error.
1638: * @param msg Any additional message.
1639: */
1640: public void fatal(Exception e, String msg) {
1641: System.out.println("*** Fatal Error, aborting");
1642: System.out.println(this .getClass().getName() + ": " + msg);
1643: e.printStackTrace();
1644: throw new RuntimeException(msg);
1645: }
1646:
1647: /**
1648: * Emit a fatal error.
1649: * @param msg Any error message
1650: */
1651: public void fatal(String msg) {
1652: System.out.println("*** Fatal error, aborting");
1653: System.out.println(this .getClass().getName() + ": " + msg);
1654: throw new RuntimeException(msg);
1655: }
1656:
1657: /**
1658: * Emit a warning.
1659: * Warnings are emited, typically if the configuration is inconsistent,
1660: * and the server can continue its work.
1661: * @param msg The warning message.
1662: */
1663: public void warning(String msg) {
1664: System.out.println("*** Warning : " + msg);
1665: }
1666:
1667: /**
1668: * Emit a warning.
1669: * @param e Any exception.
1670: * @param msg Any message.
1671: */
1672: public void warning(Exception e, String msg) {
1673: System.out.println("*** Warning: " + msg);
1674: e.printStackTrace();
1675: }
1676:
1677: /**
1678: * Get a shuffler for this server's client.
1679: * Whenever possible, we use a shuffler program to speed up communication
1680: * with the client. This methods return whatever the server deems
1681: * appropriate for this client shuffler.
1682: * @return A Shuffler instance, or <strong>null</strong>.
1683: * @see org.w3c.jigsaw.http.Shuffler
1684: */
1685: public synchronized Shuffler getShuffler(Client client) {
1686: return shuffler;
1687: }
1688:
1689: protected String getBanner() {
1690: return "Jigsaw[" + version + "]";
1691: }
1692:
1693: public void run() {
1694: // Emit some traces before starting up:
1695: System.out.println(getBanner() + ": serving at " + getURL());
1696: System.out.flush();
1697: errlog("started at: " + new Date() + ".");
1698: // Enter the evil loop:
1699: while ((!finishing) && (socket != null)) {
1700: Socket ns = null;
1701: try {
1702: ns = socket.accept();
1703: ns.setTcpNoDelay(true);
1704: } catch (IOException e) {
1705: if (debug)
1706: e.printStackTrace();
1707: errlog("failed to accept incoming connection on "
1708: + socket);
1709: // just in case, as it may have been created.
1710: try {
1711: ns.close();
1712: } catch (Exception ex) {
1713: }
1714: ;
1715: ns = null;
1716: }
1717: if ((socket != null) && (ns != null) && (factory != null))
1718: factory.handleConnection(ns);
1719: }
1720: // Our socket has been closed, perform associated cleanup.
1721: cleanup(restarting);
1722: }
1723:
1724: /**
1725: * Perform the given request on behalf of this server.
1726: * @param request The request to perform.
1727: * @return A non-null Reply instance.
1728: * @exception ProtocolException If some error occurs during processing the
1729: * request.
1730: * @exception ResourceException If a resource got a fatal error.
1731: */
1732:
1733: public ReplyInterface perform(RequestInterface req)
1734: throws ProtocolException, ResourceException {
1735: Request request = (Request) req;
1736: // This may be a server-wide request, this is an ugly hack in HTTP spec
1737: if (request.getURL() == Request.THE_SERVER) {
1738: if (request.getMethod().equals("OPTIONS")) {
1739: HttpTokenList pub = null;
1740: synchronized (this ) {
1741: if (publicHeader == null)
1742: pub = HttpFactory.makeStringList(publicMethods);
1743: publicHeader = pub;
1744: }
1745: Reply reply = request.makeReply(HTTP.OK);
1746: if (pub != null) {
1747: reply.setHeaderValue(Reply.H_PUBLIC, pub);
1748: }
1749: reply.setContentLength(0);
1750: return reply;
1751: }
1752: }
1753: if (request.getMethod().equals("TRACE")) {
1754: // check if the resource can be proxied
1755: boolean doit = true;
1756: LookupState ls = new LookupState(request);
1757: LookupResult lr = new LookupResult(root
1758: .getResourceReference());
1759: try {
1760: if (root.lookup(ls, lr)) {
1761: ResourceReference target = lr.getTarget();
1762: if (target != null) {
1763: try {
1764: // this is plain ugly and won't work for proxy
1765: // not based on this resource
1766: // do we need another way to to this?
1767: FramedResource fr = (FramedResource) target
1768: .lock();
1769: Class cff = Class
1770: .forName("org.w3c.jigsaw.proxy.ForwardFrame");
1771: doit = (fr.getFrameReference(cff) == null);
1772: } catch (Exception ex) {
1773: // fail miserably to the fallback
1774: } finally {
1775: target.unlock();
1776: }
1777: }
1778: }
1779: } catch (Exception ex) {
1780: }
1781: ;
1782: if (doit) {
1783: Reply reply = request.makeReply(HTTP.OK);
1784: reply.setNoCache(); // don't cache this
1785: reply.setMaxAge(-1);
1786: // Dump the request as the body
1787: // Removed unused headers:
1788: // FIXME should be something else for chuncked stream
1789: ByteArrayOutputStream ba = new ByteArrayOutputStream();
1790: try {
1791: reply.setContentType(new MimeType("message/http"));
1792: request.dump(ba);
1793: reply.setContentLength(ba.size());
1794: } catch (Exception ex) {
1795: ex.printStackTrace();
1796: }
1797: reply.setStream(new ByteArrayInputStream(ba
1798: .toByteArray()));
1799: return reply;
1800: }
1801: }
1802: // Create a lookup state, and a lookup result:
1803:
1804: ProtocolException error = null;
1805: LookupState ls = null;
1806: LookupResult lr = null;
1807: // Run the lookup algorithm of root resource:
1808: // catch exception to get error (FIXME)
1809: try {
1810: lr = new LookupResult(root.getResourceReference());
1811: ls = new LookupState(request);
1812:
1813: if (root.lookup(ls, lr)) {
1814: if (lr.hasReply())
1815: return lr.getReply();
1816: }
1817: } catch (ProtocolException ex) {
1818: error = ex;
1819: } catch (Exception ex) {
1820: /*
1821: * We have a problem here, the error can be a configuration
1822: * or resource/extension problem, and it should be a
1823: * 5xx error, or it is a client side error and it should be
1824: * a 4xx error, ex, try with "Authorization:" and it fails.
1825: * For now we will reply with a 400, but with a FIXME
1826: */
1827: Reply err = request.makeReply(HTTP.BAD_REQUEST);
1828: err
1829: .setContent("<html><head><title>Bad Request</title></head>\n"
1830: + "<body><p>The server was not able to "
1831: + "understand this request</p></body></html>");
1832: error = new ProtocolException(err);
1833: }
1834: // Let the target resource perform the method
1835: ResourceReference target = lr.getTarget();
1836: Reply reply = null;
1837:
1838: ResourceFilter filters[] = lr.getFilters();
1839: int infilter = 0;
1840:
1841: if (error == null) {
1842: //call the ingoing filters:
1843: try {
1844: // temporary target resource !!! WARNING
1845: request.setTargetResource(target);
1846: if (filters != null) {
1847: for (; infilter < filters.length; infilter++) {
1848: if (filters[infilter] == null)
1849: continue;
1850: reply = (Reply) filters[infilter]
1851: .ingoingFilter(request, filters,
1852: infilter);
1853: if (reply != null) {
1854: return reply;
1855: }
1856: }
1857: }
1858: } catch (ProtocolException ex) {
1859: error = ex;
1860: }
1861: //perform the request:
1862: if ((error == null) && (target != null)) {
1863: request.setFilters(filters, infilter);
1864: request.setTargetResource(target);
1865: try {
1866: FramedResource res = (FramedResource) target.lock();
1867: reply = (Reply) res.perform(request);
1868: if (reply == null) {
1869: reply = request.makeReply(HTTP.NOT_FOUND);
1870: if (uri_error) {
1871: reply
1872: .setContent("<html><head><title>Not Found"
1873: + "</title></head>\n"
1874: + "<body><h1>Invalid"
1875: + " URL</h1><p>The URL <b>"
1876: + request.getURL()
1877: + "</b> that you requested is not"
1878: + " available "
1879: + " for this protocol.</body>\n"
1880: + "</html>");
1881: } else {
1882: reply
1883: .setContent("<html><head><title>Not Found"
1884: + "</title></head>\n"
1885: + "<body><h1>Invalid"
1886: + " URL</h1><p>The URL"
1887: + "</b> that you requested is not"
1888: + " available "
1889: + " for this protocol.</body>\n"
1890: + "</html>");
1891: }
1892: reply
1893: .setContentType(org.w3c.www.mime.MimeType.TEXT_HTML);
1894: }
1895: } catch (InvalidResourceException ex) {
1896: //FIXME
1897: reply = request.makeReply(HTTP.NOT_FOUND);
1898: if (uri_error) {
1899: reply.setContent("<html><head><title>Not"
1900: + " Found</title>"
1901: + "</head><body><b>The URL <b>"
1902: + request.getURL()
1903: + "</b> that you requested is not "
1904: + "available, "
1905: + " probably deleted.</body></html>");
1906: } else {
1907: reply.setContent("<html><head><title>Not"
1908: + " Found</title>"
1909: + "</head><body><b>The URL"
1910: + " that you requested is not "
1911: + "available, "
1912: + " probably deleted.</body></html>");
1913: }
1914: reply
1915: .setContentType(org.w3c.www.mime.MimeType.TEXT_HTML);
1916: } finally {
1917: target.unlock();
1918: }
1919: } else {
1920: reply = request.makeReply(HTTP.NOT_FOUND);
1921: if (uri_error) {
1922: reply.setContent("<html><head>\n"
1923: + "<title>Not Found</title></head>"
1924: + "<body><h1>Invalid URL</h1><p>The URL"
1925: + " <b>" + request.getURL()
1926: + "</b> that you requested is not"
1927: + " available "
1928: + " on that server.</body></html>");
1929: } else {
1930: reply.setContent("<html><head>\n"
1931: + "<title>Not Found</title></head>"
1932: + "<body><h1>Invalid URL</h1><p>The URL"
1933: + " that you requested is not"
1934: + " available "
1935: + " on that server.</body></html>");
1936: }
1937: reply
1938: .setContentType(org.w3c.www.mime.MimeType.TEXT_HTML);
1939: }
1940: }
1941: // call the outgoing filters:
1942: if ((reply == null) || (reply.getStatus() != HTTP.DONE)) {
1943: if (error == null) {
1944: for (int i = infilter; --i >= 0;) {
1945: if (filters[i] == null)
1946: continue;
1947: Reply fr = (Reply) filters[i].outgoingFilter(
1948: request, reply, filters, i);
1949: if (fr != null)
1950: return fr;
1951: }
1952: } else {
1953: // Make sure we always invoke appropriate filters:
1954: if (filters != null) {
1955: for (int i = filters.length; --i >= 0;) {
1956: if (filters[i] == null) {
1957: continue;
1958: }
1959: Reply fr = (Reply) filters[i].exceptionFilter(
1960: request, error, filters, i);
1961: if (fr != null) {
1962: return fr;
1963: }
1964: }
1965: }
1966: reply = (Reply) error.getReply();
1967: if (reply == null) {
1968: reply = request
1969: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
1970: if (uri_error) {
1971: reply.setContent("<html><head>\n"
1972: + "<title>Server Error</title>"
1973: + "</head><body><h1>Invalid URL</h1>"
1974: + "<p>The URL <b>" + request.getURL()
1975: + "</b>: isn't available "
1976: + " on that server.</body></html>");
1977: } else {
1978: reply.setContent("<html><head>\n"
1979: + "<title>Server Error</title>"
1980: + "</head><body><h1>Invalid URL</h1>"
1981: + "<p>The URL" + " isn't available "
1982: + " on that server.</body></html>");
1983: }
1984: reply
1985: .setContentType(org.w3c.www.mime.MimeType.TEXT_HTML);
1986: }
1987: }
1988: }
1989: return reply;
1990: }
1991:
1992: protected boolean checkUpgrade(String id, ObservableProperties props) {
1993: // Check for an upgrade:
1994: int configvers = props.getInteger(httpd.VERSCOUNT_P, 1);
1995: if (configvers < httpd.verscount) {
1996: System.err
1997: .println("*** Jigsaw needs upgrade from internal "
1998: + "version " + configvers + " to "
1999: + httpd.verscount);
2000: if (httpd.verscount == 4) {
2001: org.w3c.tools.resources.serialization.Serializer serializer = new org.w3c.tools.resources.serialization.xml.XMLSerializer();
2002: props
2003: .put(httpd.SERIALIZER_CLASS_P,
2004: "org.w3c.tools.resources.serialization.xml.XMLSerializer");
2005: File propsfile = props.getFile(PROPS_P, null);
2006: Upgrader upgrader = new Upgrader(id,
2007: getConfigDirectory(), propsfile, serializer);
2008: props.put(httpd.VERSCOUNT_P, String
2009: .valueOf(httpd.verscount));
2010: props.put(httpd.SERVER_SOFTWARE_P, "Jigsaw/" + version);
2011: try {
2012: upgrader.upgrade(httpd.verscount);
2013: return true;
2014: } catch (Exception ex) {
2015: System.err.println(ex.getMessage());
2016: return false;
2017: }
2018: }
2019: return false;
2020: }
2021: return true;
2022: }
2023:
2024: /**
2025: * Initialize a new HTTP server.
2026: * The server wil first be initialized from the available properties,
2027: * it will than startup, and finally run in its own thread.
2028: * @param identifier The string identifying this server's occurence.
2029: * @param props A set of properties to initialize from.
2030: * @exception ServerHandlerInitException if unable to be initialized.
2031: */
2032:
2033: public void initialize(ServerHandlerManager shm, String identifier,
2034: ObservableProperties props)
2035: throws ServerHandlerInitException {
2036: // Check for an optional upgrade of config files:
2037: // checkUpgrade(shm, identifier, props);
2038: this .shm = shm;
2039: // Initialize from properties:
2040: this .identifier = identifier;
2041: this .props = props;
2042: // with an explicit cast for buggy compilers
2043: this .props.registerObserver((PropertyMonitoring) this );
2044: initializeProperties();
2045: if (!checkUpgrade(identifier, props))
2046: throw new ServerHandlerInitException("Upgrade failed.");
2047: // Create the socket, and run the server:
2048: initializeServerSocket();
2049: }
2050:
2051: /**
2052: * start the server
2053: * it will than startup, and finally run in its own thread.
2054: */
2055:
2056: public void start() throws ServerHandlerInitException {
2057: if (!isAClone) {
2058: // Create the default resource context (shared by all resources):
2059: this .context = new ResourceContext(this );
2060: //FIXME
2061: // Create the resource store manager
2062: initializeResourceSpace(identifier, root_class, props
2063: .getString(EDIT_ROOT_P, root_name), props
2064: .getString(SERIALIZER_CLASS_P, null),
2065: max_loaded_store);
2066: this .context.setSpace(getResourceSpace());
2067: // Create the resource indexer object
2068: initializeIndexer();
2069: // Resurect this server root entity:
2070: initializeRootResource();
2071: // Resurect the realms catalog
2072: initializeRealmsCatalog();
2073: // Initialize property sets
2074: initializePropertySets();
2075: // Create this server event manager
2076: initializeEventManager();
2077: // Initialize the logger object:
2078: initializeLogger();
2079: // Initialize the shuffler object:
2080: if (shuffler_path != null) {
2081: try {
2082: this .shuffler = new Shuffler(shuffler_path);
2083: } catch (Error e) {
2084: warning("unable to launch shuffler to "
2085: + shuffler_path + ": " + e.getMessage());
2086: this .shuffler = null;
2087: } catch (RuntimeException e) {
2088: warning(e, "unable to launch shuffler to "
2089: + shuffler_path + ": " + e.getMessage());
2090: this .shuffler = null;
2091: }
2092: }
2093: if (this .shuffler != null)
2094: trace("using shuffler at: " + shuffler_path);
2095: // startup classes
2096: loadStartupClasses();
2097: // Yeah, now start:
2098: this .thread.start();
2099: } else {
2100: httpd mainServ = (httpd) shm.lookupServerHandler(masterID);
2101: this .context = mainServ.getDefaultContext();
2102: this .realms = mainServ.realms;
2103: this .manager = mainServ.manager;
2104: // We basically re-use:
2105: // - the master indexer
2106: // - the master realms catalog
2107: // FIXED no need to use the same logger! - the master logger:
2108: // Initialize the logger object:
2109: initializeLogger();
2110: // Resurect this server root entity:
2111: initializeRootResource();
2112: // We use our own event manager
2113: initializeEventManager();
2114: // Yeah, now start:
2115: this .thread.start();
2116: }
2117: }
2118:
2119: /**
2120: * clone this server
2121: * @exception ServerHandlerInitException if unable to be initialized.
2122: */
2123: public ServerHandler clone(ServerHandlerManager shm, String id,
2124: ObservableProperties props)
2125: throws ServerHandlerInitException {
2126: // Clone this master server:
2127: httpd server = null;
2128: try {
2129: server = (httpd) clone();
2130: } catch (CloneNotSupportedException ex) {
2131: throw new ServerHandlerInitException(this .getClass()
2132: .getName()
2133: + ": clone not supported !");
2134: }
2135: server.shm = shm;
2136: // Nullify some of the cached instance variables:
2137: server.url = null;
2138: // Initialize
2139: server.masterID = server.identifier;
2140: server.identifier = id;
2141: server.props = props;
2142: server.props.registerObserver((PropertyMonitoring) server);
2143: server.initializeProperties();
2144: server.initializeServerSocket();
2145: server.isAClone = true;
2146: return server;
2147: }
2148:
2149: /**
2150: * get this server config resource
2151: */
2152: public ResourceReference getConfigResource() {
2153: if (rr_configResource == null) {
2154: configResource = new ConfigResource(this );
2155: rr_configResource = new DummyResourceReference(
2156: configResource);
2157: }
2158: return rr_configResource;
2159: }
2160:
2161: /**
2162: * Give the status of this class as a partial HTML text which will be added
2163: * into a block level element
2164: * @return a String, the generated HTML
2165: */
2166: public String getHTMLStatus() {
2167: StringBuffer sb = new StringBuffer();
2168: if (factory instanceof Status) {
2169: sb.append(((Status) factory).getHTMLStatus());
2170: }
2171: sb.append(manager.getHTMLStatus());
2172: return sb.toString();
2173: }
2174:
2175: /**
2176: * Create a new server instance in this process.
2177: * @param identifier The server's identifier.
2178: * @param props The server properties.
2179: */
2180: public httpd() {
2181: super ();
2182: }
2183:
2184: /**
2185: * this server's usage
2186: */
2187: public static void usage() {
2188: PrintStream o = System.out;
2189:
2190: o.println("usage: httpd [OPTIONS]");
2191: o.println("-id <id> : server identifier.");
2192: o
2193: .println("-port <number> : listen on the given port number.");
2194: o
2195: .println("-host <host> : full name of host running the server.");
2196: o.println("-root <directory> : root directory of server.");
2197: o
2198: .println("-space <directory>: space directory exported by server");
2199: o.println("-p <propfile> : property file to read.");
2200: o.println("-trace : turns debugging on.");
2201: o.println("-config : config directory to use.");
2202: o
2203: .println("-maxstores <int> : Max number of stores in memory.");
2204: System.exit(1);
2205: }
2206:
2207: /**
2208: * debugging main
2209: */
2210: public static void main(String args[]) {
2211: Integer cmdport = null;
2212: String cmdhost = null;
2213: String cmdroot = null;
2214: String cmdspace = null;
2215: String cmdprop = null;
2216: String cmdid = "http-server";
2217: String cmdconfig = "config";
2218: Boolean cmdtrace = null;
2219: boolean noupgrade = false;
2220: String maxstores = null;
2221:
2222: // Parse command line options:
2223: for (int i = 0; i < args.length; i++) {
2224: if (args[i].equals("-port")) {
2225: try {
2226: cmdport = new Integer(args[++i]);
2227: } catch (NumberFormatException ex) {
2228: System.out.println("invalid port number ["
2229: + args[i] + "]");
2230: System.exit(1);
2231: }
2232: } else if (args[i].equals("-id") && (i + 1 < args.length)) {
2233: cmdid = args[++i];
2234: } else if (args[i].equals("-maxstores")
2235: && (i + 1 < args.length)) {
2236: maxstores = args[++i];
2237: } else if (args[i].equals("-host") && (i + 1 < args.length)) {
2238: cmdhost = args[++i];
2239: } else if (args[i].equals("-root") && (i + 1 < args.length)) {
2240: cmdroot = args[++i];
2241: } else if (args[i].equals("-space")
2242: && (i + 1 < args.length)) {
2243: cmdspace = args[++i];
2244: } else if (args[i].equals("-p") && (i + 1 < args.length)) {
2245: cmdprop = args[++i];
2246: } else if (args[i].equals("-trace")) {
2247: cmdtrace = Boolean.TRUE;
2248: } else if (args[i].equals("?") || args[i].equals("-help")) {
2249: usage();
2250: } else if (args[i].equals("-config")
2251: && (i + 1 < args.length)) {
2252: cmdconfig = args[++i];
2253: } else if (args[i].equals("-noupgrade")) {
2254: noupgrade = true;
2255: } else {
2256: continue;
2257: // System.out.println ("unknown option: ["+args[i]+"]") ;
2258: // System.exit (1) ;
2259: }
2260: }
2261: // Get the properties for this server:
2262: ObservableProperties props = null;
2263: props = new ObservableProperties(System.getProperties());
2264: // Get the root and configuration directories:
2265: File root = ((cmdroot == null) ? new File(props.getProperty(
2266: "user.dir", null)) : new File(cmdroot));
2267: File config = new File(root, cmdconfig);
2268: // Locate the property file:
2269: if (cmdprop == null) {
2270: // Try to guess it, cause it is really required:
2271: File guess = new File(config, cmdid + ".props");
2272: if (!guess.exists())
2273: // A hack for smooth upgrade from 1.0alpha3 to greater:
2274: guess = new File(config, "httpd.props");
2275: cmdprop = guess.getAbsolutePath();
2276: }
2277: if (cmdprop != null) {
2278: System.out.println("loading properties from: " + cmdprop);
2279: try {
2280: File propfile = new File(cmdprop);
2281: props.load(new FileInputStream(propfile));
2282: props.put(PROPS_P, propfile.getAbsolutePath());
2283: } catch (FileNotFoundException ex) {
2284: System.out.println("Unable to load properties: "
2285: + cmdprop);
2286: System.out.println("\t" + ex.getMessage());
2287: System.exit(1);
2288: } catch (IOException ex) {
2289: System.out.println("Unable to load properties: "
2290: + cmdprop);
2291: System.out.println("\t" + ex.getMessage());
2292: System.exit(1);
2293: }
2294: System.setProperties(props);
2295: }
2296: // Check for an upgrade:
2297: int configvers = props.getInteger(httpd.VERSCOUNT_P, 1);
2298: if (configvers < httpd.verscount) {
2299: System.err
2300: .println("+ Jigsaw needs upgrade from internal version "
2301: + configvers + " to " + httpd.verscount);
2302: if (noupgrade) {
2303: System.err
2304: .println("+ Jigsaw cannot run in that version.");
2305: System.exit(1);
2306: }
2307: // upgrade(configvers, httpd.verscount, args);
2308: return;
2309: }
2310: // Override properties with our command line options:
2311: if (cmdport != null)
2312: props.put(PORT_P, cmdport.toString());
2313: if (cmdhost != null)
2314: props.put(HOST_P, cmdhost);
2315: if (cmdroot != null)
2316: props.put(ROOT_P, root.getAbsolutePath());
2317: if (cmdconfig != null)
2318: props.put(CONFIG_P, config.getAbsolutePath());
2319: if (cmdspace != null)
2320: props.put(SPACE_P, cmdspace);
2321: if (cmdtrace != null) {
2322: props.put(TRACE_P, "true");
2323: props.put(CLIENT_DEBUG_P, "true");
2324: }
2325: if (maxstores != null)
2326: props.put(MAX_LOADED_STORE_P, maxstores);
2327:
2328: // Install security manager if needed:
2329: if (Boolean.getBoolean(USE_SM_P)) {
2330: SecurityManager sm = new httpdSecurityManager();
2331: System.setSecurityManager(sm);
2332: }
2333: // Run the server:
2334: try {
2335: httpd server = new httpd();
2336: server.initialize(null, cmdid, props);
2337: } catch (Exception e) {
2338: System.out.println("*** [httpd]: fatal error, exiting !");
2339: e.printStackTrace();
2340: }
2341: }
2342: }
|