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