Java tutorial
// ======================================================================== // $Id: HttpServer.java,v 1.70 2005/12/04 11:43:21 gregwilkins Exp $ // Copyright 1999-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package net.lightbody.bmp.proxy.jetty.http; import net.lightbody.bmp.proxy.jetty.http.handler.DumpHandler; import net.lightbody.bmp.proxy.jetty.http.handler.NotFoundHandler; import net.lightbody.bmp.proxy.jetty.http.handler.ResourceHandler; import net.lightbody.bmp.proxy.jetty.log.LogFactory; import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.MalformedURLException; import java.util.*; /* ------------------------------------------------------------ */ /** HTTP Server. * Services HTTP requests by maintaining a mapping between * a collection of HttpListeners which generate requests and * HttpContexts which contain collections of HttpHandlers. * * This class is configured by API calls. The * org.mortbay.jetty.Server class uses XML configuration files to * configure instances of this class. * * The HttpServer implements the BeanContext API so that membership * events may be generated for HttpListeners, HttpContexts and WebApplications. * * @see HttpContext * @see HttpHandler * @see HttpConnection * @see HttpListener * @see net.lightbody.bmp.proxy.jetty.jetty.Server * @version $Id: HttpServer.java,v 1.70 2005/12/04 11:43:21 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ public class HttpServer extends Container implements LifeCycle, EventProvider, Serializable { private static Log log = LogFactory.getLog(HttpServer.class); /* ------------------------------------------------------------ */ private static WeakHashMap __servers = new WeakHashMap(); private static Collection __roServers = Collections.unmodifiableCollection(__servers.keySet()); private static String[] __noVirtualHost = new String[1]; /* ------------------------------------------------------------ */ /** Get HttpServer Collection. * Get a collection of all known HttpServers. Servers can be * removed from this list with the setAnonymous call. * @return Collection of all servers. */ public static Collection getHttpServers() { return __roServers; } /* ------------------------------------------------------------ */ /** * @deprecated User getHttpServers() */ public static List getHttpServerList() { return new ArrayList(__roServers); } /* ------------------------------------------------------------ */ private List _listeners = new ArrayList(3); private HashMap _realmMap = new HashMap(3); private StringMap _virtualHostMap = new StringMap(); private boolean _trace = false; private RequestLog _requestLog; private int _requestsPerGC; private boolean _resolveRemoteHost = false; private String[] _serverClasses; private String[] _systemClasses; private transient int _gcRequests; private transient HttpContext _notFoundContext = null; private transient boolean _gracefulStop; /* ------------------------------------------------------------ */ /** Constructor. */ public HttpServer() { this(false); } /* ------------------------------------------------------------ */ /** Constructor. * @param anonymous If true, the server is not included in the * static server lists and stopAll methods. */ public HttpServer(boolean anonymous) { setAnonymous(anonymous); _virtualHostMap.setIgnoreCase(true); } /* ------------------------------------------------------------ */ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); HttpListener[] listeners = getListeners(); HttpContext[] contexts = getContexts(); _listeners.clear(); _virtualHostMap.clear(); setContexts(contexts); setListeners(listeners); _statsLock = new Object[0]; } /* ------------------------------------------------------------ */ /** * @param anonymous If true, the server is not included in the * static server lists and stopAll methods. */ public void setAnonymous(boolean anonymous) { if (anonymous) __servers.remove(this); else __servers.put(this, __servers); } /* ------------------------------------------------------------ */ public void setStopGracefully(boolean graceful) { _gracefulStop = graceful; } /* ------------------------------------------------------------ */ public boolean getStopGracefully() { return _gracefulStop; } /* ------------------------------------------------------------ */ /** * @param listeners Array of HttpListeners. */ public void setListeners(HttpListener[] listeners) { List old = new ArrayList(_listeners); for (int i = 0; i < listeners.length; i++) { boolean existing = old.remove(listeners[i]); if (!existing) addListener(listeners[i]); } for (int i = 0; i < old.size(); i++) { HttpListener listener = (HttpListener) old.get(i); removeListener(listener); } } /* ------------------------------------------------------------ */ /** * @return Array of HttpListeners. */ public HttpListener[] getListeners() { if (_listeners == null) return new HttpListener[0]; HttpListener[] listeners = new HttpListener[_listeners.size()]; return (HttpListener[]) _listeners.toArray(listeners); } /* ------------------------------------------------------------ */ /** Create and add a SocketListener. * Conveniance method. * @param address * @return the HttpListener. * @exception IOException */ public HttpListener addListener(String address) throws IOException { return addListener(new InetAddrPort(address)); } /* ------------------------------------------------------------ */ /** Create and add a SocketListener. * Conveniance method. * @param address * @return the HttpListener. * @exception IOException */ public HttpListener addListener(InetAddrPort address) throws IOException { HttpListener listener = new SocketListener(address); listener.setHttpServer(this); _listeners.add(listener); addComponent(listener); return listener; } /* ------------------------------------------------------------ */ /** Add a HTTP Listener to the server. * @param listener The Listener. * @exception IllegalArgumentException If the listener is not for this * server. */ public HttpListener addListener(HttpListener listener) throws IllegalArgumentException { listener.setHttpServer(this); _listeners.add(listener); addComponent(listener); return listener; } /* ------------------------------------------------------------ */ /** Remove a HTTP Listener. * @param listener */ public void removeListener(HttpListener listener) { if (listener == null) return; for (int l = 0; l < _listeners.size(); l++) { if (listener.equals(_listeners.get(l))) { _listeners.remove(l); removeComponent(listener); if (listener.isStarted()) try { listener.stop(); } catch (InterruptedException e) { log.warn(LogSupport.EXCEPTION, e); } listener.setHttpServer(null); } } } /* ------------------------------------------------------------ */ public synchronized void setContexts(HttpContext[] contexts) { List old = Arrays.asList(getContexts()); for (int i = 0; i < contexts.length; i++) { boolean existing = old.remove(contexts[i]); if (!existing) addContext(contexts[i]); } for (int i = 0; i < old.size(); i++) removeContext((HttpContext) old.get(i)); } /* ------------------------------------------------------------ */ public synchronized HttpContext[] getContexts() { if (_virtualHostMap == null) return new HttpContext[0]; ArrayList contexts = new ArrayList(33); Iterator maps = _virtualHostMap.values().iterator(); while (maps.hasNext()) { PathMap pm = (PathMap) maps.next(); Iterator lists = pm.values().iterator(); while (lists.hasNext()) { List list = (List) lists.next(); for (int i = 0; i < list.size(); i++) { HttpContext context = (HttpContext) list.get(i); if (!contexts.contains(context)) contexts.add(context); } } } return (HttpContext[]) contexts.toArray(new HttpContext[contexts.size()]); } /* ------------------------------------------------------------ */ /** Add a context. * @param context */ public HttpContext addContext(HttpContext context) { if (context.getContextPath() == null || context.getContextPath().length() == 0) throw new IllegalArgumentException("No Context Path Set"); boolean existing = removeMappings(context); if (!existing) { context.setHttpServer(this); addComponent(context); } addMappings(context); return context; } /* ------------------------------------------------------------ */ /** Remove a context or Web application. * @exception IllegalStateException if context not stopped */ public boolean removeContext(HttpContext context) throws IllegalStateException { if (removeMappings(context)) { removeComponent(context); if (context.isStarted()) try { context.stop(); } catch (InterruptedException e) { log.warn(LogSupport.EXCEPTION, e); } context.setHttpServer(null); return true; } return false; } /* ------------------------------------------------------------ */ /** Add a context. * As contexts cannot be publicly created, this may be used to * alias an existing context. * @param virtualHost The virtual host or null for all hosts. * @param context */ public HttpContext addContext(String virtualHost, HttpContext context) { if (virtualHost != null) context.addVirtualHost(virtualHost); addContext(context); return context; } /* ------------------------------------------------------------ */ /** Create and add a new context. * Note that multiple contexts can be created for the same * virtualHost and contextPath. Requests are offered to multiple * contexts in the order they where added to the HttpServer. * @param contextPath * @return A HttpContext instance created by a call to newHttpContext. */ public HttpContext addContext(String contextPath) { HttpContext hc = newHttpContext(); hc.setContextPath(contextPath); addContext(hc); return hc; } /* ------------------------------------------------------------ */ /** Create and add a new context. * Note that multiple contexts can be created for the same * virtualHost and contextPath. Requests are offered to multiple * contexts in the order they where added to the HttpServer. * @param virtualHost Virtual hostname or null for all hosts. * @param contextPathSpec Path specification relative to the context path. * @return A HttpContext instance created by a call to newHttpContext. */ public HttpContext addContext(String virtualHost, String contextPathSpec) { if (virtualHost != null && virtualHost.length() == 0) virtualHost = null; HttpContext hc = newHttpContext(); hc.setContextPath(contextPathSpec); if (virtualHost != null) hc.addVirtualHost(virtualHost); addContext(hc); return hc; } /* ------------------------------------------------------------ */ /** Get specific context. * @param virtualHost The virtual host or null for all hosts. * @param contextPathSpec Path specification relative to the context path. * @param i Index among contexts of same virtualHost and pathSpec. * @return The HttpContext or null. */ public HttpContext getContext(String virtualHost, String contextPathSpec, int i) { HttpContext hc = null; contextPathSpec = HttpContext.canonicalContextPathSpec(contextPathSpec); PathMap contextMap = (PathMap) _virtualHostMap.get(virtualHost); if (contextMap != null) { List contextList = (List) contextMap.get(contextPathSpec); if (contextList != null) { if (i >= contextList.size()) return null; hc = (HttpContext) contextList.get(i); } } return hc; } /* ------------------------------------------------------------ */ /** Get or create context. * @param virtualHost The virtual host or null for all hosts. * @param contextPathSpec * @return HttpContext. If multiple contexts exist for the same * virtualHost and pathSpec, the most recently added context is returned. * If no context exists, a new context is created by a call to newHttpContext. */ public HttpContext getContext(String virtualHost, String contextPathSpec) { HttpContext hc = null; contextPathSpec = HttpContext.canonicalContextPathSpec(contextPathSpec); PathMap contextMap = (PathMap) _virtualHostMap.get(virtualHost); if (contextMap != null) { List contextList = (List) contextMap.get(contextPathSpec); if (contextList != null && contextList.size() > 0) hc = (HttpContext) contextList.get(contextList.size() - 1); } if (hc == null) hc = addContext(virtualHost, contextPathSpec); return hc; } /* ------------------------------------------------------------ */ /** Get or create context. * @param contextPathSpec Path specification relative to the context path. * @return The HttpContext If multiple contexts exist for the same * pathSpec, the most recently added context is returned. * If no context exists, a new context is created by a call to newHttpContext. */ public HttpContext getContext(String contextPathSpec) { return getContext(null, contextPathSpec); } /* ------------------------------------------------------------ */ /** Create a new HttpContext. * Specialized HttpServer classes may override this method to * return subclasses of HttpContext. * @return A new instance of HttpContext or a subclass of HttpContext */ protected HttpContext newHttpContext() { return new HttpContext(); } /* ------------------------------------------------------------ */ synchronized void addMapping(String virtualHost, HttpContext context) { // Get the map of contexts PathMap contextMap = (PathMap) _virtualHostMap.get(virtualHost); if (contextMap == null) { contextMap = new PathMap(7); _virtualHostMap.put(virtualHost, contextMap); } // Generalize contextPath String contextPathSpec = HttpContext.canonicalContextPathSpec(context.getContextPath()); // Get the list of contexts at this path List contextList = (List) contextMap.get(contextPathSpec); if (contextList == null) { contextList = new ArrayList(1); contextMap.put(contextPathSpec, contextList); } // Add the context to the list contextList.add(context); if (log.isDebugEnabled()) log.debug("Added " + context + " for host " + (virtualHost == null ? "*" : virtualHost)); } /* ------------------------------------------------------------ */ synchronized void addMappings(HttpContext context) { if (context == _notFoundContext) return; String[] hosts = context.getVirtualHosts(); if (hosts == null || hosts.length == 0) hosts = __noVirtualHost; // For each host name for (int h = 0; h < hosts.length; h++) { String virtualHost = hosts[h]; addMapping(virtualHost, context); } } /* ------------------------------------------------------------ */ synchronized boolean removeMapping(String virtualHost, HttpContext context) { boolean existing = false; if (_virtualHostMap != null) { PathMap contextMap = (PathMap) _virtualHostMap.get(virtualHost); Iterator i2 = contextMap.values().iterator(); while (i2.hasNext()) { List contextList = (List) i2.next(); if (contextList.remove(context)) existing = true; if (contextList.size() == 0) i2.remove(); } } return existing; } /* ------------------------------------------------------------ */ synchronized boolean removeMappings(HttpContext context) { boolean existing = false; if (_virtualHostMap != null) { Iterator i1 = _virtualHostMap.keySet().iterator(); while (i1.hasNext()) { String virtualHost = (String) i1.next(); if (removeMapping(virtualHost, context)) existing = true; } } return existing; } /* ------------------------------------------------------------ */ /** * @return True if the TRACE method is fully implemented. */ public boolean getTrace() { return _trace; } /* ------------------------------------------------------------ */ /** * @param trace True if the TRACE method is fully implemented. */ public void setTrace(boolean trace) { _trace = trace; } /* ------------------------------------------------------------ */ /** Get the requests per GC. * If this is set greater than zero, then the System garbage collector * will be invoked after approximately this number of requests. For * predictable response, it is often best to have frequent small runs of * the GC rather than infrequent large runs. The request count is only * approximate as it is not synchronized and multi CPU machines may miss * counting some requests. * @return Approx requests per garbage collection. */ public int getRequestsPerGC() { return _requestsPerGC; } /* ------------------------------------------------------------ */ /** Set the requests per GC. * If this is set greater than zero, then the System garbage collector * will be invoked after approximately this number of requests. For * predictable response, it is often best to have frequent small runs of * the GC rather than infrequent large runs. The request count is only * approximate as it is not synchronized and multi CPU machines may miss * counting some requests. * @param requestsPerGC Approx requests per garbage collection. */ public void setRequestsPerGC(int requestsPerGC) { _requestsPerGC = requestsPerGC; } /* ------------------------------------------------------------ */ /** Set system classes. * @deprecated. Use HttpContext */ public void setSystemClasses(String[] classes) { _systemClasses = classes; } /* ------------------------------------------------------------ */ /** Get system classes. * @deprecated. Use HttpContext */ public String[] getSystemClasses() { return _systemClasses; } /* ------------------------------------------------------------ */ /** Set system classes. * @deprecated. Use HttpContext */ public void setServerClasses(String[] classes) { _serverClasses = classes; } /* ------------------------------------------------------------ */ /** Get system classes. * @deprecated. Use HttpContext */ public String[] getServerClasses() { return _serverClasses; } /* ------------------------------------------------------------ */ /** Start all handlers then listeners. * If a subcomponent fails to start, it's exception is added to a * org.mortbay.util.MultiException and the start method continues. * @exception MultiException A collection of exceptions thrown by * start() method of subcomponents of the HttpServer. */ protected synchronized void doStart() throws Exception { log.info("Version " + Version.getImplVersion()); MultiException mex = new MultiException(); statsReset(); if (log.isDebugEnabled()) { log.debug("LISTENERS: " + _listeners); log.debug("HANDLER: " + _virtualHostMap); } if (_requestLog != null && !_requestLog.isStarted()) { try { _requestLog.start(); } catch (Exception e) { mex.add(e); } } HttpContext[] contexts = getContexts(); for (int i = 0; i < contexts.length; i++) { HttpContext context = contexts[i]; try { context.start(); } catch (Exception e) { mex.add(e); } } for (int l = 0; l < _listeners.size(); l++) { HttpListener listener = (HttpListener) _listeners.get(l); listener.setHttpServer(this); if (!listener.isStarted()) try { listener.start(); } catch (Exception e) { mex.add(e); } } mex.ifExceptionThrowMulti(); } /* ------------------------------------------------------------ */ /** Stop all listeners then all contexts. * Equivalent to stop(false); * @exception InterruptedException If interrupted, stop may not have * been called on everything. */ protected synchronized void doStop() throws InterruptedException { for (int l = 0; l < _listeners.size(); l++) { HttpListener listener = (HttpListener) _listeners.get(l); if (listener.isStarted()) { try { listener.stop(); } catch (Exception e) { if (log.isDebugEnabled()) log.warn(LogSupport.EXCEPTION, e); else log.warn(e.toString()); } } } HttpContext[] contexts = getContexts(); for (int i = 0; i < contexts.length; i++) { HttpContext context = contexts[i]; context.stop(_gracefulStop); } if (_notFoundContext != null) { _notFoundContext.stop(); removeComponent(_notFoundContext); } _notFoundContext = null; if (_requestLog != null && _requestLog.isStarted()) _requestLog.stop(); } /* ------------------------------------------------------------ */ /** Stop all listeners then all contexts. * @param graceful If true and statistics are on for a context, * then this method will wait for requestsActive to go to zero * before stopping that context. */ public synchronized void stop(boolean graceful) throws InterruptedException { boolean ov = _gracefulStop; try { _gracefulStop = graceful; stop(); } finally { _gracefulStop = ov; } } /* ------------------------------------------------------------ */ /** Join the listeners. * Join all listeners that are instances of ThreadPool. * @exception InterruptedException */ public void join() throws InterruptedException { for (int l = 0; l < _listeners.size(); l++) { HttpListener listener = (HttpListener) _listeners.get(l); if (listener.isStarted() && listener instanceof ThreadPool) { ((ThreadPool) listener).join(); } } } /* ------------------------------------------------------------ */ /** Define a virtual host alias. * All requests to the alias are handled the same as request for * the virtualHost. * @deprecated Use HttpContext.addVirtualHost * @param virtualHost Host name or IP * @param alias Alias hostname or IP */ public void addHostAlias(String virtualHost, String alias) { log.warn("addHostAlias is deprecated. Use HttpContext.addVirtualHost"); Object contextMap = _virtualHostMap.get(virtualHost); if (contextMap == null) throw new IllegalArgumentException("No Such Host: " + virtualHost); _virtualHostMap.put(alias, contextMap); } /* ------------------------------------------------------------ */ /** Set the request log. * @param log RequestLog to use. */ public synchronized void setRequestLog(RequestLog log) { if (_requestLog != null) removeComponent(_requestLog); _requestLog = log; if (_requestLog != null) addComponent(_requestLog); } /* ------------------------------------------------------------ */ public RequestLog getRequestLog() { return _requestLog; } /* ------------------------------------------------------------ */ /** Log a request to the request log * @param request The request. * @param response The response generated. * @param length The length of the body. */ void log(HttpRequest request, HttpResponse response, int length) { if (_requestLog != null && request != null && response != null) _requestLog.log(request, response, length); } /* ------------------------------------------------------------ */ /** Service a request. * Handle the request by passing it to the HttpHandler contained in * the mapped HttpContexts. * The requests host and path are used to select a list of * HttpContexts. Each HttpHandler in these context is offered * the request in turn, until the request is handled. * * If no handler handles the request, 404 Not Found is returned. * * @param request * @param response * @return The HttpContext that completed handling of the request or null. * @exception IOException * @exception HttpException */ public HttpContext service(HttpRequest request, HttpResponse response) throws IOException, HttpException { String host = request.getHost(); if (_requestsPerGC > 0 && _gcRequests++ > _requestsPerGC) { _gcRequests = 0; System.gc(); } while (true) { PathMap contextMap = (PathMap) _virtualHostMap.get(host); if (contextMap != null) { List contextLists = contextMap.getMatches(request.getPath()); if (contextLists != null) { if (log.isTraceEnabled()) log.trace("Contexts at " + request.getPath() + ": " + contextLists); for (int i = 0; i < contextLists.size(); i++) { Map.Entry entry = (Map.Entry) contextLists.get(i); List contextList = (List) entry.getValue(); for (int j = 0; j < contextList.size(); j++) { HttpContext context = (HttpContext) contextList.get(j); if (log.isDebugEnabled()) log.debug("Try " + context + "," + j); context.handle(request, response); if (request.isHandled()) return context; } } } } // try no host if (host == null) break; host = null; } synchronized (this) { if (_notFoundContext == null) { _notFoundContext = new HttpContext(); _notFoundContext.setContextPath("/"); _notFoundContext.setHttpServer(this); try { _notFoundContext.addHandler((NotFoundHandler) Class .forName("net.lightbody.bmp.proxy.jetty.http.handler.RootNotFoundHandler") .newInstance()); } catch (Exception e) { _notFoundContext.addHandler(new NotFoundHandler()); } addComponent(_notFoundContext); try { _notFoundContext.start(); } catch (Exception e) { log.warn(LogSupport.EXCEPTION, e); } } _notFoundContext.handle(request, response); if (!request.isHandled()) response.sendError(HttpResponse.__404_Not_Found); return _notFoundContext; } } /* ------------------------------------------------------------ */ /** Find handler. * Find a handler for a URI. This method is provided for * the servlet context getContext method to search for another * context by URI. A list of hosts may be passed to qualify the * search. * @param uri URI that must be satisfied by the servlet handler * @param vhosts null or a list of virtual hosts names to search * @return HttpHandler */ public HttpHandler findHandler(Class handlerClass, String uri, String[] vhosts) { uri = URI.stripPath(uri); if (vhosts == null || vhosts.length == 0) vhosts = __noVirtualHost; for (int h = 0; h < vhosts.length; h++) { String host = vhosts[h]; PathMap contextMap = (PathMap) _virtualHostMap.get(host); if (contextMap != null) { List contextLists = contextMap.getMatches(uri); if (contextLists != null) { for (int i = 0; i < contextLists.size(); i++) { Map.Entry entry = (Map.Entry) contextLists.get(i); List contextList = (List) entry.getValue(); for (int j = 0; j < contextList.size(); j++) { HttpContext context = (HttpContext) contextList.get(j); HttpHandler handler = context.getHandler(handlerClass); if (handler != null) return handler; } } } } } return null; } /* ------------------------------------------------------------ */ public UserRealm addRealm(UserRealm realm) { return (UserRealm) _realmMap.put(realm.getName(), realm); } /* ------------------------------------------------------------ */ /** Get a named UserRealm. * @param realmName The name of the realm or null. * @return The named realm. If the name is null and only a single realm * is known, that is returned. */ public UserRealm getRealm(String realmName) { if (realmName == null) { if (_realmMap.size() == 1) return (UserRealm) _realmMap.values().iterator().next(); log.warn("Null realmName with multiple known realms"); } return (UserRealm) _realmMap.get(realmName); } /* ------------------------------------------------------------ */ public UserRealm removeRealm(String realmName) { return (UserRealm) _realmMap.remove(realmName); } /* ------------------------------------------------------------ */ public Map getHostMap() { return _virtualHostMap; } /* ------------------------------------------------------------ */ /** * @return True if the remote host name of connections is resolved. */ public boolean getResolveRemoteHost() { return _resolveRemoteHost; } /* ------------------------------------------------------------ */ /** * @param resolveRemoteHost True if the remote host name of connections is resolved. */ public void setResolveRemoteHost(boolean resolveRemoteHost) { _resolveRemoteHost = resolveRemoteHost; } /* ------------------------------------------------------------ */ private boolean _statsOn = false; private transient Object _statsLock = new Object[0]; private transient long _statsStartedAt = 0; private transient int _connections; // total number of connections made to server private transient int _connectionsOpen; // number of connections currently open private transient int _connectionsOpenMin; // min number of connections open simultaneously private transient int _connectionsOpenMax; // max number of connections open simultaneously private transient long _connectionsDurationMin; // min duration of a connection private transient long _connectionsDurationMax; // max duration of a connection private transient long _connectionsDurationTotal; // total duration of all coneection private transient int _errors; // total bad requests to the server private transient int _requests; // total requests made to the server private transient int _requestsActive; // number of requests currently being handled private transient int _requestsActiveMin; // min number of connections handled simultaneously private transient int _requestsActiveMax; // max number of connections handled simultaneously private transient int _connectionsRequestsMin; // min requests per connection private transient int _connectionsRequestsMax; // max requests per connection private transient long _requestsDurationMin; // min request duration private transient long _requestsDurationMax; // max request duration private transient long _requestsDurationTotal; // total request duration /* ------------------------------------------------------------ */ /** Reset statistics. */ public void statsReset() { _statsStartedAt = System.currentTimeMillis(); _connections = 0; _connectionsOpenMin = _connectionsOpen; _connectionsOpenMax = _connectionsOpen; _connectionsOpen = 0; _connectionsDurationMin = 0; _connectionsDurationMax = 0; _connectionsDurationTotal = 0; _errors = 0; _requests = 0; _requestsActiveMin = _requestsActive; _requestsActiveMax = _requestsActive; _requestsActive = 0; _connectionsRequestsMin = 0; _connectionsRequestsMax = 0; _requestsDurationMin = 0; _requestsDurationMax = 0; _requestsDurationTotal = 0; } /* ------------------------------------------------------------ */ public void setStatsOn(boolean on) { log.info("Statistics on = " + on + " for " + this); _statsOn = on; } /* ------------------------------------------------------------ */ /** * @return True if statistics collection is turned on. */ public boolean getStatsOn() { return _statsOn; } /* ------------------------------------------------------------ */ /** * @return Timestamp stats were started at. */ public long getStatsOnMs() { return _statsOn ? (System.currentTimeMillis() - _statsStartedAt) : 0; } /* ------------------------------------------------------------ */ /** * @return Returns the connectionsDurationMin. */ public long getConnectionsDurationMin() { return _connectionsDurationMin; } /* ------------------------------------------------------------ */ /** * @return Returns the connectionsDurationTotal. */ public long getConnectionsDurationTotal() { return _connectionsDurationTotal; } /* ------------------------------------------------------------ */ /** * @return Returns the connectionsOpenMin. */ public int getConnectionsOpenMin() { return _connectionsOpenMin; } /* ------------------------------------------------------------ */ /** * @return Returns the connectionsRequestsMin. */ public int getConnectionsRequestsMin() { return _connectionsRequestsMin; } /* ------------------------------------------------------------ */ /** * @return Returns the requestsActiveMin. */ public int getRequestsActiveMin() { return _requestsActiveMin; } /* ------------------------------------------------------------ */ /** * @return Returns the requestsDurationMin. */ public long getRequestsDurationMin() { return _requestsDurationMin; } /* ------------------------------------------------------------ */ /** * @return Returns the requestsDurationTotal. */ public long getRequestsDurationTotal() { return _requestsDurationTotal; } /* ------------------------------------------------------------ */ /** * @return Number of connections accepted by the server since * statsReset() called. Undefined if setStatsOn(false). */ public int getConnections() { return _connections; } /* ------------------------------------------------------------ */ /** * @return Number of connections currently open that were opened * since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsOpen() { return _connectionsOpen; } /* ------------------------------------------------------------ */ /** * @return Maximum number of connections opened simultaneously * since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsOpenMax() { return _connectionsOpenMax; } /* ------------------------------------------------------------ */ /** * @return Average duration in milliseconds of open connections * since statsReset() called. Undefined if setStatsOn(false). */ public long getConnectionsDurationAve() { return _connections == 0 ? 0 : (_connectionsDurationTotal / _connections); } /* ------------------------------------------------------------ */ /** * @return Maximum duration in milliseconds of an open connection * since statsReset() called. Undefined if setStatsOn(false). */ public long getConnectionsDurationMax() { return _connectionsDurationMax; } /* ------------------------------------------------------------ */ /** * @return Average number of requests per connection * since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsRequestsAve() { return _connections == 0 ? 0 : (_requests / _connections); } /* ------------------------------------------------------------ */ /** * @return Maximum number of requests per connection * since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsRequestsMax() { return _connectionsRequestsMax; } /* ------------------------------------------------------------ */ /** * @return Number of errors generated while handling requests. * since statsReset() called. Undefined if setStatsOn(false). */ public int getErrors() { return _errors; } /* ------------------------------------------------------------ */ /** * @return Number of requests * since statsReset() called. Undefined if setStatsOn(false). */ public int getRequests() { return _requests; } /* ------------------------------------------------------------ */ /** * @return Number of requests currently active. * Undefined if setStatsOn(false). */ public int getRequestsActive() { return _requestsActive; } /* ------------------------------------------------------------ */ /** * @return Maximum number of active requests * since statsReset() called. Undefined if setStatsOn(false). */ public int getRequestsActiveMax() { return _requestsActiveMax; } /* ------------------------------------------------------------ */ /** * @return Average duration of request handling in milliseconds * since statsReset() called. Undefined if setStatsOn(false). */ public long getRequestsDurationAve() { return _requests == 0 ? 0 : (_requestsDurationTotal / _requests); } /* ------------------------------------------------------------ */ /** * @return Get maximum duration in milliseconds of request handling * since statsReset() called. Undefined if setStatsOn(false). */ public long getRequestsDurationMax() { return _requestsDurationMax; } /* ------------------------------------------------------------ */ void statsOpenConnection() { synchronized (_statsLock) { _connectionsOpen++; if (_connectionsOpen > _connectionsOpenMax) _connectionsOpenMax = _connectionsOpen; } } /* ------------------------------------------------------------ */ void statsGotRequest() { synchronized (_statsLock) { _requestsActive++; if (_requestsActive > _requestsActiveMax) _requestsActiveMax = _requestsActive; } } /* ------------------------------------------------------------ */ void statsEndRequest(long duration, boolean ok) { synchronized (_statsLock) { _requests++; _requestsActive--; if (_requestsActive < 0) _requestsActive = 0; if (_requestsActive < _requestsActiveMin) _requestsActiveMin = _requestsActive; if (ok) { _requestsDurationTotal += duration; if (_requestsDurationMin == 0 || duration < _requestsDurationMin) _requestsDurationMin = duration; if (duration > _requestsDurationMax) _requestsDurationMax = duration; } else _errors++; } } /* ------------------------------------------------------------ */ void statsCloseConnection(long duration, int requests) { synchronized (_statsLock) { _connections++; _connectionsOpen--; _connectionsDurationTotal += duration; if (_connectionsOpen < 0) _connectionsOpen = 0; if (_connectionsOpen < _connectionsOpenMin) _connectionsOpenMin = _connectionsOpen; if (_connectionsDurationMin == 0 || duration < _connectionsDurationMin) _connectionsDurationMin = duration; if (duration > _connectionsDurationMax) _connectionsDurationMax = duration; if (_connectionsRequestsMin == 0 || requests < _connectionsRequestsMin) _connectionsRequestsMin = requests; if (requests > _connectionsRequestsMax) _connectionsRequestsMax = requests; } } /* ------------------------------------------------------------ */ /** Save the HttpServer * The server is saved by serialization to the given filename or URL. * * @param saveat A file or URL to save the configuration at. * @exception MalformedURLException * @exception IOException */ public void save(String saveat) throws MalformedURLException, IOException { Resource resource = Resource.newResource(saveat); ObjectOutputStream out = new ObjectOutputStream(resource.getOutputStream()); out.writeObject(this); out.flush(); out.close(); log.info("Saved " + this + " to " + resource); } /* ------------------------------------------------------------ */ /** Destroy a stopped server. * Remove all components and send notifications to all event * listeners. The HttpServer must be stopped before it can be destroyed. */ public void destroy() { __servers.remove(this); if (isStarted()) throw new IllegalStateException("Started"); if (_listeners != null) _listeners.clear(); _listeners = null; if (_virtualHostMap != null) _virtualHostMap.clear(); _virtualHostMap = null; _notFoundContext = null; super.destroy(); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /** Construct server from command line arguments. * @param args */ public static void main(String[] args) { if (args.length == 0 || args.length > 2) { System.err.println("\nUsage - java net.lightbody.bmp.proxy.jetty.http.HttpServer [<addr>:]<port>"); System.err.println("\nUsage - java net.lightbody.bmp.proxy.jetty.http.HttpServer -r [savefile]"); System.err.println(" Serves files from '.' directory"); System.err.println(" Dump handler for not found requests"); System.err.println(" Default port is 8080"); System.exit(1); } try { if (args.length == 1) { // Create the server HttpServer server = new HttpServer(); // Default is no virtual host String host = null; HttpContext context = server.getContext(host, "/"); context.setResourceBase("."); context.addHandler(new ResourceHandler()); context.addHandler(new DumpHandler()); context.addHandler(new NotFoundHandler()); InetAddrPort address = new InetAddrPort(args[0]); server.addListener(address); server.start(); } else { Resource resource = Resource.newResource(args[1]); ObjectInputStream in = new ObjectInputStream(resource.getInputStream()); HttpServer server = (HttpServer) in.readObject(); in.close(); server.start(); } } catch (Exception e) { log.warn(LogSupport.EXCEPTION, e); } } }