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