View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server.handler;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.lang.reflect.Method;
25  import java.net.MalformedURLException;
26  import java.net.URI;
27  import java.net.URL;
28  import java.net.URLClassLoader;
29  import java.security.AccessController;
30  import java.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.Collections;
33  import java.util.Enumeration;
34  import java.util.EventListener;
35  import java.util.HashMap;
36  import java.util.HashSet;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.Locale;
40  import java.util.Map;
41  import java.util.Set;
42  import java.util.concurrent.CopyOnWriteArrayList;
43  import java.util.concurrent.Future;
44  
45  import javax.servlet.DispatcherType;
46  import javax.servlet.Filter;
47  import javax.servlet.FilterRegistration;
48  import javax.servlet.FilterRegistration.Dynamic;
49  import javax.servlet.RequestDispatcher;
50  import javax.servlet.Servlet;
51  import javax.servlet.ServletContext;
52  import javax.servlet.ServletContextAttributeEvent;
53  import javax.servlet.ServletContextAttributeListener;
54  import javax.servlet.ServletContextEvent;
55  import javax.servlet.ServletContextListener;
56  import javax.servlet.ServletException;
57  import javax.servlet.ServletRegistration;
58  import javax.servlet.ServletRequestAttributeListener;
59  import javax.servlet.ServletRequestEvent;
60  import javax.servlet.ServletRequestListener;
61  import javax.servlet.SessionCookieConfig;
62  import javax.servlet.SessionTrackingMode;
63  import javax.servlet.descriptor.JspConfigDescriptor;
64  import javax.servlet.http.HttpServletRequest;
65  import javax.servlet.http.HttpServletResponse;
66  
67  import org.eclipse.jetty.http.MimeTypes;
68  import org.eclipse.jetty.server.ClassLoaderDump;
69  import org.eclipse.jetty.server.Dispatcher;
70  import org.eclipse.jetty.server.Handler;
71  import org.eclipse.jetty.server.HandlerContainer;
72  import org.eclipse.jetty.server.Request;
73  import org.eclipse.jetty.server.Server;
74  import org.eclipse.jetty.util.Attributes;
75  import org.eclipse.jetty.util.AttributesMap;
76  import org.eclipse.jetty.util.FutureCallback;
77  import org.eclipse.jetty.util.Loader;
78  import org.eclipse.jetty.util.StringUtil;
79  import org.eclipse.jetty.util.URIUtil;
80  import org.eclipse.jetty.util.annotation.ManagedAttribute;
81  import org.eclipse.jetty.util.annotation.ManagedObject;
82  import org.eclipse.jetty.util.component.Graceful;
83  import org.eclipse.jetty.util.log.Log;
84  import org.eclipse.jetty.util.log.Logger;
85  import org.eclipse.jetty.util.resource.Resource;
86  
87  /* ------------------------------------------------------------ */
88  /**
89   * ContextHandler.
90   *
91   * This handler wraps a call to handle by setting the context and servlet path, plus setting the context classloader.
92   *
93   * <p>
94   * If the context init parameter "org.eclipse.jetty.server.context.ManagedAttributes" is set to a comma separated list of names, then they are treated as
95   * context attribute names, which if set as attributes are passed to the servers Container so that they may be managed with JMX.
96   * <p>
97   * The maximum size of a form that can be processed by this context is controlled by the system properties org.eclipse.jetty.server.Request.maxFormKeys
98   * and org.eclipse.jetty.server.Request.maxFormContentSize.  These can also be configured with {@link #setMaxFormContentSize(int)} and {@link #setMaxFormKeys(int)}
99   * <p>
100  * This servers executore is made available via a context attributed "org.eclipse.jetty.server.Executor".
101  *
102  * @org.apache.xbean.XBean description="Creates a basic HTTP context"
103  */
104 @ManagedObject("URI Context")
105 public class ContextHandler extends ScopedHandler implements Attributes, Graceful
106 {
107     public final static int SERVLET_MAJOR_VERSION=3;
108     public final static int SERVLET_MINOR_VERSION=1;
109     public static final Class<?>[] SERVLET_LISTENER_TYPES = new Class[] {ServletContextListener.class,
110                                                                       ServletContextAttributeListener.class,
111                                                                       ServletRequestListener.class,
112                                                                       ServletRequestAttributeListener.class};
113 
114     public static final int DEFAULT_LISTENER_TYPE_INDEX = 1;
115     public static final int EXTENDED_LISTENER_TYPE_INDEX = 0;
116 
117 
118     final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
119 
120     private static final Logger LOG = Log.getLogger(ContextHandler.class);
121 
122     private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
123 
124     /**
125      * If a context attribute with this name is set, it is interpreted as a comma separated list of attribute name. Any other context attributes that are set
126      * with a name from this list will result in a call to {@link #setManagedAttribute(String, Object)}, which typically initiates the creation of a JMX MBean
127      * for the attribute value.
128      */
129     public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes";
130 
131     /* ------------------------------------------------------------ */
132     /**
133      * Get the current ServletContext implementation.
134      *
135      * @return ServletContext implementation
136      */
137     public static Context getCurrentContext()
138     {
139         return __context.get();
140     }
141 
142     /* ------------------------------------------------------------ */
143     public static ContextHandler getContextHandler(ServletContext context)
144     {
145         if(context instanceof ContextHandler.Context)
146             return ((ContextHandler.Context)context).getContextHandler();
147         Context c=  getCurrentContext();
148         if (c!=null)
149             return c.getContextHandler();
150         return null;
151     }
152 
153 
154     protected Context _scontext;
155     private final AttributesMap _attributes;
156     private final Map<String, String> _initParams;
157     private ClassLoader _classLoader;
158     private String _contextPath = "/";
159 
160     private String _displayName;
161 
162     private Resource _baseResource;
163     private MimeTypes _mimeTypes;
164     private Map<String, String> _localeEncodingMap;
165     private String[] _welcomeFiles;
166     private ErrorHandler _errorHandler;
167     private String[] _vhosts;
168 
169     private Logger _logger;
170     private boolean _allowNullPathInfo;
171     private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys",-1).intValue();
172     private int _maxFormContentSize = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormContentSize",-1).intValue();
173     private boolean _compactPath = false;
174 
175     private final List<EventListener> _eventListeners=new CopyOnWriteArrayList<>();
176     private final List<EventListener> _programmaticListeners=new CopyOnWriteArrayList<>();
177     private final List<ServletContextListener> _contextListeners=new CopyOnWriteArrayList<>();
178     private final List<ServletContextAttributeListener> _contextAttributeListeners=new CopyOnWriteArrayList<>();
179     private final List<ServletRequestListener> _requestListeners=new CopyOnWriteArrayList<>();
180     private final List<ServletRequestAttributeListener> _requestAttributeListeners=new CopyOnWriteArrayList<>();
181     private final List<EventListener> _durableListeners = new CopyOnWriteArrayList<>();
182     private Map<String, Object> _managedAttributes;
183     private String[] _protectedTargets;
184     private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<ContextHandler.AliasCheck>();
185 
186     public enum Availability { UNAVAILABLE,STARTING,AVAILABLE,SHUTDOWN,};
187     private volatile Availability _availability;
188 
189     /* ------------------------------------------------------------ */
190     /**
191      *
192      */
193     public ContextHandler()
194     {
195         super();
196         _scontext = new Context();
197         _attributes = new AttributesMap();
198         _initParams = new HashMap<String, String>();
199         addAliasCheck(new ApproveNonExistentDirectoryAliases());
200     }
201 
202     /* ------------------------------------------------------------ */
203     /**
204      *
205      */
206     protected ContextHandler(Context context)
207     {
208         super();
209         _scontext = context;
210         _attributes = new AttributesMap();
211         _initParams = new HashMap<String, String>();
212         addAliasCheck(new ApproveNonExistentDirectoryAliases());
213     }
214 
215     /* ------------------------------------------------------------ */
216     /**
217      *
218      */
219     public ContextHandler(String contextPath)
220     {
221         this();
222         setContextPath(contextPath);
223     }
224 
225     /* ------------------------------------------------------------ */
226     /**
227      *
228      */
229     public ContextHandler(HandlerContainer parent, String contextPath)
230     {
231         this();
232         setContextPath(contextPath);
233         if (parent instanceof HandlerWrapper)
234             ((HandlerWrapper)parent).setHandler(this);
235         else if (parent instanceof HandlerCollection)
236             ((HandlerCollection)parent).addHandler(this);
237     }
238 
239     /* ------------------------------------------------------------ */
240     @Override
241     public void dump(Appendable out, String indent) throws IOException
242     {
243         dumpBeans(out,indent,
244             Collections.singletonList(new ClassLoaderDump(getClassLoader())),
245             _initParams.entrySet(),
246             _attributes.getAttributeEntrySet(),
247             _scontext.getAttributeEntrySet());
248     }
249 
250     /* ------------------------------------------------------------ */
251     public Context getServletContext()
252     {
253         return _scontext;
254     }
255 
256     /* ------------------------------------------------------------ */
257     /**
258      * @return the allowNullPathInfo true if /context is not redirected to /context/
259      */
260     @ManagedAttribute("Checks if the /context is not redirected to /context/")
261     public boolean getAllowNullPathInfo()
262     {
263         return _allowNullPathInfo;
264     }
265 
266     /* ------------------------------------------------------------ */
267     /**
268      * @param allowNullPathInfo
269      *            true if /context is not redirected to /context/
270      */
271     public void setAllowNullPathInfo(boolean allowNullPathInfo)
272     {
273         _allowNullPathInfo = allowNullPathInfo;
274     }
275 
276     /* ------------------------------------------------------------ */
277     @Override
278     public void setServer(Server server)
279     {
280         super.setServer(server);
281         if (_errorHandler != null)
282             _errorHandler.setServer(server);
283     }
284 
285     /* ------------------------------------------------------------ */
286     /**
287      * 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
288      * 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
289      * matching virtual host name.
290      *
291      * @param vhosts
292      *            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 be
293      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.  Hosts may start with '@', in which case they
294      *            will match the {@link Connector#getName()} for the request.
295      */
296     public void setVirtualHosts(String[] vhosts)
297     {
298         if (vhosts == null)
299         {
300             _vhosts = vhosts;
301         }
302         else
303         {
304             _vhosts = new String[vhosts.length];
305             for (int i = 0; i < vhosts.length; i++)
306                 _vhosts[i] = normalizeHostname(vhosts[i]);
307         }
308     }
309 
310     /* ------------------------------------------------------------ */
311     /** Either set virtual hosts or add to an existing set of virtual hosts.
312      *
313      * @param virtualHosts
314      *            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 be
315      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names. Host names may start with '@', in which case they
316      *            will match the {@link Connector#getName()} for the request.
317      */
318     public void addVirtualHosts(String[] virtualHosts)
319     {
320         if (virtualHosts == null)  // since this is add, we don't null the old ones
321         {
322             return;
323         }
324         else
325         {
326             List<String> currentVirtualHosts = null;
327             if (_vhosts != null)
328             {
329                 currentVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
330             }
331             else
332             {
333                 currentVirtualHosts = new ArrayList<String>();
334             }
335 
336             for (int i = 0; i < virtualHosts.length; i++)
337             {
338                 String normVhost = normalizeHostname(virtualHosts[i]);
339                 if (!currentVirtualHosts.contains(normVhost))
340                 {
341                     currentVirtualHosts.add(normVhost);
342                 }
343             }
344             _vhosts = currentVirtualHosts.toArray(new String[0]);
345         }
346     }
347 
348     /* ------------------------------------------------------------ */
349     /**
350      * Removes an array of virtual host entries, if this removes all entries the _vhosts will be set to null
351      *
352      *  @param virtualHosts
353      *            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 be
354      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
355      */
356     public void removeVirtualHosts(String[] virtualHosts)
357     {
358         if (virtualHosts == null)
359         {
360             return; // do nothing
361         }
362         else if ( _vhosts == null || _vhosts.length == 0)
363         {
364             return; // do nothing
365         }
366         else
367         {
368             List<String> existingVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
369 
370             for (int i = 0; i < virtualHosts.length; i++)
371             {
372                 String toRemoveVirtualHost = normalizeHostname(virtualHosts[i]);
373                 if (existingVirtualHosts.contains(toRemoveVirtualHost))
374                 {
375                     existingVirtualHosts.remove(toRemoveVirtualHost);
376                 }
377             }
378 
379             if (existingVirtualHosts.isEmpty())
380             {
381                 _vhosts = null; // if we ended up removing them all, just null out _vhosts
382             }
383             else
384             {
385                 _vhosts = existingVirtualHosts.toArray(new String[0]);
386             }
387         }
388     }
389 
390     /* ------------------------------------------------------------ */
391     /**
392      * Get 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
393      * 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
394      * matching virtual host name.
395      *
396      * @return Array of virtual hosts that this context responds to. A null host name or empty array means any hostname is acceptable. Host names may be String
397      *         representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
398      */
399     @ManagedAttribute(value="Virtual hosts accepted by the context", readonly=true)
400     public String[] getVirtualHosts()
401     {
402         return _vhosts;
403     }
404 
405     /* ------------------------------------------------------------ */
406     /*
407      * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
408      */
409     @Override
410     public Object getAttribute(String name)
411     {
412         return _attributes.getAttribute(name);
413     }
414 
415     /* ------------------------------------------------------------ */
416     /*
417      * @see javax.servlet.ServletContext#getAttributeNames()
418      */
419     @Override
420     public Enumeration<String> getAttributeNames()
421     {
422         return AttributesMap.getAttributeNamesCopy(_attributes);
423     }
424 
425     /* ------------------------------------------------------------ */
426     /**
427      * @return Returns the attributes.
428      */
429     public Attributes getAttributes()
430     {
431         return _attributes;
432     }
433 
434     /* ------------------------------------------------------------ */
435     /**
436      * @return Returns the classLoader.
437      */
438     public ClassLoader getClassLoader()
439     {
440         return _classLoader;
441     }
442 
443     /* ------------------------------------------------------------ */
444     /**
445      * Make best effort to extract a file classpath from the context classloader
446      *
447      * @return Returns the classLoader.
448      */
449     @ManagedAttribute("The file classpath")
450     public String getClassPath()
451     {
452         if (_classLoader == null || !(_classLoader instanceof URLClassLoader))
453             return null;
454         URLClassLoader loader = (URLClassLoader)_classLoader;
455         URL[] urls = loader.getURLs();
456         StringBuilder classpath = new StringBuilder();
457         for (int i = 0; i < urls.length; i++)
458         {
459             try
460             {
461                 Resource resource = newResource(urls[i]);
462                 File file = resource.getFile();
463                 if (file != null && file.exists())
464                 {
465                     if (classpath.length() > 0)
466                         classpath.append(File.pathSeparatorChar);
467                     classpath.append(file.getAbsolutePath());
468                 }
469             }
470             catch (IOException e)
471             {
472                 LOG.debug(e);
473             }
474         }
475         if (classpath.length() == 0)
476             return null;
477         return classpath.toString();
478     }
479 
480     /* ------------------------------------------------------------ */
481     /**
482      * @return Returns the _contextPath.
483      */
484     @ManagedAttribute("True if URLs are compacted to replace the multiple '/'s with a single '/'")
485     public String getContextPath()
486     {
487         return _contextPath;
488     }
489 
490     /* ------------------------------------------------------------ */
491     /*
492      * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
493      */
494     public String getInitParameter(String name)
495     {
496         return _initParams.get(name);
497     }
498 
499     /* ------------------------------------------------------------ */
500     /*
501      */
502     public String setInitParameter(String name, String value)
503     {
504         return _initParams.put(name,value);
505     }
506 
507     /* ------------------------------------------------------------ */
508     /*
509      * @see javax.servlet.ServletContext#getInitParameterNames()
510      */
511     public Enumeration<String> getInitParameterNames()
512     {
513         return Collections.enumeration(_initParams.keySet());
514     }
515 
516     /* ------------------------------------------------------------ */
517     /**
518      * @return Returns the initParams.
519      */
520     @ManagedAttribute("Initial Parameter map for the context")
521     public Map<String, String> getInitParams()
522     {
523         return _initParams;
524     }
525 
526     /* ------------------------------------------------------------ */
527     /*
528      * @see javax.servlet.ServletContext#getServletContextName()
529      */
530     @ManagedAttribute(value="Display name of the Context", readonly=true)
531     public String getDisplayName()
532     {
533         return _displayName;
534     }
535 
536     /* ------------------------------------------------------------ */
537     public EventListener[] getEventListeners()
538     {
539         return _eventListeners.toArray(new EventListener[_eventListeners.size()]);
540     }
541 
542     /* ------------------------------------------------------------ */
543     /**
544      * Set the context event listeners.
545      *
546      * @param eventListeners
547      *            the event listeners
548      * @see ServletContextListener
549      * @see ServletContextAttributeListener
550      * @see ServletRequestListener
551      * @see ServletRequestAttributeListener
552      */
553     public void setEventListeners(EventListener[] eventListeners)
554     {
555         _contextListeners.clear();
556         _contextAttributeListeners.clear();
557         _requestListeners.clear();
558         _requestAttributeListeners.clear();
559         _eventListeners.clear();
560 
561         if (eventListeners!=null)
562             for (EventListener listener : eventListeners)
563                 addEventListener(listener);
564     }
565 
566     /* ------------------------------------------------------------ */
567     /**
568      * Add a context event listeners.
569      *
570      * @see ServletContextListener
571      * @see ServletContextAttributeListener
572      * @see ServletRequestListener
573      * @see ServletRequestAttributeListener
574      */
575     public void addEventListener(EventListener listener)
576     {
577         _eventListeners.add(listener);
578 
579         if (!(isStarted() || isStarting()))
580             _durableListeners.add(listener);
581 
582         if (listener instanceof ServletContextListener)
583             _contextListeners.add((ServletContextListener)listener);
584 
585         if (listener instanceof ServletContextAttributeListener)
586             _contextAttributeListeners.add((ServletContextAttributeListener)listener);
587 
588         if (listener instanceof ServletRequestListener)
589             _requestListeners.add((ServletRequestListener)listener);
590 
591         if (listener instanceof ServletRequestAttributeListener)
592             _requestAttributeListeners.add((ServletRequestAttributeListener)listener);
593     }
594 
595     /* ------------------------------------------------------------ */
596     /**
597      * Remove a context event listeners.
598      *
599      * @see ServletContextListener
600      * @see ServletContextAttributeListener
601      * @see ServletRequestListener
602      * @see ServletRequestAttributeListener
603      */
604     public void removeEventListener(EventListener listener)
605     {
606         _eventListeners.remove(listener);
607 
608         if (listener instanceof ServletContextListener)
609             _contextListeners.remove(listener);
610 
611         if (listener instanceof ServletContextAttributeListener)
612             _contextAttributeListeners.remove(listener);
613 
614         if (listener instanceof ServletRequestListener)
615             _requestListeners.remove(listener);
616 
617         if (listener instanceof ServletRequestAttributeListener)
618             _requestAttributeListeners.remove(listener);
619     }
620 
621     /* ------------------------------------------------------------ */
622     /**
623      * Apply any necessary restrictions on a programmatic added listener.
624      *
625      * @param listener
626      */
627     protected void addProgrammaticListener (EventListener listener)
628     {
629         _programmaticListeners.add(listener);
630     }
631 
632     /* ------------------------------------------------------------ */
633     protected boolean isProgrammaticListener(EventListener listener)
634     {
635         return _programmaticListeners.contains(listener);
636     }
637 
638 
639 
640     /* ------------------------------------------------------------ */
641     /**
642      * @return true if this context is accepting new requests
643      */
644     @ManagedAttribute("true for graceful shutdown, which allows existing requests to complete")
645     public boolean isShutdown()
646     {
647         switch(_availability)
648         {
649             case SHUTDOWN:
650                 return true;
651             default:
652                 return false;
653         }
654     }
655 
656     /* ------------------------------------------------------------ */
657     /**
658      * Set shutdown status. This field allows for graceful shutdown of a context. A started context may be put into non accepting state so that existing
659      * requests can complete, but no new requests are accepted.
660      *
661      */
662     @Override
663     public Future<Void> shutdown()
664     {
665         _availability = isRunning() ? Availability.SHUTDOWN : Availability.UNAVAILABLE;
666         return new FutureCallback(true);
667     }
668 
669     /* ------------------------------------------------------------ */
670     /**
671      * @return false if this context is unavailable (sends 503)
672      */
673     public boolean isAvailable()
674     {
675         return _availability==Availability.AVAILABLE;
676     }
677 
678     /* ------------------------------------------------------------ */
679     /**
680      * Set Available status.
681      */
682     public void setAvailable(boolean available)
683     {
684         synchronized (this)
685         {
686             if (available && isRunning())
687                 _availability = Availability.AVAILABLE;
688             else if (!available || !isRunning())
689                 _availability = Availability.UNAVAILABLE;
690         }
691     }
692 
693     /* ------------------------------------------------------------ */
694     public Logger getLogger()
695     {
696         return _logger;
697     }
698 
699     /* ------------------------------------------------------------ */
700     public void setLogger(Logger logger)
701     {
702         _logger = logger;
703     }
704 
705     /* ------------------------------------------------------------ */
706     /*
707      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
708      */
709     @Override
710     protected void doStart() throws Exception
711     {
712         _availability = Availability.STARTING;
713 
714         if (_contextPath == null)
715             throw new IllegalStateException("Null contextPath");
716 
717         _logger = Log.getLogger(getDisplayName() == null?getContextPath():getDisplayName());
718         ClassLoader old_classloader = null;
719         Thread current_thread = null;
720         Context old_context = null;
721 
722         _attributes.setAttribute("org.eclipse.jetty.server.Executor",getServer().getThreadPool());
723         
724         try
725         {
726             // Set the classloader
727             if (_classLoader != null)
728             {
729                 current_thread = Thread.currentThread();
730                 old_classloader = current_thread.getContextClassLoader();
731                 current_thread.setContextClassLoader(_classLoader);
732             }
733 
734             if (_mimeTypes == null)
735                 _mimeTypes = new MimeTypes();
736 
737             old_context = __context.get();
738             __context.set(_scontext);
739 
740             // defers the calling of super.doStart()
741             startContext();
742 
743             _availability = Availability.AVAILABLE;
744             LOG.info("Started {}", this);
745         }
746         finally
747         {
748             __context.set(old_context);
749 
750             // reset the classloader
751             if (_classLoader != null && current_thread!=null)
752                 current_thread.setContextClassLoader(old_classloader);
753         }
754     }
755 
756     /* ------------------------------------------------------------ */
757     /**
758      * Extensible startContext. this method is called from {@link ContextHandler#doStart()} instead of a call to super.doStart(). This allows derived classes to
759      * insert additional handling (Eg configuration) before the call to super.doStart by this method will start contained handlers.
760      *
761      * @see org.eclipse.jetty.server.handler.ContextHandler.Context
762      */
763     protected void startContext() throws Exception
764     {
765         String managedAttributes = _initParams.get(MANAGED_ATTRIBUTES);
766         if (managedAttributes != null)
767         {
768             _managedAttributes = new HashMap<String, Object>();
769             String[] attributes = managedAttributes.split(",");
770             for (String attribute : attributes)
771                 _managedAttributes.put(attribute,null);
772 
773             Enumeration<String> e = _scontext.getAttributeNames();
774             while (e.hasMoreElements())
775             {
776                 String name = e.nextElement();
777                 Object value = _scontext.getAttribute(name);
778                 checkManagedAttribute(name,value);
779             }
780         }
781 
782         super.doStart();
783 
784         // Call context listeners
785         if (!_contextListeners.isEmpty())
786         {
787             ServletContextEvent event = new ServletContextEvent(_scontext);
788             for (ServletContextListener listener:_contextListeners)
789                 callContextInitialized(listener, event);
790         }
791     }
792 
793     /* ------------------------------------------------------------ */
794     protected void callContextInitialized (ServletContextListener l, ServletContextEvent e)
795     {
796         if (LOG.isDebugEnabled())
797             LOG.debug("contextInitialized: {}->{}",e,l);
798         l.contextInitialized(e);
799     }
800 
801     /* ------------------------------------------------------------ */
802     protected void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
803     {
804         if (LOG.isDebugEnabled())
805             LOG.debug("contextDestroyed: {}->{}",e,l);
806         l.contextDestroyed(e);
807     }
808 
809     /* ------------------------------------------------------------ */
810     /*
811      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
812      */
813     @Override
814     protected void doStop() throws Exception
815     {
816         _availability = Availability.UNAVAILABLE;
817 
818         ClassLoader old_classloader = null;
819         Thread current_thread = null;
820 
821         Context old_context = __context.get();
822         __context.set(_scontext);
823         try
824         {
825             // Set the classloader
826             if (_classLoader != null)
827             {
828                 current_thread = Thread.currentThread();
829                 old_classloader = current_thread.getContextClassLoader();
830                 current_thread.setContextClassLoader(_classLoader);
831             }
832 
833             super.doStop();
834 
835             // Context listeners
836             if (!_contextListeners.isEmpty())
837             {
838                 ServletContextEvent event = new ServletContextEvent(_scontext);
839                 for (int i = _contextListeners.size(); i-->0;)
840                     callContextDestroyed(_contextListeners.get(i),event);
841             }
842 
843             //retain only durable listeners
844             setEventListeners(_durableListeners.toArray(new EventListener[_durableListeners.size()]));
845             _durableListeners.clear();
846 
847             if (_errorHandler != null)
848                 _errorHandler.stop();
849 
850             Enumeration<String> e = _scontext.getAttributeNames();
851             while (e.hasMoreElements())
852             {
853                 String name = e.nextElement();
854                 checkManagedAttribute(name,null);
855             }
856 
857             for (EventListener l : _programmaticListeners)
858                 removeEventListener(l);
859             _programmaticListeners.clear();
860         }
861         finally
862         {
863             LOG.info("Stopped {}", this);
864             __context.set(old_context);
865             // reset the classloader
866             if (_classLoader != null && current_thread!=null)
867                 current_thread.setContextClassLoader(old_classloader);
868         }
869 
870         _scontext.clearAttributes();
871     }
872 
873     public boolean checkVirtualHost(final Request baseRequest)
874     {
875         if (_vhosts != null && _vhosts.length > 0)
876         {
877             String vhost = normalizeHostname(baseRequest.getServerName());
878 
879             boolean match = false;
880             boolean connectorName = false;
881             boolean connectorMatch = false;
882 
883             for (String contextVhost:_vhosts)
884             {
885                 if (contextVhost == null || contextVhost.length()==0)
886                     continue;
887                 char c=contextVhost.charAt(0);
888                 switch (c)
889                 {
890                     case '*':
891                         if (contextVhost.startsWith("*."))
892                             // wildcard only at the beginning, and only for one additional subdomain level
893                             match = match || contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
894                         break;
895                     case '@':
896                         connectorName=true;
897                         String name=baseRequest.getHttpChannel().getConnector().getName();
898                         boolean m=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
899                         match = match || m;
900                         connectorMatch = connectorMatch || m;
901                         break;
902                     default:
903                         match = match || contextVhost.equalsIgnoreCase(vhost);
904                 }
905 
906             }
907             if (!match || connectorName && !connectorMatch)
908                 return false;
909         }
910         return true;
911     }
912     
913     public boolean checkContextPath(String uri)
914     {
915         // Are we not the root context?
916         if (_contextPath.length() > 1)
917         {
918             // reject requests that are not for us
919             if (!uri.startsWith(_contextPath))
920                 return false;
921             if (uri.length() > _contextPath.length() && uri.charAt(_contextPath.length()) != '/')
922                 return false;
923         }
924         return true;
925     }
926     
927     /* ------------------------------------------------------------ */
928     /*
929      * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
930      */
931     public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException
932     {
933         DispatcherType dispatch = baseRequest.getDispatcherType();
934 
935         // Check the vhosts
936         if (!checkVirtualHost(baseRequest))
937             return false;
938 
939         if (!checkContextPath(target))
940             return false;
941         
942         // Are we not the root context?
943         // redirect null path infos
944         if (!_allowNullPathInfo && _contextPath.length() == target.length() && _contextPath.length()>1)
945         {
946             // context request must end with /
947             baseRequest.setHandled(true);
948             if (baseRequest.getQueryString() != null)
949                 response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
950             else
951                 response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
952             return false;
953         }
954 
955         switch (_availability)
956         {
957             case SHUTDOWN:
958             case UNAVAILABLE:
959                 baseRequest.setHandled(true);
960                 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
961                 return false;
962             default:
963                 if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
964                     return false;
965         }
966 
967         return true;
968     }
969 
970     /* ------------------------------------------------------------ */
971     /**
972      * @see org.eclipse.jetty.server.handler.ScopedHandler#doScope(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
973      *      javax.servlet.http.HttpServletResponse)
974      */
975     @Override
976     public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
977     {
978         if (LOG.isDebugEnabled())
979             LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this);
980 
981         Context old_context = null;
982         String old_context_path = null;
983         String old_servlet_path = null;
984         String old_path_info = null;
985         ClassLoader old_classloader = null;
986         Thread current_thread = null;
987         String pathInfo = target;
988 
989         DispatcherType dispatch = baseRequest.getDispatcherType();
990 
991         old_context = baseRequest.getContext();
992 
993         // Are we already in this context?
994         if (old_context != _scontext)
995         {
996             // check the target.
997             if (DispatcherType.REQUEST.equals(dispatch) ||
998                 DispatcherType.ASYNC.equals(dispatch) ||
999                 DispatcherType.ERROR.equals(dispatch) && baseRequest.getHttpChannelState().isAsync())
1000             {
1001                 if (_compactPath)
1002                     target = URIUtil.compactPath(target);
1003                 if (!checkContext(target,baseRequest,response))
1004                     return;
1005 
1006                 if (target.length() > _contextPath.length())
1007                 {
1008                     if (_contextPath.length() > 1)
1009                         target = target.substring(_contextPath.length());
1010                     pathInfo = target;
1011                 }
1012                 else if (_contextPath.length() == 1)
1013                 {
1014                     target = URIUtil.SLASH;
1015                     pathInfo = URIUtil.SLASH;
1016                 }
1017                 else
1018                 {
1019                     target = URIUtil.SLASH;
1020                     pathInfo = null;
1021                 }
1022             }
1023 
1024             // Set the classloader
1025             if (_classLoader != null)
1026             {
1027                 current_thread = Thread.currentThread();
1028                 old_classloader = current_thread.getContextClassLoader();
1029                 current_thread.setContextClassLoader(_classLoader);
1030             }
1031         }
1032 
1033         try
1034         {
1035             old_context_path = baseRequest.getContextPath();
1036             old_servlet_path = baseRequest.getServletPath();
1037             old_path_info = baseRequest.getPathInfo();
1038 
1039             // Update the paths
1040             baseRequest.setContext(_scontext);
1041             __context.set(_scontext);
1042             if (!DispatcherType.INCLUDE.equals(dispatch) && target.startsWith("/"))
1043             {
1044                 if (_contextPath.length() == 1)
1045                     baseRequest.setContextPath("");
1046                 else
1047                     baseRequest.setContextPath(_contextPath);
1048                 baseRequest.setServletPath(null);
1049                 baseRequest.setPathInfo(pathInfo);
1050             }
1051 
1052             if (LOG.isDebugEnabled())
1053                 LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
1054 
1055             // start manual inline of nextScope(target,baseRequest,request,response);
1056             if (never())
1057                 nextScope(target,baseRequest,request,response);
1058             else if (_nextScope != null)
1059                 _nextScope.doScope(target,baseRequest,request,response);
1060             else if (_outerScope != null)
1061                 _outerScope.doHandle(target,baseRequest,request,response);
1062             else
1063                 doHandle(target,baseRequest,request,response);
1064             // end manual inline (pathentic attempt to reduce stack depth)
1065         }
1066         finally
1067         {
1068             if (old_context != _scontext)
1069             {
1070                 // reset the classloader
1071                 if (_classLoader != null && current_thread!=null)
1072                 {
1073                     current_thread.setContextClassLoader(old_classloader);
1074                 }
1075 
1076                 // reset the context and servlet path.
1077                 baseRequest.setContext(old_context);
1078                 __context.set(old_context);
1079                 baseRequest.setContextPath(old_context_path);
1080                 baseRequest.setServletPath(old_servlet_path);
1081                 baseRequest.setPathInfo(old_path_info);
1082             }
1083         }
1084     }
1085 
1086     /* ------------------------------------------------------------ */
1087     /**
1088      * @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
1089      *      javax.servlet.http.HttpServletResponse)
1090      */
1091     @Override
1092     public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
1093     {
1094         final DispatcherType dispatch = baseRequest.getDispatcherType();
1095         final boolean new_context = baseRequest.takeNewContext();
1096         try
1097         {
1098             if (new_context)
1099             {
1100                 // Handle the REALLY SILLY request events!
1101                 if (!_requestAttributeListeners.isEmpty())
1102                     for (ServletRequestAttributeListener l :_requestAttributeListeners)
1103                         baseRequest.addEventListener(l);
1104 
1105                 if (!_requestListeners.isEmpty())
1106                 {
1107                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
1108                     for (ServletRequestListener l : _requestListeners)
1109                         l.requestInitialized(sre);
1110                 }
1111             }
1112 
1113             if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
1114             {
1115                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
1116                 baseRequest.setHandled(true);
1117                 return;
1118             }
1119 
1120             // start manual inline of nextHandle(target,baseRequest,request,response);
1121             // noinspection ConstantIfStatement
1122             if (never())
1123                 nextHandle(target,baseRequest,request,response);
1124             else if (_nextScope != null && _nextScope == _handler)
1125                 _nextScope.doHandle(target,baseRequest,request,response);
1126             else if (_handler != null)
1127                 _handler.handle(target,baseRequest,request,response);
1128             // end manual inline
1129         }
1130         finally
1131         {
1132             // Handle more REALLY SILLY request events!
1133             if (new_context)
1134             {
1135                 if (!_requestListeners.isEmpty())
1136                 {
1137                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
1138                     for (int i=_requestListeners.size();i-->0;)
1139                         _requestListeners.get(i).requestDestroyed(sre);
1140                 }
1141 
1142                 if (!_requestAttributeListeners.isEmpty())
1143                 {
1144                     for (int i=_requestAttributeListeners.size();i-->0;)
1145                         baseRequest.removeEventListener(_requestAttributeListeners.get(i));
1146                 }
1147             }
1148         }
1149     }
1150 
1151     /* ------------------------------------------------------------ */
1152     /*
1153      * Handle a runnable in this context
1154      */
1155     public void handle(Runnable runnable)
1156     {
1157         ClassLoader old_classloader = null;
1158         Thread current_thread = null;
1159         Context old_context = null;
1160         try
1161         {
1162             old_context = __context.get();
1163             __context.set(_scontext);
1164 
1165             // Set the classloader
1166             if (_classLoader != null)
1167             {
1168                 current_thread = Thread.currentThread();
1169                 old_classloader = current_thread.getContextClassLoader();
1170                 current_thread.setContextClassLoader(_classLoader);
1171             }
1172 
1173             runnable.run();
1174         }
1175         finally
1176         {
1177             __context.set(old_context);
1178             if (old_classloader != null && current_thread!=null)
1179             {
1180                 current_thread.setContextClassLoader(old_classloader);
1181             }
1182         }
1183     }
1184 
1185     /* ------------------------------------------------------------ */
1186     /**
1187      * Check the target. Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a target within a context is determined. If
1188      * the target is protected, 404 is returned.
1189      */
1190     /* ------------------------------------------------------------ */
1191     public boolean isProtectedTarget(String target)
1192     {
1193         if (target == null || _protectedTargets == null)
1194             return false;
1195 
1196         while (target.startsWith("//"))
1197             target=URIUtil.compactPath(target);
1198 
1199         for (int i=0; i<_protectedTargets.length; i++)
1200         {
1201             String t=_protectedTargets[i];
1202             if (StringUtil.startsWithIgnoreCase(target,t))
1203             {
1204                 if (target.length()==t.length())
1205                     return true;
1206                 
1207                 // Check that the target prefix really is a path segment, thus
1208                 // it can end with /, a query, a target or a parameter
1209                 char c=target.charAt(t.length());
1210                 if (c=='/'||c=='?'||c=='#'||c==';')
1211                     return true;
1212             }
1213         }
1214         return false;
1215     }
1216 
1217 
1218     /* ------------------------------------------------------------ */
1219     /**
1220      * @param targets Array of URL prefix. Each prefix is in the form /path and will match
1221      * either /path exactly or /path/anything
1222      */
1223     public void setProtectedTargets (String[] targets)
1224     {
1225         if (targets == null)
1226         {
1227             _protectedTargets = null;
1228             return;
1229         }
1230         
1231         _protectedTargets = Arrays.copyOf(targets, targets.length);
1232     }
1233 
1234     /* ------------------------------------------------------------ */
1235     public String[] getProtectedTargets()
1236     {
1237         if (_protectedTargets == null)
1238             return null;
1239 
1240         return Arrays.copyOf(_protectedTargets, _protectedTargets.length);
1241     }
1242 
1243 
1244     /* ------------------------------------------------------------ */
1245     /*
1246      * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
1247      */
1248     @Override
1249     public void removeAttribute(String name)
1250     {
1251         checkManagedAttribute(name,null);
1252         _attributes.removeAttribute(name);
1253     }
1254 
1255     /* ------------------------------------------------------------ */
1256     /*
1257      * Set a context attribute. Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. Their lifecycle spans the stop/start of
1258      * a context. No attribute listener events are triggered by this API.
1259      *
1260      * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1261      */
1262     @Override
1263     public void setAttribute( String name, Object value)
1264     {
1265         checkManagedAttribute(name,value);
1266         _attributes.setAttribute(name,value);
1267     }
1268 
1269     /* ------------------------------------------------------------ */
1270     /**
1271      * @param attributes
1272      *            The attributes to set.
1273      */
1274     public void setAttributes(Attributes attributes)
1275     {
1276         _attributes.clearAttributes();
1277         _attributes.addAll(attributes);
1278         Enumeration<String> e = _attributes.getAttributeNames();
1279         while (e.hasMoreElements())
1280         {
1281             String name = e.nextElement();
1282             checkManagedAttribute(name,attributes.getAttribute(name));
1283         }
1284     }
1285 
1286     /* ------------------------------------------------------------ */
1287     @Override
1288     public void clearAttributes()
1289     {
1290         Enumeration<String> e = _attributes.getAttributeNames();
1291         while (e.hasMoreElements())
1292         {
1293             String name = e.nextElement();
1294             checkManagedAttribute(name,null);
1295         }
1296         _attributes.clearAttributes();
1297     }
1298 
1299     /* ------------------------------------------------------------ */
1300     public void checkManagedAttribute(String name, Object value)
1301     {
1302         if (_managedAttributes != null && _managedAttributes.containsKey(name))
1303         {
1304             setManagedAttribute(name,value);
1305         }
1306     }
1307 
1308     /* ------------------------------------------------------------ */
1309     public void setManagedAttribute(String name, Object value)
1310     {
1311         Object old = _managedAttributes.put(name,value);
1312         updateBean(old,value);
1313     }
1314 
1315     /* ------------------------------------------------------------ */
1316     /**
1317      * @param classLoader
1318      *            The classLoader to set.
1319      */
1320     public void setClassLoader(ClassLoader classLoader)
1321     {
1322         _classLoader = classLoader;
1323     }
1324 
1325     /* ------------------------------------------------------------ */
1326     /**
1327      * @param contextPath
1328      *            The _contextPath to set.
1329      */
1330     public void setContextPath(String contextPath)
1331     {
1332         if (contextPath == null)
1333             throw new IllegalArgumentException("null contextPath");
1334 
1335         if (contextPath.endsWith("/*"))
1336         {
1337             LOG.warn(this+" contextPath ends with /*");
1338             contextPath=contextPath.substring(0,contextPath.length()-2);
1339         }
1340         else if (contextPath.length()>1 && contextPath.endsWith("/"))
1341         {
1342             LOG.warn(this+" contextPath ends with /");
1343             contextPath=contextPath.substring(0,contextPath.length()-1);
1344         }
1345 
1346         if (contextPath.length()==0)
1347         {
1348             LOG.warn("Empty contextPath");
1349             contextPath="/";
1350         }
1351 
1352         _contextPath = contextPath;
1353 
1354         if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
1355         {
1356             Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
1357             for (int h = 0; contextCollections != null && h < contextCollections.length; h++)
1358                 ((ContextHandlerCollection)contextCollections[h]).mapContexts();
1359         }
1360     }
1361 
1362     /* ------------------------------------------------------------ */
1363     /**
1364      * @param servletContextName
1365      *            The servletContextName to set.
1366      */
1367     public void setDisplayName(String servletContextName)
1368     {
1369         _displayName = servletContextName;
1370     }
1371 
1372     /* ------------------------------------------------------------ */
1373     /**
1374      * @return Returns the resourceBase.
1375      */
1376     public Resource getBaseResource()
1377     {
1378         if (_baseResource == null)
1379             return null;
1380         return _baseResource;
1381     }
1382 
1383     /* ------------------------------------------------------------ */
1384     /**
1385      * @return Returns the base resource as a string.
1386      */
1387     @ManagedAttribute("document root for context")
1388     public String getResourceBase()
1389     {
1390         if (_baseResource == null)
1391             return null;
1392         return _baseResource.toString();
1393     }
1394 
1395     /* ------------------------------------------------------------ */
1396     /**
1397      * Set the base resource for this context.
1398      * @param base The resource used as the base for all static content of this context.
1399      * @see #setResourceBase(String)
1400      */
1401     public void setBaseResource(Resource base)
1402     {
1403         _baseResource = base;
1404     }
1405 
1406     /* ------------------------------------------------------------ */
1407     /** 
1408      * Set the base resource for this context.
1409      * @param resourceBase A string representing the base resource for the context. Any string accepted 
1410      * by {@link Resource#newResource(String)} may be passed and the call is equivalent to 
1411      * <code>setBaseResource(newResource(resourceBase));</code>
1412      */
1413     public void setResourceBase(String resourceBase)
1414     {
1415         try
1416         {
1417             setBaseResource(newResource(resourceBase));
1418         }
1419         catch (Exception e)
1420         {
1421             LOG.warn(e.toString());
1422             LOG.debug(e);
1423             throw new IllegalArgumentException(resourceBase);
1424         }
1425     }
1426 
1427     /* ------------------------------------------------------------ */
1428     /**
1429      * @return Returns the mimeTypes.
1430      */
1431     public MimeTypes getMimeTypes()
1432     {
1433         if (_mimeTypes == null)
1434             _mimeTypes = new MimeTypes();
1435         return _mimeTypes;
1436     }
1437 
1438     /* ------------------------------------------------------------ */
1439     /**
1440      * @param mimeTypes
1441      *            The mimeTypes to set.
1442      */
1443     public void setMimeTypes(MimeTypes mimeTypes)
1444     {
1445         _mimeTypes = mimeTypes;
1446     }
1447 
1448     /* ------------------------------------------------------------ */
1449     /**
1450      */
1451     public void setWelcomeFiles(String[] files)
1452     {
1453         _welcomeFiles = files;
1454     }
1455 
1456     /* ------------------------------------------------------------ */
1457     /**
1458      * @return The names of the files which the server should consider to be welcome files in this context.
1459      * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
1460      * @see #setWelcomeFiles
1461      */
1462     @ManagedAttribute(value="Partial URIs of directory welcome files", readonly=true)
1463     public String[] getWelcomeFiles()
1464     {
1465         return _welcomeFiles;
1466     }
1467 
1468     /* ------------------------------------------------------------ */
1469     /**
1470      * @return Returns the errorHandler.
1471      */
1472     @ManagedAttribute("The error handler to use for the context")
1473     public ErrorHandler getErrorHandler()
1474     {
1475         return _errorHandler;
1476     }
1477 
1478     /* ------------------------------------------------------------ */
1479     /**
1480      * @param errorHandler
1481      *            The errorHandler to set.
1482      */
1483     public void setErrorHandler(ErrorHandler errorHandler)
1484     {
1485         if (errorHandler != null)
1486             errorHandler.setServer(getServer());
1487         updateBean(_errorHandler,errorHandler);
1488         _errorHandler = errorHandler;
1489     }
1490 
1491     /* ------------------------------------------------------------ */
1492     @ManagedAttribute("The maximum content size")
1493     public int getMaxFormContentSize()
1494     {
1495         return _maxFormContentSize;
1496     }
1497 
1498     /* ------------------------------------------------------------ */
1499     /**
1500      * Set the maximum size of a form post, to protect against DOS attacks from large forms.
1501      * @param maxSize
1502      */
1503     public void setMaxFormContentSize(int maxSize)
1504     {
1505         _maxFormContentSize = maxSize;
1506     }
1507 
1508     /* ------------------------------------------------------------ */
1509     public int getMaxFormKeys()
1510     {
1511         return _maxFormKeys;
1512     }
1513 
1514     /* ------------------------------------------------------------ */
1515     /**
1516      * Set the maximum number of form Keys to protect against DOS attack from crafted hash keys.
1517      * @param max
1518      */
1519     public void setMaxFormKeys(int max)
1520     {
1521         _maxFormKeys = max;
1522     }
1523 
1524     /* ------------------------------------------------------------ */
1525     /**
1526      * @return True if URLs are compacted to replace multiple '/'s with a single '/'
1527      */
1528     public boolean isCompactPath()
1529     {
1530         return _compactPath;
1531     }
1532 
1533     /* ------------------------------------------------------------ */
1534     /**
1535      * @param compactPath
1536      *            True if URLs are compacted to replace multiple '/'s with a single '/'
1537      */
1538     public void setCompactPath(boolean compactPath)
1539     {
1540         _compactPath = compactPath;
1541     }
1542 
1543     /* ------------------------------------------------------------ */
1544     @Override
1545     public String toString()
1546     {
1547         String[] vhosts = getVirtualHosts();
1548 
1549         StringBuilder b = new StringBuilder();
1550 
1551         Package pkg = getClass().getPackage();
1552         if (pkg != null)
1553         {
1554             String p = pkg.getName();
1555             if (p != null && p.length() > 0)
1556             {
1557                 String[] ss = p.split("\\.");
1558                 for (String s : ss)
1559                     b.append(s.charAt(0)).append('.');
1560             }
1561         }
1562         b.append(getClass().getSimpleName()).append('@').append(Integer.toString(hashCode(),16));
1563         b.append('{').append(getContextPath()).append(',').append(getBaseResource()).append(',').append(_availability);
1564 
1565         if (vhosts != null && vhosts.length > 0)
1566             b.append(',').append(vhosts[0]);
1567         b.append('}');
1568 
1569         return b.toString();
1570     }
1571 
1572     /* ------------------------------------------------------------ */
1573     public synchronized Class<?> loadClass(String className) throws ClassNotFoundException
1574     {
1575         if (className == null)
1576             return null;
1577 
1578         if (_classLoader == null)
1579             return Loader.loadClass(this.getClass(),className);
1580 
1581         return _classLoader.loadClass(className);
1582     }
1583 
1584     /* ------------------------------------------------------------ */
1585     public void addLocaleEncoding(String locale, String encoding)
1586     {
1587         if (_localeEncodingMap == null)
1588             _localeEncodingMap = new HashMap<String, String>();
1589         _localeEncodingMap.put(locale,encoding);
1590     }
1591 
1592     /* ------------------------------------------------------------ */
1593     public String getLocaleEncoding(String locale)
1594     {
1595         if (_localeEncodingMap == null)
1596             return null;
1597         String encoding = _localeEncodingMap.get(locale);
1598         return encoding;
1599     }
1600 
1601     /* ------------------------------------------------------------ */
1602     /**
1603      * Get the character encoding for a locale. The full locale name is first looked up in the map of encodings. If no encoding is found, then the locale
1604      * language is looked up.
1605      *
1606      * @param locale
1607      *            a <code>Locale</code> value
1608      * @return a <code>String</code> representing the character encoding for the locale or null if none found.
1609      */
1610     public String getLocaleEncoding(Locale locale)
1611     {
1612         if (_localeEncodingMap == null)
1613             return null;
1614         String encoding = _localeEncodingMap.get(locale.toString());
1615         if (encoding == null)
1616             encoding = _localeEncodingMap.get(locale.getLanguage());
1617         return encoding;
1618     }
1619     
1620     /* ------------------------------------------------------------ */
1621     /** 
1622      * Get all of the locale encodings
1623      * 
1624      * @return a map of all the locale encodings: key is name of the locale and value is the char encoding
1625      */
1626     public Map<String,String> getLocaleEncodings()
1627     {
1628         if (_localeEncodingMap == null)
1629             return null;
1630         return Collections.unmodifiableMap(_localeEncodingMap);
1631     }
1632 
1633     /* ------------------------------------------------------------ */
1634     /*
1635      */
1636     public Resource getResource(String path) throws MalformedURLException
1637     {
1638         if (path == null || !path.startsWith(URIUtil.SLASH))
1639             throw new MalformedURLException(path);
1640 
1641         if (_baseResource == null)
1642             return null;
1643 
1644         try
1645         {
1646             path = URIUtil.canonicalPath(path);
1647             Resource resource = _baseResource.addPath(path);
1648             
1649             if (checkAlias(path,resource))
1650                 return resource;
1651             return null;
1652         }
1653         catch (Exception e)
1654         {
1655             LOG.ignore(e);
1656         }
1657 
1658         return null;
1659     }
1660 
1661     /* ------------------------------------------------------------ */
1662     /**
1663      * @param path
1664      * @param resource
1665      * @return True if the alias is OK
1666      */
1667     public boolean checkAlias(String path, Resource resource)
1668     {
1669         // Is the resource aliased?
1670         if (resource.getAlias() != null)
1671         {
1672             if (LOG.isDebugEnabled())
1673                 LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
1674 
1675             // alias checks
1676             for (Iterator<AliasCheck> i=_aliasChecks.iterator();i.hasNext();)
1677             {
1678                 AliasCheck check = i.next();
1679                 if (check.check(path,resource))
1680                 {
1681                     if (LOG.isDebugEnabled())
1682                         LOG.debug("Aliased resource: " + resource + " approved by " + check);
1683                     return true;
1684                 }
1685             }
1686             return false;
1687         }
1688         return true;
1689     }
1690     
1691     /* ------------------------------------------------------------ */
1692     /**
1693      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
1694      */
1695     public Resource newResource(URL url) throws IOException
1696     {
1697         return Resource.newResource(url);
1698     }
1699     
1700     /* ------------------------------------------------------------ */
1701     /**
1702      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
1703      */
1704     public Resource newResource(URI uri) throws IOException
1705     {
1706         return Resource.newResource(uri);
1707     }
1708 
1709     /* ------------------------------------------------------------ */
1710     /**
1711      * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
1712      *
1713      * @param urlOrPath
1714      *            The URL or path to convert
1715      * @return The Resource for the URL/path
1716      * @throws IOException
1717      *             The Resource could not be created.
1718      */
1719     public Resource newResource(String urlOrPath) throws IOException
1720     {
1721         return Resource.newResource(urlOrPath);
1722     }
1723 
1724     /* ------------------------------------------------------------ */
1725     /*
1726      */
1727     public Set<String> getResourcePaths(String path)
1728     {
1729         try
1730         {
1731             path = URIUtil.canonicalPath(path);
1732             Resource resource = getResource(path);
1733 
1734             if (resource != null && resource.exists())
1735             {
1736                 if (!path.endsWith(URIUtil.SLASH))
1737                     path = path + URIUtil.SLASH;
1738 
1739                 String[] l = resource.list();
1740                 if (l != null)
1741                 {
1742                     HashSet<String> set = new HashSet<String>();
1743                     for (int i = 0; i < l.length; i++)
1744                         set.add(path + l[i]);
1745                     return set;
1746                 }
1747             }
1748         }
1749         catch (Exception e)
1750         {
1751             LOG.ignore(e);
1752         }
1753         return Collections.emptySet();
1754     }
1755 
1756     /* ------------------------------------------------------------ */
1757     private String normalizeHostname(String host)
1758     {
1759         if (host == null)
1760             return null;
1761 
1762         if (host.endsWith("."))
1763             return host.substring(0,host.length() - 1);
1764 
1765         return host;
1766     }
1767 
1768     /* ------------------------------------------------------------ */
1769     /**
1770      * Add an AliasCheck instance to possibly permit aliased resources
1771      * @param check The alias checker
1772      */
1773     public void addAliasCheck(AliasCheck check)
1774     {
1775         _aliasChecks.add(check);
1776     }
1777 
1778     /* ------------------------------------------------------------ */
1779     /**
1780      * @return Mutable list of Alias checks
1781      */
1782     public List<AliasCheck> getAliasChecks()
1783     {
1784         return _aliasChecks;
1785     }
1786     
1787     /* ------------------------------------------------------------ */
1788     /**
1789      * @param checks list of AliasCheck instances
1790      */
1791     public void setAliasChecks(List<AliasCheck> checks)
1792     {
1793         _aliasChecks.clear();
1794         _aliasChecks.addAll(checks);
1795     }
1796 
1797     /* ------------------------------------------------------------ */
1798     /**
1799      * Context.
1800      * <p>
1801      * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
1802      * </p>
1803      *
1804      *
1805      */
1806     public class Context extends NoContext
1807     {
1808         protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
1809         protected boolean _extendedListenerTypes = false;
1810 
1811 
1812         /* ------------------------------------------------------------ */
1813         protected Context()
1814         {
1815         }
1816 
1817         /* ------------------------------------------------------------ */
1818         public ContextHandler getContextHandler()
1819         {
1820             return ContextHandler.this;
1821         }
1822 
1823         /* ------------------------------------------------------------ */
1824         /*
1825          * @see javax.servlet.ServletContext#getContext(java.lang.String)
1826          */
1827         @Override
1828         public ServletContext getContext(String uripath)
1829         {
1830             List<ContextHandler> contexts = new ArrayList<ContextHandler>();
1831             Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
1832             String matched_path = null;
1833 
1834             for (Handler handler : handlers)
1835             {
1836                 if (handler == null)
1837                     continue;
1838                 ContextHandler ch = (ContextHandler)handler;
1839                 String context_path = ch.getContextPath();
1840 
1841                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1842                         || "/".equals(context_path))
1843                 {
1844                     // look first for vhost matching context only
1845                     if (getVirtualHosts() != null && getVirtualHosts().length > 0)
1846                     {
1847                         if (ch.getVirtualHosts() != null && ch.getVirtualHosts().length > 0)
1848                         {
1849                             for (String h1 : getVirtualHosts())
1850                                 for (String h2 : ch.getVirtualHosts())
1851                                     if (h1.equals(h2))
1852                                     {
1853                                         if (matched_path == null || context_path.length() > matched_path.length())
1854                                         {
1855                                             contexts.clear();
1856                                             matched_path = context_path;
1857                                         }
1858 
1859                                         if (matched_path.equals(context_path))
1860                                             contexts.add(ch);
1861                                     }
1862                         }
1863                     }
1864                     else
1865                     {
1866                         if (matched_path == null || context_path.length() > matched_path.length())
1867                         {
1868                             contexts.clear();
1869                             matched_path = context_path;
1870                         }
1871 
1872                         if (matched_path.equals(context_path))
1873                             contexts.add(ch);
1874                     }
1875                 }
1876             }
1877 
1878             if (contexts.size() > 0)
1879                 return contexts.get(0)._scontext;
1880 
1881             // try again ignoring virtual hosts
1882             matched_path = null;
1883             for (Handler handler : handlers)
1884             {
1885                 if (handler == null)
1886                     continue;
1887                 ContextHandler ch = (ContextHandler)handler;
1888                 String context_path = ch.getContextPath();
1889 
1890                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1891                         || "/".equals(context_path))
1892                 {
1893                     if (matched_path == null || context_path.length() > matched_path.length())
1894                     {
1895                         contexts.clear();
1896                         matched_path = context_path;
1897                     }
1898 
1899                     if (matched_path != null && matched_path.equals(context_path))
1900                         contexts.add(ch);
1901                 }
1902             }
1903 
1904             if (contexts.size() > 0)
1905                 return contexts.get(0)._scontext;
1906             return null;
1907         }
1908 
1909         /* ------------------------------------------------------------ */
1910         /*
1911          * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
1912          */
1913         @Override
1914         public String getMimeType(String file)
1915         {
1916             if (_mimeTypes == null)
1917                 return null;
1918             return _mimeTypes.getMimeByExtension(file);
1919         }
1920 
1921         /* ------------------------------------------------------------ */
1922         /*
1923          * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
1924          */
1925         @Override
1926         public RequestDispatcher getRequestDispatcher(String uriInContext)
1927         {
1928             if (uriInContext == null)
1929                 return null;
1930 
1931             if (!uriInContext.startsWith("/"))
1932                 return null;
1933 
1934             try
1935             {
1936                 String query = null;
1937                 int q = 0;
1938                 if ((q = uriInContext.indexOf('?')) > 0)
1939                 {
1940                     query = uriInContext.substring(q + 1);
1941                     uriInContext = uriInContext.substring(0,q);
1942                 }
1943 
1944                 String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
1945                 if (pathInContext!=null)
1946                 {
1947                     String uri = URIUtil.addPaths(getContextPath(),uriInContext);
1948                     ContextHandler context = ContextHandler.this;
1949                     return new Dispatcher(context,uri,pathInContext,query);
1950                 }
1951             }
1952             catch (Exception e)
1953             {
1954                 LOG.ignore(e);
1955             }
1956             return null;
1957         }
1958 
1959         /* ------------------------------------------------------------ */
1960         /*
1961          * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
1962          */
1963         @Override
1964         public String getRealPath(String path)
1965         {
1966             if (path == null)
1967                 return null;
1968             if (path.length() == 0)
1969                 path = URIUtil.SLASH;
1970             else if (path.charAt(0) != '/')
1971                 path = URIUtil.SLASH + path;
1972 
1973             try
1974             {
1975                 Resource resource = ContextHandler.this.getResource(path);
1976                 if (resource != null)
1977                 {
1978                     File file = resource.getFile();
1979                     if (file != null)
1980                         return file.getCanonicalPath();
1981                 }
1982             }
1983             catch (Exception e)
1984             {
1985                 LOG.ignore(e);
1986             }
1987 
1988             return null;
1989         }
1990 
1991         /* ------------------------------------------------------------ */
1992         @Override
1993         public URL getResource(String path) throws MalformedURLException
1994         {
1995             Resource resource = ContextHandler.this.getResource(path);
1996             if (resource != null && resource.exists())
1997                 return resource.getURL();
1998             return null;
1999         }
2000 
2001         /* ------------------------------------------------------------ */
2002         /*
2003          * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
2004          */
2005         @Override
2006         public InputStream getResourceAsStream(String path)
2007         {
2008             try
2009             {
2010                 URL url = getResource(path);
2011                 if (url == null)
2012                     return null;
2013                 Resource r = Resource.newResource(url);
2014                 return r.getInputStream();
2015             }
2016             catch (Exception e)
2017             {
2018                 LOG.ignore(e);
2019                 return null;
2020             }
2021         }
2022 
2023         /* ------------------------------------------------------------ */
2024         /*
2025          * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
2026          */
2027         @Override
2028         public Set<String> getResourcePaths(String path)
2029         {
2030             return ContextHandler.this.getResourcePaths(path);
2031         }
2032 
2033         /* ------------------------------------------------------------ */
2034         /*
2035          * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
2036          */
2037         @Override
2038         public void log(Exception exception, String msg)
2039         {
2040             _logger.warn(msg,exception);
2041         }
2042 
2043         /* ------------------------------------------------------------ */
2044         /*
2045          * @see javax.servlet.ServletContext#log(java.lang.String)
2046          */
2047         @Override
2048         public void log(String msg)
2049         {
2050             _logger.info(msg);
2051         }
2052 
2053         /* ------------------------------------------------------------ */
2054         /*
2055          * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
2056          */
2057         @Override
2058         public void log(String message, Throwable throwable)
2059         {
2060             _logger.warn(message,throwable);
2061         }
2062 
2063         /* ------------------------------------------------------------ */
2064         /*
2065          * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
2066          */
2067         @Override
2068         public String getInitParameter(String name)
2069         {
2070             return ContextHandler.this.getInitParameter(name);
2071         }
2072 
2073         /* ------------------------------------------------------------ */
2074         /*
2075          * @see javax.servlet.ServletContext#getInitParameterNames()
2076          */
2077         @Override
2078         public Enumeration<String> getInitParameterNames()
2079         {
2080             return ContextHandler.this.getInitParameterNames();
2081         }
2082 
2083         /* ------------------------------------------------------------ */
2084         /*
2085          * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
2086          */
2087         @Override
2088         public synchronized Object getAttribute(String name)
2089         {
2090             Object o = ContextHandler.this.getAttribute(name);
2091             if (o == null)
2092                 o = super.getAttribute(name);
2093             return o;
2094         }
2095 
2096         /* ------------------------------------------------------------ */
2097         /*
2098          * @see javax.servlet.ServletContext#getAttributeNames()
2099          */
2100         @Override
2101         public synchronized Enumeration<String> getAttributeNames()
2102         {
2103             HashSet<String> set = new HashSet<String>();
2104             Enumeration<String> e = super.getAttributeNames();
2105             while (e.hasMoreElements())
2106                 set.add(e.nextElement());
2107             e = _attributes.getAttributeNames();
2108             while (e.hasMoreElements())
2109                 set.add(e.nextElement());
2110 
2111             return Collections.enumeration(set);
2112         }
2113 
2114         /* ------------------------------------------------------------ */
2115         /*
2116          * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
2117          */
2118         @Override
2119         public synchronized void setAttribute(String name, Object value)
2120         {
2121             checkManagedAttribute(name,value);
2122             Object old_value = super.getAttribute(name);
2123 
2124             if (value == null)
2125                 super.removeAttribute(name);
2126             else
2127                 super.setAttribute(name,value);
2128 
2129             if (!_contextAttributeListeners.isEmpty())
2130             {
2131                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
2132 
2133                 for (ServletContextAttributeListener l : _contextAttributeListeners)
2134                 {
2135                     if (old_value == null)
2136                         l.attributeAdded(event);
2137                     else if (value == null)
2138                         l.attributeRemoved(event);
2139                     else
2140                         l.attributeReplaced(event);
2141                 }
2142             }
2143         }
2144 
2145         /* ------------------------------------------------------------ */
2146         /*
2147          * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
2148          */
2149         @Override
2150         public synchronized void removeAttribute(String name)
2151         {
2152             checkManagedAttribute(name,null);
2153 
2154             Object old_value = super.getAttribute(name);
2155             super.removeAttribute(name);
2156             if (old_value != null &&!_contextAttributeListeners.isEmpty())
2157             {
2158                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
2159 
2160                 for (ServletContextAttributeListener l : _contextAttributeListeners)
2161                     l.attributeRemoved(event);
2162             }
2163         }
2164 
2165         /* ------------------------------------------------------------ */
2166         /*
2167          * @see javax.servlet.ServletContext#getServletContextName()
2168          */
2169         @Override
2170         public String getServletContextName()
2171         {
2172             String name = ContextHandler.this.getDisplayName();
2173             if (name == null)
2174                 name = ContextHandler.this.getContextPath();
2175             return name;
2176         }
2177 
2178         /* ------------------------------------------------------------ */
2179         @Override
2180         public String getContextPath()
2181         {
2182             if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
2183                 return "";
2184 
2185             return _contextPath;
2186         }
2187 
2188         /* ------------------------------------------------------------ */
2189         @Override
2190         public String toString()
2191         {
2192             return "ServletContext@" + ContextHandler.this.toString();
2193         }
2194 
2195         /* ------------------------------------------------------------ */
2196         @Override
2197         public boolean setInitParameter(String name, String value)
2198         {
2199             if (ContextHandler.this.getInitParameter(name) != null)
2200                 return false;
2201             ContextHandler.this.getInitParams().put(name,value);
2202             return true;
2203         }
2204 
2205         @Override
2206         public void addListener(String className)
2207         {
2208             if (!_enabled)
2209                 throw new UnsupportedOperationException();
2210 
2211             try
2212             {
2213                 @SuppressWarnings("unchecked")
2214                 Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
2215                 addListener(clazz);
2216             }
2217             catch (ClassNotFoundException e)
2218             {
2219                 throw new IllegalArgumentException(e);
2220             }
2221         }
2222 
2223         @Override
2224         public <T extends EventListener> void addListener(T t)
2225         {
2226             if (!_enabled)
2227                 throw new UnsupportedOperationException();
2228 
2229             checkListener(t.getClass());
2230 
2231             ContextHandler.this.addEventListener(t);
2232             ContextHandler.this.addProgrammaticListener(t);
2233         }
2234 
2235         @Override
2236         public void addListener(Class<? extends EventListener> listenerClass)
2237         {
2238             if (!_enabled)
2239                 throw new UnsupportedOperationException();
2240 
2241             try
2242             {
2243                 EventListener e = createListener(listenerClass);
2244                 addListener(e);
2245             }
2246             catch (ServletException e)
2247             {
2248                 throw new IllegalArgumentException(e);
2249             }
2250         }
2251 
2252         @Override
2253         public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
2254         {
2255             try
2256             {
2257                 return createInstance(clazz);
2258             }
2259             catch (Exception e)
2260             {
2261                 throw new ServletException(e);
2262             }
2263         }
2264 
2265 
2266         public void checkListener (Class<? extends EventListener> listener) throws IllegalStateException
2267         {
2268             boolean ok = false;
2269             int startIndex = (isExtendedListenerTypes()?EXTENDED_LISTENER_TYPE_INDEX:DEFAULT_LISTENER_TYPE_INDEX);
2270             for (int i=startIndex;i<SERVLET_LISTENER_TYPES.length;i++)
2271             {
2272                 if (SERVLET_LISTENER_TYPES[i].isAssignableFrom(listener))
2273                 {
2274                     ok = true;
2275                     break;
2276                 }
2277             }
2278             if (!ok)
2279                 throw new IllegalArgumentException("Inappropriate listener class "+listener.getName());
2280         }
2281 
2282         public void setExtendedListenerTypes (boolean extended)
2283         {
2284            _extendedListenerTypes = extended;
2285         }
2286 
2287        public boolean isExtendedListenerTypes()
2288        {
2289            return _extendedListenerTypes;
2290        }
2291 
2292 
2293        @Override
2294        public ClassLoader getClassLoader()
2295        {
2296            if (!_enabled)
2297                throw new UnsupportedOperationException();
2298            
2299            //no security manager just return the classloader
2300            if (System.getSecurityManager() == null)
2301                return _classLoader;
2302            else
2303            {
2304                //check to see if the classloader of the caller is the same as the context
2305                //classloader, or a parent of it
2306                try
2307                {
2308                    Class<?> reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
2309                    Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
2310                    Class<?> caller = (Class<?>)getCallerClass.invoke(null, 2);
2311 
2312                    boolean ok = false;
2313                    ClassLoader callerLoader = caller.getClassLoader();
2314                    while (!ok && callerLoader != null)
2315                    {
2316                        if (callerLoader == _classLoader) 
2317                            ok = true;
2318                        else
2319                            callerLoader = callerLoader.getParent();    
2320                    }
2321 
2322                    if (ok)
2323                        return _classLoader;
2324                }
2325                catch (Exception e)      
2326                {
2327                    LOG.warn("Unable to check classloader of caller",e);
2328                }
2329               
2330                AccessController.checkPermission(new RuntimePermission("getClassLoader"));
2331                return _classLoader;
2332            }
2333         }
2334 
2335         @Override
2336         public JspConfigDescriptor getJspConfigDescriptor()
2337         {
2338             LOG.warn(__unimplmented);
2339             return null;
2340         }
2341 
2342         public void setJspConfigDescriptor(JspConfigDescriptor d)
2343         {
2344 
2345         }
2346 
2347         @Override
2348         public void declareRoles(String... roleNames)
2349         {
2350             if (!isStarting())
2351                 throw new IllegalStateException ();
2352             if (!_enabled)
2353                 throw new UnsupportedOperationException();
2354         }
2355 
2356         public void setEnabled(boolean enabled)
2357         {
2358             _enabled = enabled;
2359         }
2360 
2361         public boolean isEnabled()
2362         {
2363             return _enabled;
2364         }
2365 
2366 
2367 
2368         public <T> T createInstance (Class<T> clazz) throws Exception
2369         {
2370             T o = clazz.newInstance();
2371             return o;
2372         }
2373     }
2374 
2375 
2376     public static class NoContext extends AttributesMap implements ServletContext
2377     {
2378         private int _effectiveMajorVersion = SERVLET_MAJOR_VERSION;
2379         private int _effectiveMinorVersion = SERVLET_MINOR_VERSION;
2380 
2381         /* ------------------------------------------------------------ */
2382         public NoContext()
2383         {
2384         }
2385 
2386         @Override
2387         public ServletContext getContext(String uripath)
2388         {
2389             return null;
2390         }
2391 
2392         @Override
2393         public int getMajorVersion()
2394         {
2395             return SERVLET_MAJOR_VERSION;
2396         }
2397 
2398         @Override
2399         public String getMimeType(String file)
2400         {
2401             return null;
2402         }
2403 
2404         @Override
2405         public int getMinorVersion()
2406         {
2407             return SERVLET_MINOR_VERSION;
2408         }
2409 
2410         @Override
2411         public RequestDispatcher getNamedDispatcher(String name)
2412         {
2413             return null;
2414         }
2415 
2416         @Override
2417         public RequestDispatcher getRequestDispatcher(String uriInContext)
2418         {
2419             return null;
2420         }
2421 
2422         @Override
2423         public String getRealPath(String path)
2424         {
2425             return null;
2426         }
2427 
2428         @Override
2429         public URL getResource(String path) throws MalformedURLException
2430         {
2431             return null;
2432         }
2433 
2434         @Override
2435         public InputStream getResourceAsStream(String path)
2436         {
2437             return null;
2438         }
2439 
2440         @Override
2441         public Set<String> getResourcePaths(String path)
2442         {
2443             return null;
2444         }
2445 
2446         @Override
2447         public String getServerInfo()
2448         {
2449             // NOTE: DO NOT CHANGE
2450             //   this is used by weld to detect Jetty
2451             //   implementation version
2452             // See: https://github.com/weld/core/blob/master/environments/servlet/core/src/main/java/org/jboss/weld/environment/jetty/JettyContainer.java
2453             //   and its touch(ContainerContext) method
2454             return "jetty/" + Server.getVersion();
2455         }
2456 
2457         @Override
2458         @Deprecated
2459         public Servlet getServlet(String name) throws ServletException
2460         {
2461             return null;
2462         }
2463 
2464         @SuppressWarnings("unchecked")
2465         @Override
2466         @Deprecated
2467         public Enumeration<String> getServletNames()
2468         {
2469             return Collections.enumeration(Collections.EMPTY_LIST);
2470         }
2471 
2472         @SuppressWarnings("unchecked")
2473         @Override
2474         @Deprecated
2475         public Enumeration<Servlet> getServlets()
2476         {
2477             return Collections.enumeration(Collections.EMPTY_LIST);
2478         }
2479 
2480         @Override
2481         public void log(Exception exception, String msg)
2482         {
2483             LOG.warn(msg,exception);
2484         }
2485 
2486         @Override
2487         public void log(String msg)
2488         {
2489             LOG.info(msg);
2490         }
2491 
2492         @Override
2493         public void log(String message, Throwable throwable)
2494         {
2495             LOG.warn(message,throwable);
2496         }
2497 
2498         @Override
2499         public String getInitParameter(String name)
2500         {
2501             return null;
2502         }
2503 
2504         @SuppressWarnings("unchecked")
2505         @Override
2506         public Enumeration<String> getInitParameterNames()
2507         {
2508             return Collections.enumeration(Collections.EMPTY_LIST);
2509         }
2510 
2511 
2512         @Override
2513         public String getServletContextName()
2514         {
2515             return "No Context";
2516         }
2517 
2518         @Override
2519         public String getContextPath()
2520         {
2521             return null;
2522         }
2523 
2524 
2525         @Override
2526         public boolean setInitParameter(String name, String value)
2527         {
2528             return false;
2529         }
2530 
2531         @Override
2532         public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
2533         {
2534             LOG.warn(__unimplmented);
2535             return null;
2536         }
2537 
2538         @Override
2539         public Dynamic addFilter(String filterName, Filter filter)
2540         {
2541             LOG.warn(__unimplmented);
2542             return null;
2543         }
2544 
2545         @Override
2546         public Dynamic addFilter(String filterName, String className)
2547         {
2548             LOG.warn(__unimplmented);
2549             return null;
2550         }
2551 
2552         @Override
2553         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
2554         {
2555             LOG.warn(__unimplmented);
2556             return null;
2557         }
2558 
2559         @Override
2560         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
2561         {
2562             LOG.warn(__unimplmented);
2563             return null;
2564         }
2565 
2566         @Override
2567         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
2568         {
2569             LOG.warn(__unimplmented);
2570             return null;
2571         }
2572 
2573         @Override
2574         public <T extends Filter> T createFilter(Class<T> c) throws ServletException
2575         {
2576             LOG.warn(__unimplmented);
2577             return null;
2578         }
2579 
2580         @Override
2581         public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
2582         {
2583             LOG.warn(__unimplmented);
2584             return null;
2585         }
2586 
2587         @Override
2588         public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
2589         {
2590             LOG.warn(__unimplmented);
2591             return null;
2592         }
2593 
2594         @Override
2595         public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
2596         {
2597             LOG.warn(__unimplmented);
2598             return null;
2599         }
2600 
2601         @Override
2602         public FilterRegistration getFilterRegistration(String filterName)
2603         {
2604             LOG.warn(__unimplmented);
2605             return null;
2606         }
2607 
2608         @Override
2609         public Map<String, ? extends FilterRegistration> getFilterRegistrations()
2610         {
2611             LOG.warn(__unimplmented);
2612             return null;
2613         }
2614 
2615         @Override
2616         public ServletRegistration getServletRegistration(String servletName)
2617         {
2618             LOG.warn(__unimplmented);
2619             return null;
2620         }
2621 
2622         @Override
2623         public Map<String, ? extends ServletRegistration> getServletRegistrations()
2624         {
2625             LOG.warn(__unimplmented);
2626             return null;
2627         }
2628 
2629         @Override
2630         public SessionCookieConfig getSessionCookieConfig()
2631         {
2632             LOG.warn(__unimplmented);
2633             return null;
2634         }
2635 
2636         @Override
2637         public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
2638         {
2639             LOG.warn(__unimplmented);
2640         }
2641 
2642         @Override
2643         public void addListener(String className)
2644         {
2645             LOG.warn(__unimplmented);
2646         }
2647 
2648         @Override
2649         public <T extends EventListener> void addListener(T t)
2650         {
2651             LOG.warn(__unimplmented);
2652         }
2653 
2654         @Override
2655         public void addListener(Class<? extends EventListener> listenerClass)
2656         {
2657             LOG.warn(__unimplmented);
2658         }
2659 
2660         @Override
2661         public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
2662         {
2663             try
2664             {
2665                 return clazz.newInstance();
2666             }
2667             catch (InstantiationException e)
2668             {
2669                 throw new ServletException(e);
2670             }
2671             catch (IllegalAccessException e)
2672             {
2673                 throw new ServletException(e);
2674             }
2675         }
2676 
2677         @Override
2678         public ClassLoader getClassLoader()
2679         {
2680             AccessController.checkPermission(new RuntimePermission("getClassLoader"));
2681             return ContextHandler.class.getClassLoader();
2682         }
2683 
2684         @Override
2685         public int getEffectiveMajorVersion()
2686         {
2687             return _effectiveMajorVersion;
2688         }
2689 
2690         @Override
2691         public int getEffectiveMinorVersion()
2692         {
2693             return _effectiveMinorVersion;
2694         }
2695 
2696         public void setEffectiveMajorVersion (int v)
2697         {
2698             _effectiveMajorVersion = v;
2699         }
2700 
2701         public void setEffectiveMinorVersion (int v)
2702         {
2703             _effectiveMinorVersion = v;
2704         }
2705 
2706         @Override
2707         public JspConfigDescriptor getJspConfigDescriptor()
2708         {
2709             LOG.warn(__unimplmented);
2710             return null;
2711         }
2712 
2713         @Override
2714         public void declareRoles(String... roleNames)
2715         {
2716             LOG.warn(__unimplmented);
2717         }
2718 
2719         /**
2720          * @see javax.servlet.ServletContext#getVirtualServerName()
2721          */
2722         @Override
2723         public String getVirtualServerName()
2724         {
2725             // TODO 3.1 Auto-generated method stub
2726             return null;
2727         }
2728     }
2729 
2730 
2731     /* ------------------------------------------------------------ */
2732     /** Interface to check aliases
2733      */
2734     public interface AliasCheck
2735     {
2736         /* ------------------------------------------------------------ */
2737         /** Check an alias
2738          * @param path The path the aliased resource was created for
2739          * @param resource The aliased resourced
2740          * @return True if the resource is OK to be served.
2741          */
2742         boolean check(String path, Resource resource);
2743     }
2744 
2745 
2746     /* ------------------------------------------------------------ */
2747     /** Approve all aliases.
2748      */
2749     public static class ApproveAliases implements AliasCheck
2750     {
2751         @Override
2752         public boolean check(String path, Resource resource)
2753         {
2754             return true;
2755         }
2756     }
2757 
2758     /* ------------------------------------------------------------ */
2759     /** Approve Aliases with same suffix.
2760      * Eg. a symbolic link from /foobar.html to /somewhere/wibble.html would be
2761      * approved because both the resource and alias end with ".html".
2762      */
2763     @Deprecated
2764     public static class ApproveSameSuffixAliases implements AliasCheck
2765     {
2766         {
2767             LOG.warn("ApproveSameSuffixAlias is not safe for production");
2768         }
2769         
2770         @Override
2771         public boolean check(String path, Resource resource)
2772         {
2773             int dot = path.lastIndexOf('.');
2774             if (dot<0)
2775                 return false;
2776             String suffix=path.substring(dot);
2777             return resource.toString().endsWith(suffix);
2778         }
2779     }
2780 
2781 
2782     /* ------------------------------------------------------------ */
2783     /** Approve Aliases with a path prefix.
2784      * Eg. a symbolic link from /dirA/foobar.html to /dirB/foobar.html would be
2785      * approved because both the resource and alias end with "/foobar.html".
2786      */
2787     @Deprecated
2788     public static class ApprovePathPrefixAliases implements AliasCheck
2789     {
2790         {
2791             LOG.warn("ApprovePathPrefixAliases is not safe for production");
2792         }
2793         
2794         @Override
2795         public boolean check(String path, Resource resource)
2796         {
2797             int slash = path.lastIndexOf('/');
2798             if (slash<0 || slash==path.length()-1)
2799                 return false;
2800             String suffix=path.substring(slash);
2801             return resource.toString().endsWith(suffix);
2802         }
2803     }
2804     
2805     /* ------------------------------------------------------------ */
2806     /** Approve Aliases of a non existent directory.
2807      * If a directory "/foobar/" does not exist, then the resource is
2808      * aliased to "/foobar".  Accept such aliases.
2809      */
2810     public static class ApproveNonExistentDirectoryAliases implements AliasCheck
2811     {
2812         @Override
2813         public boolean check(String path, Resource resource)
2814         {
2815             if (resource.exists())
2816                 return false;
2817             
2818             String a=resource.getAlias().toString();
2819             String r=resource.getURL().toString();
2820             
2821             if (a.length()>r.length())
2822                 return a.startsWith(r) && a.length()==r.length()+1 && a.endsWith("/");
2823             if (a.length()<r.length())
2824                 return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
2825             
2826             return a.equals(r); 
2827         }
2828     }
2829 
2830 }