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