View Javadoc

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