View Javadoc

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