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