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