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