View Javadoc

1   // ========================================================================
2   // Copyright (c) 1999-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.servlet;
15  
16  import java.io.IOException;
17  import java.util.ArrayList;
18  import java.util.Arrays;
19  import java.util.EnumSet;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Queue;
25  import java.util.concurrent.ConcurrentHashMap;
26  import java.util.concurrent.ConcurrentLinkedQueue;
27  import java.util.concurrent.ConcurrentMap;
28  
29  import org.eclipse.jetty.server.DispatcherType;
30  import javax.servlet.Filter;
31  import javax.servlet.FilterChain;
32  import javax.servlet.RequestDispatcher;
33  import javax.servlet.Servlet;
34  import javax.servlet.ServletContext;
35  import javax.servlet.ServletException;
36  import javax.servlet.ServletRequest;
37  import javax.servlet.ServletResponse;
38  import javax.servlet.UnavailableException;
39  import javax.servlet.http.HttpServletRequest;
40  import javax.servlet.http.HttpServletResponse;
41  
42  import org.eclipse.jetty.continuation.ContinuationThrowable;
43  import org.eclipse.jetty.http.HttpException;
44  import org.eclipse.jetty.http.PathMap;
45  import org.eclipse.jetty.io.EofException;
46  import org.eclipse.jetty.io.RuntimeIOException;
47  import org.eclipse.jetty.security.IdentityService;
48  import org.eclipse.jetty.security.SecurityHandler;
49  import org.eclipse.jetty.server.Dispatcher;
50  import org.eclipse.jetty.server.HttpConnection;
51  import org.eclipse.jetty.server.Request;
52  import org.eclipse.jetty.server.Server;
53  import org.eclipse.jetty.server.ServletRequestHttpWrapper;
54  import org.eclipse.jetty.server.ServletResponseHttpWrapper;
55  import org.eclipse.jetty.server.UserIdentity;
56  import org.eclipse.jetty.server.handler.ContextHandler;
57  import org.eclipse.jetty.server.handler.ScopedHandler;
58  import org.eclipse.jetty.util.LazyList;
59  import org.eclipse.jetty.util.MultiException;
60  import org.eclipse.jetty.util.MultiMap;
61  import org.eclipse.jetty.util.TypeUtil;
62  import org.eclipse.jetty.util.URIUtil;
63  import org.eclipse.jetty.util.log.Log;
64  
65  /* --------------------------------------------------------------------- */
66  /** Servlet HttpHandler.
67   * This handler maps requests to servlets that implement the
68   * javax.servlet.http.HttpServlet API.
69   * <P>
70   * This handler does not implement the full J2EE features and is intended to
71   * be used directly when a full web application is not required.  If a Web application is required,
72   * then this handler should be used as part of a <code>org.eclipse.jetty.webapp.WebAppContext</code>.
73   * <p>
74   * Unless run as part of a {@link ServletContextHandler} or derivative, the {@link #initialize()}
75   * method must be called manually after start().
76   */
77  public class ServletHandler extends ScopedHandler
78  {
79      /* ------------------------------------------------------------ */
80      public static final String __DEFAULT_SERVLET="default";
81          
82      /* ------------------------------------------------------------ */
83      private ServletContextHandler _contextHandler;
84      private ContextHandler.Context _servletContext;
85      private FilterHolder[] _filters=new FilterHolder[0];
86      private FilterMapping[] _filterMappings;
87      private boolean _filterChainsCached=true;
88      private int _maxFilterChainsCacheSize=512;
89      private boolean _startWithUnavailable=true;
90      private IdentityService _identityService;
91      
92      private ServletHolder[] _servlets=new ServletHolder[0];
93      private ServletMapping[] _servletMappings;
94      
95      private final Map<String,FilterHolder> _filterNameMap= new HashMap<String,FilterHolder>();
96      private List<FilterMapping> _filterPathMappings;
97      private MultiMap<String> _filterNameMappings;
98      
99      private final Map<String,ServletHolder> _servletNameMap=new HashMap<String,ServletHolder>();
100     private PathMap _servletPathMap;
101     
102     protected final ConcurrentMap<String,FilterChain> _chainCache[] = new ConcurrentMap[FilterMapping.ALL];
103     protected final Queue<String>[] _chainLRU = new Queue[FilterMapping.ALL];
104 
105 
106     /* ------------------------------------------------------------ */
107     /** Constructor. 
108      */
109     public ServletHandler()
110     {
111     }
112 
113     /* ------------------------------------------------------------ */
114     /* 
115      * @see org.eclipse.jetty.server.handler.AbstractHandler#setServer(org.eclipse.jetty.server.Server)
116      */
117     public void setServer(Server server)
118     {
119         Server old=getServer();
120         if (old!=null && old!=server)
121         {
122             getServer().getContainer().update(this, _filters, null, "filter",true);
123             getServer().getContainer().update(this, _filterMappings, null, "filterMapping",true);
124             getServer().getContainer().update(this, _servlets, null, "servlet",true);
125             getServer().getContainer().update(this, _servletMappings, null, "servletMapping",true);
126         }
127 
128         super.setServer(server);
129         
130         if (server!=null && old!=server)
131         {
132             server.getContainer().update(this, null, _filters, "filter",true);
133             server.getContainer().update(this, null, _filterMappings, "filterMapping",true);
134             server.getContainer().update(this, null, _servlets, "servlet",true);
135             server.getContainer().update(this, null, _servletMappings, "servletMapping",true);
136         }
137     }
138 
139     /* ----------------------------------------------------------------- */
140     @Override
141     protected synchronized void doStart()
142         throws Exception
143     {
144         _servletContext=ContextHandler.getCurrentContext();
145         _contextHandler=(ServletContextHandler)(_servletContext==null?null:_servletContext.getContextHandler());
146 
147         if (_contextHandler!=null)
148         {
149             SecurityHandler security_handler = (SecurityHandler)_contextHandler.getChildHandlerByClass(SecurityHandler.class);
150             if (security_handler!=null)
151                 _identityService=security_handler.getIdentityService();
152         }
153         
154         updateNameMappings();
155         updateMappings();
156         
157         if(_filterChainsCached)
158         {
159             _chainCache[FilterMapping.REQUEST]=new ConcurrentHashMap<String,FilterChain>();
160             _chainCache[FilterMapping.FORWARD]=new ConcurrentHashMap<String,FilterChain>();
161             _chainCache[FilterMapping.INCLUDE]=new ConcurrentHashMap<String,FilterChain>();
162             _chainCache[FilterMapping.ERROR]=new ConcurrentHashMap<String,FilterChain>();
163             _chainCache[FilterMapping.ASYNC]=new ConcurrentHashMap<String,FilterChain>();
164             
165             _chainLRU[FilterMapping.REQUEST]=new ConcurrentLinkedQueue<String>();
166             _chainLRU[FilterMapping.FORWARD]=new ConcurrentLinkedQueue<String>();
167             _chainLRU[FilterMapping.INCLUDE]=new ConcurrentLinkedQueue<String>();
168             _chainLRU[FilterMapping.ERROR]=new ConcurrentLinkedQueue<String>();
169             _chainLRU[FilterMapping.ASYNC]=new ConcurrentLinkedQueue<String>();
170         }
171 
172         super.doStart();
173         
174         if (_contextHandler==null || !(_contextHandler instanceof ServletContextHandler))
175             initialize();
176     }   
177     
178     /* ----------------------------------------------------------------- */
179     @Override
180     protected synchronized void doStop()
181         throws Exception
182     {
183         super.doStop();
184         
185         // Stop filters
186         if (_filters!=null)
187         {
188             for (int i=_filters.length; i-->0;)
189             {
190                 try { _filters[i].stop(); }catch(Exception e){Log.warn(Log.EXCEPTION,e);}
191             }
192         }
193         
194         // Stop servlets
195         if (_servlets!=null)
196         {
197             for (int i=_servlets.length; i-->0;)
198             {
199                 try { _servlets[i].stop(); }catch(Exception e){Log.warn(Log.EXCEPTION,e);}
200             }
201         }
202 
203         _filterPathMappings=null;
204         _filterNameMappings=null;
205         
206         _servletPathMap=null;
207     }
208 
209     /* ------------------------------------------------------------ */
210     IdentityService getIdentityService()
211     {
212         return _identityService;
213     }
214     
215     /* ------------------------------------------------------------ */
216     /**
217      * @return Returns the contextLog.
218      */
219     public Object getContextLog()
220     {
221         return null;
222     }
223     
224     /* ------------------------------------------------------------ */
225     /**
226      * @return Returns the filterMappings.
227      */
228     public FilterMapping[] getFilterMappings()
229     {
230         return _filterMappings;
231     }
232     
233     /* ------------------------------------------------------------ */
234     /** Get Filters.
235      * @return Array of defined servlets
236      */
237     public FilterHolder[] getFilters()
238     {
239         return _filters;
240     }
241     
242     /* ------------------------------------------------------------ */
243     /** ServletHolder matching path.
244      * @param pathInContext Path within _context.
245      * @return PathMap Entries pathspec to ServletHolder
246      */
247     public PathMap.Entry getHolderEntry(String pathInContext)
248     {
249         if (_servletPathMap==null)
250             return null;
251         return _servletPathMap.getMatch(pathInContext);
252     }
253 
254     /* ------------------------------------------------------------ */
255     /**
256      * @param uriInContext uri to get dispatcher for
257      * @return A {@link RequestDispatcher dispatcher} wrapping the resource at <code>uriInContext</code>,
258      *  or <code>null</code> if the specified uri cannot be dispatched to.
259      */
260     public RequestDispatcher getRequestDispatcher(String uriInContext)
261     {
262         if (uriInContext == null || _contextHandler==null)
263             return null;
264 
265         if (!uriInContext.startsWith("/"))
266             return null;
267         
268         try
269         {
270             String query=null;
271             int q;
272             if ((q=uriInContext.indexOf('?'))>0)
273             {
274                 query=uriInContext.substring(q+1);
275                 uriInContext=uriInContext.substring(0,q);
276             }
277             if ((q=uriInContext.indexOf(';'))>0)
278                 uriInContext=uriInContext.substring(0,q);
279 
280             String pathInContext=URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
281             String uri=URIUtil.addPaths(_contextHandler.getContextPath(), uriInContext);
282             return new Dispatcher(_contextHandler, uri, pathInContext, query);
283         }
284         catch(Exception e)
285         {
286             Log.ignore(e);
287         }
288         return null;
289     }
290 
291     /* ------------------------------------------------------------ */
292     public ServletContext getServletContext()
293     {
294         return _servletContext;
295     }
296     
297     /* ------------------------------------------------------------ */
298     /**
299      * @return Returns the servletMappings.
300      */
301     public ServletMapping[] getServletMappings()
302     {
303         return _servletMappings;
304     }
305     
306     /* ------------------------------------------------------------ */
307     /**
308      * @return Returns the servletMappings.
309      */
310     public ServletMapping getServletMapping(String pattern)
311     {
312         if (_servletMappings!=null)
313         {
314             for (ServletMapping m:_servletMappings)
315             {
316                 String[] paths=m.getPathSpecs();
317                 if (paths!=null)
318                 {
319                     for (String path:paths)
320                     {
321                         if (pattern.equals(path))
322                             return m;
323                     }
324                 }
325             }
326         }
327         return null;
328     }
329         
330     /* ------------------------------------------------------------ */
331     /** Get Servlets.
332      * @return Array of defined servlets
333      */
334     public ServletHolder[] getServlets()
335     {
336         return _servlets;
337     }
338 
339     /* ------------------------------------------------------------ */
340     public ServletHolder getServlet(String name)
341     {
342         return (ServletHolder)_servletNameMap.get(name);
343     }
344 
345     /* ------------------------------------------------------------ */
346     @Override
347     public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
348     {
349         // Get the base requests
350         final String old_servlet_path=baseRequest.getServletPath();
351         final String old_path_info=baseRequest.getPathInfo();
352 
353         DispatcherType type = baseRequest.getDispatcherType();
354        
355         ServletHolder servlet_holder=null;
356         UserIdentity.Scope old_scope=null;
357 
358         // find the servlet
359         if (target.startsWith("/"))
360         {
361             // Look for the servlet by path
362             PathMap.Entry entry=getHolderEntry(target);
363             if (entry!=null)
364             {
365                 servlet_holder=(ServletHolder)entry.getValue();
366 
367                 String servlet_path_spec=(String)entry.getKey(); 
368                 String servlet_path=entry.getMapped()!=null?entry.getMapped():PathMap.pathMatch(servlet_path_spec,target);
369                 String path_info=PathMap.pathInfo(servlet_path_spec,target);
370 
371                 if (DispatcherType.INCLUDE.equals(type))
372                 {
373                     baseRequest.setAttribute(Dispatcher.INCLUDE_SERVLET_PATH,servlet_path);
374                     baseRequest.setAttribute(Dispatcher.INCLUDE_PATH_INFO, path_info);
375                 }
376                 else
377                 {
378                     baseRequest.setServletPath(servlet_path);
379                     baseRequest.setPathInfo(path_info);
380                 }
381             }      
382         }
383         else
384         {
385             // look for a servlet by name!
386             servlet_holder=(ServletHolder)_servletNameMap.get(target);
387         }
388 
389         if (Log.isDebugEnabled())
390             Log.debug("servlet {} -> {}",baseRequest.getContextPath()+"|"+baseRequest.getServletPath()+"|"+baseRequest.getPathInfo(),servlet_holder);
391 
392         try
393         {
394             // Do the filter/handling thang
395             if (servlet_holder!=null)
396             {
397                 old_scope=baseRequest.getUserIdentityScope();
398                 baseRequest.setUserIdentityScope(servlet_holder);
399 
400                 // start manual inline of nextScope(target,baseRequest,request,response);
401                 if (never())
402                     nextScope(target,baseRequest,request,response);
403                 else if (_nextScope!=null)
404                     _nextScope.doScope(target,baseRequest,request, response);
405                 else if (_outerScope!=null)
406                     _outerScope.doHandle(target,baseRequest,request, response);
407                 else 
408                     doHandle(target,baseRequest,request, response);
409                 // end manual inline (pathentic attempt to reduce stack depth)
410             }
411         }
412         finally
413         {
414             if (old_scope!=null)
415                 baseRequest.setUserIdentityScope(old_scope);
416 
417             if (!(DispatcherType.INCLUDE.equals(type)))
418             {
419                 baseRequest.setServletPath(old_servlet_path);
420                 baseRequest.setPathInfo(old_path_info); 
421             }
422         }
423     }
424     
425     /* ------------------------------------------------------------ */
426     /* 
427      * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
428      */
429     @Override
430     public void doHandle(String target, Request baseRequest,HttpServletRequest request, HttpServletResponse response)
431         throws IOException, ServletException
432     {
433         DispatcherType type = baseRequest.getDispatcherType();
434         
435         ServletHolder servlet_holder=(ServletHolder) baseRequest.getUserIdentityScope();
436         FilterChain chain=null;
437 
438         // find the servlet
439         if (target.startsWith("/"))
440         {
441             if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0)
442                 chain=getFilterChain(baseRequest, target, servlet_holder);
443         }
444         else
445         {
446             if (servlet_holder!=null)
447             {
448                 if (_filterMappings!=null && _filterMappings.length>0)
449                 {
450                     chain=getFilterChain(baseRequest, null,servlet_holder);
451                 }
452             }
453         }
454 
455         Log.debug("chain=",chain);
456         
457         try
458         {
459             if (servlet_holder==null)
460             {
461                 notFound(request, response);
462             }
463             else
464             {
465                 // unwrap any tunnelling of base Servlet request/responses
466                 ServletRequest req = request;
467                 if (req instanceof ServletRequestHttpWrapper)
468                     req = ((ServletRequestHttpWrapper)req).getRequest();
469                 ServletResponse res = response;
470                 if (res instanceof ServletResponseHttpWrapper)
471                     res = ((ServletResponseHttpWrapper)res).getResponse();
472 
473                 // Do the filter/handling thang
474                 baseRequest.setHandled(true);
475                 if (chain!=null)
476                     chain.doFilter(req, res);
477                 else 
478                     servlet_holder.handle(baseRequest,req,res);
479             }
480         }
481         catch(EofException e)
482         {
483             throw e;
484         }
485         catch(RuntimeIOException e)
486         {
487             throw e;
488         }
489         catch(ContinuationThrowable e)
490         {   
491             throw e;
492         }
493         catch(Exception e)
494         {
495             if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
496             {
497                 if (e instanceof IOException)
498                     throw (IOException)e;
499                 if (e instanceof RuntimeException)
500                     throw (RuntimeException)e;
501                 if (e instanceof ServletException)
502                     throw (ServletException)e;
503             }
504 
505             // unwrap cause
506             Throwable th=e;
507             if (th instanceof UnavailableException)
508             {
509                 Log.debug(th); 
510             }
511             else if (th instanceof ServletException)
512             {
513                 Log.debug(th);
514                 Throwable cause=((ServletException)th).getRootCause();
515                 if (cause!=null)
516                     th=cause;
517             }
518             else if (th instanceof RuntimeIOException)
519             {
520                 Log.debug(th);
521                 Throwable cause=(IOException)((RuntimeIOException)th).getCause();
522                 if (cause!=null)
523                     th=cause;
524             }
525 
526             // handle or log exception
527             if (th instanceof HttpException)
528                 throw (HttpException)th;
529             else if (th instanceof RuntimeIOException)
530                 throw (RuntimeIOException)th;
531             else if (th instanceof EofException)
532                 throw (EofException)th;
533 
534             else if (Log.isDebugEnabled())
535             {
536                 Log.warn(request.getRequestURI(), th); 
537                 Log.debug(request.toString()); 
538             }
539             else if (th instanceof IOException || th instanceof UnavailableException)
540             {
541                 Log.debug(request.getRequestURI(),th);
542             }
543             else
544             {
545                 Log.warn(request.getRequestURI(),th);
546             }
547 
548             if (!response.isCommitted())
549             {
550                 request.setAttribute(Dispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
551                 request.setAttribute(Dispatcher.ERROR_EXCEPTION,th);
552                 if (th instanceof UnavailableException)
553                 {
554                     UnavailableException ue = (UnavailableException)th;
555                     if (ue.isPermanent())
556                         response.sendError(HttpServletResponse.SC_NOT_FOUND,th.getMessage());
557                     else
558                         response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,th.getMessage());
559                 }
560                 else
561                     response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,th.getMessage());
562             }
563             else
564                 Log.debug("Response already committed for handling "+th);
565         }
566         catch(Error e)
567         {   
568             if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
569                 throw e;
570             Log.warn("Error for "+request.getRequestURI(),e);
571             if(Log.isDebugEnabled())Log.debug(request.toString());
572 
573             // TODO httpResponse.getHttpConnection().forceClose();
574             if (!response.isCommitted())
575             {
576                 request.setAttribute(Dispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
577                 request.setAttribute(Dispatcher.ERROR_EXCEPTION,e);
578                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,e.getMessage());
579             }
580             else
581                 Log.debug("Response already committed for handling ",e);
582         }
583     }
584 
585     /* ------------------------------------------------------------ */
586     private FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder) 
587     {
588         String key=pathInContext==null?servletHolder.getName():pathInContext;
589         int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType());
590         
591         if (_filterChainsCached && _chainCache!=null)
592         {
593             FilterChain chain = (FilterChain)_chainCache[dispatch].get(key);
594             if (chain!=null)
595                 return chain;
596         }
597         
598         // Build list of filters
599         Object filters= null;
600         // Path filters
601         if (pathInContext!=null && _filterPathMappings!=null)
602         {
603             for (int i= 0; i < _filterPathMappings.size(); i++)
604             {
605                 FilterMapping mapping = (FilterMapping)_filterPathMappings.get(i);
606                 if (mapping.appliesTo(pathInContext, dispatch))
607                     filters= LazyList.add(filters, mapping.getFilterHolder());
608             }
609         }
610 
611         // Servlet name filters
612         if (servletHolder != null && _filterNameMappings!=null && _filterNameMappings.size() > 0)
613         {
614             // Servlet name filters
615             if (_filterNameMappings.size() > 0)
616             {
617                 Object o= _filterNameMappings.get(servletHolder.getName());
618                 for (int i=0; i<LazyList.size(o);i++)
619                 {
620                     FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
621                     if (mapping.appliesTo(dispatch))
622                         filters=LazyList.add(filters,mapping.getFilterHolder());
623                 }
624                 
625                 o= _filterNameMappings.get("*");
626                 for (int i=0; i<LazyList.size(o);i++)
627                 {
628                     FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
629                     if (mapping.appliesTo(dispatch))
630                         filters=LazyList.add(filters,mapping.getFilterHolder());
631                 }
632             }
633         }
634         
635         if (filters==null)
636             return null;
637         
638         
639         FilterChain chain = null;
640         if (_filterChainsCached)
641         {
642             if (LazyList.size(filters) > 0)
643                 chain= new CachedChain(filters, servletHolder);
644 
645             final Map<String,FilterChain> cache=_chainCache[dispatch];
646             final Queue<String> lru=_chainLRU[dispatch];
647 
648         	// Do we have too many cached chains?
649         	while (_maxFilterChainsCacheSize>0 && cache.size()>=_maxFilterChainsCacheSize)
650         	{
651         	    // The LRU list is not atomic with the cache map, so be prepared to invalidate if 
652         	    // a key is not found to delete.
653         	    // Delete by LRU (where U==created)
654         	    String k=lru.poll();
655         	    if (k==null)
656         	    {
657         	        cache.clear();
658         	        break;
659         	    }
660         	    cache.remove(k);
661         	}
662         	
663         	cache.put(key,chain);
664         	lru.add(key);
665         }
666         else if (LazyList.size(filters) > 0)
667             chain = new Chain(baseRequest,filters, servletHolder);
668     
669         return chain;
670     }
671     
672     /* ------------------------------------------------------------ */
673     private void invalidateChainsCache()
674     {
675         if (_chainLRU[FilterMapping.REQUEST]!=null)
676         {
677             _chainLRU[FilterMapping.REQUEST].clear();
678             _chainLRU[FilterMapping.FORWARD].clear();
679             _chainLRU[FilterMapping.INCLUDE].clear();
680             _chainLRU[FilterMapping.ERROR].clear();
681             _chainLRU[FilterMapping.ASYNC].clear();
682 
683             _chainCache[FilterMapping.REQUEST].clear();
684             _chainCache[FilterMapping.FORWARD].clear();
685             _chainCache[FilterMapping.INCLUDE].clear();
686             _chainCache[FilterMapping.ERROR].clear();
687             _chainCache[FilterMapping.ASYNC].clear();
688         }
689     }
690 
691     /* ------------------------------------------------------------ */
692     /**
693      * @return true if the handler is started and there are no unavailable servlets 
694      */
695     public boolean isAvailable()
696     {
697         if (!isStarted())
698             return false;
699         ServletHolder[] holders = getServlets();
700         for (int i=0;i<holders.length;i++)
701         {
702             ServletHolder holder = holders[i];
703             if (holder!=null && !holder.isAvailable())
704                 return false;
705         }
706         return true;
707     }
708     
709     /* ------------------------------------------------------------ */
710     /**
711      * @param start True if this handler will start with unavailable servlets
712      */
713     public void setStartWithUnavailable(boolean start)
714     {
715         _startWithUnavailable=start;
716     }
717     
718     /* ------------------------------------------------------------ */
719     /**
720      * @return True if this handler will start with unavailable servlets
721      */
722     public boolean isStartWithUnavailable()
723     {
724         return _startWithUnavailable;
725     }
726     
727     
728     
729     /* ------------------------------------------------------------ */
730     /** Initialize filters and load-on-startup servlets.
731      * Called automatically from start if autoInitializeServlet is true.
732      */
733     public void initialize()
734         throws Exception
735     {
736         MultiException mx = new MultiException();
737 
738         // Start filters
739         if (_filters!=null)
740         {
741             for (int i=0;i<_filters.length; i++)
742                 _filters[i].start();
743         }
744         
745         if (_servlets!=null)
746         {
747             // Sort and Initialize servlets
748             ServletHolder[] servlets = (ServletHolder[])_servlets.clone();
749             Arrays.sort(servlets);
750             for (int i=0; i<servlets.length; i++)
751             {
752                 try
753                 {
754                     if (servlets[i].getClassName()==null && servlets[i].getForcedPath()!=null)
755                     {
756                         ServletHolder forced_holder = (ServletHolder)_servletPathMap.match(servlets[i].getForcedPath());
757                         if (forced_holder==null || forced_holder.getClassName()==null)
758                         {    
759                             mx.add(new IllegalStateException("No forced path servlet for "+servlets[i].getForcedPath()));
760                             continue;
761                         }
762                         servlets[i].setClassName(forced_holder.getClassName());
763                     }
764                     
765                     servlets[i].start();
766                 }
767                 catch(Throwable e)
768                 {
769                     Log.debug(Log.EXCEPTION,e);
770                     mx.add(e);
771                 }
772             } 
773             mx.ifExceptionThrow();  
774         }
775     }
776     
777     /* ------------------------------------------------------------ */
778     /**
779      * @return Returns the filterChainsCached.
780      */
781     public boolean isFilterChainsCached()
782     {
783         return _filterChainsCached;
784     }
785 
786     /* ------------------------------------------------------------ */
787     /**
788      * see also newServletHolder(Class)
789      */
790     public ServletHolder newServletHolder()
791     {
792         return new ServletHolder();
793     }
794     
795     /* ------------------------------------------------------------ */
796     public ServletHolder newServletHolder(Class<? extends Servlet> servlet)
797     {
798         return new ServletHolder(servlet);
799     }
800     
801     /* ------------------------------------------------------------ */
802     /** Convenience method to add a servlet.
803      * @return The servlet holder.
804      */
805     public ServletHolder addServletWithMapping (String className,String pathSpec)
806     {
807         ServletHolder holder = newServletHolder(null);
808         holder.setName(className+"-"+LazyList.size(_servlets));
809         holder.setClassName(className);
810         addServletWithMapping(holder,pathSpec);
811         return holder;
812     }   
813     
814     /* ------------------------------------------------------------ */
815     /** conveniance method to add a servlet.
816      * @return The servlet holder.
817      */
818     public ServletHolder addServletWithMapping (Class<? extends Servlet> servlet,String pathSpec)
819     {
820         ServletHolder holder = newServletHolder();
821         holder.setHeldClass(servlet);
822         setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
823         addServletWithMapping(holder,pathSpec);
824         
825         return holder;
826     }   
827     
828     /* ------------------------------------------------------------ */
829     /** conveniance method to add a servlet.
830      * @param servlet servlet holder to add
831      * @param pathSpec servlet mappings for the servletHolder
832      */
833     public void addServletWithMapping (ServletHolder servlet,String pathSpec)
834     {
835         ServletHolder[] holders=getServlets();
836         if (holders!=null)
837             holders = holders.clone();
838         
839         try
840         {
841             setServlets((ServletHolder[])LazyList.addToArray(holders, servlet, ServletHolder.class));
842             
843             ServletMapping mapping = new ServletMapping();
844             mapping.setServletName(servlet.getName());
845             mapping.setPathSpec(pathSpec);
846             setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class));
847         }
848         catch (Exception e)
849         {
850             setServlets(holders);
851             if (e instanceof RuntimeException)
852                 throw (RuntimeException)e;
853             throw new RuntimeException(e);
854         }
855     }
856 
857     
858     /* ------------------------------------------------------------ */    
859     /**Convenience method to add a pre-constructed ServletHolder.
860      * @param holder
861      */
862     public void addServlet(ServletHolder holder)
863     {
864         setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
865     }
866     
867     /* ------------------------------------------------------------ */    
868     /** Convenience method to add a pre-constructed ServletMapping.
869      * @param mapping
870      */
871     public void addServletMapping (ServletMapping mapping)
872     {
873         setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class));
874     }
875     
876     /* ------------------------------------------------------------ */
877     public FilterHolder newFilterHolder(Class<? extends Filter> filter)
878     {
879         return new FilterHolder(filter);
880     }
881     
882     /* ------------------------------------------------------------ */
883     /** 
884      * @see #newFilterHolder(Class)
885      */
886     public FilterHolder newFilterHolder()
887     {
888         return new FilterHolder();
889     }
890 
891 
892     /* ------------------------------------------------------------ */
893     public FilterHolder getFilter(String name)
894     {
895         return (FilterHolder)_filterNameMap.get(name);
896     }
897 
898     
899     /* ------------------------------------------------------------ */
900     /** Convenience method to add a filter.
901      * @param filter  class of filter to create
902      * @param pathSpec filter mappings for filter
903      * @param dispatches see {@link FilterMapping#setDispatches(int)}
904      * @return The filter holder.
905      */
906     public FilterHolder addFilterWithMapping (Class<? extends Filter> filter,String pathSpec,EnumSet<DispatcherType> dispatches)
907     {
908         FilterHolder holder = newFilterHolder();
909         holder.setHeldClass(filter);
910         addFilterWithMapping(holder,pathSpec,dispatches);
911         
912         return holder;
913     }
914     
915     /* ------------------------------------------------------------ */
916     /** Convenience method to add a filter.
917      * @param className of filter
918      * @param pathSpec filter mappings for filter
919      * @param dispatches see {@link FilterMapping#setDispatches(int)}
920      * @return The filter holder.
921      */
922     public FilterHolder addFilterWithMapping (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
923     {
924         FilterHolder holder = newFilterHolder();
925         holder.setName(className+"-"+_filters.length);
926         holder.setClassName(className);
927         
928         addFilterWithMapping(holder,pathSpec,dispatches);
929         return holder;
930     }
931     
932     /* ------------------------------------------------------------ */
933     /** Convenience method to add a filter.
934      * @param holder filter holder to add
935      * @param pathSpec filter mappings for filter
936      * @param dispatches see {@link FilterMapping#setDispatches(int)}
937      */
938     public void addFilterWithMapping (FilterHolder holder,String pathSpec,EnumSet<DispatcherType> dispatches)
939     {
940         FilterHolder[] holders = getFilters();
941         if (holders!=null)
942             holders = (FilterHolder[])holders.clone();
943         
944         try
945         {
946             setFilters((FilterHolder[])LazyList.addToArray(holders, holder, FilterHolder.class));
947             
948             FilterMapping mapping = new FilterMapping();
949             mapping.setFilterName(holder.getName());
950             mapping.setPathSpec(pathSpec);
951             mapping.setDispatcherTypes(dispatches);
952             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
953         }
954         catch (RuntimeException e)
955         {
956             setFilters(holders);
957             throw e;
958         }
959         catch (Error e)
960         {
961             setFilters(holders);
962             throw e;
963         }
964             
965     }
966     
967     
968     /* ------------------------------------------------------------ */
969     /** Convenience method to add a filter.
970      * @param filter  class of filter to create
971      * @param pathSpec filter mappings for filter
972      * @param dispatches see {@link FilterMapping#setDispatches(int)}
973      * @return The filter holder.
974      */
975     public FilterHolder addFilterWithMapping (Class<? extends Filter> filter,String pathSpec,int dispatches)
976     {
977         FilterHolder holder = newFilterHolder(filter);
978         addFilterWithMapping(holder,pathSpec,dispatches);
979         
980         return holder;
981     }
982     
983     /* ------------------------------------------------------------ */
984     /** Convenience method to add a filter.
985      * @param className of filter
986      * @param pathSpec filter mappings for filter
987      * @param dispatches see {@link FilterMapping#setDispatches(int)}
988      * @return The filter holder.
989      */
990     public FilterHolder addFilterWithMapping (String className,String pathSpec,int dispatches)
991     {
992         FilterHolder holder = newFilterHolder(null);
993         holder.setName(className+"-"+_filters.length);
994         holder.setClassName(className);
995         
996         addFilterWithMapping(holder,pathSpec,dispatches);
997         return holder;
998     }
999     
1000     /* ------------------------------------------------------------ */
1001     /** Convenience method to add a filter.
1002      * @param holder filter holder to add
1003      * @param pathSpec filter mappings for filter
1004      * @param dispatches see {@link FilterMapping#setDispatches(int)}
1005      */
1006     public void addFilterWithMapping (FilterHolder holder,String pathSpec,int dispatches)
1007     {
1008         FilterHolder[] holders = getFilters();
1009         if (holders!=null)
1010             holders = (FilterHolder[])holders.clone();
1011         
1012         try
1013         {
1014             setFilters((FilterHolder[])LazyList.addToArray(holders, holder, FilterHolder.class));
1015             
1016             FilterMapping mapping = new FilterMapping();
1017             mapping.setFilterName(holder.getName());
1018             mapping.setPathSpec(pathSpec);
1019             mapping.setDispatches(dispatches);
1020             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
1021         }
1022         catch (RuntimeException e)
1023         {
1024             setFilters(holders);
1025             throw e;
1026         }
1027         catch (Error e)
1028         {
1029             setFilters(holders);
1030             throw e;
1031         }
1032             
1033     }
1034     
1035     
1036     /* ------------------------------------------------------------ */
1037     /** Convenience method to add a filter with a mapping
1038      * @param className
1039      * @param pathSpec
1040      * @param dispatches
1041      * @return the filter holder created
1042      * @deprecated use {@link #addFilterWithMapping(Class, String, int)} instead
1043      */
1044     public FilterHolder addFilter (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
1045     {
1046         return addFilterWithMapping(className, pathSpec, dispatches);
1047     }
1048     
1049     /* ------------------------------------------------------------ */
1050     /**
1051      * convenience method to add a filter and mapping
1052      * @param filter
1053      * @param filterMapping
1054      */
1055     public void addFilter (FilterHolder filter, FilterMapping filterMapping)
1056     {
1057         if (filter != null)
1058             setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class));
1059         if (filterMapping != null)
1060             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), filterMapping, FilterMapping.class));
1061     }
1062     
1063     /* ------------------------------------------------------------ */  
1064     /** Convenience method to add a preconstructed FilterHolder
1065      * @param filter
1066      */
1067     public void addFilter (FilterHolder filter)
1068     {
1069         if (filter != null)
1070             setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class));
1071     }
1072     
1073     /* ------------------------------------------------------------ */
1074     /** Convenience method to add a preconstructed FilterMapping
1075      * @param mapping
1076      */
1077     public void addFilterMapping (FilterMapping mapping)
1078     {
1079         if (mapping != null)
1080             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
1081     }
1082     
1083     /* ------------------------------------------------------------ */
1084     /** Convenience method to add a preconstructed FilterMapping
1085      * @param mapping
1086      */
1087     public void prependFilterMapping (FilterMapping mapping)
1088     {
1089         if (mapping != null)
1090         {
1091             FilterMapping[] mappings =getFilterMappings();
1092             if (mappings==null || mappings.length==0)
1093                 setFilterMappings(new FilterMapping[] {mapping});
1094             else
1095             {
1096 
1097                 FilterMapping[] new_mappings=new FilterMapping[mappings.length+1];
1098                 System.arraycopy(mappings,0,new_mappings,1,mappings.length);
1099                 new_mappings[0]=mapping;
1100                 setFilterMappings(new_mappings);
1101             }
1102         }
1103     }
1104 
1105     /* ------------------------------------------------------------ */
1106     protected synchronized void updateNameMappings()
1107     {   
1108         // update filter name map
1109         _filterNameMap.clear();
1110         if (_filters!=null)
1111         {   
1112             for (int i=0;i<_filters.length;i++)
1113             {
1114                 _filterNameMap.put(_filters[i].getName(),_filters[i]);
1115                 _filters[i].setServletHandler(this);
1116             }
1117         }
1118 
1119         // Map servlet names to holders
1120         _servletNameMap.clear();
1121         if (_servlets!=null)
1122         {   
1123             // update the maps
1124             for (int i=0;i<_servlets.length;i++)
1125             {
1126                 _servletNameMap.put(_servlets[i].getName(),_servlets[i]);
1127                 _servlets[i].setServletHandler(this);
1128             }
1129         }
1130     }
1131     
1132     /* ------------------------------------------------------------ */
1133     protected synchronized void updateMappings()
1134     {   
1135         // update filter mappings
1136         if (_filterMappings==null)
1137         {
1138             _filterPathMappings=null;
1139             _filterNameMappings=null;
1140         }
1141         else 
1142         {
1143             _filterPathMappings=new ArrayList();
1144             _filterNameMappings=new MultiMap();
1145             for (int i=0;i<_filterMappings.length;i++)
1146             {
1147                 FilterHolder filter_holder = (FilterHolder)_filterNameMap.get(_filterMappings[i].getFilterName());
1148                 if (filter_holder==null)
1149                     throw new IllegalStateException("No filter named "+_filterMappings[i].getFilterName());
1150                 _filterMappings[i].setFilterHolder(filter_holder);    
1151                 if (_filterMappings[i].getPathSpecs()!=null)
1152                     _filterPathMappings.add(_filterMappings[i]);
1153                 
1154                 if (_filterMappings[i].getServletNames()!=null)
1155                 {
1156                     String[] names=_filterMappings[i].getServletNames();
1157                     for (int j=0;j<names.length;j++)
1158                     {
1159                         if (names[j]!=null)
1160                             _filterNameMappings.add(names[j], _filterMappings[i]);  
1161                     }
1162                 }
1163             }
1164         }
1165 
1166         // Map servlet paths to holders
1167         if (_servletMappings==null || _servletNameMap==null)
1168         {
1169             _servletPathMap=null;
1170         }
1171         else
1172         {
1173             PathMap pm = new PathMap();
1174             
1175             // update the maps
1176             for (int i=0;i<_servletMappings.length;i++)
1177             {
1178                 ServletHolder servlet_holder = (ServletHolder)_servletNameMap.get(_servletMappings[i].getServletName());
1179                 if (servlet_holder==null)
1180                     throw new IllegalStateException("No such servlet: "+_servletMappings[i].getServletName());
1181                 else if (_servletMappings[i].getPathSpecs()!=null)
1182                 {
1183                     String[] pathSpecs = _servletMappings[i].getPathSpecs();
1184                     for (int j=0;j<pathSpecs.length;j++)
1185                         if (pathSpecs[j]!=null)
1186                             pm.put(pathSpecs[j],servlet_holder);
1187                 }
1188             }
1189             
1190             _servletPathMap=pm;
1191         }
1192         
1193         // flush filter chain cache
1194         if (_chainCache!=null)
1195         {
1196             for (int i=_chainCache.length;i-->0;)
1197             {
1198                 if (_chainCache[i]!=null)
1199                     _chainCache[i].clear();
1200             }
1201         }
1202 
1203         if (Log.isDebugEnabled()) 
1204         {
1205             Log.debug("filterNameMap="+_filterNameMap);
1206             Log.debug("pathFilters="+_filterPathMappings);
1207             Log.debug("servletFilterMap="+_filterNameMappings);
1208             Log.debug("servletPathMap="+_servletPathMap);
1209             Log.debug("servletNameMap="+_servletNameMap);
1210         }
1211         
1212         try
1213         {
1214             if (isStarted())
1215                 initialize();
1216         }
1217         catch (Exception e)
1218         {
1219             throw new RuntimeException(e);
1220         }
1221     }
1222 
1223     /* ------------------------------------------------------------ */
1224     protected void notFound(HttpServletRequest request,
1225                   HttpServletResponse response)
1226         throws IOException
1227     {
1228         if(Log.isDebugEnabled())Log.debug("Not Found "+request.getRequestURI());
1229         response.sendError(HttpServletResponse.SC_NOT_FOUND);
1230     }
1231     
1232     /* ------------------------------------------------------------ */
1233     /**
1234      * @param filterChainsCached The filterChainsCached to set.
1235      */
1236     public void setFilterChainsCached(boolean filterChainsCached)
1237     {
1238         _filterChainsCached = filterChainsCached;
1239     }
1240     
1241     /* ------------------------------------------------------------ */
1242     /**
1243      * @param filterMappings The filterMappings to set.
1244      */
1245     public void setFilterMappings(FilterMapping[] filterMappings)
1246     {
1247         if (getServer()!=null)
1248             getServer().getContainer().update(this,_filterMappings,filterMappings,"filterMapping",true);
1249         _filterMappings = filterMappings;
1250         updateMappings();
1251         invalidateChainsCache();
1252     }
1253     
1254     /* ------------------------------------------------------------ */
1255     public synchronized void setFilters(FilterHolder[] holders)
1256     {
1257         if (getServer()!=null)
1258             getServer().getContainer().update(this,_filters,holders,"filter",true);
1259         _filters=holders;
1260         updateNameMappings();
1261         invalidateChainsCache();
1262     }
1263     
1264     /* ------------------------------------------------------------ */
1265     /**
1266      * @param servletMappings The servletMappings to set.
1267      */
1268     public void setServletMappings(ServletMapping[] servletMappings)
1269     {
1270         if (getServer()!=null)
1271             getServer().getContainer().update(this,_servletMappings,servletMappings,"servletMapping",true);
1272         _servletMappings = servletMappings;
1273         updateMappings();
1274         invalidateChainsCache();
1275     }
1276     
1277     /* ------------------------------------------------------------ */
1278     /** Set Servlets.
1279      * @param holders Array of servletsto define
1280      */
1281     public synchronized void setServlets(ServletHolder[] holders)
1282     {
1283         if (getServer()!=null)
1284             getServer().getContainer().update(this,_servlets,holders,"servlet",true);
1285         _servlets=holders;
1286         updateNameMappings();
1287         invalidateChainsCache();
1288     }
1289 
1290 
1291     /* ------------------------------------------------------------ */
1292     /* ------------------------------------------------------------ */
1293     private class CachedChain implements FilterChain
1294     {
1295         FilterHolder _filterHolder;
1296         CachedChain _next;
1297         ServletHolder _servletHolder;
1298 
1299         /* ------------------------------------------------------------ */
1300         CachedChain(Object filters, ServletHolder servletHolder)
1301         {
1302             if (LazyList.size(filters)>0)
1303             {
1304                 _filterHolder=(FilterHolder)LazyList.get(filters, 0);
1305                 filters=LazyList.remove(filters,0);
1306                 _next=new CachedChain(filters,servletHolder);
1307             }
1308             else
1309                 _servletHolder=servletHolder;
1310         }
1311 
1312         /* ------------------------------------------------------------ */
1313         public void doFilter(ServletRequest request, ServletResponse response) 
1314             throws IOException, ServletException
1315         {
1316             // pass to next filter
1317             if (_filterHolder!=null)
1318             {
1319                 if (Log.isDebugEnabled())
1320                     Log.debug("call filter " + _filterHolder);
1321                 Filter filter= _filterHolder.getFilter();
1322                 if (_filterHolder.isAsyncSupported())
1323                     filter.doFilter(request, response, _next);
1324                 else
1325                 {
1326                     final Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
1327                     final boolean suspendable=baseRequest.isAsyncSupported();
1328                     if (suspendable)
1329                     {
1330                         try
1331                         {
1332                             baseRequest.setAsyncSupported(false);
1333                             filter.doFilter(request, response, _next);
1334                         }
1335                         finally
1336                         {
1337                             baseRequest.setAsyncSupported(true);
1338                         }
1339                     }
1340                     else
1341                         filter.doFilter(request, response, _next);
1342                 }
1343                 return;
1344             }
1345 
1346             // Call servlet
1347             if (_servletHolder != null)
1348             {
1349                 if (Log.isDebugEnabled())
1350                     Log.debug("call servlet " + _servletHolder);
1351                 final Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
1352                 _servletHolder.handle(baseRequest,request, response);
1353             }
1354             else // Not found
1355                 notFound((HttpServletRequest)request, (HttpServletResponse)response);
1356         }
1357         
1358         public String toString()
1359         {
1360             if (_filterHolder!=null)
1361                 return _filterHolder+"->"+_next.toString();
1362             if (_servletHolder!=null)
1363                 return _servletHolder.toString();
1364             return "null";
1365         }
1366     }  
1367     
1368     /* ------------------------------------------------------------ */
1369     /* ------------------------------------------------------------ */
1370     private class Chain implements FilterChain
1371     {
1372         final Request _baseRequest;
1373         final Object _chain;
1374         final ServletHolder _servletHolder;
1375         int _filter= 0;
1376 
1377         /* ------------------------------------------------------------ */
1378         Chain(Request baseRequest, Object filters, ServletHolder servletHolder)
1379         {
1380             _baseRequest=baseRequest;
1381             _chain= filters;
1382             _servletHolder= servletHolder;
1383         }
1384 
1385         /* ------------------------------------------------------------ */
1386         public void doFilter(ServletRequest request, ServletResponse response)
1387             throws IOException, ServletException
1388         {
1389             if (Log.isDebugEnabled()) Log.debug("doFilter " + _filter);
1390 
1391             // pass to next filter
1392             if (_filter < LazyList.size(_chain))
1393             {
1394                 FilterHolder holder= (FilterHolder)LazyList.get(_chain, _filter++);
1395                 if (Log.isDebugEnabled()) Log.debug("call filter " + holder);
1396                 Filter filter= holder.getFilter();
1397                 
1398                 if (holder.isAsyncSupported() || !_baseRequest.isAsyncSupported())
1399                 {
1400                     filter.doFilter(request, response, this);
1401                 }
1402                 else
1403                 {
1404                     try
1405                     {
1406                         _baseRequest.setAsyncSupported(false);
1407                         filter.doFilter(request, response, this);
1408                     }
1409                     finally
1410                     {
1411                         _baseRequest.setAsyncSupported(true);
1412                     }
1413                 }
1414                     
1415                 return;
1416             }
1417 
1418             // Call servlet
1419             if (_servletHolder != null)
1420             {
1421                 if (Log.isDebugEnabled()) Log.debug("call servlet " + _servletHolder);
1422                 _servletHolder.handle(_baseRequest,request, response);
1423             }
1424             else // Not found
1425                 notFound((HttpServletRequest)request, (HttpServletResponse)response);
1426         }
1427 
1428         /* ------------------------------------------------------------ */
1429         public String toString()
1430         {
1431             StringBuilder b = new StringBuilder();
1432             for (int i=0; i<LazyList.size(_chain);i++)
1433             {
1434                 Object o=LazyList.get(_chain, i);
1435                 b.append(o.toString());
1436                 b.append("->");
1437             }
1438             b.append(_servletHolder);
1439             return b.toString();
1440         }
1441     }
1442 
1443     /* ------------------------------------------------------------ */
1444     /**
1445      * @return The maximum entries in a filter chain cache.
1446      */
1447     public int getMaxFilterChainsCacheSize()
1448     {
1449         return _maxFilterChainsCacheSize;
1450     }
1451 
1452     /* ------------------------------------------------------------ */
1453     /** Set the maximum filter chain cache size.
1454      * Filter chains are cached if {@link #isFilterChainsCached()} is true. If the max cache size
1455      * is greater than zero, then the cache is flushed whenever it grows to be this size.
1456      * 
1457      * @param maxFilterChainsCacheSize  the maximum number of entries in a filter chain cache.
1458      */
1459     public void setMaxFilterChainsCacheSize(int maxFilterChainsCacheSize)
1460     {
1461         _maxFilterChainsCacheSize = maxFilterChainsCacheSize;
1462     }
1463     
1464     /* ------------------------------------------------------------ */
1465     void destroyServlet(Servlet servlet)
1466     {
1467         if (_contextHandler!=null)
1468             _contextHandler.destroyServlet(servlet);
1469     }
1470 
1471     /* ------------------------------------------------------------ */
1472     void destroyFilter(Filter filter)
1473     {
1474         if (_contextHandler!=null)
1475             _contextHandler.destroyFilter(filter);
1476     }
1477     
1478     /* ------------------------------------------------------------ */
1479     @Override
1480     public void dump(Appendable out,String indent) throws IOException
1481     {
1482         super.dumpThis(out);
1483         dump(out,indent,
1484                 TypeUtil.asList(getHandlers()),
1485                 getBeans(),
1486                 TypeUtil.asList(getFilterMappings()),
1487                 TypeUtil.asList(getFilters()),
1488                 TypeUtil.asList(getServletMappings()),
1489                 TypeUtil.asList(getServlets()));
1490     }
1491 }