View Javadoc

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