View Javadoc

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