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  
34  import javax.servlet.RequestDispatcher;
35  import javax.servlet.Servlet;
36  import javax.servlet.ServletContext;
37  import javax.servlet.ServletContextAttributeEvent;
38  import javax.servlet.ServletContextAttributeListener;
39  import javax.servlet.ServletContextEvent;
40  import javax.servlet.ServletContextListener;
41  import javax.servlet.ServletException;
42  import javax.servlet.ServletRequestAttributeListener;
43  import javax.servlet.ServletRequestEvent;
44  import javax.servlet.ServletRequestListener;
45  import javax.servlet.http.HttpServletRequest;
46  import javax.servlet.http.HttpServletResponse;
47  
48  import org.eclipse.jetty.http.HttpException;
49  import org.eclipse.jetty.http.MimeTypes;
50  import org.eclipse.jetty.io.Buffer;
51  import org.eclipse.jetty.server.AbstractHttpConnection;
52  import org.eclipse.jetty.server.Dispatcher;
53  import org.eclipse.jetty.server.DispatcherType;
54  import org.eclipse.jetty.server.Handler;
55  import org.eclipse.jetty.server.HandlerContainer;
56  import org.eclipse.jetty.server.Request;
57  import org.eclipse.jetty.server.Server;
58  import org.eclipse.jetty.util.Attributes;
59  import org.eclipse.jetty.util.AttributesMap;
60  import org.eclipse.jetty.util.LazyList;
61  import org.eclipse.jetty.util.Loader;
62  import org.eclipse.jetty.util.TypeUtil;
63  import org.eclipse.jetty.util.URIUtil;
64  import org.eclipse.jetty.util.component.AggregateLifeCycle;
65  import org.eclipse.jetty.util.component.Dumpable;
66  import org.eclipse.jetty.util.log.Log;
67  import org.eclipse.jetty.util.log.Logger;
68  import org.eclipse.jetty.util.resource.Resource;
69  
70  /* ------------------------------------------------------------ */
71  /**
72   * ContextHandler.
73   * 
74   * This handler wraps a call to handle by setting the context and servlet path, plus setting the context classloader.
75   * 
76   * <p>
77   * 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
78   * context attribute names, which if set as attributes are passed to the servers Container so that they may be managed with JMX.
79   * 
80   * @org.apache.xbean.XBean description="Creates a basic HTTP context"
81   */
82  public class ContextHandler extends ScopedHandler implements Attributes, Server.Graceful
83  {
84      private static final Logger LOG = Log.getLogger(ContextHandler.class);
85  
86      private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
87  
88      /**
89       * 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
90       * 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
91       * for the attribute value.
92       */
93      public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes";
94  
95      /* ------------------------------------------------------------ */
96      /**
97       * Get the current ServletContext implementation.
98       * 
99       * @return ServletContext implementation
100      */
101     public static Context getCurrentContext()
102     {
103         return __context.get();
104     }
105 
106     protected Context _scontext;
107 
108     private final AttributesMap _attributes;
109     private final AttributesMap _contextAttributes;
110     private final Map<String, String> _initParams;
111     private ClassLoader _classLoader;
112     private String _contextPath = "/";
113     private String _displayName;
114     private Resource _baseResource;
115     private MimeTypes _mimeTypes;
116     private Map<String, String> _localeEncodingMap;
117     private String[] _welcomeFiles;
118     private ErrorHandler _errorHandler;
119     private String[] _vhosts;
120     private Set<String> _connectors;
121     private EventListener[] _eventListeners;
122     private Logger _logger;
123     private boolean _allowNullPathInfo;
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     public void setMaxFormContentSize(int maxSize)
1353     {
1354         _maxFormContentSize = maxSize;
1355     }
1356 
1357     /* ------------------------------------------------------------ */
1358     /**
1359      * @return True if URLs are compacted to replace multiple '/'s with a single '/'
1360      */
1361     public boolean isCompactPath()
1362     {
1363         return _compactPath;
1364     }
1365 
1366     /* ------------------------------------------------------------ */
1367     /**
1368      * @param compactPath
1369      *            True if URLs are compacted to replace multiple '/'s with a single '/'
1370      */
1371     public void setCompactPath(boolean compactPath)
1372     {
1373         _compactPath = compactPath;
1374     }
1375 
1376     /* ------------------------------------------------------------ */
1377     @Override
1378     public String toString()
1379     {
1380         String[] vhosts = getVirtualHosts();
1381 
1382         StringBuilder b = new StringBuilder();
1383 
1384         String p = getClass().getPackage().getName();
1385         if (p != null && p.length() > 0)
1386         {
1387             String[] ss = p.split("\\.");
1388             for (String s : ss)
1389                 b.append(s.charAt(0)).append('.');
1390         }
1391 
1392         b.append(getClass().getSimpleName());
1393         b.append('{').append(getContextPath()).append(',').append(getBaseResource());
1394 
1395         if (vhosts != null && vhosts.length > 0)
1396             b.append(',').append(vhosts[0]);
1397         b.append('}');
1398 
1399         return b.toString();
1400     }
1401 
1402     /* ------------------------------------------------------------ */
1403     public synchronized Class<?> loadClass(String className) throws ClassNotFoundException
1404     {
1405         if (className == null)
1406             return null;
1407 
1408         if (_classLoader == null)
1409             return Loader.loadClass(this.getClass(),className);
1410 
1411         return _classLoader.loadClass(className);
1412     }
1413 
1414     /* ------------------------------------------------------------ */
1415     public void addLocaleEncoding(String locale, String encoding)
1416     {
1417         if (_localeEncodingMap == null)
1418             _localeEncodingMap = new HashMap<String, String>();
1419         _localeEncodingMap.put(locale,encoding);
1420     }
1421 
1422     public String getLocaleEncoding(String locale)
1423     {
1424         if (_localeEncodingMap == null)
1425             return null;
1426         String encoding = _localeEncodingMap.get(locale);
1427         return encoding;
1428     }
1429 
1430     /* ------------------------------------------------------------ */
1431     /**
1432      * 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
1433      * language is looked up.
1434      * 
1435      * @param locale
1436      *            a <code>Locale</code> value
1437      * @return a <code>String</code> representing the character encoding for the locale or null if none found.
1438      */
1439     public String getLocaleEncoding(Locale locale)
1440     {
1441         if (_localeEncodingMap == null)
1442             return null;
1443         String encoding = _localeEncodingMap.get(locale.toString());
1444         if (encoding == null)
1445             encoding = _localeEncodingMap.get(locale.getLanguage());
1446         return encoding;
1447     }
1448 
1449     /* ------------------------------------------------------------ */
1450     /*
1451      */
1452     public Resource getResource(String path) throws MalformedURLException
1453     {
1454         if (path == null || !path.startsWith(URIUtil.SLASH))
1455             throw new MalformedURLException(path);
1456 
1457         if (_baseResource == null)
1458             return null;
1459 
1460         try
1461         {
1462             path = URIUtil.canonicalPath(path);
1463             Resource resource = _baseResource.addPath(path);
1464 
1465             if (!_aliases && resource.getAlias() != null)
1466             {
1467                 if (resource.exists())
1468                     LOG.warn("Aliased resource: " + resource + "~=" + resource.getAlias());
1469                 else if (path.endsWith("/") && resource.getAlias().toString().endsWith(path))
1470                     return resource;
1471                 else if (LOG.isDebugEnabled())
1472                     LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
1473                 return null;
1474             }
1475 
1476             return resource;
1477         }
1478         catch (Exception e)
1479         {
1480             LOG.ignore(e);
1481         }
1482 
1483         return null;
1484     }
1485 
1486     /* ------------------------------------------------------------ */
1487     /**
1488      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
1489      */
1490     public Resource newResource(URL url) throws IOException
1491     {
1492         return Resource.newResource(url);
1493     }
1494 
1495     /* ------------------------------------------------------------ */
1496     /**
1497      * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
1498      * 
1499      * @param urlOrPath
1500      *            The URL or path to convert
1501      * @return The Resource for the URL/path
1502      * @throws IOException
1503      *             The Resource could not be created.
1504      */
1505     public Resource newResource(String urlOrPath) throws IOException
1506     {
1507         return Resource.newResource(urlOrPath);
1508     }
1509 
1510     /* ------------------------------------------------------------ */
1511     /*
1512      */
1513     public Set<String> getResourcePaths(String path)
1514     {
1515         try
1516         {
1517             path = URIUtil.canonicalPath(path);
1518             Resource resource = getResource(path);
1519 
1520             if (resource != null && resource.exists())
1521             {
1522                 if (!path.endsWith(URIUtil.SLASH))
1523                     path = path + URIUtil.SLASH;
1524 
1525                 String[] l = resource.list();
1526                 if (l != null)
1527                 {
1528                     HashSet<String> set = new HashSet<String>();
1529                     for (int i = 0; i < l.length; i++)
1530                         set.add(path + l[i]);
1531                     return set;
1532                 }
1533             }
1534         }
1535         catch (Exception e)
1536         {
1537             LOG.ignore(e);
1538         }
1539         return Collections.emptySet();
1540     }
1541 
1542     /* ------------------------------------------------------------ */
1543     private String normalizeHostname(String host)
1544     {
1545         if (host == null)
1546             return null;
1547 
1548         if (host.endsWith("."))
1549             return host.substring(0,host.length() - 1);
1550 
1551         return host;
1552     }
1553 
1554     /* ------------------------------------------------------------ */
1555     /**
1556      * Context.
1557      * <p>
1558      * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
1559      * </p>
1560      * 
1561      * 
1562      */
1563     public class Context implements ServletContext
1564     {
1565         /* ------------------------------------------------------------ */
1566         protected Context()
1567         {
1568         }
1569 
1570         /* ------------------------------------------------------------ */
1571         public ContextHandler getContextHandler()
1572         {
1573             // TODO reduce visibility of this method
1574             return ContextHandler.this;
1575         }
1576 
1577         /* ------------------------------------------------------------ */
1578         /*
1579          * @see javax.servlet.ServletContext#getContext(java.lang.String)
1580          */
1581         public ServletContext getContext(String uripath)
1582         {
1583             List<ContextHandler> contexts = new ArrayList<ContextHandler>();
1584             Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
1585             String matched_path = null;
1586 
1587             for (Handler handler : handlers)
1588             {
1589                 if (handler == null)
1590                     continue;
1591                 ContextHandler ch = (ContextHandler)handler;
1592                 String context_path = ch.getContextPath();
1593 
1594                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1595                         || "/".equals(context_path))
1596                 {
1597                     // look first for vhost matching context only
1598                     if (getVirtualHosts() != null && getVirtualHosts().length > 0)
1599                     {
1600                         if (ch.getVirtualHosts() != null && ch.getVirtualHosts().length > 0)
1601                         {
1602                             for (String h1 : getVirtualHosts())
1603                                 for (String h2 : ch.getVirtualHosts())
1604                                     if (h1.equals(h2))
1605                                     {
1606                                         if (matched_path == null || context_path.length() > matched_path.length())
1607                                         {
1608                                             contexts.clear();
1609                                             matched_path = context_path;
1610                                         }
1611 
1612                                         if (matched_path.equals(context_path))
1613                                             contexts.add(ch);
1614                                     }
1615                         }
1616                     }
1617                     else
1618                     {
1619                         if (matched_path == null || context_path.length() > matched_path.length())
1620                         {
1621                             contexts.clear();
1622                             matched_path = context_path;
1623                         }
1624 
1625                         if (matched_path.equals(context_path))
1626                             contexts.add(ch);
1627                     }
1628                 }
1629             }
1630 
1631             if (contexts.size() > 0)
1632                 return contexts.get(0)._scontext;
1633 
1634             // try again ignoring virtual hosts
1635             matched_path = null;
1636             for (Handler handler : handlers)
1637             {
1638                 if (handler == null)
1639                     continue;
1640                 ContextHandler ch = (ContextHandler)handler;
1641                 String context_path = ch.getContextPath();
1642 
1643                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1644                         || "/".equals(context_path))
1645                 {
1646                     if (matched_path == null || context_path.length() > matched_path.length())
1647                     {
1648                         contexts.clear();
1649                         matched_path = context_path;
1650                     }
1651 
1652                     if (matched_path.equals(context_path))
1653                         contexts.add(ch);
1654                 }
1655             }
1656 
1657             if (contexts.size() > 0)
1658                 return contexts.get(0)._scontext;
1659             return null;
1660         }
1661 
1662         /* ------------------------------------------------------------ */
1663         /*
1664          * @see javax.servlet.ServletContext#getMajorVersion()
1665          */
1666         public int getMajorVersion()
1667         {
1668             return 2;
1669         }
1670 
1671         /* ------------------------------------------------------------ */
1672         /*
1673          * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
1674          */
1675         public String getMimeType(String file)
1676         {
1677             if (_mimeTypes == null)
1678                 return null;
1679             Buffer mime = _mimeTypes.getMimeByExtension(file);
1680             if (mime != null)
1681                 return mime.toString();
1682             return null;
1683         }
1684 
1685         /* ------------------------------------------------------------ */
1686         /*
1687          * @see javax.servlet.ServletContext#getMinorVersion()
1688          */
1689         public int getMinorVersion()
1690         {
1691             return 5;
1692         }
1693 
1694         /* ------------------------------------------------------------ */
1695         /*
1696          * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
1697          */
1698         public RequestDispatcher getNamedDispatcher(String name)
1699         {
1700             return null;
1701         }
1702 
1703         /* ------------------------------------------------------------ */
1704         /*
1705          * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
1706          */
1707         public RequestDispatcher getRequestDispatcher(String uriInContext)
1708         {
1709             if (uriInContext == null)
1710                 return null;
1711 
1712             if (!uriInContext.startsWith("/"))
1713                 return null;
1714 
1715             try
1716             {
1717                 String query = null;
1718                 int q = 0;
1719                 if ((q = uriInContext.indexOf('?')) > 0)
1720                 {
1721                     query = uriInContext.substring(q + 1);
1722                     uriInContext = uriInContext.substring(0,q);
1723                 }
1724                 if ((q = uriInContext.indexOf(';')) > 0)
1725                     uriInContext = uriInContext.substring(0,q);
1726 
1727                 String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
1728                 String uri = URIUtil.addPaths(getContextPath(),uriInContext);
1729                 ContextHandler context = ContextHandler.this;
1730                 return new Dispatcher(context,uri,pathInContext,query);
1731             }
1732             catch (Exception e)
1733             {
1734                 LOG.ignore(e);
1735             }
1736             return null;
1737         }
1738 
1739         /* ------------------------------------------------------------ */
1740         /*
1741          * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
1742          */
1743         public String getRealPath(String path)
1744         {
1745             if (path == null)
1746                 return null;
1747             if (path.length() == 0)
1748                 path = URIUtil.SLASH;
1749             else if (path.charAt(0) != '/')
1750                 path = URIUtil.SLASH + path;
1751 
1752             try
1753             {
1754                 Resource resource = ContextHandler.this.getResource(path);
1755                 if (resource != null)
1756                 {
1757                     File file = resource.getFile();
1758                     if (file != null)
1759                         return file.getCanonicalPath();
1760                 }
1761             }
1762             catch (Exception e)
1763             {
1764                 LOG.ignore(e);
1765             }
1766 
1767             return null;
1768         }
1769 
1770         /* ------------------------------------------------------------ */
1771         public URL getResource(String path) throws MalformedURLException
1772         {
1773             Resource resource = ContextHandler.this.getResource(path);
1774             if (resource != null && resource.exists())
1775                 return resource.getURL();
1776             return null;
1777         }
1778 
1779         /* ------------------------------------------------------------ */
1780         /*
1781          * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
1782          */
1783         public InputStream getResourceAsStream(String path)
1784         {
1785             try
1786             {
1787                 URL url = getResource(path);
1788                 if (url == null)
1789                     return null;
1790                 return url.openStream();
1791             }
1792             catch (Exception e)
1793             {
1794                 LOG.ignore(e);
1795                 return null;
1796             }
1797         }
1798 
1799         /* ------------------------------------------------------------ */
1800         /*
1801          * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
1802          */
1803         public Set getResourcePaths(String path)
1804         {
1805             return ContextHandler.this.getResourcePaths(path);
1806         }
1807 
1808         /* ------------------------------------------------------------ */
1809         /*
1810          * @see javax.servlet.ServletContext#getServerInfo()
1811          */
1812         public String getServerInfo()
1813         {
1814             return "jetty/" + Server.getVersion();
1815         }
1816 
1817         /* ------------------------------------------------------------ */
1818         /*
1819          * @see javax.servlet.ServletContext#getServlet(java.lang.String)
1820          */
1821         public Servlet getServlet(String name) throws ServletException
1822         {
1823             return null;
1824         }
1825 
1826         /* ------------------------------------------------------------ */
1827         /*
1828          * @see javax.servlet.ServletContext#getServletNames()
1829          */
1830         @SuppressWarnings("unchecked")
1831         public Enumeration getServletNames()
1832         {
1833             return Collections.enumeration(Collections.EMPTY_LIST);
1834         }
1835 
1836         /* ------------------------------------------------------------ */
1837         /*
1838          * @see javax.servlet.ServletContext#getServlets()
1839          */
1840         @SuppressWarnings("unchecked")
1841         public Enumeration getServlets()
1842         {
1843             return Collections.enumeration(Collections.EMPTY_LIST);
1844         }
1845 
1846         /* ------------------------------------------------------------ */
1847         /*
1848          * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
1849          */
1850         public void log(Exception exception, String msg)
1851         {
1852             _logger.warn(msg,exception);
1853         }
1854 
1855         /* ------------------------------------------------------------ */
1856         /*
1857          * @see javax.servlet.ServletContext#log(java.lang.String)
1858          */
1859         public void log(String msg)
1860         {
1861             _logger.info(msg);
1862         }
1863 
1864         /* ------------------------------------------------------------ */
1865         /*
1866          * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
1867          */
1868         public void log(String message, Throwable throwable)
1869         {
1870             _logger.warn(message,throwable);
1871         }
1872 
1873         /* ------------------------------------------------------------ */
1874         /*
1875          * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
1876          */
1877         public String getInitParameter(String name)
1878         {
1879             return ContextHandler.this.getInitParameter(name);
1880         }
1881 
1882         /* ------------------------------------------------------------ */
1883         /*
1884          * @see javax.servlet.ServletContext#getInitParameterNames()
1885          */
1886         @SuppressWarnings("unchecked")
1887         public Enumeration getInitParameterNames()
1888         {
1889             return ContextHandler.this.getInitParameterNames();
1890         }
1891 
1892         /* ------------------------------------------------------------ */
1893         /*
1894          * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
1895          */
1896         public synchronized Object getAttribute(String name)
1897         {
1898             Object o = ContextHandler.this.getAttribute(name);
1899             if (o == null && _contextAttributes != null)
1900                 o = _contextAttributes.getAttribute(name);
1901             return o;
1902         }
1903 
1904         /* ------------------------------------------------------------ */
1905         /*
1906          * @see javax.servlet.ServletContext#getAttributeNames()
1907          */
1908         @SuppressWarnings("unchecked")
1909         public synchronized Enumeration getAttributeNames()
1910         {
1911             HashSet<String> set = new HashSet<String>();
1912             if (_contextAttributes != null)
1913             {
1914                 Enumeration<String> e = _contextAttributes.getAttributeNames();
1915                 while (e.hasMoreElements())
1916                     set.add(e.nextElement());
1917             }
1918             Enumeration<String> e = _attributes.getAttributeNames();
1919             while (e.hasMoreElements())
1920                 set.add(e.nextElement());
1921 
1922             return Collections.enumeration(set);
1923         }
1924 
1925         /* ------------------------------------------------------------ */
1926         /*
1927          * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1928          */
1929         public synchronized void setAttribute(String name, Object value)
1930         {
1931             checkManagedAttribute(name,value);
1932             Object old_value = _contextAttributes.getAttribute(name);
1933 
1934             if (value == null)
1935                 _contextAttributes.removeAttribute(name);
1936             else
1937                 _contextAttributes.setAttribute(name,value);
1938 
1939             if (_contextAttributeListeners != null)
1940             {
1941                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
1942 
1943                 for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
1944                 {
1945                     ServletContextAttributeListener l = (ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i);
1946 
1947                     if (old_value == null)
1948                         l.attributeAdded(event);
1949                     else if (value == null)
1950                         l.attributeRemoved(event);
1951                     else
1952                         l.attributeReplaced(event);
1953                 }
1954             }
1955         }
1956 
1957         /* ------------------------------------------------------------ */
1958         /*
1959          * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
1960          */
1961         public synchronized void removeAttribute(String name)
1962         {
1963             checkManagedAttribute(name,null);
1964 
1965             if (_contextAttributes == null)
1966             {
1967                 // Set it on the handler
1968                 _attributes.removeAttribute(name);
1969                 return;
1970             }
1971 
1972             Object old_value = _contextAttributes.getAttribute(name);
1973             _contextAttributes.removeAttribute(name);
1974             if (old_value != null)
1975             {
1976                 if (_contextAttributeListeners != null)
1977                 {
1978                     ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
1979 
1980                     for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
1981                         ((ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i)).attributeRemoved(event);
1982                 }
1983             }
1984         }
1985 
1986         /* ------------------------------------------------------------ */
1987         /*
1988          * @see javax.servlet.ServletContext#getServletContextName()
1989          */
1990         public String getServletContextName()
1991         {
1992             String name = ContextHandler.this.getDisplayName();
1993             if (name == null)
1994                 name = ContextHandler.this.getContextPath();
1995             return name;
1996         }
1997 
1998         /* ------------------------------------------------------------ */
1999         public String getContextPath()
2000         {
2001             if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
2002                 return "";
2003 
2004             return _contextPath;
2005         }
2006 
2007         /* ------------------------------------------------------------ */
2008         @Override
2009         public String toString()
2010         {
2011             return "ServletContext@" + ContextHandler.this.toString();
2012         }
2013 
2014         /* ------------------------------------------------------------ */
2015         public boolean setInitParameter(String name, String value)
2016         {
2017             if (ContextHandler.this.getInitParameter(name) != null)
2018                 return false;
2019             ContextHandler.this.getInitParams().put(name,value);
2020             return true;
2021         }
2022 
2023     }
2024 
2025     private static class CLDump implements Dumpable
2026     {
2027         final ClassLoader _loader;
2028 
2029         CLDump(ClassLoader loader)
2030         {
2031             _loader = loader;
2032         }
2033 
2034         public String dump()
2035         {
2036             return AggregateLifeCycle.dump(this);
2037         }
2038 
2039         public void dump(Appendable out, String indent) throws IOException
2040         {
2041             out.append(String.valueOf(_loader)).append("\n");
2042 
2043             if (_loader != null)
2044             {
2045                 Object parent = _loader.getParent();
2046                 if (parent != null)
2047                 {
2048                     if (!(parent instanceof Dumpable))
2049                         parent = new CLDump((ClassLoader)parent);
2050 
2051                     if (_loader instanceof URLClassLoader)
2052                         AggregateLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(parent));
2053                     else
2054                         AggregateLifeCycle.dump(out,indent,Collections.singleton(parent));
2055                 }
2056             }
2057         }
2058 
2059     }
2060 }