Merge branch 'libs/jetty/upstream' into libs/jetty/local
[gigi.git] / lib / jetty / org / eclipse / jetty / server / handler / ContextHandler.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2016 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             {
772                 _managedAttributes.put(attribute.trim(),null);
773             }
774
775             Enumeration<String> e = _scontext.getAttributeNames();
776             while (e.hasMoreElements())
777             {
778                 String name = e.nextElement();
779                 Object value = _scontext.getAttribute(name);
780                 checkManagedAttribute(name,value);
781             }
782         }
783
784         super.doStart();
785
786         // Call context listeners
787         if (!_contextListeners.isEmpty())
788         {
789             ServletContextEvent event = new ServletContextEvent(_scontext);
790             for (ServletContextListener listener:_contextListeners)
791                 callContextInitialized(listener, event);
792         }
793     }
794
795     /* ------------------------------------------------------------ */
796     protected void callContextInitialized (ServletContextListener l, ServletContextEvent e)
797     {
798         if (LOG.isDebugEnabled())
799             LOG.debug("contextInitialized: {}->{}",e,l);
800         l.contextInitialized(e);
801     }
802
803     /* ------------------------------------------------------------ */
804     protected void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
805     {
806         if (LOG.isDebugEnabled())
807             LOG.debug("contextDestroyed: {}->{}",e,l);
808         l.contextDestroyed(e);
809     }
810
811     /* ------------------------------------------------------------ */
812     /*
813      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
814      */
815     @Override
816     protected void doStop() throws Exception
817     {
818         _availability = Availability.UNAVAILABLE;
819
820         ClassLoader old_classloader = null;
821         Thread current_thread = null;
822
823         Context old_context = __context.get();
824         __context.set(_scontext);
825         try
826         {
827             // Set the classloader
828             if (_classLoader != null)
829             {
830                 current_thread = Thread.currentThread();
831                 old_classloader = current_thread.getContextClassLoader();
832                 current_thread.setContextClassLoader(_classLoader);
833             }
834
835             super.doStop();
836
837             // Context listeners
838             if (!_contextListeners.isEmpty())
839             {
840                 ServletContextEvent event = new ServletContextEvent(_scontext);
841                 for (int i = _contextListeners.size(); i-->0;)
842                     callContextDestroyed(_contextListeners.get(i),event);
843             }
844
845             //retain only durable listeners
846             setEventListeners(_durableListeners.toArray(new EventListener[_durableListeners.size()]));
847             _durableListeners.clear();
848
849             if (_errorHandler != null)
850                 _errorHandler.stop();
851
852             Enumeration<String> e = _scontext.getAttributeNames();
853             while (e.hasMoreElements())
854             {
855                 String name = e.nextElement();
856                 checkManagedAttribute(name,null);
857             }
858
859             for (EventListener l : _programmaticListeners)
860                 removeEventListener(l);
861             _programmaticListeners.clear();
862         }
863         finally
864         {
865             LOG.info("Stopped {}", this);
866             __context.set(old_context);
867             // reset the classloader
868             if (_classLoader != null && current_thread!=null)
869                 current_thread.setContextClassLoader(old_classloader);
870         }
871
872         _scontext.clearAttributes();
873     }
874
875     public boolean checkVirtualHost(final Request baseRequest)
876     {
877         if (_vhosts != null && _vhosts.length > 0)
878         {
879             String vhost = normalizeHostname(baseRequest.getServerName());
880
881             boolean match = false;
882             boolean connectorName = false;
883             boolean connectorMatch = false;
884
885             for (String contextVhost:_vhosts)
886             {
887                 if (contextVhost == null || contextVhost.length()==0)
888                     continue;
889                 char c=contextVhost.charAt(0);
890                 switch (c)
891                 {
892                     case '*':
893                         if (contextVhost.startsWith("*."))
894                             // wildcard only at the beginning, and only for one additional subdomain level
895                             match = match || contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
896                         break;
897                     case '@':
898                         connectorName=true;
899                         String name=baseRequest.getHttpChannel().getConnector().getName();
900                         boolean m=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
901                         match = match || m;
902                         connectorMatch = connectorMatch || m;
903                         break;
904                     default:
905                         match = match || contextVhost.equalsIgnoreCase(vhost);
906                 }
907
908             }
909             if (!match || connectorName && !connectorMatch)
910                 return false;
911         }
912         return true;
913     }
914     
915     public boolean checkContextPath(String uri)
916     {
917         // Are we not the root context?
918         if (_contextPath.length() > 1)
919         {
920             // reject requests that are not for us
921             if (!uri.startsWith(_contextPath))
922                 return false;
923             if (uri.length() > _contextPath.length() && uri.charAt(_contextPath.length()) != '/')
924                 return false;
925         }
926         return true;
927     }
928     
929     /* ------------------------------------------------------------ */
930     /*
931      * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
932      */
933     public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException
934     {
935         DispatcherType dispatch = baseRequest.getDispatcherType();
936
937         // Check the vhosts
938         if (!checkVirtualHost(baseRequest))
939             return false;
940
941         if (!checkContextPath(target))
942             return false;
943         
944         // Are we not the root context?
945         // redirect null path infos
946         if (!_allowNullPathInfo && _contextPath.length() == target.length() && _contextPath.length()>1)
947         {
948             // context request must end with /
949             baseRequest.setHandled(true);
950             if (baseRequest.getQueryString() != null)
951                 response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
952             else
953                 response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
954             return false;
955         }
956
957         switch (_availability)
958         {
959             case SHUTDOWN:
960             case UNAVAILABLE:
961                 baseRequest.setHandled(true);
962                 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
963                 return false;
964             default:
965                 if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
966                     return false;
967         }
968
969         return true;
970     }
971
972     /* ------------------------------------------------------------ */
973     /**
974      * @see org.eclipse.jetty.server.handler.ScopedHandler#doScope(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
975      *      javax.servlet.http.HttpServletResponse)
976      */
977     @Override
978     public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
979     {
980         if (LOG.isDebugEnabled())
981             LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this);
982
983         Context old_context = null;
984         String old_context_path = null;
985         String old_servlet_path = null;
986         String old_path_info = null;
987         ClassLoader old_classloader = null;
988         Thread current_thread = null;
989         String pathInfo = target;
990
991         DispatcherType dispatch = baseRequest.getDispatcherType();
992
993         old_context = baseRequest.getContext();
994
995         // Are we already in this context?
996         if (old_context != _scontext)
997         {
998             // check the target.
999             if (DispatcherType.REQUEST.equals(dispatch) ||
1000                 DispatcherType.ASYNC.equals(dispatch) ||
1001                 DispatcherType.ERROR.equals(dispatch) && baseRequest.getHttpChannelState().isAsync())
1002             {
1003                 if (_compactPath)
1004                     target = URIUtil.compactPath(target);
1005                 if (!checkContext(target,baseRequest,response))
1006                     return;
1007
1008                 if (target.length() > _contextPath.length())
1009                 {
1010                     if (_contextPath.length() > 1)
1011                         target = target.substring(_contextPath.length());
1012                     pathInfo = target;
1013                 }
1014                 else if (_contextPath.length() == 1)
1015                 {
1016                     target = URIUtil.SLASH;
1017                     pathInfo = URIUtil.SLASH;
1018                 }
1019                 else
1020                 {
1021                     target = URIUtil.SLASH;
1022                     pathInfo = null;
1023                 }
1024             }
1025
1026             // Set the classloader
1027             if (_classLoader != null)
1028             {
1029                 current_thread = Thread.currentThread();
1030                 old_classloader = current_thread.getContextClassLoader();
1031                 current_thread.setContextClassLoader(_classLoader);
1032             }
1033         }
1034
1035         try
1036         {
1037             old_context_path = baseRequest.getContextPath();
1038             old_servlet_path = baseRequest.getServletPath();
1039             old_path_info = baseRequest.getPathInfo();
1040
1041             // Update the paths
1042             baseRequest.setContext(_scontext);
1043             __context.set(_scontext);
1044             if (!DispatcherType.INCLUDE.equals(dispatch) && target.startsWith("/"))
1045             {
1046                 if (_contextPath.length() == 1)
1047                     baseRequest.setContextPath("");
1048                 else
1049                     baseRequest.setContextPath(_contextPath);
1050                 baseRequest.setServletPath(null);
1051                 baseRequest.setPathInfo(pathInfo);
1052             }
1053
1054             if (LOG.isDebugEnabled())
1055                 LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
1056
1057             // start manual inline of nextScope(target,baseRequest,request,response);
1058             if (never())
1059                 nextScope(target,baseRequest,request,response);
1060             else if (_nextScope != null)
1061                 _nextScope.doScope(target,baseRequest,request,response);
1062             else if (_outerScope != null)
1063                 _outerScope.doHandle(target,baseRequest,request,response);
1064             else
1065                 doHandle(target,baseRequest,request,response);
1066             // end manual inline (pathentic attempt to reduce stack depth)
1067         }
1068         finally
1069         {
1070             if (old_context != _scontext)
1071             {
1072                 // reset the classloader
1073                 if (_classLoader != null && current_thread!=null)
1074                 {
1075                     current_thread.setContextClassLoader(old_classloader);
1076                 }
1077
1078                 // reset the context and servlet path.
1079                 baseRequest.setContext(old_context);
1080                 __context.set(old_context);
1081                 baseRequest.setContextPath(old_context_path);
1082                 baseRequest.setServletPath(old_servlet_path);
1083                 baseRequest.setPathInfo(old_path_info);
1084             }
1085         }
1086     }
1087
1088     /* ------------------------------------------------------------ */
1089     /**
1090      * @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
1091      *      javax.servlet.http.HttpServletResponse)
1092      */
1093     @Override
1094     public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
1095     {
1096         final DispatcherType dispatch = baseRequest.getDispatcherType();
1097         final boolean new_context = baseRequest.takeNewContext();
1098         try
1099         {
1100             if (new_context)
1101             {
1102                 // Handle the REALLY SILLY request events!
1103                 if (!_requestAttributeListeners.isEmpty())
1104                     for (ServletRequestAttributeListener l :_requestAttributeListeners)
1105                         baseRequest.addEventListener(l);
1106
1107                 if (!_requestListeners.isEmpty())
1108                 {
1109                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
1110                     for (ServletRequestListener l : _requestListeners)
1111                         l.requestInitialized(sre);
1112                 }
1113             }
1114
1115             if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
1116             {
1117                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
1118                 baseRequest.setHandled(true);
1119                 return;
1120             }
1121
1122             // start manual inline of nextHandle(target,baseRequest,request,response);
1123             // noinspection ConstantIfStatement
1124             if (never())
1125                 nextHandle(target,baseRequest,request,response);
1126             else if (_nextScope != null && _nextScope == _handler)
1127                 _nextScope.doHandle(target,baseRequest,request,response);
1128             else if (_handler != null)
1129                 _handler.handle(target,baseRequest,request,response);
1130             // end manual inline
1131         }
1132         finally
1133         {
1134             // Handle more REALLY SILLY request events!
1135             if (new_context)
1136             {
1137                 if (!_requestListeners.isEmpty())
1138                 {
1139                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
1140                     for (int i=_requestListeners.size();i-->0;)
1141                         _requestListeners.get(i).requestDestroyed(sre);
1142                 }
1143
1144                 if (!_requestAttributeListeners.isEmpty())
1145                 {
1146                     for (int i=_requestAttributeListeners.size();i-->0;)
1147                         baseRequest.removeEventListener(_requestAttributeListeners.get(i));
1148                 }
1149             }
1150         }
1151     }
1152
1153     /* ------------------------------------------------------------ */
1154     /*
1155      * Handle a runnable in this context
1156      */
1157     public void handle(Runnable runnable)
1158     {
1159         ClassLoader old_classloader = null;
1160         Thread current_thread = null;
1161         Context old_context = null;
1162         try
1163         {
1164             old_context = __context.get();
1165             __context.set(_scontext);
1166
1167             // Set the classloader
1168             if (_classLoader != null)
1169             {
1170                 current_thread = Thread.currentThread();
1171                 old_classloader = current_thread.getContextClassLoader();
1172                 current_thread.setContextClassLoader(_classLoader);
1173             }
1174
1175             runnable.run();
1176         }
1177         finally
1178         {
1179             __context.set(old_context);
1180             if (old_classloader != null && current_thread!=null)
1181             {
1182                 current_thread.setContextClassLoader(old_classloader);
1183             }
1184         }
1185     }
1186
1187     /* ------------------------------------------------------------ */
1188     /**
1189      * Check the target. Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a target within a context is determined. If
1190      * the target is protected, 404 is returned.
1191      */
1192     /* ------------------------------------------------------------ */
1193     public boolean isProtectedTarget(String target)
1194     {
1195         if (target == null || _protectedTargets == null)
1196             return false;
1197
1198         while (target.startsWith("//"))
1199             target=URIUtil.compactPath(target);
1200
1201         for (int i=0; i<_protectedTargets.length; i++)
1202         {
1203             String t=_protectedTargets[i];
1204             if (StringUtil.startsWithIgnoreCase(target,t))
1205             {
1206                 if (target.length()==t.length())
1207                     return true;
1208                 
1209                 // Check that the target prefix really is a path segment, thus
1210                 // it can end with /, a query, a target or a parameter
1211                 char c=target.charAt(t.length());
1212                 if (c=='/'||c=='?'||c=='#'||c==';')
1213                     return true;
1214             }
1215         }
1216         return false;
1217     }
1218
1219
1220     /* ------------------------------------------------------------ */
1221     /**
1222      * @param targets Array of URL prefix. Each prefix is in the form /path and will match
1223      * either /path exactly or /path/anything
1224      */
1225     public void setProtectedTargets (String[] targets)
1226     {
1227         if (targets == null)
1228         {
1229             _protectedTargets = null;
1230             return;
1231         }
1232         
1233         _protectedTargets = Arrays.copyOf(targets, targets.length);
1234     }
1235
1236     /* ------------------------------------------------------------ */
1237     public String[] getProtectedTargets()
1238     {
1239         if (_protectedTargets == null)
1240             return null;
1241
1242         return Arrays.copyOf(_protectedTargets, _protectedTargets.length);
1243     }
1244
1245
1246     /* ------------------------------------------------------------ */
1247     /*
1248      * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
1249      */
1250     @Override
1251     public void removeAttribute(String name)
1252     {
1253         checkManagedAttribute(name,null);
1254         _attributes.removeAttribute(name);
1255     }
1256
1257     /* ------------------------------------------------------------ */
1258     /*
1259      * Set a context attribute. Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. Their lifecycle spans the stop/start of
1260      * a context. No attribute listener events are triggered by this API.
1261      *
1262      * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1263      */
1264     @Override
1265     public void setAttribute( String name, Object value)
1266     {
1267         checkManagedAttribute(name,value);
1268         _attributes.setAttribute(name,value);
1269     }
1270
1271     /* ------------------------------------------------------------ */
1272     /**
1273      * @param attributes
1274      *            The attributes to set.
1275      */
1276     public void setAttributes(Attributes attributes)
1277     {
1278         _attributes.clearAttributes();
1279         _attributes.addAll(attributes);
1280         Enumeration<String> e = _attributes.getAttributeNames();
1281         while (e.hasMoreElements())
1282         {
1283             String name = e.nextElement();
1284             checkManagedAttribute(name,attributes.getAttribute(name));
1285         }
1286     }
1287
1288     /* ------------------------------------------------------------ */
1289     @Override
1290     public void clearAttributes()
1291     {
1292         Enumeration<String> e = _attributes.getAttributeNames();
1293         while (e.hasMoreElements())
1294         {
1295             String name = e.nextElement();
1296             checkManagedAttribute(name,null);
1297         }
1298         _attributes.clearAttributes();
1299     }
1300
1301     /* ------------------------------------------------------------ */
1302     public void checkManagedAttribute(String name, Object value)
1303     {
1304         if (_managedAttributes != null && _managedAttributes.containsKey(name))
1305         {
1306             setManagedAttribute(name,value);
1307         }
1308     }
1309
1310     /* ------------------------------------------------------------ */
1311     public void setManagedAttribute(String name, Object value)
1312     {
1313         Object old = _managedAttributes.put(name,value);
1314         updateBean(old,value);
1315     }
1316
1317     /* ------------------------------------------------------------ */
1318     /**
1319      * @param classLoader
1320      *            The classLoader to set.
1321      */
1322     public void setClassLoader(ClassLoader classLoader)
1323     {
1324         _classLoader = classLoader;
1325     }
1326
1327     /* ------------------------------------------------------------ */
1328     /**
1329      * @param contextPath
1330      *            The _contextPath to set.
1331      */
1332     public void setContextPath(String contextPath)
1333     {
1334         if (contextPath == null)
1335             throw new IllegalArgumentException("null contextPath");
1336
1337         if (contextPath.endsWith("/*"))
1338         {
1339             LOG.warn(this+" contextPath ends with /*");
1340             contextPath=contextPath.substring(0,contextPath.length()-2);
1341         }
1342         else if (contextPath.length()>1 && contextPath.endsWith("/"))
1343         {
1344             LOG.warn(this+" contextPath ends with /");
1345             contextPath=contextPath.substring(0,contextPath.length()-1);
1346         }
1347
1348         if (contextPath.length()==0)
1349         {
1350             LOG.warn("Empty contextPath");
1351             contextPath="/";
1352         }
1353
1354         _contextPath = contextPath;
1355
1356         if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
1357         {
1358             Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
1359             for (int h = 0; contextCollections != null && h < contextCollections.length; h++)
1360                 ((ContextHandlerCollection)contextCollections[h]).mapContexts();
1361         }
1362     }
1363
1364     /* ------------------------------------------------------------ */
1365     /**
1366      * @param servletContextName
1367      *            The servletContextName to set.
1368      */
1369     public void setDisplayName(String servletContextName)
1370     {
1371         _displayName = servletContextName;
1372     }
1373
1374     /* ------------------------------------------------------------ */
1375     /**
1376      * @return Returns the resourceBase.
1377      */
1378     public Resource getBaseResource()
1379     {
1380         if (_baseResource == null)
1381             return null;
1382         return _baseResource;
1383     }
1384
1385     /* ------------------------------------------------------------ */
1386     /**
1387      * @return Returns the base resource as a string.
1388      */
1389     @ManagedAttribute("document root for context")
1390     public String getResourceBase()
1391     {
1392         if (_baseResource == null)
1393             return null;
1394         return _baseResource.toString();
1395     }
1396
1397     /* ------------------------------------------------------------ */
1398     /**
1399      * Set the base resource for this context.
1400      * @param base The resource used as the base for all static content of this context.
1401      * @see #setResourceBase(String)
1402      */
1403     public void setBaseResource(Resource base)
1404     {
1405         _baseResource = base;
1406     }
1407
1408     /* ------------------------------------------------------------ */
1409     /** 
1410      * Set the base resource for this context.
1411      * @param resourceBase A string representing the base resource for the context. Any string accepted 
1412      * by {@link Resource#newResource(String)} may be passed and the call is equivalent to 
1413      * <code>setBaseResource(newResource(resourceBase));</code>
1414      */
1415     public void setResourceBase(String resourceBase)
1416     {
1417         try
1418         {
1419             setBaseResource(newResource(resourceBase));
1420         }
1421         catch (Exception e)
1422         {
1423             LOG.warn(e.toString());
1424             LOG.debug(e);
1425             throw new IllegalArgumentException(resourceBase);
1426         }
1427     }
1428
1429     /* ------------------------------------------------------------ */
1430     /**
1431      * @return Returns the mimeTypes.
1432      */
1433     public MimeTypes getMimeTypes()
1434     {
1435         if (_mimeTypes == null)
1436             _mimeTypes = new MimeTypes();
1437         return _mimeTypes;
1438     }
1439
1440     /* ------------------------------------------------------------ */
1441     /**
1442      * @param mimeTypes
1443      *            The mimeTypes to set.
1444      */
1445     public void setMimeTypes(MimeTypes mimeTypes)
1446     {
1447         _mimeTypes = mimeTypes;
1448     }
1449
1450     /* ------------------------------------------------------------ */
1451     /**
1452      */
1453     public void setWelcomeFiles(String[] files)
1454     {
1455         _welcomeFiles = files;
1456     }
1457
1458     /* ------------------------------------------------------------ */
1459     /**
1460      * @return The names of the files which the server should consider to be welcome files in this context.
1461      * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
1462      * @see #setWelcomeFiles
1463      */
1464     @ManagedAttribute(value="Partial URIs of directory welcome files", readonly=true)
1465     public String[] getWelcomeFiles()
1466     {
1467         return _welcomeFiles;
1468     }
1469
1470     /* ------------------------------------------------------------ */
1471     /**
1472      * @return Returns the errorHandler.
1473      */
1474     @ManagedAttribute("The error handler to use for the context")
1475     public ErrorHandler getErrorHandler()
1476     {
1477         return _errorHandler;
1478     }
1479
1480     /* ------------------------------------------------------------ */
1481     /**
1482      * @param errorHandler
1483      *            The errorHandler to set.
1484      */
1485     public void setErrorHandler(ErrorHandler errorHandler)
1486     {
1487         if (errorHandler != null)
1488             errorHandler.setServer(getServer());
1489         updateBean(_errorHandler,errorHandler);
1490         _errorHandler = errorHandler;
1491     }
1492
1493     /* ------------------------------------------------------------ */
1494     @ManagedAttribute("The maximum content size")
1495     public int getMaxFormContentSize()
1496     {
1497         return _maxFormContentSize;
1498     }
1499
1500     /* ------------------------------------------------------------ */
1501     /**
1502      * Set the maximum size of a form post, to protect against DOS attacks from large forms.
1503      * @param maxSize
1504      */
1505     public void setMaxFormContentSize(int maxSize)
1506     {
1507         _maxFormContentSize = maxSize;
1508     }
1509
1510     /* ------------------------------------------------------------ */
1511     public int getMaxFormKeys()
1512     {
1513         return _maxFormKeys;
1514     }
1515
1516     /* ------------------------------------------------------------ */
1517     /**
1518      * Set the maximum number of form Keys to protect against DOS attack from crafted hash keys.
1519      * @param max
1520      */
1521     public void setMaxFormKeys(int max)
1522     {
1523         _maxFormKeys = max;
1524     }
1525
1526     /* ------------------------------------------------------------ */
1527     /**
1528      * @return True if URLs are compacted to replace multiple '/'s with a single '/'
1529      */
1530     public boolean isCompactPath()
1531     {
1532         return _compactPath;
1533     }
1534
1535     /* ------------------------------------------------------------ */
1536     /**
1537      * @param compactPath
1538      *            True if URLs are compacted to replace multiple '/'s with a single '/'
1539      */
1540     public void setCompactPath(boolean compactPath)
1541     {
1542         _compactPath = compactPath;
1543     }
1544
1545     /* ------------------------------------------------------------ */
1546     @Override
1547     public String toString()
1548     {
1549         String[] vhosts = getVirtualHosts();
1550
1551         StringBuilder b = new StringBuilder();
1552
1553         Package pkg = getClass().getPackage();
1554         if (pkg != null)
1555         {
1556             String p = pkg.getName();
1557             if (p != null && p.length() > 0)
1558             {
1559                 String[] ss = p.split("\\.");
1560                 for (String s : ss)
1561                     b.append(s.charAt(0)).append('.');
1562             }
1563         }
1564         b.append(getClass().getSimpleName()).append('@').append(Integer.toString(hashCode(),16));
1565         b.append('{').append(getContextPath()).append(',').append(getBaseResource()).append(',').append(_availability);
1566
1567         if (vhosts != null && vhosts.length > 0)
1568             b.append(',').append(vhosts[0]);
1569         b.append('}');
1570
1571         return b.toString();
1572     }
1573
1574     /* ------------------------------------------------------------ */
1575     public synchronized Class<?> loadClass(String className) throws ClassNotFoundException
1576     {
1577         if (className == null)
1578             return null;
1579
1580         if (_classLoader == null)
1581             return Loader.loadClass(this.getClass(),className);
1582
1583         return _classLoader.loadClass(className);
1584     }
1585
1586     /* ------------------------------------------------------------ */
1587     public void addLocaleEncoding(String locale, String encoding)
1588     {
1589         if (_localeEncodingMap == null)
1590             _localeEncodingMap = new HashMap<String, String>();
1591         _localeEncodingMap.put(locale,encoding);
1592     }
1593
1594     /* ------------------------------------------------------------ */
1595     public String getLocaleEncoding(String locale)
1596     {
1597         if (_localeEncodingMap == null)
1598             return null;
1599         String encoding = _localeEncodingMap.get(locale);
1600         return encoding;
1601     }
1602
1603     /* ------------------------------------------------------------ */
1604     /**
1605      * 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
1606      * language is looked up.
1607      *
1608      * @param locale
1609      *            a <code>Locale</code> value
1610      * @return a <code>String</code> representing the character encoding for the locale or null if none found.
1611      */
1612     public String getLocaleEncoding(Locale locale)
1613     {
1614         if (_localeEncodingMap == null)
1615             return null;
1616         String encoding = _localeEncodingMap.get(locale.toString());
1617         if (encoding == null)
1618             encoding = _localeEncodingMap.get(locale.getLanguage());
1619         return encoding;
1620     }
1621     
1622     /* ------------------------------------------------------------ */
1623     /** 
1624      * Get all of the locale encodings
1625      * 
1626      * @return a map of all the locale encodings: key is name of the locale and value is the char encoding
1627      */
1628     public Map<String,String> getLocaleEncodings()
1629     {
1630         if (_localeEncodingMap == null)
1631             return null;
1632         return Collections.unmodifiableMap(_localeEncodingMap);
1633     }
1634
1635     /* ------------------------------------------------------------ */
1636     /*
1637      */
1638     public Resource getResource(String path) throws MalformedURLException
1639     {
1640         if (path == null || !path.startsWith(URIUtil.SLASH))
1641             throw new MalformedURLException(path);
1642
1643         if (_baseResource == null)
1644             return null;
1645
1646         try
1647         {
1648             path = URIUtil.canonicalPath(path);
1649             Resource resource = _baseResource.addPath(path);
1650             
1651             if (checkAlias(path,resource))
1652                 return resource;
1653             return null;
1654         }
1655         catch (Exception e)
1656         {
1657             LOG.ignore(e);
1658         }
1659
1660         return null;
1661     }
1662
1663     /* ------------------------------------------------------------ */
1664     /**
1665      * @param path
1666      * @param resource
1667      * @return True if the alias is OK
1668      */
1669     public boolean checkAlias(String path, Resource resource)
1670     {
1671         // Is the resource aliased?
1672         if (resource.getAlias() != null)
1673         {
1674             if (LOG.isDebugEnabled())
1675                 LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
1676
1677             // alias checks
1678             for (Iterator<AliasCheck> i=_aliasChecks.iterator();i.hasNext();)
1679             {
1680                 AliasCheck check = i.next();
1681                 if (check.check(path,resource))
1682                 {
1683                     if (LOG.isDebugEnabled())
1684                         LOG.debug("Aliased resource: " + resource + " approved by " + check);
1685                     return true;
1686                 }
1687             }
1688             return false;
1689         }
1690         return true;
1691     }
1692     
1693     /* ------------------------------------------------------------ */
1694     /**
1695      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
1696      */
1697     public Resource newResource(URL url) throws IOException
1698     {
1699         return Resource.newResource(url);
1700     }
1701     
1702     /* ------------------------------------------------------------ */
1703     /**
1704      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
1705      */
1706     public Resource newResource(URI uri) throws IOException
1707     {
1708         return Resource.newResource(uri);
1709     }
1710
1711     /* ------------------------------------------------------------ */
1712     /**
1713      * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
1714      *
1715      * @param urlOrPath
1716      *            The URL or path to convert
1717      * @return The Resource for the URL/path
1718      * @throws IOException
1719      *             The Resource could not be created.
1720      */
1721     public Resource newResource(String urlOrPath) throws IOException
1722     {
1723         return Resource.newResource(urlOrPath);
1724     }
1725
1726     /* ------------------------------------------------------------ */
1727     /*
1728      */
1729     public Set<String> getResourcePaths(String path)
1730     {
1731         try
1732         {
1733             path = URIUtil.canonicalPath(path);
1734             Resource resource = getResource(path);
1735
1736             if (resource != null && resource.exists())
1737             {
1738                 if (!path.endsWith(URIUtil.SLASH))
1739                     path = path + URIUtil.SLASH;
1740
1741                 String[] l = resource.list();
1742                 if (l != null)
1743                 {
1744                     HashSet<String> set = new HashSet<String>();
1745                     for (int i = 0; i < l.length; i++)
1746                         set.add(path + l[i]);
1747                     return set;
1748                 }
1749             }
1750         }
1751         catch (Exception e)
1752         {
1753             LOG.ignore(e);
1754         }
1755         return Collections.emptySet();
1756     }
1757
1758     /* ------------------------------------------------------------ */
1759     private String normalizeHostname(String host)
1760     {
1761         if (host == null)
1762             return null;
1763
1764         if (host.endsWith("."))
1765             return host.substring(0,host.length() - 1);
1766
1767         return host;
1768     }
1769
1770     /* ------------------------------------------------------------ */
1771     /**
1772      * Add an AliasCheck instance to possibly permit aliased resources
1773      * @param check The alias checker
1774      */
1775     public void addAliasCheck(AliasCheck check)
1776     {
1777         _aliasChecks.add(check);
1778     }
1779
1780     /* ------------------------------------------------------------ */
1781     /**
1782      * @return Mutable list of Alias checks
1783      */
1784     public List<AliasCheck> getAliasChecks()
1785     {
1786         return _aliasChecks;
1787     }
1788     
1789     /* ------------------------------------------------------------ */
1790     /**
1791      * @param checks list of AliasCheck instances
1792      */
1793     public void setAliasChecks(List<AliasCheck> checks)
1794     {
1795         _aliasChecks.clear();
1796         _aliasChecks.addAll(checks);
1797     }
1798
1799     /* ------------------------------------------------------------ */
1800     /**
1801      * Context.
1802      * <p>
1803      * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
1804      * </p>
1805      *
1806      *
1807      */
1808     public class Context extends NoContext
1809     {
1810         protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
1811         protected boolean _extendedListenerTypes = false;
1812
1813
1814         /* ------------------------------------------------------------ */
1815         protected Context()
1816         {
1817         }
1818
1819         /* ------------------------------------------------------------ */
1820         public ContextHandler getContextHandler()
1821         {
1822             return ContextHandler.this;
1823         }
1824
1825         /* ------------------------------------------------------------ */
1826         /*
1827          * @see javax.servlet.ServletContext#getContext(java.lang.String)
1828          */
1829         @Override
1830         public ServletContext getContext(String uripath)
1831         {
1832             List<ContextHandler> contexts = new ArrayList<ContextHandler>();
1833             Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
1834             String matched_path = null;
1835
1836             for (Handler handler : handlers)
1837             {
1838                 if (handler == null)
1839                     continue;
1840                 ContextHandler ch = (ContextHandler)handler;
1841                 String context_path = ch.getContextPath();
1842
1843                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1844                         || "/".equals(context_path))
1845                 {
1846                     // look first for vhost matching context only
1847                     if (getVirtualHosts() != null && getVirtualHosts().length > 0)
1848                     {
1849                         if (ch.getVirtualHosts() != null && ch.getVirtualHosts().length > 0)
1850                         {
1851                             for (String h1 : getVirtualHosts())
1852                                 for (String h2 : ch.getVirtualHosts())
1853                                     if (h1.equals(h2))
1854                                     {
1855                                         if (matched_path == null || context_path.length() > matched_path.length())
1856                                         {
1857                                             contexts.clear();
1858                                             matched_path = context_path;
1859                                         }
1860
1861                                         if (matched_path.equals(context_path))
1862                                             contexts.add(ch);
1863                                     }
1864                         }
1865                     }
1866                     else
1867                     {
1868                         if (matched_path == null || context_path.length() > matched_path.length())
1869                         {
1870                             contexts.clear();
1871                             matched_path = context_path;
1872                         }
1873
1874                         if (matched_path.equals(context_path))
1875                             contexts.add(ch);
1876                     }
1877                 }
1878             }
1879
1880             if (contexts.size() > 0)
1881                 return contexts.get(0)._scontext;
1882
1883             // try again ignoring virtual hosts
1884             matched_path = null;
1885             for (Handler handler : handlers)
1886             {
1887                 if (handler == null)
1888                     continue;
1889                 ContextHandler ch = (ContextHandler)handler;
1890                 String context_path = ch.getContextPath();
1891
1892                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1893                         || "/".equals(context_path))
1894                 {
1895                     if (matched_path == null || context_path.length() > matched_path.length())
1896                     {
1897                         contexts.clear();
1898                         matched_path = context_path;
1899                     }
1900
1901                     if (matched_path != null && matched_path.equals(context_path))
1902                         contexts.add(ch);
1903                 }
1904             }
1905
1906             if (contexts.size() > 0)
1907                 return contexts.get(0)._scontext;
1908             return null;
1909         }
1910
1911         /* ------------------------------------------------------------ */
1912         /*
1913          * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
1914          */
1915         @Override
1916         public String getMimeType(String file)
1917         {
1918             if (_mimeTypes == null)
1919                 return null;
1920             return _mimeTypes.getMimeByExtension(file);
1921         }
1922
1923         /* ------------------------------------------------------------ */
1924         /*
1925          * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
1926          */
1927         @Override
1928         public RequestDispatcher getRequestDispatcher(String uriInContext)
1929         {
1930             if (uriInContext == null)
1931                 return null;
1932
1933             if (!uriInContext.startsWith("/"))
1934                 return null;
1935
1936             try
1937             {
1938                 String query = null;
1939                 int q = 0;
1940                 if ((q = uriInContext.indexOf('?')) > 0)
1941                 {
1942                     query = uriInContext.substring(q + 1);
1943                     uriInContext = uriInContext.substring(0,q);
1944                 }
1945
1946                 String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
1947                 if (pathInContext!=null)
1948                 {
1949                     String uri = URIUtil.addPaths(getContextPath(),uriInContext);
1950                     ContextHandler context = ContextHandler.this;
1951                     return new Dispatcher(context,uri,pathInContext,query);
1952                 }
1953             }
1954             catch (Exception e)
1955             {
1956                 LOG.ignore(e);
1957             }
1958             return null;
1959         }
1960
1961         /* ------------------------------------------------------------ */
1962         /*
1963          * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
1964          */
1965         @Override
1966         public String getRealPath(String path)
1967         {
1968             if (path == null)
1969                 return null;
1970             if (path.length() == 0)
1971                 path = URIUtil.SLASH;
1972             else if (path.charAt(0) != '/')
1973                 path = URIUtil.SLASH + path;
1974
1975             try
1976             {
1977                 Resource resource = ContextHandler.this.getResource(path);
1978                 if (resource != null)
1979                 {
1980                     File file = resource.getFile();
1981                     if (file != null)
1982                         return file.getCanonicalPath();
1983                 }
1984             }
1985             catch (Exception e)
1986             {
1987                 LOG.ignore(e);
1988             }
1989
1990             return null;
1991         }
1992
1993         /* ------------------------------------------------------------ */
1994         @Override
1995         public URL getResource(String path) throws MalformedURLException
1996         {
1997             Resource resource = ContextHandler.this.getResource(path);
1998             if (resource != null && resource.exists())
1999                 return resource.getURL();
2000             return null;
2001         }
2002
2003         /* ------------------------------------------------------------ */
2004         /*
2005          * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
2006          */
2007         @Override
2008         public InputStream getResourceAsStream(String path)
2009         {
2010             try
2011             {
2012                 URL url = getResource(path);
2013                 if (url == null)
2014                     return null;
2015                 Resource r = Resource.newResource(url);
2016                 return r.getInputStream();
2017             }
2018             catch (Exception e)
2019             {
2020                 LOG.ignore(e);
2021                 return null;
2022             }
2023         }
2024
2025         /* ------------------------------------------------------------ */
2026         /*
2027          * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
2028          */
2029         @Override
2030         public Set<String> getResourcePaths(String path)
2031         {
2032             return ContextHandler.this.getResourcePaths(path);
2033         }
2034
2035         /* ------------------------------------------------------------ */
2036         /*
2037          * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
2038          */
2039         @Override
2040         public void log(Exception exception, String msg)
2041         {
2042             _logger.warn(msg,exception);
2043         }
2044
2045         /* ------------------------------------------------------------ */
2046         /*
2047          * @see javax.servlet.ServletContext#log(java.lang.String)
2048          */
2049         @Override
2050         public void log(String msg)
2051         {
2052             _logger.info(msg);
2053         }
2054
2055         /* ------------------------------------------------------------ */
2056         /*
2057          * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
2058          */
2059         @Override
2060         public void log(String message, Throwable throwable)
2061         {
2062             _logger.warn(message,throwable);
2063         }
2064
2065         /* ------------------------------------------------------------ */
2066         /*
2067          * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
2068          */
2069         @Override
2070         public String getInitParameter(String name)
2071         {
2072             return ContextHandler.this.getInitParameter(name);
2073         }
2074
2075         /* ------------------------------------------------------------ */
2076         /*
2077          * @see javax.servlet.ServletContext#getInitParameterNames()
2078          */
2079         @Override
2080         public Enumeration<String> getInitParameterNames()
2081         {
2082             return ContextHandler.this.getInitParameterNames();
2083         }
2084
2085         /* ------------------------------------------------------------ */
2086         /*
2087          * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
2088          */
2089         @Override
2090         public synchronized Object getAttribute(String name)
2091         {
2092             Object o = ContextHandler.this.getAttribute(name);
2093             if (o == null)
2094                 o = super.getAttribute(name);
2095             return o;
2096         }
2097
2098         /* ------------------------------------------------------------ */
2099         /*
2100          * @see javax.servlet.ServletContext#getAttributeNames()
2101          */
2102         @Override
2103         public synchronized Enumeration<String> getAttributeNames()
2104         {
2105             HashSet<String> set = new HashSet<String>();
2106             Enumeration<String> e = super.getAttributeNames();
2107             while (e.hasMoreElements())
2108                 set.add(e.nextElement());
2109             e = _attributes.getAttributeNames();
2110             while (e.hasMoreElements())
2111                 set.add(e.nextElement());
2112
2113             return Collections.enumeration(set);
2114         }
2115
2116         /* ------------------------------------------------------------ */
2117         /*
2118          * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
2119          */
2120         @Override
2121         public synchronized void setAttribute(String name, Object value)
2122         {
2123             checkManagedAttribute(name,value);
2124             Object old_value = super.getAttribute(name);
2125
2126             if (value == null)
2127                 super.removeAttribute(name);
2128             else
2129                 super.setAttribute(name,value);
2130
2131             if (!_contextAttributeListeners.isEmpty())
2132             {
2133                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
2134
2135                 for (ServletContextAttributeListener l : _contextAttributeListeners)
2136                 {
2137                     if (old_value == null)
2138                         l.attributeAdded(event);
2139                     else if (value == null)
2140                         l.attributeRemoved(event);
2141                     else
2142                         l.attributeReplaced(event);
2143                 }
2144             }
2145         }
2146
2147         /* ------------------------------------------------------------ */
2148         /*
2149          * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
2150          */
2151         @Override
2152         public synchronized void removeAttribute(String name)
2153         {
2154             checkManagedAttribute(name,null);
2155
2156             Object old_value = super.getAttribute(name);
2157             super.removeAttribute(name);
2158             if (old_value != null &&!_contextAttributeListeners.isEmpty())
2159             {
2160                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
2161
2162                 for (ServletContextAttributeListener l : _contextAttributeListeners)
2163                     l.attributeRemoved(event);
2164             }
2165         }
2166
2167         /* ------------------------------------------------------------ */
2168         /*
2169          * @see javax.servlet.ServletContext#getServletContextName()
2170          */
2171         @Override
2172         public String getServletContextName()
2173         {
2174             String name = ContextHandler.this.getDisplayName();
2175             if (name == null)
2176                 name = ContextHandler.this.getContextPath();
2177             return name;
2178         }
2179
2180         /* ------------------------------------------------------------ */
2181         @Override
2182         public String getContextPath()
2183         {
2184             if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
2185                 return "";
2186
2187             return _contextPath;
2188         }
2189
2190         /* ------------------------------------------------------------ */
2191         @Override
2192         public String toString()
2193         {
2194             return "ServletContext@" + ContextHandler.this.toString();
2195         }
2196
2197         /* ------------------------------------------------------------ */
2198         @Override
2199         public boolean setInitParameter(String name, String value)
2200         {
2201             if (ContextHandler.this.getInitParameter(name) != null)
2202                 return false;
2203             ContextHandler.this.getInitParams().put(name,value);
2204             return true;
2205         }
2206
2207         @Override
2208         public void addListener(String className)
2209         {
2210             if (!_enabled)
2211                 throw new UnsupportedOperationException();
2212
2213             try
2214             {
2215                 @SuppressWarnings("unchecked")
2216                 Class<? extends EventListener> clazz = (Class<? extends EventListener>) (_classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className));
2217                 addListener(clazz);
2218             }
2219             catch (ClassNotFoundException e)
2220             {
2221                 throw new IllegalArgumentException(e);
2222             }
2223         }
2224
2225         @Override
2226         public <T extends EventListener> void addListener(T t)
2227         {
2228             if (!_enabled)
2229                 throw new UnsupportedOperationException();
2230
2231             checkListener(t.getClass());
2232
2233             ContextHandler.this.addEventListener(t);
2234             ContextHandler.this.addProgrammaticListener(t);
2235         }
2236
2237         @Override
2238         public void addListener(Class<? extends EventListener> listenerClass)
2239         {
2240             if (!_enabled)
2241                 throw new UnsupportedOperationException();
2242
2243             try
2244             {
2245                 EventListener e = createListener(listenerClass);
2246                 addListener(e);
2247             }
2248             catch (ServletException e)
2249             {
2250                 throw new IllegalArgumentException(e);
2251             }
2252         }
2253
2254         @Override
2255         public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
2256         {
2257             try
2258             {
2259                 return createInstance(clazz);
2260             }
2261             catch (Exception e)
2262             {
2263                 throw new ServletException(e);
2264             }
2265         }
2266
2267
2268         public void checkListener (Class<? extends EventListener> listener) throws IllegalStateException
2269         {
2270             boolean ok = false;
2271             int startIndex = (isExtendedListenerTypes()?EXTENDED_LISTENER_TYPE_INDEX:DEFAULT_LISTENER_TYPE_INDEX);
2272             for (int i=startIndex;i<SERVLET_LISTENER_TYPES.length;i++)
2273             {
2274                 if (SERVLET_LISTENER_TYPES[i].isAssignableFrom(listener))
2275                 {
2276                     ok = true;
2277                     break;
2278                 }
2279             }
2280             if (!ok)
2281                 throw new IllegalArgumentException("Inappropriate listener class "+listener.getName());
2282         }
2283
2284         public void setExtendedListenerTypes (boolean extended)
2285         {
2286            _extendedListenerTypes = extended;
2287         }
2288
2289        public boolean isExtendedListenerTypes()
2290        {
2291            return _extendedListenerTypes;
2292        }
2293
2294
2295        @Override
2296        public ClassLoader getClassLoader()
2297        {
2298            if (!_enabled)
2299                throw new UnsupportedOperationException();
2300            
2301            //no security manager just return the classloader
2302            if (System.getSecurityManager() == null)
2303                return _classLoader;
2304            else
2305            {
2306                //check to see if the classloader of the caller is the same as the context
2307                //classloader, or a parent of it
2308                try
2309                {
2310                    Class<?> reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
2311                    Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
2312                    Class<?> caller = (Class<?>)getCallerClass.invoke(null, 2);
2313
2314                    boolean ok = false;
2315                    ClassLoader callerLoader = caller.getClassLoader();
2316                    while (!ok && callerLoader != null)
2317                    {
2318                        if (callerLoader == _classLoader) 
2319                            ok = true;
2320                        else
2321                            callerLoader = callerLoader.getParent();    
2322                    }
2323
2324                    if (ok)
2325                        return _classLoader;
2326                }
2327                catch (Exception e)      
2328                {
2329                    LOG.warn("Unable to check classloader of caller",e);
2330                }
2331               
2332                AccessController.checkPermission(new RuntimePermission("getClassLoader"));
2333                return _classLoader;
2334            }
2335         }
2336
2337         @Override
2338         public JspConfigDescriptor getJspConfigDescriptor()
2339         {
2340             LOG.warn(__unimplmented);
2341             return null;
2342         }
2343
2344         public void setJspConfigDescriptor(JspConfigDescriptor d)
2345         {
2346
2347         }
2348
2349         @Override
2350         public void declareRoles(String... roleNames)
2351         {
2352             if (!isStarting())
2353                 throw new IllegalStateException ();
2354             if (!_enabled)
2355                 throw new UnsupportedOperationException();
2356         }
2357
2358         public void setEnabled(boolean enabled)
2359         {
2360             _enabled = enabled;
2361         }
2362
2363         public boolean isEnabled()
2364         {
2365             return _enabled;
2366         }
2367
2368
2369
2370         public <T> T createInstance (Class<T> clazz) throws Exception
2371         {
2372             T o = clazz.newInstance();
2373             return o;
2374         }
2375     }
2376
2377
2378     public static class NoContext extends AttributesMap implements ServletContext
2379     {
2380         private int _effectiveMajorVersion = SERVLET_MAJOR_VERSION;
2381         private int _effectiveMinorVersion = SERVLET_MINOR_VERSION;
2382
2383         /* ------------------------------------------------------------ */
2384         public NoContext()
2385         {
2386         }
2387
2388         @Override
2389         public ServletContext getContext(String uripath)
2390         {
2391             return null;
2392         }
2393
2394         @Override
2395         public int getMajorVersion()
2396         {
2397             return SERVLET_MAJOR_VERSION;
2398         }
2399
2400         @Override
2401         public String getMimeType(String file)
2402         {
2403             return null;
2404         }
2405
2406         @Override
2407         public int getMinorVersion()
2408         {
2409             return SERVLET_MINOR_VERSION;
2410         }
2411
2412         @Override
2413         public RequestDispatcher getNamedDispatcher(String name)
2414         {
2415             return null;
2416         }
2417
2418         @Override
2419         public RequestDispatcher getRequestDispatcher(String uriInContext)
2420         {
2421             return null;
2422         }
2423
2424         @Override
2425         public String getRealPath(String path)
2426         {
2427             return null;
2428         }
2429
2430         @Override
2431         public URL getResource(String path) throws MalformedURLException
2432         {
2433             return null;
2434         }
2435
2436         @Override
2437         public InputStream getResourceAsStream(String path)
2438         {
2439             return null;
2440         }
2441
2442         @Override
2443         public Set<String> getResourcePaths(String path)
2444         {
2445             return null;
2446         }
2447
2448         @Override
2449         public String getServerInfo()
2450         {
2451             // NOTE: DO NOT CHANGE
2452             //   this is used by weld to detect Jetty
2453             //   implementation version
2454             // See: https://github.com/weld/core/blob/master/environments/servlet/core/src/main/java/org/jboss/weld/environment/jetty/JettyContainer.java
2455             //   and its touch(ContainerContext) method
2456             return "jetty/" + Server.getVersion();
2457         }
2458
2459         @Override
2460         @Deprecated
2461         public Servlet getServlet(String name) throws ServletException
2462         {
2463             return null;
2464         }
2465
2466         @SuppressWarnings("unchecked")
2467         @Override
2468         @Deprecated
2469         public Enumeration<String> getServletNames()
2470         {
2471             return Collections.enumeration(Collections.EMPTY_LIST);
2472         }
2473
2474         @SuppressWarnings("unchecked")
2475         @Override
2476         @Deprecated
2477         public Enumeration<Servlet> getServlets()
2478         {
2479             return Collections.enumeration(Collections.EMPTY_LIST);
2480         }
2481
2482         @Override
2483         public void log(Exception exception, String msg)
2484         {
2485             LOG.warn(msg,exception);
2486         }
2487
2488         @Override
2489         public void log(String msg)
2490         {
2491             LOG.info(msg);
2492         }
2493
2494         @Override
2495         public void log(String message, Throwable throwable)
2496         {
2497             LOG.warn(message,throwable);
2498         }
2499
2500         @Override
2501         public String getInitParameter(String name)
2502         {
2503             return null;
2504         }
2505
2506         @SuppressWarnings("unchecked")
2507         @Override
2508         public Enumeration<String> getInitParameterNames()
2509         {
2510             return Collections.enumeration(Collections.EMPTY_LIST);
2511         }
2512
2513
2514         @Override
2515         public String getServletContextName()
2516         {
2517             return "No Context";
2518         }
2519
2520         @Override
2521         public String getContextPath()
2522         {
2523             return null;
2524         }
2525
2526
2527         @Override
2528         public boolean setInitParameter(String name, String value)
2529         {
2530             return false;
2531         }
2532
2533         @Override
2534         public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
2535         {
2536             LOG.warn(__unimplmented);
2537             return null;
2538         }
2539
2540         @Override
2541         public Dynamic addFilter(String filterName, Filter filter)
2542         {
2543             LOG.warn(__unimplmented);
2544             return null;
2545         }
2546
2547         @Override
2548         public Dynamic addFilter(String filterName, String className)
2549         {
2550             LOG.warn(__unimplmented);
2551             return null;
2552         }
2553
2554         @Override
2555         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
2556         {
2557             LOG.warn(__unimplmented);
2558             return null;
2559         }
2560
2561         @Override
2562         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
2563         {
2564             LOG.warn(__unimplmented);
2565             return null;
2566         }
2567
2568         @Override
2569         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
2570         {
2571             LOG.warn(__unimplmented);
2572             return null;
2573         }
2574
2575         @Override
2576         public <T extends Filter> T createFilter(Class<T> c) throws ServletException
2577         {
2578             LOG.warn(__unimplmented);
2579             return null;
2580         }
2581
2582         @Override
2583         public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
2584         {
2585             LOG.warn(__unimplmented);
2586             return null;
2587         }
2588
2589         @Override
2590         public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
2591         {
2592             LOG.warn(__unimplmented);
2593             return null;
2594         }
2595
2596         @Override
2597         public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
2598         {
2599             LOG.warn(__unimplmented);
2600             return null;
2601         }
2602
2603         @Override
2604         public FilterRegistration getFilterRegistration(String filterName)
2605         {
2606             LOG.warn(__unimplmented);
2607             return null;
2608         }
2609
2610         @Override
2611         public Map<String, ? extends FilterRegistration> getFilterRegistrations()
2612         {
2613             LOG.warn(__unimplmented);
2614             return null;
2615         }
2616
2617         @Override
2618         public ServletRegistration getServletRegistration(String servletName)
2619         {
2620             LOG.warn(__unimplmented);
2621             return null;
2622         }
2623
2624         @Override
2625         public Map<String, ? extends ServletRegistration> getServletRegistrations()
2626         {
2627             LOG.warn(__unimplmented);
2628             return null;
2629         }
2630
2631         @Override
2632         public SessionCookieConfig getSessionCookieConfig()
2633         {
2634             LOG.warn(__unimplmented);
2635             return null;
2636         }
2637
2638         @Override
2639         public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
2640         {
2641             LOG.warn(__unimplmented);
2642         }
2643
2644         @Override
2645         public void addListener(String className)
2646         {
2647             LOG.warn(__unimplmented);
2648         }
2649
2650         @Override
2651         public <T extends EventListener> void addListener(T t)
2652         {
2653             LOG.warn(__unimplmented);
2654         }
2655
2656         @Override
2657         public void addListener(Class<? extends EventListener> listenerClass)
2658         {
2659             LOG.warn(__unimplmented);
2660         }
2661
2662         @Override
2663         public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
2664         {
2665             try
2666             {
2667                 return clazz.newInstance();
2668             }
2669             catch (InstantiationException e)
2670             {
2671                 throw new ServletException(e);
2672             }
2673             catch (IllegalAccessException e)
2674             {
2675                 throw new ServletException(e);
2676             }
2677         }
2678
2679         @Override
2680         public ClassLoader getClassLoader()
2681         {
2682             AccessController.checkPermission(new RuntimePermission("getClassLoader"));
2683             return ContextHandler.class.getClassLoader();
2684         }
2685
2686         @Override
2687         public int getEffectiveMajorVersion()
2688         {
2689             return _effectiveMajorVersion;
2690         }
2691
2692         @Override
2693         public int getEffectiveMinorVersion()
2694         {
2695             return _effectiveMinorVersion;
2696         }
2697
2698         public void setEffectiveMajorVersion (int v)
2699         {
2700             _effectiveMajorVersion = v;
2701         }
2702
2703         public void setEffectiveMinorVersion (int v)
2704         {
2705             _effectiveMinorVersion = v;
2706         }
2707
2708         @Override
2709         public JspConfigDescriptor getJspConfigDescriptor()
2710         {
2711             LOG.warn(__unimplmented);
2712             return null;
2713         }
2714
2715         @Override
2716         public void declareRoles(String... roleNames)
2717         {
2718             LOG.warn(__unimplmented);
2719         }
2720
2721         /**
2722          * @see javax.servlet.ServletContext#getVirtualServerName()
2723          */
2724         @Override
2725         public String getVirtualServerName()
2726         {
2727             // TODO 3.1 Auto-generated method stub
2728             return null;
2729         }
2730     }
2731
2732
2733     /* ------------------------------------------------------------ */
2734     /** Interface to check aliases
2735      */
2736     public interface AliasCheck
2737     {
2738         /* ------------------------------------------------------------ */
2739         /** Check an alias
2740          * @param path The path the aliased resource was created for
2741          * @param resource The aliased resourced
2742          * @return True if the resource is OK to be served.
2743          */
2744         boolean check(String path, Resource resource);
2745     }
2746
2747
2748     /* ------------------------------------------------------------ */
2749     /** Approve all aliases.
2750      */
2751     public static class ApproveAliases implements AliasCheck
2752     {
2753         @Override
2754         public boolean check(String path, Resource resource)
2755         {
2756             return true;
2757         }
2758     }
2759
2760     /* ------------------------------------------------------------ */
2761     /** Approve Aliases with same suffix.
2762      * Eg. a symbolic link from /foobar.html to /somewhere/wibble.html would be
2763      * approved because both the resource and alias end with ".html".
2764      */
2765     @Deprecated
2766     public static class ApproveSameSuffixAliases implements AliasCheck
2767     {
2768         {
2769             LOG.warn("ApproveSameSuffixAlias is not safe for production");
2770         }
2771         
2772         @Override
2773         public boolean check(String path, Resource resource)
2774         {
2775             int dot = path.lastIndexOf('.');
2776             if (dot<0)
2777                 return false;
2778             String suffix=path.substring(dot);
2779             return resource.toString().endsWith(suffix);
2780         }
2781     }
2782
2783
2784     /* ------------------------------------------------------------ */
2785     /** Approve Aliases with a path prefix.
2786      * Eg. a symbolic link from /dirA/foobar.html to /dirB/foobar.html would be
2787      * approved because both the resource and alias end with "/foobar.html".
2788      */
2789     @Deprecated
2790     public static class ApprovePathPrefixAliases implements AliasCheck
2791     {
2792         {
2793             LOG.warn("ApprovePathPrefixAliases is not safe for production");
2794         }
2795         
2796         @Override
2797         public boolean check(String path, Resource resource)
2798         {
2799             int slash = path.lastIndexOf('/');
2800             if (slash<0 || slash==path.length()-1)
2801                 return false;
2802             String suffix=path.substring(slash);
2803             return resource.toString().endsWith(suffix);
2804         }
2805     }
2806     
2807     /* ------------------------------------------------------------ */
2808     /** Approve Aliases of a non existent directory.
2809      * If a directory "/foobar/" does not exist, then the resource is
2810      * aliased to "/foobar".  Accept such aliases.
2811      */
2812     public static class ApproveNonExistentDirectoryAliases implements AliasCheck
2813     {
2814         @Override
2815         public boolean check(String path, Resource resource)
2816         {
2817             if (resource.exists())
2818                 return false;
2819             
2820             String a=resource.getAlias().toString();
2821             String r=resource.getURL().toString();
2822             
2823             if (a.length()>r.length())
2824                 return a.startsWith(r) && a.length()==r.length()+1 && a.endsWith("/");
2825             if (a.length()<r.length())
2826                 return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
2827             
2828             return a.equals(r); 
2829         }
2830     }
2831
2832 }