View Javadoc

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