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.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.servlet;
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;
31 import java.util.Queue;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.util.concurrent.ConcurrentLinkedQueue;
35 import java.util.concurrent.ConcurrentMap;
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;
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;
80 /* --------------------------------------------------------------------- */
81 /** Servlet HttpHandler.
82 * This handler maps requests to servlets that implement the
83 * javax.servlet.http.HttpServlet API.
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>.
89 * Unless run as part of a {@link ServletContextHandler} or derivative, the {@link #initialize()}
90 * method must be called manually after start().
93 /* ------------------------------------------------------------ */
96 @ManagedObject("Servlet Handler")
97 public class ServletHandler extends ScopedHandler
99 private static final Logger LOG = Log.getLogger(ServletHandler.class);
101 /* ------------------------------------------------------------ */
102 public static final String __DEFAULT_SERVLET="default";
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;
117 private ServletHolder[] _servlets=new ServletHolder[0];
118 private ServletMapping[] _servletMappings;
119 private Map<String,ServletMapping> _servletPathMappings = new HashMap<String,ServletMapping>();
121 private final Map<String,FilterHolder> _filterNameMap= new HashMap<>();
122 private List<FilterMapping> _filterPathMappings;
123 private MultiMap<FilterMapping> _filterNameMappings;
125 private final Map<String,ServletHolder> _servletNameMap=new HashMap<>();
126 private PathMap<ServletHolder> _servletPathMap;
128 private ListenerHolder[] _listeners=new ListenerHolder[0];
130 protected final ConcurrentMap<?, ?> _chainCache[] = new ConcurrentMap[FilterMapping.ALL];
131 protected final Queue<?>[] _chainLRU = new Queue[FilterMapping.ALL];
135 /* ------------------------------------------------------------ */
138 public ServletHandler()
142 /* ----------------------------------------------------------------- */
144 protected synchronized void doStart()
147 ContextHandler.Context context=ContextHandler.getCurrentContext();
148 _servletContext=context==null?new ContextHandler.NoContext():context;
149 _contextHandler=(ServletContextHandler)(context==null?null:context.getContextHandler());
151 if (_contextHandler!=null)
153 SecurityHandler security_handler = _contextHandler.getChildHandlerByClass(SecurityHandler.class);
154 if (security_handler!=null)
155 _identityService=security_handler.getIdentityService();
158 updateNameMappings();
161 if (getServletMapping("/")==null && _ensureDefaultServlet)
163 LOG.debug("Adding Default404Servlet to {}",this);
164 addServletWithMapping(Default404Servlet.class,"/");
166 getServletMapping("/").setDefault(true);
169 if(_filterChainsCached)
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>();
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>();
184 if (_contextHandler==null)
191 /* ------------------------------------------------------------ */
193 * @return true if ServletHandler always has a default servlet, using {@link Default404Servlet} if no other
194 * default servlet is configured.
196 public boolean isEnsureDefaultServlet()
198 return _ensureDefaultServlet;
201 /* ------------------------------------------------------------ */
203 * @param ensureDefaultServlet true if ServletHandler always has a default servlet, using {@link Default404Servlet} if no other
204 * default servlet is configured.
206 public void setEnsureDefaultServlet(boolean ensureDefaultServlet)
208 _ensureDefaultServlet=ensureDefaultServlet;
211 /* ----------------------------------------------------------------- */
213 protected void start(LifeCycle l) throws Exception
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))
223 /* ----------------------------------------------------------------- */
225 protected synchronized void doStop()
231 List<FilterHolder> filterHolders = new ArrayList<FilterHolder>();
232 List<FilterMapping> filterMappings = ArrayUtil.asMutableList(_filterMappings);
235 for (int i=_filters.length; i-->0;)
243 LOG.warn(Log.EXCEPTION,e);
245 if (_filters[i].getSource() != Source.EMBEDDED)
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())
253 FilterMapping fm = fmitor.next();
254 if (fm.getFilterName().equals(_filters[i].getName()))
259 filterHolders.add(_filters[i]); //only retain embedded
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);
267 FilterMapping[] fms = (FilterMapping[]) LazyList.toArray(filterMappings, FilterMapping.class);
268 updateBeans(_filterMappings, fms);
269 _filterMappings = fms;
271 _matchAfterIndex = (_filterMappings == null || _filterMappings.length == 0 ? -1 : _filterMappings.length-1);
272 _matchBeforeIndex = -1;
275 List<ServletHolder> servletHolders = new ArrayList<ServletHolder>(); //will be remaining servlets
276 List<ServletMapping> servletMappings = ArrayUtil.asMutableList(_servletMappings); //will be remaining mappings
279 for (int i=_servlets.length; i-->0;)
287 LOG.warn(Log.EXCEPTION,e);
290 if (_servlets[i].getSource() != Source.EMBEDDED)
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())
298 ServletMapping sm = smitor.next();
299 if (sm.getServletName().equals(_servlets[i].getName()))
304 servletHolders.add(_servlets[i]); //only retain embedded
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);
312 ServletMapping[] sms = (ServletMapping[])LazyList.toArray(servletMappings, ServletMapping.class);
313 updateBeans(_servletMappings, sms);
314 _servletMappings = sms;
316 //Retain only Listeners added via jetty apis (is Source.EMBEDDED)
317 List<ListenerHolder> listenerHolders = new ArrayList<ListenerHolder>();
318 if (_listeners != null)
320 for (int i=_listeners.length; i-->0;)
324 _listeners[i].stop();
328 LOG.warn(Log.EXCEPTION,e);
330 if (_listeners[i].getSource() == Source.EMBEDDED)
331 listenerHolders.add(_listeners[i]);
334 ListenerHolder[] listeners = (ListenerHolder[])LazyList.toArray(listenerHolders, ListenerHolder.class);
335 updateBeans(_listeners, listeners);
336 _listeners = listeners;
338 //will be regenerated on next start
339 _filterPathMappings=null;
340 _filterNameMappings=null;
341 _servletPathMap=null;
342 _servletPathMappings=null;
345 /* ------------------------------------------------------------ */
346 protected IdentityService getIdentityService()
348 return _identityService;
351 /* ------------------------------------------------------------ */
353 * @return Returns the contextLog.
355 public Object getContextLog()
360 /* ------------------------------------------------------------ */
362 * @return Returns the filterMappings.
364 @ManagedAttribute(value="filters", readonly=true)
365 public FilterMapping[] getFilterMappings()
367 return _filterMappings;
370 /* ------------------------------------------------------------ */
372 * @return Array of defined servlets
374 @ManagedAttribute(value="filters", readonly=true)
375 public FilterHolder[] getFilters()
380 /* ------------------------------------------------------------ */
381 /** ServletHolder matching path.
382 * @param pathInContext Path within _context.
383 * @return PathMap Entries pathspec to ServletHolder
385 public PathMap.MappedEntry<ServletHolder> getHolderEntry(String pathInContext)
387 if (_servletPathMap==null)
389 return _servletPathMap.getMatch(pathInContext);
392 /* ------------------------------------------------------------ */
393 public ServletContext getServletContext()
395 return _servletContext;
398 /* ------------------------------------------------------------ */
400 * @return Returns the servletMappings.
402 @ManagedAttribute(value="mappings of servlets", readonly=true)
403 public ServletMapping[] getServletMappings()
405 return _servletMappings;
408 /* ------------------------------------------------------------ */
410 * Get the ServletMapping matching the path
415 public ServletMapping getServletMapping(String pathSpec)
417 if (pathSpec == null || _servletPathMappings == null)
420 return _servletPathMappings.get(pathSpec);
423 /* ------------------------------------------------------------ */
425 * @return Array of defined servlets
427 @ManagedAttribute(value="servlets", readonly=true)
428 public ServletHolder[] getServlets()
433 /* ------------------------------------------------------------ */
434 public ServletHolder getServlet(String name)
436 return _servletNameMap.get(name);
439 /* ------------------------------------------------------------ */
441 public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
443 // Get the base requests
444 final String old_servlet_path=baseRequest.getServletPath();
445 final String old_path_info=baseRequest.getPathInfo();
447 DispatcherType type = baseRequest.getDispatcherType();
449 ServletHolder servlet_holder=null;
450 UserIdentity.Scope old_scope=null;
453 if (target.startsWith("/"))
455 // Look for the servlet by path
456 PathMap.MappedEntry<ServletHolder> entry=getHolderEntry(target);
459 servlet_holder=entry.getValue();
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);
465 if (DispatcherType.INCLUDE.equals(type))
467 baseRequest.setAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH,servlet_path);
468 baseRequest.setAttribute(RequestDispatcher.INCLUDE_PATH_INFO, path_info);
472 baseRequest.setServletPath(servlet_path);
473 baseRequest.setPathInfo(path_info);
479 // look for a servlet by name!
480 servlet_holder= _servletNameMap.get(target);
483 if (LOG.isDebugEnabled())
484 LOG.debug("servlet {}|{}|{} -> {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),servlet_holder);
488 // Do the filter/handling thang
489 old_scope=baseRequest.getUserIdentityScope();
490 baseRequest.setUserIdentityScope(servlet_holder);
492 // start manual inline of nextScope(target,baseRequest,request,response);
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);
500 doHandle(target,baseRequest,request, response);
501 // end manual inline (pathentic attempt to reduce stack depth)
506 baseRequest.setUserIdentityScope(old_scope);
508 if (!(DispatcherType.INCLUDE.equals(type)))
510 baseRequest.setServletPath(old_servlet_path);
511 baseRequest.setPathInfo(old_path_info);
516 /* ------------------------------------------------------------ */
518 * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
521 public void doHandle(String target, Request baseRequest,HttpServletRequest request, HttpServletResponse response)
522 throws IOException, ServletException
524 DispatcherType type = baseRequest.getDispatcherType();
526 ServletHolder servlet_holder=(ServletHolder) baseRequest.getUserIdentityScope();
527 FilterChain chain=null;
530 if (target.startsWith("/"))
532 if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0)
533 chain=getFilterChain(baseRequest, target, servlet_holder);
537 if (servlet_holder!=null)
539 if (_filterMappings!=null && _filterMappings.length>0)
541 chain=getFilterChain(baseRequest, null,servlet_holder);
546 LOG.debug("chain={}",chain);
550 if (servlet_holder==null)
551 notFound(baseRequest,request, response);
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();
562 // Do the filter/handling thang
564 chain.doFilter(req, res);
566 servlet_holder.handle(baseRequest,req,res);
569 catch(EofException e)
573 catch(RuntimeIOException e)
579 if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
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;
591 if (th instanceof ServletException)
593 if (th instanceof QuietServletException)
595 LOG.warn(th.toString());
601 else if (th instanceof EofException)
603 throw (EofException)th;
607 LOG.warn(request.getRequestURI(),th);
608 if (LOG.isDebugEnabled())
609 LOG.debug(request.toString());
612 request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
613 request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th);
614 if (!response.isCommitted())
616 baseRequest.getResponse().getHttpFields().put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);
617 if (th instanceof UnavailableException)
619 UnavailableException ue = (UnavailableException)th;
620 if (ue.isPermanent())
621 response.sendError(HttpServletResponse.SC_NOT_FOUND);
623 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
626 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
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);
641 if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
644 if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
646 LOG.warn("Error for "+request.getRequestURI(),e);
647 if(LOG.isDebugEnabled())
648 LOG.debug(request.toString());
650 request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
651 request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
652 if (!response.isCommitted())
654 baseRequest.getResponse().getHttpFields().put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);
655 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
658 LOG.debug("Response already committed for handling ",e);
662 // Complete async errored requests
663 if (th!=null && request.isAsyncStarted())
664 baseRequest.getHttpChannelState().errorComplete();
666 if (servlet_holder!=null)
667 baseRequest.setHandled(true);
671 /* ------------------------------------------------------------ */
672 protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
674 String key=pathInContext==null?servletHolder.getName():pathInContext;
675 int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType());
677 if (_filterChainsCached && _chainCache!=null)
679 FilterChain chain = (FilterChain)_chainCache[dispatch].get(key);
684 // Build list of filters (list of FilterHolder objects)
685 List<FilterHolder> filters = new ArrayList<>();
688 if (pathInContext!=null && _filterPathMappings!=null)
690 for (FilterMapping filterPathMapping : _filterPathMappings)
692 if (filterPathMapping.appliesTo(pathInContext, dispatch))
693 filters.add(filterPathMapping.getFilterHolder());
697 // Servlet name filters
698 if (servletHolder != null && _filterNameMappings!=null && _filterNameMappings.size() > 0)
700 // Servlet name filters
701 if (_filterNameMappings.size() > 0)
703 Object o= _filterNameMappings.get(servletHolder.getName());
705 for (int i=0; i<LazyList.size(o);i++)
707 FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
708 if (mapping.appliesTo(dispatch))
709 filters.add(mapping.getFilterHolder());
712 o= _filterNameMappings.get("*");
713 for (int i=0; i<LazyList.size(o);i++)
715 FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
716 if (mapping.appliesTo(dispatch))
717 filters.add(mapping.getFilterHolder());
722 if (filters.isEmpty())
726 FilterChain chain = null;
727 if (_filterChainsCached)
729 if (filters.size() > 0)
730 chain= new CachedChain(filters, servletHolder);
732 final Map<String,FilterChain> cache=(Map<String, FilterChain>)_chainCache[dispatch];
733 final Queue<String> lru=(Queue<String>)_chainLRU[dispatch];
735 // Do we have too many cached chains?
736 while (_maxFilterChainsCacheSize>0 && cache.size()>=_maxFilterChainsCacheSize)
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)
750 cache.put(key,chain);
753 else if (filters.size() > 0)
754 chain = new Chain(baseRequest,filters, servletHolder);
759 /* ------------------------------------------------------------ */
760 protected void invalidateChainsCache()
762 if (_chainLRU[FilterMapping.REQUEST]!=null)
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();
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();
778 /* ------------------------------------------------------------ */
780 * @return true if the handler is started and there are no unavailable servlets
782 public boolean isAvailable()
786 ServletHolder[] holders = getServlets();
787 for (ServletHolder holder : holders)
789 if (holder != null && !holder.isAvailable())
795 /* ------------------------------------------------------------ */
797 * @param start True if this handler will start with unavailable servlets
799 public void setStartWithUnavailable(boolean start)
801 _startWithUnavailable=start;
804 /* ------------------------------------------------------------ */
806 * @return True if this handler will start with unavailable servlets
808 public boolean isStartWithUnavailable()
810 return _startWithUnavailable;
815 /* ------------------------------------------------------------ */
816 /** Initialize filters and load-on-startup servlets.
818 public void initialize()
821 MultiException mx = new MultiException();
823 //start filter holders now
824 if (_filters != null)
826 for (FilterHolder f: _filters)
840 // Sort and Initialize servlets
843 ServletHolder[] servlets = _servlets.clone();
844 Arrays.sort(servlets);
845 for (ServletHolder servlet : servlets)
849 /* if (servlet.getClassName() == null && servlet.getForcedPath() != null)
851 ServletHolder forced_holder = _servletPathMap.match(servlet.getForcedPath());
852 if (forced_holder == null || forced_holder.getClassName() == null)
854 mx.add(new IllegalStateException("No forced path servlet for " + servlet.getForcedPath()));
857 System.err.println("ServletHandler setting forced path classname to "+forced_holder.getClassName()+ " for "+servlet.getForcedPath());
858 servlet.setClassName(forced_holder.getClassName());
862 servlet.initialize();
866 LOG.debug(Log.EXCEPTION, e);
873 for (Holder<?> h: getBeans(Holder.class))
889 mx.ifExceptionThrow();
892 /* ------------------------------------------------------------ */
894 * @return Returns the filterChainsCached.
896 public boolean isFilterChainsCached()
898 return _filterChainsCached;
901 /* ------------------------------------------------------------ */
902 /** Add a holder for a listener
905 public void addListener (ListenerHolder listener)
907 if (listener != null)
908 setListeners(ArrayUtil.addToArray(getListeners(), listener, ListenerHolder.class));
912 /* ------------------------------------------------------------ */
913 public ListenerHolder[] getListeners()
918 /* ------------------------------------------------------------ */
919 public void setListeners(ListenerHolder[] listeners)
922 for (ListenerHolder holder:listeners)
923 holder.setServletHandler(this);
925 updateBeans(_listeners,listeners);
926 _listeners = listeners;
929 /* ------------------------------------------------------------ */
930 public ListenerHolder newListenerHolder(Holder.Source source)
932 return new ListenerHolder(source);
935 /* ------------------------------------------------------------ */
937 * see also newServletHolder(Class)
939 public ServletHolder newServletHolder(Holder.Source source)
941 return new ServletHolder(source);
944 /* ------------------------------------------------------------ */
945 /** Convenience method to add a servlet.
946 * @return The servlet holder.
948 public ServletHolder addServletWithMapping (String className,String pathSpec)
950 ServletHolder holder = newServletHolder(Source.EMBEDDED);
951 holder.setClassName(className);
952 addServletWithMapping(holder,pathSpec);
956 /* ------------------------------------------------------------ */
957 /** conveniance method to add a servlet.
958 * @return The servlet holder.
960 public ServletHolder addServletWithMapping (Class<? extends Servlet> servlet,String pathSpec)
962 ServletHolder holder = newServletHolder(Source.EMBEDDED);
963 holder.setHeldClass(servlet);
964 addServletWithMapping(holder,pathSpec);
969 /* ------------------------------------------------------------ */
970 /** conveniance method to add a servlet.
971 * @param servlet servlet holder to add
972 * @param pathSpec servlet mappings for the servletHolder
974 public void addServletWithMapping (ServletHolder servlet,String pathSpec)
976 ServletHolder[] holders=getServlets();
978 holders = holders.clone();
982 setServlets(ArrayUtil.addToArray(holders, servlet, ServletHolder.class));
984 ServletMapping mapping = new ServletMapping();
985 mapping.setServletName(servlet.getName());
986 mapping.setPathSpec(pathSpec);
987 setServletMappings(ArrayUtil.addToArray(getServletMappings(), mapping, ServletMapping.class));
991 setServlets(holders);
992 if (e instanceof RuntimeException)
993 throw (RuntimeException)e;
994 throw new RuntimeException(e);
999 /* ------------------------------------------------------------ */
1000 /**Convenience method to add a pre-constructed ServletHolder.
1003 public void addServlet(ServletHolder holder)
1005 setServlets(ArrayUtil.addToArray(getServlets(), holder, ServletHolder.class));
1008 /* ------------------------------------------------------------ */
1009 /** Convenience method to add a pre-constructed ServletMapping.
1012 public void addServletMapping (ServletMapping mapping)
1014 setServletMappings(ArrayUtil.addToArray(getServletMappings(), mapping, ServletMapping.class));
1017 /* ------------------------------------------------------------ */
1018 public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement)
1020 if (_contextHandler != null)
1022 return _contextHandler.setServletSecurity(registration, servletSecurityElement);
1024 return Collections.emptySet();
1027 /* ------------------------------------------------------------ */
1028 public FilterHolder newFilterHolder(Holder.Source source)
1030 return new FilterHolder(source);
1033 /* ------------------------------------------------------------ */
1034 public FilterHolder getFilter(String name)
1036 return _filterNameMap.get(name);
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.
1047 public FilterHolder addFilterWithMapping (Class<? extends Filter> filter,String pathSpec,EnumSet<DispatcherType> dispatches)
1049 FilterHolder holder = newFilterHolder(Source.EMBEDDED);
1050 holder.setHeldClass(filter);
1051 addFilterWithMapping(holder,pathSpec,dispatches);
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.
1063 public FilterHolder addFilterWithMapping (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
1065 FilterHolder holder = newFilterHolder(Source.EMBEDDED);
1066 holder.setClassName(className);
1068 addFilterWithMapping(holder,pathSpec,dispatches);
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)}
1078 public void addFilterWithMapping (FilterHolder holder,String pathSpec,EnumSet<DispatcherType> dispatches)
1080 FilterHolder[] holders = getFilters();
1082 holders = holders.clone();
1086 setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
1088 FilterMapping mapping = new FilterMapping();
1089 mapping.setFilterName(holder.getName());
1090 mapping.setPathSpec(pathSpec);
1091 mapping.setDispatcherTypes(dispatches);
1092 addFilterMapping(mapping);
1095 catch (RuntimeException e)
1097 setFilters(holders);
1102 setFilters(holders);
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.
1115 public FilterHolder addFilterWithMapping (Class<? extends Filter> filter,String pathSpec,int dispatches)
1117 FilterHolder holder = newFilterHolder(Source.EMBEDDED);
1118 holder.setHeldClass(filter);
1119 addFilterWithMapping(holder,pathSpec,dispatches);
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.
1131 public FilterHolder addFilterWithMapping (String className,String pathSpec,int dispatches)
1133 FilterHolder holder = newFilterHolder(Source.EMBEDDED);
1134 holder.setClassName(className);
1136 addFilterWithMapping(holder,pathSpec,dispatches);
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)}
1146 public void addFilterWithMapping (FilterHolder holder,String pathSpec,int dispatches)
1148 FilterHolder[] holders = getFilters();
1150 holders = holders.clone();
1154 setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
1156 FilterMapping mapping = new FilterMapping();
1157 mapping.setFilterName(holder.getName());
1158 mapping.setPathSpec(pathSpec);
1159 mapping.setDispatches(dispatches);
1160 addFilterMapping(mapping);
1162 catch (RuntimeException e)
1164 setFilters(holders);
1169 setFilters(holders);
1175 /* ------------------------------------------------------------ */
1176 /** Convenience method to add a filter with a mapping
1180 * @return the filter holder created
1181 * @deprecated use {@link #addFilterWithMapping(Class, String, EnumSet)} instead
1183 public FilterHolder addFilter (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
1185 return addFilterWithMapping(className, pathSpec, dispatches);
1188 /* ------------------------------------------------------------ */
1190 * convenience method to add a filter and mapping
1192 * @param filterMapping
1194 public void addFilter (FilterHolder filter, FilterMapping filterMapping)
1197 setFilters(ArrayUtil.addToArray(getFilters(), filter, FilterHolder.class));
1198 if (filterMapping != null)
1199 addFilterMapping(filterMapping);
1202 /* ------------------------------------------------------------ */
1203 /** Convenience method to add a preconstructed FilterHolder
1206 public void addFilter (FilterHolder filter)
1209 setFilters(ArrayUtil.addToArray(getFilters(), filter, FilterHolder.class));
1212 /* ------------------------------------------------------------ */
1213 /** Convenience method to add a preconstructed FilterMapping
1216 public void addFilterMapping (FilterMapping mapping)
1218 if (mapping != null)
1220 Source source = (mapping.getFilterHolder()==null?null:mapping.getFilterHolder().getSource());
1221 FilterMapping[] mappings =getFilterMappings();
1222 if (mappings==null || mappings.length==0)
1224 setFilterMappings(insertFilterMapping(mapping,0,false));
1225 if (source != null && source == Source.JAVAX_API)
1226 _matchAfterIndex = 0;
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)
1235 setFilterMappings(insertFilterMapping(mapping,mappings.length-1, false));
1236 if (_matchAfterIndex < 0)
1237 _matchAfterIndex = getFilterMappings().length-1;
1241 //insert non-programmatic filter mappings before any matchAfters, if any
1242 if (_matchAfterIndex < 0)
1243 setFilterMappings(insertFilterMapping(mapping,mappings.length-1, false));
1246 FilterMapping[] new_mappings = insertFilterMapping(mapping, _matchAfterIndex, true);
1248 setFilterMappings(new_mappings);
1256 /* ------------------------------------------------------------ */
1257 /** Convenience method to add a preconstructed FilterMapping
1260 public void prependFilterMapping (FilterMapping mapping)
1262 if (mapping != null)
1264 Source source = mapping.getFilterHolder().getSource();
1266 FilterMapping[] mappings = getFilterMappings();
1267 if (mappings==null || mappings.length==0)
1269 setFilterMappings(insertFilterMapping(mapping, 0, false));
1270 if (source != null && Source.JAVAX_API == source)
1271 _matchBeforeIndex = 0;
1275 if (source != null && Source.JAVAX_API == source)
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.
1281 if (_matchBeforeIndex < 0)
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);
1290 FilterMapping[] new_mappings = insertFilterMapping(mapping,_matchBeforeIndex, false);
1291 ++_matchBeforeIndex;
1292 setFilterMappings(new_mappings);
1297 //non programmatically defined, just prepend to list
1298 FilterMapping[] new_mappings = insertFilterMapping(mapping, 0, true);
1299 setFilterMappings(new_mappings);
1302 //adjust matchAfterIndex ptr to take account of the mapping we just prepended
1303 if (_matchAfterIndex >= 0)
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
1318 protected FilterMapping[] insertFilterMapping (FilterMapping mapping, int pos, boolean before)
1321 throw new IllegalArgumentException("FilterMapping insertion pos < 0");
1322 FilterMapping[] mappings = getFilterMappings();
1324 if (mappings==null || mappings.length==0)
1326 return new FilterMapping[] {mapping};
1328 FilterMapping[] new_mappings = new FilterMapping[mappings.length+1];
1333 //copy existing filter mappings up to but not including the pos
1334 System.arraycopy(mappings,0,new_mappings,0,pos);
1336 //add in the new mapping
1337 new_mappings[pos] = mapping;
1339 //copy the old pos mapping and any remaining existing mappings
1340 System.arraycopy(mappings,pos,new_mappings,pos+1, mappings.length-pos);
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;
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));
1354 return new_mappings;
1358 /* ------------------------------------------------------------ */
1359 protected synchronized void updateNameMappings()
1361 // update filter name map
1362 _filterNameMap.clear();
1365 for (FilterHolder filter : _filters)
1367 _filterNameMap.put(filter.getName(), filter);
1368 filter.setServletHandler(this);
1372 // Map servlet names to holders
1373 _servletNameMap.clear();
1374 if (_servlets!=null)
1377 for (ServletHolder servlet : _servlets)
1379 _servletNameMap.put(servlet.getName(), servlet);
1380 servlet.setServletHandler(this);
1385 /* ------------------------------------------------------------ */
1386 protected synchronized void updateMappings()
1388 // update filter mappings
1389 if (_filterMappings==null)
1391 _filterPathMappings=null;
1392 _filterNameMappings=null;
1396 _filterPathMappings=new ArrayList<>();
1397 _filterNameMappings=new MultiMap<FilterMapping>();
1398 for (FilterMapping filtermapping : _filterMappings)
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);
1407 if (filtermapping.getServletNames() != null)
1409 String[] names = filtermapping.getServletNames();
1410 for (String name : names)
1413 _filterNameMappings.add(name, filtermapping);
1419 // Map servlet paths to holders
1420 if (_servletMappings==null || _servletNameMap==null)
1422 _servletPathMap=null;
1426 PathMap<ServletHolder> pm = new PathMap<>();
1427 Map<String,ServletMapping> servletPathMappings = new HashMap<String,ServletMapping>();
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)
1433 String[] pathSpecs = servletMapping.getPathSpecs();
1434 if (pathSpecs != null)
1436 for (String pathSpec : pathSpecs)
1438 Set<ServletMapping> mappings = sms.get(pathSpec);
1439 if (mappings == null)
1441 mappings = new HashSet<ServletMapping>();
1442 sms.put(pathSpec, mappings);
1444 mappings.add(servletMapping);
1449 //evaluate path to servlet map based on servlet mappings
1450 for (String pathSpec : sms.keySet())
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);
1458 ServletMapping finalMapping = null;
1459 for (ServletMapping mapping : mappings)
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())
1469 //only accept a default mapping if we don't have any other
1470 if (finalMapping == null)
1471 finalMapping = mapping;
1474 //already have a candidate - only accept another one if the candidate is a default
1475 if (finalMapping.isDefault())
1476 finalMapping = mapping;
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());
1485 if (finalMapping == null)
1486 throw new IllegalStateException ("No acceptable servlet mappings for "+pathSpec);
1488 if (LOG.isDebugEnabled()) LOG.debug("Chose path={} mapped to servlet={} from default={}", pathSpec, finalMapping.getServletName(), finalMapping.isDefault());
1490 servletPathMappings.put(pathSpec, finalMapping);
1491 pm.put(pathSpec,_servletNameMap.get(finalMapping.getServletName()));
1495 _servletPathMappings=servletPathMappings;
1498 // flush filter chain cache
1499 if (_chainCache!=null)
1501 for (int i=_chainCache.length;i-->0;)
1503 if (_chainCache[i]!=null)
1504 _chainCache[i].clear();
1508 if (LOG.isDebugEnabled())
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);
1519 if (_contextHandler!=null && _contextHandler.isStarted() || _contextHandler==null && isStarted())
1524 throw new RuntimeException(e);
1528 /* ------------------------------------------------------------ */
1529 protected void notFound(Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
1531 LOG.debug("Not Found {}",request.getRequestURI());
1532 if (getHandler()!=null)
1533 nextHandle(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()),baseRequest,request,response);
1536 /* ------------------------------------------------------------ */
1538 * @param filterChainsCached The filterChainsCached to set.
1540 public void setFilterChainsCached(boolean filterChainsCached)
1542 _filterChainsCached = filterChainsCached;
1545 /* ------------------------------------------------------------ */
1547 * @param filterMappings The filterMappings to set.
1549 public void setFilterMappings(FilterMapping[] filterMappings)
1551 updateBeans(_filterMappings,filterMappings);
1552 _filterMappings = filterMappings;
1554 invalidateChainsCache();
1557 /* ------------------------------------------------------------ */
1558 public synchronized void setFilters(FilterHolder[] holders)
1561 for (FilterHolder holder:holders)
1562 holder.setServletHandler(this);
1564 updateBeans(_filters,holders);
1566 updateNameMappings();
1567 invalidateChainsCache();
1570 /* ------------------------------------------------------------ */
1572 * @param servletMappings The servletMappings to set.
1574 public void setServletMappings(ServletMapping[] servletMappings)
1576 updateBeans(_servletMappings,servletMappings);
1577 _servletMappings = servletMappings;
1579 invalidateChainsCache();
1582 /* ------------------------------------------------------------ */
1584 * @param holders Array of servlets to define
1586 public synchronized void setServlets(ServletHolder[] holders)
1589 for (ServletHolder holder:holders)
1590 holder.setServletHandler(this);
1592 updateBeans(_servlets,holders);
1594 updateNameMappings();
1595 invalidateChainsCache();
1598 /* ------------------------------------------------------------ */
1599 /* ------------------------------------------------------------ */
1600 private class CachedChain implements FilterChain
1602 FilterHolder _filterHolder;
1604 ServletHolder _servletHolder;
1606 /* ------------------------------------------------------------ */
1608 * @param filters list of {@link FilterHolder} objects
1609 * @param servletHolder
1611 CachedChain(List<FilterHolder> filters, ServletHolder servletHolder)
1613 if (filters.size()>0)
1615 _filterHolder=filters.get(0);
1617 _next=new CachedChain(filters,servletHolder);
1620 _servletHolder=servletHolder;
1623 /* ------------------------------------------------------------ */
1625 public void doFilter(ServletRequest request, ServletResponse response)
1626 throws IOException, ServletException
1628 final Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
1630 // pass to next filter
1631 if (_filterHolder!=null)
1633 LOG.debug("call filter {}", _filterHolder);
1634 Filter filter= _filterHolder.getFilter();
1635 if (_filterHolder.isAsyncSupported())
1636 filter.doFilter(request, response, _next);
1639 final boolean suspendable=baseRequest.isAsyncSupported();
1644 baseRequest.setAsyncSupported(false);
1645 filter.doFilter(request, response, _next);
1649 baseRequest.setAsyncSupported(true);
1653 filter.doFilter(request, response, _next);
1659 HttpServletRequest srequest = (HttpServletRequest)request;
1660 if (_servletHolder == null)
1661 notFound(baseRequest, srequest, (HttpServletResponse)response);
1664 if (LOG.isDebugEnabled())
1665 LOG.debug("call servlet " + _servletHolder);
1666 _servletHolder.handle(baseRequest,request, response);
1671 public String toString()
1673 if (_filterHolder!=null)
1674 return _filterHolder+"->"+_next.toString();
1675 if (_servletHolder!=null)
1676 return _servletHolder.toString();
1681 /* ------------------------------------------------------------ */
1682 /* ------------------------------------------------------------ */
1683 private class Chain implements FilterChain
1685 final Request _baseRequest;
1686 final List<FilterHolder> _chain;
1687 final ServletHolder _servletHolder;
1690 /* ------------------------------------------------------------ */
1691 Chain(Request baseRequest, List<FilterHolder> filters, ServletHolder servletHolder)
1693 _baseRequest=baseRequest;
1695 _servletHolder= servletHolder;
1698 /* ------------------------------------------------------------ */
1700 public void doFilter(ServletRequest request, ServletResponse response)
1701 throws IOException, ServletException
1703 if (LOG.isDebugEnabled())
1704 LOG.debug("doFilter " + _filter);
1706 // pass to next filter
1707 if (_filter < _chain.size())
1709 FilterHolder holder= _chain.get(_filter++);
1710 if (LOG.isDebugEnabled())
1711 LOG.debug("call filter " + holder);
1712 Filter filter= holder.getFilter();
1714 if (holder.isAsyncSupported() || !_baseRequest.isAsyncSupported())
1716 filter.doFilter(request, response, this);
1722 _baseRequest.setAsyncSupported(false);
1723 filter.doFilter(request, response, this);
1727 _baseRequest.setAsyncSupported(true);
1735 HttpServletRequest srequest = (HttpServletRequest)request;
1736 if (_servletHolder == null)
1737 notFound((request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(), srequest, (HttpServletResponse)response);
1740 LOG.debug("call servlet {}", _servletHolder);
1741 _servletHolder.handle(_baseRequest,request, response);
1745 /* ------------------------------------------------------------ */
1747 public String toString()
1749 StringBuilder b = new StringBuilder();
1750 for(FilterHolder f: _chain)
1752 b.append(f.toString());
1755 b.append(_servletHolder);
1756 return b.toString();
1760 /* ------------------------------------------------------------ */
1762 * @return The maximum entries in a filter chain cache.
1764 public int getMaxFilterChainsCacheSize()
1766 return _maxFilterChainsCacheSize;
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.
1774 * @param maxFilterChainsCacheSize the maximum number of entries in a filter chain cache.
1776 public void setMaxFilterChainsCacheSize(int maxFilterChainsCacheSize)
1778 _maxFilterChainsCacheSize = maxFilterChainsCacheSize;
1781 /* ------------------------------------------------------------ */
1782 void destroyServlet(Servlet servlet)
1784 if (_contextHandler!=null)
1785 _contextHandler.destroyServlet(servlet);
1788 /* ------------------------------------------------------------ */
1789 void destroyFilter(Filter filter)
1791 if (_contextHandler!=null)
1792 _contextHandler.destroyFilter(filter);
1795 /* ------------------------------------------------------------ */
1796 /* ------------------------------------------------------------ */
1797 /* ------------------------------------------------------------ */
1798 public static class Default404Servlet extends HttpServlet
1801 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
1802 throws ServletException, IOException
1804 resp.sendError(HttpServletResponse.SC_NOT_FOUND);