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