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