diff --git a/apps/jetty/java/src/org/mortbay/http/HttpContext.java b/apps/jetty/java/src/org/mortbay/http/HttpContext.java
new file mode 100644
index 000000000..061d3ad17
--- /dev/null
+++ b/apps/jetty/java/src/org/mortbay/http/HttpContext.java
@@ -0,0 +1,2198 @@
+// ========================================================================
+// $Id: HttpContext.java,v 1.136 2006/02/21 09:47:43 gregwilkins Exp $
+// Copyright 2000-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 org.mortbay.http;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.UnknownHostException;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.commons.logging.Log;
+import org.mortbay.log.LogFactory;
+import org.mortbay.http.ResourceCache.ResourceMetaData;
+import org.mortbay.http.handler.ErrorPageHandler;
+import org.mortbay.util.Container;
+import org.mortbay.util.EventProvider;
+import org.mortbay.util.IO;
+import org.mortbay.util.LazyList;
+import org.mortbay.util.LifeCycle;
+import org.mortbay.util.LogSupport;
+import org.mortbay.util.MultiException;
+import org.mortbay.util.Resource;
+import org.mortbay.util.URI;
+
+
+/* ------------------------------------------------------------ */
+/** Context for a collection of HttpHandlers.
+ * HTTP Context provides an ordered container for HttpHandlers
+ * that share the same path prefix, filebase, resourcebase and/or
+ * classpath.
+ *
+ * A HttpContext is analagous to a ServletContext in the
+ * Servlet API, except that it may contain other types of handler
+ * other than servlets.
+ *
+ * A ClassLoader is created for the context and it uses
+ * Thread.currentThread().getContextClassLoader(); as it's parent loader.
+ * The class loader is initialized during start(), when a derived
+ * context calls initClassLoader() or on the first call to loadClass()
+ *
+ *
+ * Note. that order is important when configuring a HttpContext.
+ * For example, if resource serving is enabled before servlets, then resources
+ * take priority.
+ *
+ * @see HttpServer
+ * @see HttpHandler
+ * @see org.mortbay.jetty.servlet.ServletHttpContext
+ * @version $Id: HttpContext.java,v 1.136 2006/02/21 09:47:43 gregwilkins Exp $
+ * @author Greg Wilkins (gregw)
+ */
+public class HttpContext extends Container
+ implements LifeCycle,
+ HttpHandler,
+ EventProvider,
+ Serializable
+{
+ private static Log log = LogFactory.getLog(HttpContext.class);
+
+ /* ------------------------------------------------------------ */
+ /** File class path attribute.
+ * If this name is set as a context init parameter, then the attribute
+ * name given will be used to set the file classpath for the context as a
+ * context attribute.
+ */
+ public final static String __fileClassPathAttr=
+ "org.mortbay.http.HttpContext.FileClassPathAttribute";
+
+ public final static String __ErrorHandler=
+ "org.mortbay.http.ErrorHandler";
+
+
+ /* ------------------------------------------------------------ */
+ /* ------------------------------------------------------------ */
+ // These attributes are serialized by WebApplicationContext, which needs
+ // to be updated if you add to these
+ private String _contextPath;
+ private List _vhosts=new ArrayList(2);
+ private List _hosts=new ArrayList(2);
+ private List _handlers=new ArrayList(3);
+ private Map _attributes = new HashMap(3);
+ private boolean _redirectNullPath=true;
+ private boolean _statsOn=false;
+ private PermissionCollection _permissions;
+ private boolean _classLoaderJava2Compliant=true;
+ private ResourceCache _resources;
+ private String[] _systemClasses=new String [] {"java.","javax.servlet.","javax.xml.","org.mortbay.","org.xml.","org.w3c.","org.apache.commons.logging."};
+ private String[] _serverClasses = new String[] {"-org.mortbay.http.PathMap","-org.mortbay.jetty.servlet.Invoker","-org.mortbay.jetty.servlet.JSR154Filter","-org.mortbay.jetty.servlet.Default","org.mortbay.jetty.Server","org.mortbay.http.","org.mortbay.start.","org.mortbay.stop."};
+
+ /* ------------------------------------------------------------ */
+ private String _contextName;
+ private String _classPath;
+ private Map _initParams = new HashMap(11);
+ private UserRealm _userRealm;
+ private String _realmName;
+ private PathMap _constraintMap=new PathMap();
+ private Authenticator _authenticator;
+ private RequestLog _requestLog;
+
+
+ private String[] _welcomes=
+ {
+ "welcome.html",
+ "index.html",
+ "index.htm",
+ "index.jsp"
+ };
+
+
+ /* ------------------------------------------------------------ */
+ private transient boolean _gracefulStop;
+ private transient ClassLoader _parent;
+ private transient ClassLoader _loader;
+ private transient HttpServer _httpServer;
+ private transient File _tmpDir;
+ private transient HttpHandler[] _handlersArray;
+ private transient String[] _vhostsArray;
+
+
+ /* ------------------------------------------------------------ */
+ transient Object _statsLock=new Object[0];
+ transient long _statsStartedAt;
+ transient int _requests;
+ transient int _requestsActive;
+ transient int _requestsActiveMax;
+ transient int _responses1xx; // Informal
+ transient int _responses2xx; // Success
+ transient int _responses3xx; // Redirection
+ transient int _responses4xx; // Client Error
+ transient int _responses5xx; // Server Error
+
+
+ /* ------------------------------------------------------------ */
+ /** Constructor.
+ */
+ public HttpContext()
+ {
+ setAttribute(__ErrorHandler, new ErrorPageHandler());
+ _resources=new ResourceCache();
+ addComponent(_resources);
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Constructor.
+ * @param httpServer
+ * @param contextPathSpec
+ */
+ public HttpContext(HttpServer httpServer,String contextPathSpec)
+ {
+ this();
+ setHttpServer(httpServer);
+ setContextPath(contextPathSpec);
+ }
+
+ /* ------------------------------------------------------------ */
+ private void readObject(java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+ _statsLock=new Object[0];
+ getHandlers();
+ for (int i=0;i<_handlersArray.length;i++)
+ _handlersArray[i].initialize(this);
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Get the ThreadLocal HttpConnection.
+ * Get the HttpConnection for current thread, if any. This method is
+ * not static in order to control access.
+ * @return HttpConnection for this thread.
+ */
+ public HttpConnection getHttpConnection()
+ {
+ return HttpConnection.getHttpConnection();
+ }
+
+ /* ------------------------------------------------------------ */
+ void setHttpServer(HttpServer httpServer)
+ {
+ _httpServer=httpServer;
+ _contextName=null;
+
+ }
+
+ /* ------------------------------------------------------------ */
+ public HttpServer getHttpServer()
+ {
+ return _httpServer;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setStopGracefully(boolean graceful)
+ {
+ _gracefulStop=graceful;
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean getStopGracefully()
+ {
+ return _gracefulStop;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public static String canonicalContextPathSpec(String contextPathSpec)
+ {
+ // check context path
+ if (contextPathSpec==null ||
+ contextPathSpec.indexOf(',')>=0 ||
+ contextPathSpec.startsWith("*"))
+ throw new IllegalArgumentException ("Illegal context spec:"+contextPathSpec);
+
+ if(!contextPathSpec.startsWith("/"))
+ contextPathSpec='/'+contextPathSpec;
+
+ if (contextPathSpec.length()>1)
+ {
+ if (contextPathSpec.endsWith("/"))
+ contextPathSpec+="*";
+ else if (!contextPathSpec.endsWith("/*"))
+ contextPathSpec+="/*";
+ }
+
+ return contextPathSpec;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setContextPath(String contextPathSpec)
+ {
+ if (_httpServer!=null)
+ _httpServer.removeMappings(this);
+
+ contextPathSpec=canonicalContextPathSpec(contextPathSpec);
+
+ if (contextPathSpec.length()>1)
+ _contextPath=contextPathSpec.substring(0,contextPathSpec.length()-2);
+ else
+ _contextPath="/";
+
+ _contextName=null;
+
+ if (_httpServer!=null)
+ _httpServer.addMappings(this);
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return The context prefix
+ */
+ public String getContextPath()
+ {
+ return _contextPath;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Add a virtual host alias to this context.
+ * @see #setVirtualHosts
+ * @param hostname A hostname. A null host name means any hostname is
+ * acceptable. Host names may String representation of IP addresses.
+ */
+ public void addVirtualHost(String hostname)
+ {
+ // Note that null hosts are also added.
+ if (!_vhosts.contains(hostname))
+ {
+ _vhosts.add(hostname);
+ _contextName=null;
+
+ if (_httpServer!=null)
+ {
+ if (_vhosts.size()==1)
+ _httpServer.removeMapping(null,this);
+ _httpServer.addMapping(hostname,this);
+ }
+ _vhostsArray=null;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** remove a virtual host alias to this context.
+ * @see #setVirtualHosts
+ * @param hostname A hostname. A null host name means any hostname is
+ * acceptable. Host names may String representation of IP addresses.
+ */
+ public void removeVirtualHost(String hostname)
+ {
+ // Note that null hosts are also added.
+ if (_vhosts.remove(hostname))
+ {
+ _contextName=null;
+ if (_httpServer!=null)
+ {
+ _httpServer.removeMapping(hostname,this);
+ if (_vhosts.size()==0)
+ _httpServer.addMapping(null,this);
+ }
+ _vhostsArray=null;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Set the virtual hosts for the context.
+ * Only requests that have a matching host header or fully qualified
+ * URL will be passed to that context with a virtual host name.
+ * A context with no virtual host names or a null virtual host name is
+ * available to all requests that are not served by a context with a
+ * matching virtual host name.
+ * @param hosts Array of virtual hosts that this context responds to. A
+ * null host name or null/empty array means any hostname is acceptable.
+ * Host names may String representation of IP addresses.
+ */
+ public void setVirtualHosts(String[] hosts)
+ {
+ List old = new ArrayList(_vhosts);
+
+ if (hosts!=null)
+ {
+ for (int i=0;i0)
+ buf.append(File.pathSeparator);
+ buf.append(iter.next().toString());
+ }
+
+ if (log.isDebugEnabled()) log.debug("fileClassPath="+buf);
+ return buf.toString();
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Sets the class path for the context.
+ * A class path is only required for a context if it uses classes
+ * that are not in the system class path.
+ * @param classPath a comma or ';' separated list of class
+ * resources. These may be jar files, directories or URLs to jars
+ * or directories.
+ */
+ public void setClassPath(String classPath)
+ {
+ _classPath=classPath;
+ if (isStarted())
+ log.warn("classpath set while started");
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Add the class path element to the context.
+ * A class path is only required for a context if it uses classes
+ * that are not in the system class path.
+ * @param classPath a comma or ';' separated list of class
+ * resources. These may be jar files, directories or URLs to jars
+ * or directories.
+ */
+ public void addClassPath(String classPath)
+ {
+ if (_classPath==null || _classPath.length()==0)
+ _classPath=classPath;
+ else
+ _classPath+=","+classPath;
+
+ if (isStarted())
+ log.warn("classpath set while started");
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Add elements to the class path for the context from the jar and zip files found
+ * in the specified resource.
+ * @param lib the resource that contains the jar and/or zip files.
+ * @param append true if the classpath entries are to be appended to any
+ * existing classpath, or false if they replace the existing classpath.
+ * @see #setClassPath(String)
+ */
+ public void addClassPaths(Resource lib)
+ {
+ if (isStarted())
+ log.warn("classpaths set while started");
+
+ if (lib.exists() && lib.isDirectory())
+ {
+ String[] files=lib.list();
+ for (int f=0;files!=null && f 0)
+ {
+ Object constraints= null;
+
+ // for each path match
+ // Add only constraints that have the correct method
+ // break if the matching pattern changes. This allows only
+ // constraints with matching pattern and method to be combined.
+ loop:
+ for (int m= 0; m < scss.size(); m++)
+ {
+ Map.Entry entry= (Map.Entry)scss.get(m);
+ Object scs= entry.getValue();
+ String p=(String)entry.getKey();
+ for (int c=0;c0)
+ {
+ Object o = request.getHttpConnection().getConnection();
+ if (o instanceof Socket)
+ {
+ Socket s=(Socket)o;
+ if (!_hosts.contains(s.getLocalAddress()))
+ {
+ if(log.isDebugEnabled())log.debug(s.getLocalAddress()+" not in "+_hosts);
+ return;
+ }
+ }
+ }
+
+ // handle stats
+ if (_statsOn)
+ {
+ synchronized(_statsLock)
+ {
+ _requests++;
+ _requestsActive++;
+ if (_requestsActive>_requestsActiveMax)
+ _requestsActiveMax=_requestsActive;
+ }
+ }
+
+ String pathInContext = URI.canonicalPath(request.getPath());
+ if (pathInContext==null)
+ {
+ // Must be a bad request.
+ throw new HttpException(HttpResponse.__400_Bad_Request);
+ }
+
+ if (_contextPath.length()>1)
+ pathInContext=pathInContext.substring(_contextPath.length());
+
+ if (_redirectNullPath && (pathInContext==null ||
+ pathInContext.length()==0))
+ {
+ StringBuffer buf=request.getRequestURL();
+ buf.append("/");
+ String q=request.getQuery();
+ if (q!=null&&q.length()!=0)
+ buf.append("?"+q);
+
+ response.sendRedirect(buf.toString());
+ if (log.isDebugEnabled())
+ log.debug(this+" consumed all of path "+
+ request.getPath()+
+ ", redirect to "+buf.toString());
+ return;
+ }
+
+ String pathParams=null;
+ int semi = pathInContext.lastIndexOf(';');
+ if (semi>=0)
+ {
+ int pl = pathInContext.length()-semi;
+ String ep=request.getEncodedPath();
+ if(';'==ep.charAt(ep.length()-pl))
+ {
+ pathParams=pathInContext.substring(semi+1);
+ pathInContext=pathInContext.substring(0,semi);
+ }
+ }
+
+ try
+ {
+ handle(pathInContext,pathParams,request,response);
+ }
+ finally
+ {
+ if (_userRealm!=null && request.hasUserPrincipal())
+ _userRealm.disassociate(request.getUserPrincipal());
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Handler request.
+ * Call each HttpHandler until request is handled.
+ * @param pathInContext Path in context
+ * @param pathParams Path parameters such as encoded Session ID
+ * @param request
+ * @param response
+ * @return True if the request has been handled.
+ * @exception HttpException
+ * @exception IOException
+ */
+ public void handle(String pathInContext,
+ String pathParams,
+ HttpRequest request,
+ HttpResponse response)
+ throws HttpException, IOException
+ {
+ Object old_scope= null;
+ try
+ {
+ old_scope= enterContextScope(request,response);
+ HttpHandler[] handlers= getHandlers();
+ for (int k= 0; k < handlers.length; k++)
+ {
+ HttpHandler handler= handlers[k];
+ if (handler == null)
+ {
+ handlers= getHandlers();
+ k= -1;
+ continue;
+ }
+ if (!handler.isStarted())
+ {
+ if (log.isDebugEnabled())
+ log.debug(handler + " not started in " + this);
+ continue;
+ }
+ if (log.isDebugEnabled())
+ log.debug("Handler " + handler);
+ handler.handle(pathInContext, pathParams, request, response);
+ if (request.isHandled())
+ {
+ if (log.isDebugEnabled())
+ log.debug("Handled by " + handler);
+ return;
+ }
+ }
+ return;
+ }
+ finally
+ {
+ leaveContextScope(request, response, old_scope);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Enter the context scope.
+ * This method is called (by handle or servlet dispatchers) to indicate that
+ * request handling is entering the scope of this context. The opaque scope object
+ * returned, should be passed to the leaveContextScope method.
+ */
+ public Object enterContextScope(HttpRequest request, HttpResponse response)
+ {
+ // Save the thread context loader
+ Thread thread = Thread.currentThread();
+ ClassLoader cl=thread.getContextClassLoader();
+ HttpContext c=response.getHttpContext();
+
+ Scope scope=null;
+ if (cl!=HttpContext.class.getClassLoader() || c!=null)
+ {
+ scope=new Scope();
+ scope._classLoader=cl;
+ scope._httpContext=c;
+ }
+
+ if (_loader!=null)
+ thread.setContextClassLoader(_loader);
+ response.setHttpContext(this);
+
+ return scope;
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Leave the context scope.
+ * This method is called (by handle or servlet dispatchers) to indicate that
+ * request handling is leaveing the scope of this context. The opaque scope object
+ * returned by enterContextScope should be passed in.
+ */
+ public void leaveContextScope(HttpRequest request, HttpResponse response,Object oldScope)
+ {
+ if (oldScope==null)
+ {
+ Thread.currentThread()
+ .setContextClassLoader(HttpContext.class.getClassLoader());
+ response.setHttpContext(null);
+ }
+ else
+ {
+ Scope old = (Scope)oldScope;
+ Thread.currentThread().setContextClassLoader(old._classLoader);
+ response.setHttpContext(old._httpContext);
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public String getHttpContextName()
+ {
+ if (_contextName==null)
+ _contextName = (_vhosts.size()>1?(_vhosts.toString()+":"):"")+_contextPath;
+ return _contextName;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setHttpContextName(String s)
+ {
+ _contextName=s;
+ }
+
+ /* ------------------------------------------------------------ */
+ public String toString()
+ {
+ return "HttpContext["+getContextPath()+","+getHttpContextName()+"]";
+ }
+
+ /* ------------------------------------------------------------ */
+ public String toString(boolean detail)
+ {
+ return "HttpContext["+getContextPath()+","+getHttpContextName()+"]" +
+ (detail?("="+_handlers):"");
+ }
+
+
+ /* ------------------------------------------------------------ */
+ protected synchronized void doStart()
+ throws Exception
+ {
+ if (isStarted())
+ return;
+
+ if (_httpServer.getServerClasses()!=null)
+ _serverClasses=_httpServer.getServerClasses();
+ if (_httpServer.getSystemClasses()!=null)
+ _systemClasses=_httpServer.getSystemClasses();
+
+ _resources.start();
+
+ statsReset();
+
+ if (_httpServer==null)
+ throw new IllegalStateException("No server for "+this);
+
+ // start the context itself
+ _resources.getMimeMap();
+ _resources.getEncodingMap();
+
+ // Setup realm
+ if (_userRealm==null && _authenticator!=null)
+ {
+ _userRealm=_httpServer.getRealm(_realmName);
+ if (_userRealm==null)
+ log.warn("No Realm: "+_realmName);
+ }
+
+ // setup the context loader
+ initClassLoader(false);
+
+ // Set attribute if needed
+ String attr = getInitParameter(__fileClassPathAttr);
+ if (attr!=null && attr.length()>0)
+ setAttribute(attr,getFileClassPath());
+
+ // Start the handlers
+ Thread thread = Thread.currentThread();
+ ClassLoader lastContextLoader=thread.getContextClassLoader();
+ try
+ {
+ if (_loader!=null)
+ thread.setContextClassLoader(_loader);
+
+ if (_requestLog!=null)
+ _requestLog.start();
+
+ startHandlers();
+ }
+ finally
+ {
+ thread.setContextClassLoader(lastContextLoader);
+ getHandlers();
+ }
+
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Start the handlers.
+ * This is called by start after the classloader has been
+ * initialized and set as the thread context loader.
+ * It may be specialized to provide custom handling
+ * before any handlers are started.
+ * @exception Exception
+ */
+ protected void startHandlers()
+ throws Exception
+ {
+ // Prepare a multi exception
+ MultiException mx = new MultiException();
+
+ Iterator handlers = _handlers.iterator();
+ while(handlers.hasNext())
+ {
+ HttpHandler handler=(HttpHandler)handlers.next();
+ if (!handler.isStarted())
+ try{handler.start();}catch(Exception e){mx.add(e);}
+ }
+ mx.ifExceptionThrow();
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Stop the context.
+ * @param graceful If true and statistics are on, then this method will wait
+ * for requestsActive to go to zero before calling stop()
+ */
+ public void stop(boolean graceful)
+ throws InterruptedException
+ {
+ boolean gs=_gracefulStop;
+ try
+ {
+ _gracefulStop=true;
+
+ // wait for all requests to complete.
+ while (graceful && _statsOn && _requestsActive>0 && _httpServer!=null)
+ try {Thread.sleep(100);}
+ catch (InterruptedException e){throw e;}
+ catch (Exception e){LogSupport.ignore(log,e);}
+
+ stop();
+ }
+ finally
+ {
+ _gracefulStop=gs;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Stop the context.
+ */
+ protected void doStop()
+ throws Exception
+ {
+ if (_httpServer==null)
+ throw new InterruptedException("Destroy called");
+
+ synchronized(this)
+ {
+ // Notify the container for the stop
+ Thread thread = Thread.currentThread();
+ ClassLoader lastContextLoader=thread.getContextClassLoader();
+ try
+ {
+ if (_loader!=null)
+ thread.setContextClassLoader(_loader);
+ Iterator handlers = _handlers.iterator();
+ while(handlers.hasNext())
+ {
+ HttpHandler handler=(HttpHandler)handlers.next();
+ if (handler.isStarted())
+ {
+ try{handler.stop();}
+ catch(Exception e){log.warn(LogSupport.EXCEPTION,e);}
+ }
+ }
+
+ if (_requestLog!=null)
+ _requestLog.stop();
+ }
+ finally
+ {
+ thread.setContextClassLoader(lastContextLoader);
+ }
+
+ // TODO this is a poor test
+ if (_loader instanceof ContextLoader)
+ {
+ ((ContextLoader)_loader).destroy();
+ LogFactory.release(_loader);
+ }
+
+ _loader=null;
+ }
+ _resources.flushCache();
+ _resources.stop();
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Destroy a context.
+ * Destroy a context and remove it from the HttpServer. The
+ * HttpContext must be stopped before it can be destroyed.
+ */
+ public void destroy()
+ {
+ if (isStarted())
+ throw new IllegalStateException("Started");
+
+ if (_httpServer!=null)
+ _httpServer.removeContext(this);
+
+ _httpServer=null;
+
+ if (_handlers!=null)
+ _handlers.clear();
+
+ _handlers=null;
+ _parent=null;
+ _loader=null;
+ if (_attributes!=null)
+ _attributes.clear();
+ _attributes=null;
+ if (_initParams!=null)
+ _initParams.clear();
+ _initParams=null;
+ if (_vhosts!=null)
+ _vhosts.clear();
+ _vhosts=null;
+ _hosts=null;
+ _tmpDir=null;
+
+ _permissions=null;
+
+ removeComponent(_resources);
+ if (_resources!=null)
+ {
+ _resources.flushCache();
+ if (_resources.isStarted())
+ try{_resources.stop();}catch(Exception e){LogSupport.ignore(log,e);}
+ _resources.destroy();
+ }
+ _resources=null;
+
+ super.destroy();
+
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Set the request log.
+ * @param log RequestLog to use.
+ */
+ public void setRequestLog(RequestLog log)
+ {
+ _requestLog=log;
+ }
+
+ /* ------------------------------------------------------------ */
+ public RequestLog getRequestLog()
+ {
+ return _requestLog;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Send an error response.
+ * This method may be specialized to provide alternative error handling for
+ * errors generated by the container. The default implemenation calls HttpResponse.sendError
+ * @param response the response to send
+ * @param code The error code
+ * @param msg The message for the error or null for the default
+ * @throws IOException Problem sending response.
+ */
+ public void sendError(HttpResponse response,int code,String msg)
+ throws IOException
+ {
+ response.sendError(code,msg);
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Send an error response.
+ * This method obtains the responses context and call sendError for context specific
+ * error handling.
+ * @param response the response to send
+ * @param code The error code
+ * @param msg The message for the error or null for the default
+ * @throws IOException Problem sending response.
+ */
+ public static void sendContextError(HttpResponse response,int code,String msg)
+ throws IOException
+ {
+ HttpContext context = response.getHttpContext();
+ if (context!=null)
+ context.sendError(response,code,msg);
+ else
+ response.sendError(code,msg);
+ }
+
+ /* ------------------------------------------------------------ */
+ /** True set statistics recording on for this context.
+ * @param on If true, statistics will be recorded for this context.
+ */
+ public void setStatsOn(boolean on)
+ {
+ log.info("setStatsOn "+on+" for "+this);
+ _statsOn=on;
+ statsReset();
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean getStatsOn() {return _statsOn;}
+
+ /* ------------------------------------------------------------ */
+ public long getStatsOnMs()
+ {return _statsOn?(System.currentTimeMillis()-_statsStartedAt):0;}
+
+ /* ------------------------------------------------------------ */
+ public void statsReset()
+ {
+ synchronized(_statsLock)
+ {
+ if (_statsOn)
+ _statsStartedAt=System.currentTimeMillis();
+ _requests=0;
+ _requestsActiveMax=_requestsActive;
+ _responses1xx=0;
+ _responses2xx=0;
+ _responses3xx=0;
+ _responses4xx=0;
+ _responses5xx=0;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Get the number of requests handled by this context
+ * since last call of statsReset(). If setStatsOn(false) then this
+ * is undefined.
+ */
+ 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 Get the number of responses with a 2xx status returned
+ * by this context since last call of statsReset(). Undefined if
+ * if setStatsOn(false).
+ */
+ public int getResponses1xx() {return _responses1xx;}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Get the number of responses with a 100 status returned
+ * by this context since last call of statsReset(). Undefined if
+ * if setStatsOn(false).
+ */
+ public int getResponses2xx() {return _responses2xx;}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Get the number of responses with a 3xx status returned
+ * by this context since last call of statsReset(). Undefined if
+ * if setStatsOn(false).
+ */
+ public int getResponses3xx() {return _responses3xx;}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Get the number of responses with a 4xx status returned
+ * by this context since last call of statsReset(). Undefined if
+ * if setStatsOn(false).
+ */
+ public int getResponses4xx() {return _responses4xx;}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Get the number of responses with a 5xx status returned
+ * by this context since last call of statsReset(). Undefined if
+ * if setStatsOn(false).
+ */
+ public int getResponses5xx() {return _responses5xx;}
+
+
+ /* ------------------------------------------------------------ */
+ /** Log a request and response.
+ * Statistics are also collected by this method.
+ * @param request
+ * @param response
+ */
+ public void log(HttpRequest request,
+ HttpResponse response,
+ int length)
+ {
+ if (_statsOn)
+ {
+ synchronized(_statsLock)
+ {
+ if (--_requestsActive<0)
+ _requestsActive=0;
+
+ if (response!=null)
+ {
+ switch(response.getStatus()/100)
+ {
+ case 1: _responses1xx++;break;
+ case 2: _responses2xx++;break;
+ case 3: _responses3xx++;break;
+ case 4: _responses4xx++;break;
+ case 5: _responses5xx++;break;
+ }
+ }
+ }
+ }
+
+ if (_requestLog!=null &&
+ request!=null &&
+ response!=null)
+ _requestLog.log(request,response,length);
+ else if (_httpServer!=null)
+ _httpServer.log(request,response,length);
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /* Class to save scope of nested context calls
+ */
+ private static class Scope
+ {
+ ClassLoader _classLoader;
+ HttpContext _httpContext;
+ }
+
+ /*
+ * @see org.mortbay.http.HttpHandler#getName()
+ */
+ public String getName()
+ {
+ return this.getContextPath();
+ }
+
+ /*
+ * @see org.mortbay.http.HttpHandler#getHttpContext()
+ */
+ public HttpContext getHttpContext()
+ {
+ return this;
+ }
+
+ /*
+ * @see org.mortbay.http.HttpHandler#initialize(org.mortbay.http.HttpContext)
+ */
+ public void initialize(HttpContext context)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * @return
+ */
+ public Resource getBaseResource()
+ {
+ return _resources.getBaseResource();
+ }
+ /**
+ * @param type
+ * @return
+ */
+ public String getEncodingByMimeType(String type)
+ {
+ return _resources.getEncodingByMimeType(type);
+ }
+ /**
+ * @return
+ */
+ public Map getEncodingMap()
+ {
+ return _resources.getEncodingMap();
+ }
+ /**
+ * @return
+ */
+ public int getMaxCachedFileSize()
+ {
+ return _resources.getMaxCachedFileSize();
+ }
+ /**
+ * @return
+ */
+ public int getMaxCacheSize()
+ {
+ return _resources.getMaxCacheSize();
+ }
+ /**
+ * @param filename
+ * @return
+ */
+ public String getMimeByExtension(String filename)
+ {
+ return _resources.getMimeByExtension(filename);
+ }
+ /**
+ * @return
+ */
+ public Map getMimeMap()
+ {
+ return _resources.getMimeMap();
+ }
+ /**
+ * @param pathInContext
+ * @return
+ * @throws IOException
+ */
+ public Resource getResource(String pathInContext) throws IOException
+ {
+ return _resources.getResource(pathInContext);
+ }
+ /**
+ * @return
+ */
+ public String getResourceBase()
+ {
+ return _resources.getResourceBase();
+ }
+ /**
+ * @param resource
+ * @return
+ */
+ public ResourceMetaData getResourceMetaData(Resource resource)
+ {
+ return _resources.getResourceMetaData(resource);
+ }
+ /**
+ * @param base
+ */
+ public void setBaseResource(Resource base)
+ {
+ _resources.setBaseResource(base);
+ }
+ /**
+ * @param encodingMap
+ */
+ public void setEncodingMap(Map encodingMap)
+ {
+ _resources.setEncodingMap(encodingMap);
+ }
+ /**
+ * @param maxCachedFileSize
+ */
+ public void setMaxCachedFileSize(int maxCachedFileSize)
+ {
+ _resources.setMaxCachedFileSize(maxCachedFileSize);
+ }
+ /**
+ * @param maxCacheSize
+ */
+ public void setMaxCacheSize(int maxCacheSize)
+ {
+ _resources.setMaxCacheSize(maxCacheSize);
+ }
+ /**
+ * @param mimeMap
+ */
+ public void setMimeMap(Map mimeMap)
+ {
+ _resources.setMimeMap(mimeMap);
+ }
+ /**
+ * @param extension
+ * @param type
+ */
+ public void setMimeMapping(String extension, String type)
+ {
+ _resources.setMimeMapping(extension, type);
+ }
+ /**
+ * @param resourceBase
+ */
+ public void setResourceBase(String resourceBase)
+ {
+ _resources.setResourceBase(resourceBase);
+ }
+ /**
+ * @param mimeType
+ * @param encoding
+ */
+ public void setTypeEncoding(String mimeType, String encoding)
+ {
+ _resources.setTypeEncoding(mimeType, encoding);
+ }
+}