All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sunlabs.brazil.server.Server Maven / Gradle / Ivy

The newest version!
/*
 * Server.java
 *
 * Brazil project web application toolkit,
 * export version: 2.3 
 * Copyright (c) 1998-2005 Sun Microsystems, Inc.
 *
 * Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License Version 
 * 1.0 (the "License"). You may not use this file except in compliance with 
 * the License. A copy of the License is included as the file "license.terms",
 * and also available at http://www.sun.com/
 * 
 * The Original Code is from:
 *    Brazil project web application toolkit release 2.3.
 * The Initial Developer of the Original Code is: suhler.
 * Portions created by suhler are Copyright (C) Sun Microsystems, Inc.
 * All Rights Reserved.
 * 
 * Contributor(s): cstevens, drach, rinaldo, suhler.
 *
 * Version:  2.4
 * Created by suhler on 98/09/14
 * Last modified by suhler on 05/07/12 12:46:41
 *
 * Version Histories:
 *
 * 2.4 05/07/12-12:46:41 (suhler)
 *   better failure diags
 *
 * 2.3 04/11/30-15:11:26 (suhler)
 *   fixed sccs version string
 *
 * 2.2 04/09/14-13:48:15 (suhler)
 *   added "restart" method to enable server restarts with new configuration
 *
 * 2.1 02/10/01-16:34:53 (suhler)
 *   version change
 *
 * 1.46 02/02/12-09:17:47 (suhler)
 *   Don't exit silently if localHost can't resolve.
 *   .
 *
 * 1.45 01/08/21-10:59:39 (suhler)
 *   add "maxPost" to limit size of request data
 *
 * 1.44 01/08/13-09:09:30 (drach)
 *   Change server.props back to Properties object.
 *
 * 1.43 01/08/07-14:19:29 (drach)
 *   Move PropertiesList debug initialization to earliest possible point.
 *
 * 1.42 01/08/03-15:29:58 (drach)
 *   Add PropertiesList
 *
 * 1.41 01/07/16-16:51:09 (suhler)
 *   bump default server id
 *
 * 1.40 01/06/05-22:13:06 (drach)
 *   Reduce access control for brazil.servlet package
 *
 * 1.39 01/04/12-11:28:46 (suhler)
 *   remove shadow "name"
 *
 * 1.38 01/04/12-10:23:48 (suhler)
 *   Fixed a performance bug (introduced version 1.18) that caused a
 *   reverse DNS lookup on every connection bu calling:
 *   sock.getInetAddress().getHostName()
 *
 * 1.37 01/03/12-17:30:19 (cstevens)
 *   Close listening socket when Server finished running, to make testing easier.
 *
 * 1.36 01/03/05-16:22:06 (cstevens)
 *   log message consistency
 *
 * 1.35 00/11/06-10:53:17 (suhler)
 *   Make initFailure public
 *
 * 1.34 00/10/17-09:31:42 (suhler)
 *   Added instance variable "initFailure" which may be set externally, causing
 *   the server to stop after initialization completes.
 *
 * 1.33 00/06/28-15:22:24 (cstevens)
 *   Merged changes between child workspace "/home/cstevens/ws/brazil/naws" and
 *   parent workspace "/export/ws/brazil/naws".
 *
 * 1.30.1.1 00/06/28-15:16:59 (cstevens)
 *
 * 1.32 00/05/31-13:51:41 (suhler)
 *   docs
 *
 * 1.31 00/05/17-10:39:09 (suhler)
 *   Added null constructor so Server can be instantiaded with newInstance()
 *
 * 1.30 00/04/12-15:59:41 (cstevens)
 *   Server uses initHandler to init initial handler.
 *
 * 1.29 00/03/29-16:43:59 (cstevens)
 *   documentation
 *
 * 1.28 00/03/10-17:11:49 (cstevens)
 *   Formatting of log messages.
 *
 * 1.27 99/11/17-15:37:17 (cstevens)
 *   init
 *
 * 1.26 99/11/16-19:09:18 (cstevens)
 *   expunge Server.initHandler and Server.initObject.
 *
 * 1.25 99/11/16-14:37:07 (cstevens)
 *   Server.java:
 *   1. Name printed for thread in log message when a connection is accepted
 *   should agree with the name printed for the thread when the Request does
 *   something.
 *   2. Thread.interrupt() issues interacting with the test scripts.
 *
 * 1.24 99/11/09-20:24:30 (cstevens)
 *   bugs revealed by writing tests.
 *
 * 1.23 99/11/03-17:52:45 (cstevens)
 *   MultiHostHandler.
 *
 * 1.22 99/10/26-18:54:59 (cstevens)
 *   Eliminate public methods Server.initHandler() and Server.initObject().
 *   Get rid of public variables Request.server and Request.sock:
 *   A. In all cases, Request.server was not necessary; it was mainly used for
 *   constructing the absolute URL for a redirect, so Request.redirect() was
 *   rewritten to take an absolute or relative URL and do the right thing.
 *   B. Request.sock was changed to Request.getSock(); it is still rarely used
 *   for diagnostics and logging (e.g., ChainSawHandler).
 *
 * 1.21 99/10/25-15:40:34 (cstevens)
 *   spelling
 *
 * 1.20 99/10/25-15:39:16 (cstevens)
 *   Don't make initObject and initHandler public
 *
 * 1.19 99/10/14-14:57:38 (cstevens)
 *   resolve wilcard imports.
 *
 * 1.18 99/10/14-14:16:21 (cstevens)
 *   merge issues.  logging "null" should be ignored.
 *
 * 1.17 99/10/14-13:17:32 (cstevens)
 *   Merged changes between child workspace "/home/cstevens/ws/brazil/naws" and
 *   parent workspace "/export/ws/brazil/naws".
 *
 * 1.15.1.3 99/10/14-13:11:08 (cstevens)
 *   @author & @version
 *
 * 1.15.1.2 99/10/14-12:56:36 (cstevens)
 *   Server.initFields removed
 *   Server.initHandler and Server.initObject have extensive documentation now
 *   that will hopefully make their existence compelling.
 *
 * 1.16 99/10/11-12:37:10 (suhler)
 *   Merged changes between child workspace "/home/suhler/brazil/naws" and
 *   parent workspace "/net/mack.eng/export/ws/brazil/naws".
 *
 * 1.14.1.1 99/10/11-12:32:42 (suhler)
 *   Change to server, allowing it to stop.  This uses undocumented
 *   behavior of the JDK, so I'm ntsure its a good idea
 *
 * 1.15.1.1 99/10/08-16:53:35 (cstevens)
 *
 * 1.15 99/10/07-13:01:39 (cstevens)
 *   javadoc lint.
 *
 * 1.14 99/10/04-17:16:28 (cstevens)
 *   merge conflict
 *
 * 1.13 99/10/04-16:11:09 (cstevens)
 *   Merged changes between child workspace "/home/cstevens/ws/brazil/naws" and
 *   parent workspace "/export/ws/brazil/naws".
 *
 * 1.11.1.2 99/10/01-13:07:06 (cstevens)
 *   Getting better with server.initHandler();
 *
 * 1.12 99/10/01-11:44:05 (suhler)
 *   Merged changes between child workspace "/home/suhler/brazil/naws" and
 *   parent workspace "/net/mack.eng/export/ws/brazil/naws".
 *
 * 1.11.1.1 99/10/01-11:27:15 (cstevens)
 *   Change logging to show prefix of Handler generating the log message.
 *
 * 1.11 99/09/30-12:12:14 (cstevens)
 *   better logging.
 *   handler= line in config file can contain either name of class, in which
 *   case prefix will be "" or a symbolic name, in which case prefix will be
 *   name + ".".  This preserved backwards compatibility, but allows symbolic
 *   names to be used.
 *
 * 1.10 99/09/29-16:10:56 (cstevens)
 *   Consistent way of initializing Handlers and other things that want to get
 *   attributes from the config file.  Convenience method that constructs the
 *   object, sets (via reflection) all the variables in the object that correspond
 *   to values specified in the config file, and then calls init() on the object.
 *
 * 1.9.1.1 99/09/22-16:04:08 (suhler)
 *   - put each server in its own thread group
 *   - change the way log messages are generated (the "socket" argument is
 *   no longer used and should be removed
 *   - added a close() to stop the server (untested)
 *
 * 1.9 99/07/22-15:04:39 (suhler)
 *   Added public maxThreads field to limit the max # of threads that
 *   may be spawned by the server at once
 *
 * 1.8 99/06/29-14:35:09 (suhler)
 *   added serverUrl method that overrides the default host name
 *
 * 1.7 99/06/04-13:55:01 (suhler)
 *   modify log level
 *
 * 1.6 99/03/30-09:27:19 (suhler)
 *   - documentation update
 *   - bug fix: too many open files would kill server
 *
 * 1.5 99/02/03-14:13:09 (suhler)
 *   added defaultPrefix to override the default, ("")
 *
 * 1.4 98/12/09-15:04:12 (suhler)
 *   added thread count to log message
 *
 * 1.3 98/09/21-14:52:05 (suhler)
 *   changed the package names
 *
 * 1.2 98/09/17-17:59:14 (rinaldo)
 *
 * 1.2 98/09/14-18:03:10 (Codemgr)
 *   SunPro Code Manager data about conflicts, renames, etc...
 *   Name history : 2 1 server/Server.java
 *   Name history : 1 0 Server.java
 *
 * 1.1 98/09/14-18:03:09 (suhler)
 *   date and time created 98/09/14 18:03:09 by suhler
 *
 */

