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