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