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