View Javadoc

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