package sunlabs.brazil.server;

import sunlabs.brazil.properties.PropertiesList;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Properties;

/**
 * Yet another HTTP/1.1 server.
 * This class is the core of a light weight Web Server.  This server
 * is started as a Thread listening on the supplied port, and
 * dispatches to an implementation of
 * a {@link Handler} to service http requests.  If no handler is
 * supplied, then the {@link FileHandler} is used.
 * A {@link ChainHandler} is provided to allow multiple handlers in one server.
 * 

* Limitations: *

    *
  • Starts a new thread for each connection. This may be expensive. *
* * @author Stephen Uhler ([email protected]) * @author Colin Stevens ([email protected]) * @version 2.4 */ public class Server extends Thread { /** * The listening socket. Every time a new socket is accepted, * a new thread is created to read the HTTP requests from it. */ public ServerSocket listen; /** * The main Handler whose respond method is called for * every HTTP request. The respond method must be * thread-safe since it handles HTTP requests concurrently from all the * accepted sockets. * * @see Handler#respond */ private String handlerName; public Handler handler; /** * Hashtable containing arbitrary information that may be of interest to * a Handler. This table is available to both methods of the * {@link Handler} interface, as {@link Server#props} in the * {@link Handler#init(Server, String)} * method, and as the default properties of * {@link Request#props} in the {@link Handler#respond(Request)} * method. */ public Properties props = null; /** * The hostname that this Server should use to identify itself in * an HTTP Redirect. If null, the hostname is derived * by calling InetAddress.getHostAddress. *

* InetAddress.getHostName would generally be the wrong * thing to return because it returns only the base machine name * xxx and not the machine name as it needs to appear * to the rest of the network, such as xxx.yyy.com. *

* The default value is null. */ public String hostName = null; /** * The protocol used to access this resource. Normally http, but * can be changed for ssl to https */ public String protocol = "http"; /** * If non-null, restrict connections to just the specified ip addresses. *

* The default value is null. */ public InetAddress[] restrict = null; /** * The string to return as the value for the "Server:" line in the HTTP * response header. If null, then no "Server:" line is * returned. */ public String name = "Brazil/2.0"; /** * The handler is passed a prefix to identify which items in the * properties object are relevent. By convention, non-empty strings * end with ".", allowing nested prefixes to be easily distinguished. */ public String prefix = ""; /** * Time in milliseconds before this Server closes an idle socket or * in-progress request. *

* The default value is 30000. */ public int timeout = 30000; /** * Maximum number of consecutive requests allowed on a single * kept-alive socket. *

* The default value is 25. */ public int maxRequests = 25; /** * The max number of threads allowed for the entire VM * (default is 250). */ public int maxThreads = 250; /** * Maximum amout of POST data allowed per request (in bytes) * (default = 2Meg). */ public int maxPost = 2097152; // 2 Meg /** * Default buffer size for copies to and from client sockets. * (default is 8192) */ public int bufsize = 8192; /** * Count of accepted connections so far. */ public int acceptCount = 0; /** * Count of HTTP requests received so far. */ public int requestCount = 0; /** * Count of errors that occurred so far. */ public int errorCount = 0; /** * The diagnostic level. 0->least, 5->most */ public int logLevel = LOG_LOG; /** * If set, the server will terminate with an initialization failure * just before creating the listen socket. */ public boolean initFailure = false; ThreadGroup group; /** * Create a server using the provided listener socket. *

* This server will call the Handler.respond method * of the specified handler. The specified handler should either * respond to the request or perform further dispatches to other * handlers. * * @param listen * The socket this server should listen to. * For ordinary sockets, this is simply: * new ServerSocket(port), where port * is the network port to listen on. Alternate implementations * of ServerSocket, such as ssl versions * may be used instead. * @param handlerName * The name of the handler used to process http requests. * It must implement the {@link Handler} interface. * @param props * Arbitrary information made available to the handler. * May be null. * * @see FileHandler * @see ChainHandler */ public Server(ServerSocket listen, String handlerName, Properties props) { setup(listen, handlerName, props); } /** * Set up the server. this allows a server to be created with * newInstance() followed by setup(), instead of using the * above initializer, making it easier to start sub-classes * of the server. */ public Server() {} public boolean setup(ServerSocket listen, String handlerName, Properties props) { if (this.props != null) { return false; // alreasdy initialized } if (props == null) { props = new Properties(); } this.listen = listen; this.handlerName = handlerName; this.props=props; if (props.get("debugProps") != null) { PropertiesList.debug = true; } return true; } public boolean init() { if (props == null) { log(LOG_ERROR, "server", "Not properly initialized!"); return false; } group = new ThreadGroup(prefix); if (hostName == null) { try { hostName = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { log(LOG_ERROR, "server", "Can't find my own name, using \"localhost\"" + " (redirects may not work)"); hostName="localhost"; } } if (Thread.currentThread().getName().startsWith("Thread-")) { Thread.currentThread().setName("server"); } handler = ChainHandler.initHandler(this, prefix, handlerName); if (handler == null) { return false; } if (initFailure) { log(LOG_ERROR, handlerName, "Initilization failure"); return false; } return true; } /** * Restart the server with a new handler. * @param newHandler Name of the handler to restart the server with */ public synchronized boolean restart(String newHandler) { String oldHandlerName=handlerName; Handler oldHandler=handler; handlerName=newHandler; if (init()) { log(LOG_INFORMATIONAL, this, "restarting with: " + newHandler); return true; } else { log(LOG_WARNING, this, newHandler + " is invalid, retaining old handler"); handlerName = oldHandlerName; handler=oldHandler; return false; } } /** * Loops, accepting socket connections and replying to HTTP requests. * This is called indirectly via Thread.start(). *

* Many things in the server are not initialized until this point, * because the user may have set some related configuration options * between the time this server was allocated and the time it was * started. For instance, the main Handler is not * initialized until now, because its Handler.init method * may have wanted to examine server member variables such as * hostName or bufsize. */ public void run() { try { if (init() == false) { return; } listen.setSoTimeout(0); while (true) { /* * Blocks until we have a connection on the socket. */ Socket sock = listen.accept(); String threadName = sock.getInetAddress().getHostAddress(); log(LOG_INFORMATIONAL, threadName, "new connection"); allowed: if (restrict != null) { InetAddress addr = sock.getInetAddress(); for (int i = 0; i < restrict.length; i++) { if (restrict[i].equals(addr)) { break allowed; } } log(LOG_DIAGNOSTIC, addr, "rejected request"); sock.close(); continue; } // A pseudo-busy loop!!! boolean warn=false; while (Thread.activeCount() > maxThreads) { if (!warn) { log(LOG_WARNING, sock, "Too many threads: " + acceptCount); } Thread.yield(); warn = true; } new Thread(group, new Connection(this, sock), threadName + "-" + acceptCount).start(); acceptCount++; } } catch (IOException e) { System.err.println("Server failed to start: " + e); } finally { try { listen.close(); Thread[] sub = new Thread[100]; int count; while ((count = group.enumerate(sub, true)) > 0) { for (int i = 0; i < count; i++) { sub[i].interrupt(); sub[i].join(); } this.yield(); } } catch (Exception e) {} group = null; } } /** * Stop the server, and kill all pending requests */ public void close() { try { this.interrupt(); this.join(); } catch (Exception e) {} log(LOG_WARNING, null, "server stopped"); } public static final int LOG_ERROR=1; // most severe public static final int LOG_WARNING=2; public static final int LOG_LOG=3; public static final int LOG_INFORMATIONAL=4; public static final int LOG_DIAGNOSTIC=5; // least useful /** * Logs information about the socket to System.out. * * @param level Controls the verbosity (0=least 5=most) * @param obj The object that the message relates to. * @param message The message to be logged. */ public void log(int level, Object obj, String message) { if (level <= logLevel) { System.out.print("LOG: " + level + " " + prefix + listen.getLocalPort() + "-" + Thread.currentThread().getName() + ": "); if (obj != null) { System.out.print(obj); System.out.print(": "); } System.out.println(message); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy