View Javadoc

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