2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
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 final Map<String,FilterHolder> _filterNameMap= new HashMap<>();
120 private List<FilterMapping> _filterPathMappings;
121 private MultiMap<FilterMapping> _filterNameMappings;
123 private final Map<String,ServletHolder> _servletNameMap=new HashMap<>();
124 private PathMap<ServletHolder> _servletPathMap;
126 private ListenerHolder[] _listeners=new ListenerHolder[0];
128 @SuppressWarnings("unchecked")
129 protected final ConcurrentMap<String, FilterChain> _chainCache[] = new ConcurrentMap[FilterMapping.ALL];
131 @SuppressWarnings("unchecked")
132 protected final Queue<String>[] _chainLRU = new Queue[FilterMapping.ALL];
136 /* ------------------------------------------------------------ */
139 public ServletHandler()
143 /* ----------------------------------------------------------------- */
145 protected synchronized void doStart()
148 ContextHandler.Context context=ContextHandler.getCurrentContext();
149 _servletContext=context==null?new ContextHandler.NoContext():context;
150 _contextHandler=(ServletContextHandler)(context==null?null:context.getContextHandler());
152 if (_contextHandler!=null)
154 SecurityHandler security_handler = _contextHandler.getChildHandlerByClass(SecurityHandler.class);
155 if (security_handler!=null)
156 _identityService=security_handler.getIdentityService();
159 updateNameMappings();
162 if (getServletMapping("/")==null && _ensureDefaultServlet)
164 if (LOG.isDebugEnabled())
165 LOG.debug("Adding Default404Servlet to {}",this);
166 addServletWithMapping(Default404Servlet.class,"/");
168 getServletMapping("/").setDefault(true);
171 if(_filterChainsCached)
173 _chainCache[FilterMapping.REQUEST]=new ConcurrentHashMap<String,FilterChain>();
174 _chainCache[FilterMapping.FORWARD]=new ConcurrentHashMap<String,FilterChain>();
175 _chainCache[FilterMapping.INCLUDE]=new ConcurrentHashMap<String,FilterChain>();
176 _chainCache[FilterMapping.ERROR]=new ConcurrentHashMap<String,FilterChain>();
177 _chainCache[FilterMapping.ASYNC]=new ConcurrentHashMap<String,FilterChain>();
179 _chainLRU[FilterMapping.REQUEST]=new ConcurrentLinkedQueue<String>();
180 _chainLRU[FilterMapping.FORWARD]=new ConcurrentLinkedQueue<String>();
181 _chainLRU[FilterMapping.INCLUDE]=new ConcurrentLinkedQueue<String>();
182 _chainLRU[FilterMapping.ERROR]=new ConcurrentLinkedQueue<String>();
183 _chainLRU[FilterMapping.ASYNC]=new ConcurrentLinkedQueue<String>();
186 if (_contextHandler==null)
193 /* ------------------------------------------------------------ */
195 * @return true if ServletHandler always has a default servlet, using {@link Default404Servlet} if no other
196 * default servlet is configured.
198 public boolean isEnsureDefaultServlet()
200 return _ensureDefaultServlet;
203 /* ------------------------------------------------------------ */
205 * @param ensureDefaultServlet true if ServletHandler always has a default servlet, using {@link Default404Servlet} if no other
206 * default servlet is configured.
208 public void setEnsureDefaultServlet(boolean ensureDefaultServlet)
210 _ensureDefaultServlet=ensureDefaultServlet;
213 /* ----------------------------------------------------------------- */
215 protected void start(LifeCycle l) throws Exception
217 //Don't start the whole object tree (ie all the servlet and filter Holders) when
218 //this handler starts. They have a slightly special lifecycle, and should only be
219 //started AFTER the handlers have all started (and the ContextHandler has called
220 //the context listeners).
221 if (!(l instanceof Holder))
225 /* ----------------------------------------------------------------- */
227 protected synchronized void doStop()
233 List<FilterHolder> filterHolders = new ArrayList<FilterHolder>();
234 List<FilterMapping> filterMappings = ArrayUtil.asMutableList(_filterMappings);
237 for (int i=_filters.length; i-->0;)
245 LOG.warn(Log.EXCEPTION,e);
247 if (_filters[i].getSource() != Source.EMBEDDED)
249 //remove all of the mappings that were for non-embedded filters
250 _filterNameMap.remove(_filters[i].getName());
251 //remove any mappings associated with this filter
252 ListIterator<FilterMapping> fmitor = filterMappings.listIterator();
253 while (fmitor.hasNext())
255 FilterMapping fm = fmitor.next();
256 if (fm.getFilterName().equals(_filters[i].getName()))
261 filterHolders.add(_filters[i]); //only retain embedded
265 //Retain only filters and mappings that were added using jetty api (ie Source.EMBEDDED)
266 FilterHolder[] fhs = (FilterHolder[]) LazyList.toArray(filterHolders, FilterHolder.class);
267 updateBeans(_filters, fhs);
269 FilterMapping[] fms = (FilterMapping[]) LazyList.toArray(filterMappings, FilterMapping.class);
270 updateBeans(_filterMappings, fms);
271 _filterMappings = fms;
273 _matchAfterIndex = (_filterMappings == null || _filterMappings.length == 0 ? -1 : _filterMappings.length-1);
274 _matchBeforeIndex = -1;
277 List<ServletHolder> servletHolders = new ArrayList<ServletHolder>(); //will be remaining servlets
278 List<ServletMapping> servletMappings = ArrayUtil.asMutableList(_servletMappings); //will be remaining mappings
281 for (int i=_servlets.length; i-->0;)
289 LOG.warn(Log.EXCEPTION,e);
292 if (_servlets[i].getSource() != Source.EMBEDDED)
294 //remove from servlet name map
295 _servletNameMap.remove(_servlets[i].getName());
296 //remove any mappings associated with this servlet
297 ListIterator<ServletMapping> smitor = servletMappings.listIterator();
298 while (smitor.hasNext())
300 ServletMapping sm = smitor.next();
301 if (sm.getServletName().equals(_servlets[i].getName()))
306 servletHolders.add(_servlets[i]); //only retain embedded
310 //Retain only Servlets and mappings added via jetty apis (ie Source.EMBEDDED)
311 ServletHolder[] shs = (ServletHolder[]) LazyList.toArray(servletHolders, ServletHolder.class);
312 updateBeans(_servlets, shs);
314 ServletMapping[] sms = (ServletMapping[])LazyList.toArray(servletMappings, ServletMapping.class);
315 updateBeans(_servletMappings, sms);
316 _servletMappings = sms;
318 //Retain only Listeners added via jetty apis (is Source.EMBEDDED)
319 List<ListenerHolder> listenerHolders = new ArrayList<ListenerHolder>();
320 if (_listeners != null)
322 for (int i=_listeners.length; i-->0;)
326 _listeners[i].stop();
330 LOG.warn(Log.EXCEPTION,e);
332 if (_listeners[i].getSource() == Source.EMBEDDED)
333 listenerHolders.add(_listeners[i]);
336 ListenerHolder[] listeners = (ListenerHolder[])LazyList.toArray(listenerHolders, ListenerHolder.class);
337 updateBeans(_listeners, listeners);
338 _listeners = listeners;
340 //will be regenerated on next start
341 _filterPathMappings=null;
342 _filterNameMappings=null;
343 _servletPathMap=null;
346 /* ------------------------------------------------------------ */
347 protected IdentityService getIdentityService()
349 return _identityService;
352 /* ------------------------------------------------------------ */
354 * @return Returns the contextLog.
356 public Object getContextLog()
361 /* ------------------------------------------------------------ */
363 * @return Returns the filterMappings.
365 @ManagedAttribute(value="filters", readonly=true)
366 public FilterMapping[] getFilterMappings()
368 return _filterMappings;
371 /* ------------------------------------------------------------ */
373 * @return Array of defined servlets
375 @ManagedAttribute(value="filters", readonly=true)
376 public FilterHolder[] getFilters()
381 /* ------------------------------------------------------------ */
382 /** ServletHolder matching path.
383 * @param pathInContext Path within _context.
384 * @return PathMap Entries pathspec to ServletHolder
386 public PathMap.MappedEntry<ServletHolder> getHolderEntry(String pathInContext)
388 if (_servletPathMap==null)
390 return _servletPathMap.getMatch(pathInContext);
393 /* ------------------------------------------------------------ */
394 public ServletContext getServletContext()
396 return _servletContext;
399 /* ------------------------------------------------------------ */
401 * @return Returns the servletMappings.
403 @ManagedAttribute(value="mappings of servlets", readonly=true)
404 public ServletMapping[] getServletMappings()
406 return _servletMappings;
409 /* ------------------------------------------------------------ */
411 * Get the ServletMapping matching the path
416 public ServletMapping getServletMapping(String pathSpec)
418 if (pathSpec == null || _servletMappings == null)
421 ServletMapping mapping = null;
422 for (int i=0; i<_servletMappings.length && mapping == null; i++)
424 ServletMapping m = _servletMappings[i];
425 if (m.getPathSpecs() != null)
427 for (String p:m.getPathSpecs())
429 if (pathSpec.equals(p))
440 /* ------------------------------------------------------------ */
442 * @return Array of defined servlets
444 @ManagedAttribute(value="servlets", readonly=true)
445 public ServletHolder[] getServlets()
450 /* ------------------------------------------------------------ */
451 public ServletHolder getServlet(String name)
453 return _servletNameMap.get(name);
456 /* ------------------------------------------------------------ */
458 public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
460 // Get the base requests
461 final String old_servlet_path=baseRequest.getServletPath();
462 final String old_path_info=baseRequest.getPathInfo();
464 DispatcherType type = baseRequest.getDispatcherType();
466 ServletHolder servlet_holder=null;
467 UserIdentity.Scope old_scope=null;
470 if (target.startsWith("/"))
472 // Look for the servlet by path
473 PathMap.MappedEntry<ServletHolder> entry=getHolderEntry(target);
476 servlet_holder=entry.getValue();
478 String servlet_path_spec= entry.getKey();
479 String servlet_path=entry.getMapped()!=null?entry.getMapped():PathMap.pathMatch(servlet_path_spec,target);
480 String path_info=PathMap.pathInfo(servlet_path_spec,target);
482 if (DispatcherType.INCLUDE.equals(type))
484 baseRequest.setAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH,servlet_path);
485 baseRequest.setAttribute(RequestDispatcher.INCLUDE_PATH_INFO, path_info);
489 baseRequest.setServletPath(servlet_path);
490 baseRequest.setPathInfo(path_info);
496 // look for a servlet by name!
497 servlet_holder= _servletNameMap.get(target);
500 if (LOG.isDebugEnabled())
501 LOG.debug("servlet {}|{}|{} -> {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),servlet_holder);
505 // Do the filter/handling thang
506 old_scope=baseRequest.getUserIdentityScope();
507 baseRequest.setUserIdentityScope(servlet_holder);
509 // start manual inline of nextScope(target,baseRequest,request,response);
511 nextScope(target,baseRequest,request,response);
512 else if (_nextScope!=null)
513 _nextScope.doScope(target,baseRequest,request, response);
514 else if (_outerScope!=null)
515 _outerScope.doHandle(target,baseRequest,request, response);
517 doHandle(target,baseRequest,request, response);
518 // end manual inline (pathentic attempt to reduce stack depth)
523 baseRequest.setUserIdentityScope(old_scope);
525 if (!(DispatcherType.INCLUDE.equals(type)))
527 baseRequest.setServletPath(old_servlet_path);
528 baseRequest.setPathInfo(old_path_info);
533 /* ------------------------------------------------------------ */
535 * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
538 public void doHandle(String target, Request baseRequest,HttpServletRequest request, HttpServletResponse response)
539 throws IOException, ServletException
541 DispatcherType type = baseRequest.getDispatcherType();
543 ServletHolder servlet_holder=(ServletHolder) baseRequest.getUserIdentityScope();
544 FilterChain chain=null;
547 if (target.startsWith("/"))
549 if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0)
550 chain=getFilterChain(baseRequest, target, servlet_holder);
554 if (servlet_holder!=null)
556 if (_filterMappings!=null && _filterMappings.length>0)
558 chain=getFilterChain(baseRequest, null,servlet_holder);
563 if (LOG.isDebugEnabled())
564 LOG.debug("chain={}",chain);
569 if (servlet_holder==null)
570 notFound(baseRequest,request, response);
573 // unwrap any tunnelling of base Servlet request/responses
574 ServletRequest req = request;
575 if (req instanceof ServletRequestHttpWrapper)
576 req = ((ServletRequestHttpWrapper)req).getRequest();
577 ServletResponse res = response;
578 if (res instanceof ServletResponseHttpWrapper)
579 res = ((ServletResponseHttpWrapper)res).getResponse();
581 // Do the filter/handling thang
582 servlet_holder.prepare(baseRequest, req, res);
585 chain.doFilter(req, res);
587 servlet_holder.handle(baseRequest,req,res);
590 catch(EofException e)
594 catch(RuntimeIOException e)
600 if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
602 if (e instanceof IOException)
603 throw (IOException)e;
604 if (e instanceof RuntimeException)
605 throw (RuntimeException)e;
606 if (e instanceof ServletException)
607 throw (ServletException)e;
612 if (th instanceof ServletException)
614 if (th instanceof QuietServletException)
616 LOG.warn(th.toString());
622 else if (th instanceof EofException)
624 throw (EofException)th;
628 LOG.warn(request.getRequestURI(),th);
629 if (LOG.isDebugEnabled())
630 LOG.debug(request.toString());
633 request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
634 request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th);
635 if (!response.isCommitted())
637 baseRequest.getResponse().getHttpFields().put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);
638 if (th instanceof UnavailableException)
640 UnavailableException ue = (UnavailableException)th;
641 if (ue.isPermanent())
642 response.sendError(HttpServletResponse.SC_NOT_FOUND);
644 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
647 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
651 if (th instanceof IOException)
652 throw (IOException)th;
653 if (th instanceof RuntimeException)
654 throw (RuntimeException)th;
655 if (th instanceof ServletException)
656 throw (ServletException)th;
657 throw new IllegalStateException("response already committed",th);
662 if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
665 if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
667 LOG.warn("Error for "+request.getRequestURI(),e);
668 if(LOG.isDebugEnabled())
669 LOG.debug(request.toString());
671 request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
672 request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
673 if (!response.isCommitted())
675 baseRequest.getResponse().getHttpFields().put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);
676 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
679 LOG.debug("Response already committed for handling ",e);
683 // Complete async errored requests
684 if (th!=null && request.isAsyncStarted())
685 baseRequest.getHttpChannelState().errorComplete();
687 if (servlet_holder!=null)
688 baseRequest.setHandled(true);
692 /* ------------------------------------------------------------ */
693 protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
695 String key=pathInContext==null?servletHolder.getName():pathInContext;
696 int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType());
698 if (_filterChainsCached && _chainCache!=null)
700 FilterChain chain = (FilterChain)_chainCache[dispatch].get(key);
705 // Build list of filters (list of FilterHolder objects)
706 List<FilterHolder> filters = new ArrayList<>();
709 if (pathInContext!=null && _filterPathMappings!=null)
711 for (FilterMapping filterPathMapping : _filterPathMappings)
713 if (filterPathMapping.appliesTo(pathInContext, dispatch))
714 filters.add(filterPathMapping.getFilterHolder());
718 // Servlet name filters
719 if (servletHolder != null && _filterNameMappings!=null && _filterNameMappings.size() > 0)
721 // Servlet name filters
722 if (_filterNameMappings.size() > 0)
724 Object o= _filterNameMappings.get(servletHolder.getName());
726 for (int i=0; i<LazyList.size(o);i++)
728 FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
729 if (mapping.appliesTo(dispatch))
730 filters.add(mapping.getFilterHolder());
733 o= _filterNameMappings.get("*");
734 for (int i=0; i<LazyList.size(o);i++)
736 FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
737 if (mapping.appliesTo(dispatch))
738 filters.add(mapping.getFilterHolder());
743 if (filters.isEmpty())
747 FilterChain chain = null;
748 if (_filterChainsCached)
750 if (filters.size() > 0)
751 chain= new CachedChain(filters, servletHolder);
753 final Map<String,FilterChain> cache=_chainCache[dispatch];
754 final Queue<String> lru=_chainLRU[dispatch];
756 // Do we have too many cached chains?
757 while (_maxFilterChainsCacheSize>0 && cache.size()>=_maxFilterChainsCacheSize)
759 // The LRU list is not atomic with the cache map, so be prepared to invalidate if
760 // a key is not found to delete.
761 // Delete by LRU (where U==created)
771 cache.put(key,chain);
774 else if (filters.size() > 0)
775 chain = new Chain(baseRequest,filters, servletHolder);
780 /* ------------------------------------------------------------ */
781 protected void invalidateChainsCache()
783 if (_chainLRU[FilterMapping.REQUEST]!=null)
785 _chainLRU[FilterMapping.REQUEST].clear();
786 _chainLRU[FilterMapping.FORWARD].clear();
787 _chainLRU[FilterMapping.INCLUDE].clear();
788 _chainLRU[FilterMapping.ERROR].clear();
789 _chainLRU[FilterMapping.ASYNC].clear();
791 _chainCache[FilterMapping.REQUEST].clear();
792 _chainCache[FilterMapping.FORWARD].clear();
793 _chainCache[FilterMapping.INCLUDE].clear();
794 _chainCache[FilterMapping.ERROR].clear();
795 _chainCache[FilterMapping.ASYNC].clear();
799 /* ------------------------------------------------------------ */
801 * @return true if the handler is started and there are no unavailable servlets
803 public boolean isAvailable()
807 ServletHolder[] holders = getServlets();
808 for (ServletHolder holder : holders)
810 if (holder != null && !holder.isAvailable())
816 /* ------------------------------------------------------------ */
818 * @param start True if this handler will start with unavailable servlets
820 public void setStartWithUnavailable(boolean start)
822 _startWithUnavailable=start;
825 /* ------------------------------------------------------------ */
827 * @return True if this handler will start with unavailable servlets
829 public boolean isStartWithUnavailable()
831 return _startWithUnavailable;
836 /* ------------------------------------------------------------ */
837 /** Initialize filters and load-on-startup servlets.
839 public void initialize()
842 MultiException mx = new MultiException();
844 //start filter holders now
845 if (_filters != null)
847 for (FilterHolder f: _filters)
861 // Sort and Initialize servlets
864 ServletHolder[] servlets = _servlets.clone();
865 Arrays.sort(servlets);
866 for (ServletHolder servlet : servlets)
871 servlet.initialize();
875 LOG.debug(Log.EXCEPTION, e);
882 for (Holder<?> h: getBeans(Holder.class))
898 mx.ifExceptionThrow();
901 /* ------------------------------------------------------------ */
903 * @return Returns the filterChainsCached.
905 public boolean isFilterChainsCached()
907 return _filterChainsCached;
910 /* ------------------------------------------------------------ */
911 /** Add a holder for a listener
914 public void addListener (ListenerHolder listener)
916 if (listener != null)
917 setListeners(ArrayUtil.addToArray(getListeners(), listener, ListenerHolder.class));
921 /* ------------------------------------------------------------ */
922 public ListenerHolder[] getListeners()
927 /* ------------------------------------------------------------ */
928 public void setListeners(ListenerHolder[] listeners)
931 for (ListenerHolder holder:listeners)
932 holder.setServletHandler(this);
934 updateBeans(_listeners,listeners);
935 _listeners = listeners;
938 /* ------------------------------------------------------------ */
939 public ListenerHolder newListenerHolder(Holder.Source source)
941 return new ListenerHolder(source);
944 /* ------------------------------------------------------------ */
946 * see also newServletHolder(Class)
948 public ServletHolder newServletHolder(Holder.Source source)
950 return new ServletHolder(source);
953 /* ------------------------------------------------------------ */
954 /** Convenience method to add a servlet.
955 * @return The servlet holder.
957 public ServletHolder addServletWithMapping (String className,String pathSpec)
959 ServletHolder holder = newServletHolder(Source.EMBEDDED);
960 holder.setClassName(className);
961 addServletWithMapping(holder,pathSpec);
965 /* ------------------------------------------------------------ */
966 /** conveniance method to add a servlet.
967 * @return The servlet holder.
969 public ServletHolder addServletWithMapping (Class<? extends Servlet> servlet,String pathSpec)
971 ServletHolder holder = newServletHolder(Source.EMBEDDED);
972 holder.setHeldClass(servlet);
973 addServletWithMapping(holder,pathSpec);
978 /* ------------------------------------------------------------ */
979 /** conveniance method to add a servlet.
980 * @param servlet servlet holder to add
981 * @param pathSpec servlet mappings for the servletHolder
983 public void addServletWithMapping (ServletHolder servlet,String pathSpec)
985 ServletHolder[] holders=getServlets();
987 holders = holders.clone();
991 setServlets(ArrayUtil.addToArray(holders, servlet, ServletHolder.class));
993 ServletMapping mapping = new ServletMapping();
994 mapping.setServletName(servlet.getName());
995 mapping.setPathSpec(pathSpec);
996 setServletMappings(ArrayUtil.addToArray(getServletMappings(), mapping, ServletMapping.class));
1000 setServlets(holders);
1001 if (e instanceof RuntimeException)
1002 throw (RuntimeException)e;
1003 throw new RuntimeException(e);
1008 /* ------------------------------------------------------------ */
1009 /**Convenience method to add a pre-constructed ServletHolder.
1012 public void addServlet(ServletHolder holder)
1014 setServlets(ArrayUtil.addToArray(getServlets(), holder, ServletHolder.class));
1017 /* ------------------------------------------------------------ */
1018 /** Convenience method to add a pre-constructed ServletMapping.
1021 public void addServletMapping (ServletMapping mapping)
1023 setServletMappings(ArrayUtil.addToArray(getServletMappings(), mapping, ServletMapping.class));
1026 /* ------------------------------------------------------------ */
1027 public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement)
1029 if (_contextHandler != null)
1031 return _contextHandler.setServletSecurity(registration, servletSecurityElement);
1033 return Collections.emptySet();
1036 /* ------------------------------------------------------------ */
1037 public FilterHolder newFilterHolder(Holder.Source source)
1039 return new FilterHolder(source);
1042 /* ------------------------------------------------------------ */
1043 public FilterHolder getFilter(String name)
1045 return _filterNameMap.get(name);
1049 /* ------------------------------------------------------------ */
1050 /** Convenience method to add a filter.
1051 * @param filter class of filter to create
1052 * @param pathSpec filter mappings for filter
1053 * @param dispatches see {@link FilterMapping#setDispatches(int)}
1054 * @return The filter holder.
1056 public FilterHolder addFilterWithMapping (Class<? extends Filter> filter,String pathSpec,EnumSet<DispatcherType> dispatches)
1058 FilterHolder holder = newFilterHolder(Source.EMBEDDED);
1059 holder.setHeldClass(filter);
1060 addFilterWithMapping(holder,pathSpec,dispatches);
1065 /* ------------------------------------------------------------ */
1066 /** Convenience method to add a filter.
1067 * @param className of filter
1068 * @param pathSpec filter mappings for filter
1069 * @param dispatches see {@link FilterMapping#setDispatches(int)}
1070 * @return The filter holder.
1072 public FilterHolder addFilterWithMapping (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
1074 FilterHolder holder = newFilterHolder(Source.EMBEDDED);
1075 holder.setClassName(className);
1077 addFilterWithMapping(holder,pathSpec,dispatches);
1081 /* ------------------------------------------------------------ */
1082 /** Convenience method to add a filter.
1083 * @param holder filter holder to add
1084 * @param pathSpec filter mappings for filter
1085 * @param dispatches see {@link FilterMapping#setDispatches(int)}
1087 public void addFilterWithMapping (FilterHolder holder,String pathSpec,EnumSet<DispatcherType> dispatches)
1089 FilterHolder[] holders = getFilters();
1091 holders = holders.clone();
1095 setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
1097 FilterMapping mapping = new FilterMapping();
1098 mapping.setFilterName(holder.getName());
1099 mapping.setPathSpec(pathSpec);
1100 mapping.setDispatcherTypes(dispatches);
1101 addFilterMapping(mapping);
1104 catch (RuntimeException e)
1106 setFilters(holders);
1111 setFilters(holders);
1117 /* ------------------------------------------------------------ */
1118 /** Convenience method to add a filter.
1119 * @param filter class of filter to create
1120 * @param pathSpec filter mappings for filter
1121 * @param dispatches see {@link FilterMapping#setDispatches(int)}
1122 * @return The filter holder.
1124 public FilterHolder addFilterWithMapping (Class<? extends Filter> filter,String pathSpec,int dispatches)
1126 FilterHolder holder = newFilterHolder(Source.EMBEDDED);
1127 holder.setHeldClass(filter);
1128 addFilterWithMapping(holder,pathSpec,dispatches);
1133 /* ------------------------------------------------------------ */
1134 /** Convenience method to add a filter.
1135 * @param className of filter
1136 * @param pathSpec filter mappings for filter
1137 * @param dispatches see {@link FilterMapping#setDispatches(int)}
1138 * @return The filter holder.
1140 public FilterHolder addFilterWithMapping (String className,String pathSpec,int dispatches)
1142 FilterHolder holder = newFilterHolder(Source.EMBEDDED);
1143 holder.setClassName(className);
1145 addFilterWithMapping(holder,pathSpec,dispatches);
1149 /* ------------------------------------------------------------ */
1150 /** Convenience method to add a filter.
1151 * @param holder filter holder to add
1152 * @param pathSpec filter mappings for filter
1153 * @param dispatches see {@link FilterMapping#setDispatches(int)}
1155 public void addFilterWithMapping (FilterHolder holder,String pathSpec,int dispatches)
1157 FilterHolder[] holders = getFilters();
1159 holders = holders.clone();
1163 setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
1165 FilterMapping mapping = new FilterMapping();
1166 mapping.setFilterName(holder.getName());
1167 mapping.setPathSpec(pathSpec);
1168 mapping.setDispatches(dispatches);
1169 addFilterMapping(mapping);
1171 catch (RuntimeException e)
1173 setFilters(holders);
1178 setFilters(holders);
1184 /* ------------------------------------------------------------ */
1185 /** Convenience method to add a filter with a mapping
1189 * @return the filter holder created
1190 * @deprecated use {@link #addFilterWithMapping(Class, String, EnumSet)} instead
1192 public FilterHolder addFilter (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
1194 return addFilterWithMapping(className, pathSpec, dispatches);
1197 /* ------------------------------------------------------------ */
1199 * convenience method to add a filter and mapping
1201 * @param filterMapping
1203 public void addFilter (FilterHolder filter, FilterMapping filterMapping)
1206 setFilters(ArrayUtil.addToArray(getFilters(), filter, FilterHolder.class));
1207 if (filterMapping != null)
1208 addFilterMapping(filterMapping);
1211 /* ------------------------------------------------------------ */
1212 /** Convenience method to add a preconstructed FilterHolder
1215 public void addFilter (FilterHolder filter)
1218 setFilters(ArrayUtil.addToArray(getFilters(), filter, FilterHolder.class));
1221 /* ------------------------------------------------------------ */
1222 /** Convenience method to add a preconstructed FilterMapping
1225 public void addFilterMapping (FilterMapping mapping)
1227 if (mapping != null)
1229 Source source = (mapping.getFilterHolder()==null?null:mapping.getFilterHolder().getSource());
1230 FilterMapping[] mappings =getFilterMappings();
1231 if (mappings==null || mappings.length==0)
1233 setFilterMappings(insertFilterMapping(mapping,0,false));
1234 if (source != null && source == Source.JAVAX_API)
1235 _matchAfterIndex = 0;
1239 //there are existing entries. If this is a programmatic filtermapping, it is added at the end of the list.
1240 //If this is a normal filtermapping, it is inserted after all the other filtermappings (matchBefores and normals),
1241 //but before the first matchAfter filtermapping.
1242 if (source != null && Source.JAVAX_API == source)
1244 setFilterMappings(insertFilterMapping(mapping,mappings.length-1, false));
1245 if (_matchAfterIndex < 0)
1246 _matchAfterIndex = getFilterMappings().length-1;
1250 //insert non-programmatic filter mappings before any matchAfters, if any
1251 if (_matchAfterIndex < 0)
1252 setFilterMappings(insertFilterMapping(mapping,mappings.length-1, false));
1255 FilterMapping[] new_mappings = insertFilterMapping(mapping, _matchAfterIndex, true);
1257 setFilterMappings(new_mappings);
1265 /* ------------------------------------------------------------ */
1266 /** Convenience method to add a preconstructed FilterMapping
1269 public void prependFilterMapping (FilterMapping mapping)
1271 if (mapping != null)
1273 Source source = mapping.getFilterHolder().getSource();
1275 FilterMapping[] mappings = getFilterMappings();
1276 if (mappings==null || mappings.length==0)
1278 setFilterMappings(insertFilterMapping(mapping, 0, false));
1279 if (source != null && Source.JAVAX_API == source)
1280 _matchBeforeIndex = 0;
1284 if (source != null && Source.JAVAX_API == source)
1286 //programmatically defined filter mappings are prepended to mapping list in the order
1287 //in which they were defined. In other words, insert this mapping at the tail of the
1288 //programmatically prepended filter mappings, BEFORE the first web.xml defined filter mapping.
1290 if (_matchBeforeIndex < 0)
1292 //no programmatically defined prepended filter mappings yet, prepend this one
1293 _matchBeforeIndex = 0;
1294 FilterMapping[] new_mappings = insertFilterMapping(mapping, 0, true);
1295 setFilterMappings(new_mappings);
1299 FilterMapping[] new_mappings = insertFilterMapping(mapping,_matchBeforeIndex, false);
1300 ++_matchBeforeIndex;
1301 setFilterMappings(new_mappings);
1306 //non programmatically defined, just prepend to list
1307 FilterMapping[] new_mappings = insertFilterMapping(mapping, 0, true);
1308 setFilterMappings(new_mappings);
1311 //adjust matchAfterIndex ptr to take account of the mapping we just prepended
1312 if (_matchAfterIndex >= 0)
1321 * Insert a filtermapping in the list
1322 * @param mapping the FilterMapping to add
1323 * @param pos the position in the existing arry at which to add it
1324 * @param before if true, insert before pos, if false insert after it
1327 protected FilterMapping[] insertFilterMapping (FilterMapping mapping, int pos, boolean before)
1330 throw new IllegalArgumentException("FilterMapping insertion pos < 0");
1331 FilterMapping[] mappings = getFilterMappings();
1333 if (mappings==null || mappings.length==0)
1335 return new FilterMapping[] {mapping};
1337 FilterMapping[] new_mappings = new FilterMapping[mappings.length+1];
1342 //copy existing filter mappings up to but not including the pos
1343 System.arraycopy(mappings,0,new_mappings,0,pos);
1345 //add in the new mapping
1346 new_mappings[pos] = mapping;
1348 //copy the old pos mapping and any remaining existing mappings
1349 System.arraycopy(mappings,pos,new_mappings,pos+1, mappings.length-pos);
1354 //copy existing filter mappings up to and including the pos
1355 System.arraycopy(mappings,0,new_mappings,0,pos+1);
1356 //add in the new mapping after the pos
1357 new_mappings[pos+1] = mapping;
1359 //copy the remaining existing mappings
1360 if (mappings.length > pos+1)
1361 System.arraycopy(mappings,pos+1,new_mappings,pos+2, mappings.length-(pos+1));
1363 return new_mappings;
1367 /* ------------------------------------------------------------ */
1368 protected synchronized void updateNameMappings()
1370 // update filter name map
1371 _filterNameMap.clear();
1374 for (FilterHolder filter : _filters)
1376 _filterNameMap.put(filter.getName(), filter);
1377 filter.setServletHandler(this);
1381 // Map servlet names to holders
1382 _servletNameMap.clear();
1383 if (_servlets!=null)
1386 for (ServletHolder servlet : _servlets)
1388 _servletNameMap.put(servlet.getName(), servlet);
1389 servlet.setServletHandler(this);
1394 /* ------------------------------------------------------------ */
1395 protected synchronized void updateMappings()
1397 // update filter mappings
1398 if (_filterMappings==null)
1400 _filterPathMappings=null;
1401 _filterNameMappings=null;
1405 _filterPathMappings=new ArrayList<>();
1406 _filterNameMappings=new MultiMap<FilterMapping>();
1407 for (FilterMapping filtermapping : _filterMappings)
1409 FilterHolder filter_holder = _filterNameMap.get(filtermapping.getFilterName());
1410 if (filter_holder == null)
1411 throw new IllegalStateException("No filter named " + filtermapping.getFilterName());
1412 filtermapping.setFilterHolder(filter_holder);
1413 if (filtermapping.getPathSpecs() != null)
1414 _filterPathMappings.add(filtermapping);
1416 if (filtermapping.getServletNames() != null)
1418 String[] names = filtermapping.getServletNames();
1419 for (String name : names)
1422 _filterNameMappings.add(name, filtermapping);
1428 // Map servlet paths to holders
1429 if (_servletMappings==null || _servletNameMap==null)
1431 _servletPathMap=null;
1435 PathMap<ServletHolder> pm = new PathMap<>();
1436 Map<String,ServletMapping> servletPathMappings = new HashMap<String,ServletMapping>();
1438 //create a map of paths to set of ServletMappings that define that mapping
1439 HashMap<String, Set<ServletMapping>> sms = new HashMap<String, Set<ServletMapping>>();
1440 for (ServletMapping servletMapping : _servletMappings)
1442 String[] pathSpecs = servletMapping.getPathSpecs();
1443 if (pathSpecs != null)
1445 for (String pathSpec : pathSpecs)
1447 Set<ServletMapping> mappings = sms.get(pathSpec);
1448 if (mappings == null)
1450 mappings = new HashSet<ServletMapping>();
1451 sms.put(pathSpec, mappings);
1453 mappings.add(servletMapping);
1458 //evaluate path to servlet map based on servlet mappings
1459 for (String pathSpec : sms.keySet())
1461 //for each path, look at the mappings where it is referenced
1462 //if a mapping is for a servlet that is not enabled, skip it
1463 Set<ServletMapping> mappings = sms.get(pathSpec);
1465 ServletMapping finalMapping = null;
1466 for (ServletMapping mapping : mappings)
1468 //Get servlet associated with the mapping and check it is enabled
1469 ServletHolder servlet_holder = _servletNameMap.get(mapping.getServletName());
1470 if (servlet_holder == null)
1471 throw new IllegalStateException("No such servlet: " + mapping.getServletName());
1472 //if the servlet related to the mapping is not enabled, skip it from consideration
1473 if (!servlet_holder.isEnabled())
1476 //only accept a default mapping if we don't have any other
1477 if (finalMapping == null)
1478 finalMapping = mapping;
1481 //already have a candidate - only accept another one if the candidate is a default
1482 if (finalMapping.isDefault())
1483 finalMapping = mapping;
1486 //existing candidate isn't a default, if the one we're looking at isn't a default either, then its an error
1487 if (!mapping.isDefault())
1488 throw new IllegalStateException("Multiple servlets map to path: "+pathSpec+": "+finalMapping.getServletName()+","+mapping.getServletName());
1492 if (finalMapping == null)
1493 throw new IllegalStateException ("No acceptable servlet mappings for "+pathSpec);
1495 if (LOG.isDebugEnabled()) LOG.debug("Chose path={} mapped to servlet={} from default={}", pathSpec, finalMapping.getServletName(), finalMapping.isDefault());
1497 servletPathMappings.put(pathSpec, finalMapping);
1498 pm.put(pathSpec,_servletNameMap.get(finalMapping.getServletName()));
1504 // flush filter chain cache
1505 if (_chainCache!=null)
1507 for (int i=_chainCache.length;i-->0;)
1509 if (_chainCache[i]!=null)
1510 _chainCache[i].clear();
1514 if (LOG.isDebugEnabled())
1516 LOG.debug("filterNameMap="+_filterNameMap);
1517 LOG.debug("pathFilters="+_filterPathMappings);
1518 LOG.debug("servletFilterMap="+_filterNameMappings);
1519 LOG.debug("servletPathMap="+_servletPathMap);
1520 LOG.debug("servletNameMap="+_servletNameMap);
1525 if (_contextHandler!=null && _contextHandler.isStarted() || _contextHandler==null && isStarted())
1530 throw new RuntimeException(e);
1534 /* ------------------------------------------------------------ */
1535 protected void notFound(Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
1537 if (LOG.isDebugEnabled())
1538 LOG.debug("Not Found {}",request.getRequestURI());
1539 if (getHandler()!=null)
1540 nextHandle(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()),baseRequest,request,response);
1543 /* ------------------------------------------------------------ */
1545 * @param filterChainsCached The filterChainsCached to set.
1547 public void setFilterChainsCached(boolean filterChainsCached)
1549 _filterChainsCached = filterChainsCached;
1552 /* ------------------------------------------------------------ */
1554 * @param filterMappings The filterMappings to set.
1556 public void setFilterMappings(FilterMapping[] filterMappings)
1558 updateBeans(_filterMappings,filterMappings);
1559 _filterMappings = filterMappings;
1560 if (isStarted()) updateMappings();
1561 invalidateChainsCache();
1564 /* ------------------------------------------------------------ */
1565 public synchronized void setFilters(FilterHolder[] holders)
1568 for (FilterHolder holder:holders)
1569 holder.setServletHandler(this);
1571 updateBeans(_filters,holders);
1573 updateNameMappings();
1574 invalidateChainsCache();
1577 /* ------------------------------------------------------------ */
1579 * @param servletMappings The servletMappings to set.
1581 public void setServletMappings(ServletMapping[] servletMappings)
1583 updateBeans(_servletMappings,servletMappings);
1584 _servletMappings = servletMappings;
1585 if (isStarted()) updateMappings();
1586 invalidateChainsCache();
1589 /* ------------------------------------------------------------ */
1591 * @param holders Array of servlets to define
1593 public synchronized void setServlets(ServletHolder[] holders)
1596 for (ServletHolder holder:holders)
1597 holder.setServletHandler(this);
1599 updateBeans(_servlets,holders);
1601 updateNameMappings();
1602 invalidateChainsCache();
1605 /* ------------------------------------------------------------ */
1606 /* ------------------------------------------------------------ */
1607 private class CachedChain implements FilterChain
1609 FilterHolder _filterHolder;
1611 ServletHolder _servletHolder;
1613 /* ------------------------------------------------------------ */
1615 * @param filters list of {@link FilterHolder} objects
1616 * @param servletHolder
1618 CachedChain(List<FilterHolder> filters, ServletHolder servletHolder)
1620 if (filters.size()>0)
1622 _filterHolder=filters.get(0);
1624 _next=new CachedChain(filters,servletHolder);
1627 _servletHolder=servletHolder;
1630 /* ------------------------------------------------------------ */
1632 public void doFilter(ServletRequest request, ServletResponse response)
1633 throws IOException, ServletException
1635 final Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
1637 // pass to next filter
1638 if (_filterHolder!=null)
1640 if (LOG.isDebugEnabled())
1641 LOG.debug("call filter {}", _filterHolder);
1642 Filter filter= _filterHolder.getFilter();
1644 //if the request already does not support async, then the setting for the filter
1645 //is irrelevant. However if the request supports async but this filter does not
1646 //temporarily turn it off for the execution of the filter
1647 boolean requestAsyncSupported = baseRequest.isAsyncSupported();
1650 if (!_filterHolder.isAsyncSupported() && requestAsyncSupported)
1651 baseRequest.setAsyncSupported(false);
1652 filter.doFilter(request, response, _next);
1656 baseRequest.setAsyncSupported(requestAsyncSupported);
1662 HttpServletRequest srequest = (HttpServletRequest)request;
1663 if (_servletHolder == null)
1664 notFound(baseRequest, srequest, (HttpServletResponse)response);
1667 if (LOG.isDebugEnabled())
1668 LOG.debug("call servlet " + _servletHolder);
1669 _servletHolder.handle(baseRequest,request, response);
1674 public String toString()
1676 if (_filterHolder!=null)
1677 return _filterHolder+"->"+_next.toString();
1678 if (_servletHolder!=null)
1679 return _servletHolder.toString();
1684 /* ------------------------------------------------------------ */
1685 /* ------------------------------------------------------------ */
1686 private class Chain implements FilterChain
1688 final Request _baseRequest;
1689 final List<FilterHolder> _chain;
1690 final ServletHolder _servletHolder;
1693 /* ------------------------------------------------------------ */
1694 Chain(Request baseRequest, List<FilterHolder> filters, ServletHolder servletHolder)
1696 _baseRequest=baseRequest;
1698 _servletHolder= servletHolder;
1701 /* ------------------------------------------------------------ */
1703 public void doFilter(ServletRequest request, ServletResponse response)
1704 throws IOException, ServletException
1706 if (LOG.isDebugEnabled())
1707 LOG.debug("doFilter " + _filter);
1709 // pass to next filter
1710 if (_filter < _chain.size())
1712 FilterHolder holder= _chain.get(_filter++);
1713 if (LOG.isDebugEnabled())
1714 LOG.debug("call filter " + holder);
1715 Filter filter= holder.getFilter();
1717 //if the request already does not support async, then the setting for the filter
1718 //is irrelevant. However if the request supports async but this filter does not
1719 //temporarily turn it off for the execution of the filter
1720 boolean requestAsyncSupported = _baseRequest.isAsyncSupported();
1723 if (!holder.isAsyncSupported() && requestAsyncSupported)
1724 _baseRequest.setAsyncSupported(false);
1725 filter.doFilter(request, response, this);
1729 _baseRequest.setAsyncSupported(requestAsyncSupported);
1735 HttpServletRequest srequest = (HttpServletRequest)request;
1736 if (_servletHolder == null)
1737 notFound((request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(), srequest, (HttpServletResponse)response);
1740 if (LOG.isDebugEnabled())
1741 LOG.debug("call servlet {}", _servletHolder);
1742 _servletHolder.handle(_baseRequest,request, response);
1746 /* ------------------------------------------------------------ */
1748 public String toString()
1750 StringBuilder b = new StringBuilder();
1751 for(FilterHolder f: _chain)
1753 b.append(f.toString());
1756 b.append(_servletHolder);
1757 return b.toString();
1761 /* ------------------------------------------------------------ */
1763 * @return The maximum entries in a filter chain cache.
1765 public int getMaxFilterChainsCacheSize()
1767 return _maxFilterChainsCacheSize;
1770 /* ------------------------------------------------------------ */
1771 /** Set the maximum filter chain cache size.
1772 * Filter chains are cached if {@link #isFilterChainsCached()} is true. If the max cache size
1773 * is greater than zero, then the cache is flushed whenever it grows to be this size.
1775 * @param maxFilterChainsCacheSize the maximum number of entries in a filter chain cache.
1777 public void setMaxFilterChainsCacheSize(int maxFilterChainsCacheSize)
1779 _maxFilterChainsCacheSize = maxFilterChainsCacheSize;
1782 /* ------------------------------------------------------------ */
1783 void destroyServlet(Servlet servlet)
1785 if (_contextHandler!=null)
1786 _contextHandler.destroyServlet(servlet);
1789 /* ------------------------------------------------------------ */
1790 void destroyFilter(Filter filter)
1792 if (_contextHandler!=null)
1793 _contextHandler.destroyFilter(filter);
1796 /* ------------------------------------------------------------ */
1797 /* ------------------------------------------------------------ */
1798 /* ------------------------------------------------------------ */
1799 public static class Default404Servlet extends HttpServlet
1802 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
1803 throws ServletException, IOException
1805 resp.sendError(HttpServletResponse.SC_NOT_FOUND);