// // ======================================================================== // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.servlet; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.ListIterator; import java.util.Set; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import javax.servlet.AsyncContext; import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.ServletSecurityElement; import javax.servlet.UnavailableException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.continuation.ContinuationThrowable; import org.eclipse.jetty.http.HttpException; import org.eclipse.jetty.http.PathMap; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.AsyncContinuation; import org.eclipse.jetty.server.Dispatcher; import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServletRequestHttpWrapper; import org.eclipse.jetty.server.ServletResponseHttpWrapper; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ScopedHandler; import org.eclipse.jetty.servlet.Holder.Source; import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.MultiMap; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /* --------------------------------------------------------------------- */ /** Servlet HttpHandler. * This handler maps requests to servlets that implement the * javax.servlet.http.HttpServlet API. *

* This handler does not implement the full J2EE features and is intended to * be used directly when a full web application is not required. If a Web application is required, * then this handler should be used as part of a org.eclipse.jetty.webapp.WebAppContext. *

* Unless run as part of a {@link ServletContextHandler} or derivative, the {@link #initialize()} * method must be called manually after start(). */ public class ServletHandler extends ScopedHandler { private static final Logger LOG = Log.getLogger(ServletHandler.class); /* ------------------------------------------------------------ */ public static final String __DEFAULT_SERVLET="default"; /* ------------------------------------------------------------ */ private ServletContextHandler _contextHandler; private ContextHandler.Context _servletContext; private FilterHolder[] _filters=new FilterHolder[0]; private FilterMapping[] _filterMappings; private int _matchBeforeIndex = -1; //index of last programmatic FilterMapping with isMatchAfter=false private int _matchAfterIndex = -1; //index of 1st programmatic FilterMapping with isMatchAfter=true private boolean _filterChainsCached=true; private int _maxFilterChainsCacheSize=512; private boolean _startWithUnavailable=false; private IdentityService _identityService; private ServletHolder[] _servlets=new ServletHolder[0]; private ServletMapping[] _servletMappings; private final Map _filterNameMap= new HashMap(); private List _filterPathMappings; private MultiMap _filterNameMappings; private final Map _servletNameMap=new HashMap(); private PathMap _servletPathMap; protected final ConcurrentMap _chainCache[] = new ConcurrentMap[FilterMapping.ALL]; protected final Queue[] _chainLRU = new Queue[FilterMapping.ALL]; /* ------------------------------------------------------------ */ /** Constructor. */ public ServletHandler() { } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.handler.AbstractHandler#setServer(org.eclipse.jetty.server.Server) */ public void setServer(Server server) { Server old=getServer(); if (old!=null && old!=server) { getServer().getContainer().update(this, _filters, null, "filter",true); getServer().getContainer().update(this, _filterMappings, null, "filterMapping",true); getServer().getContainer().update(this, _servlets, null, "servlet",true); getServer().getContainer().update(this, _servletMappings, null, "servletMapping",true); } super.setServer(server); if (server!=null && old!=server) { server.getContainer().update(this, null, _filters, "filter",true); server.getContainer().update(this, null, _filterMappings, "filterMapping",true); server.getContainer().update(this, null, _servlets, "servlet",true); server.getContainer().update(this, null, _servletMappings, "servletMapping",true); } } /* ----------------------------------------------------------------- */ @Override protected synchronized void doStart() throws Exception { _servletContext=ContextHandler.getCurrentContext(); _contextHandler=(ServletContextHandler)(_servletContext==null?null:_servletContext.getContextHandler()); if (_contextHandler!=null) { SecurityHandler security_handler = (SecurityHandler)_contextHandler.getChildHandlerByClass(SecurityHandler.class); if (security_handler!=null) _identityService=security_handler.getIdentityService(); } updateNameMappings(); updateMappings(); if(_filterChainsCached) { _chainCache[FilterMapping.REQUEST]=new ConcurrentHashMap(); _chainCache[FilterMapping.FORWARD]=new ConcurrentHashMap(); _chainCache[FilterMapping.INCLUDE]=new ConcurrentHashMap(); _chainCache[FilterMapping.ERROR]=new ConcurrentHashMap(); _chainCache[FilterMapping.ASYNC]=new ConcurrentHashMap(); _chainLRU[FilterMapping.REQUEST]=new ConcurrentLinkedQueue(); _chainLRU[FilterMapping.FORWARD]=new ConcurrentLinkedQueue(); _chainLRU[FilterMapping.INCLUDE]=new ConcurrentLinkedQueue(); _chainLRU[FilterMapping.ERROR]=new ConcurrentLinkedQueue(); _chainLRU[FilterMapping.ASYNC]=new ConcurrentLinkedQueue(); } super.doStart(); if (_contextHandler==null || !(_contextHandler instanceof ServletContextHandler)) initialize(); } /* ----------------------------------------------------------------- */ @Override protected synchronized void doStop() throws Exception { super.doStop(); // Stop filters List filterHolders = new ArrayList(); List filterMappings = LazyList.array2List(_filterMappings); if (_filters!=null) { for (int i=_filters.length; i-->0;) { try { _filters[i].stop(); }catch(Exception e){LOG.warn(Log.EXCEPTION,e);} if (_filters[i].getSource() != Source.EMBEDDED) { //remove all of the mappings that were for non-embedded filters _filterNameMap.remove(_filters[i].getName()); //remove any mappings associated with this filter ListIterator fmitor = filterMappings.listIterator(); while (fmitor.hasNext()) { FilterMapping fm = fmitor.next(); if (fm.getFilterName().equals(_filters[i].getName())) fmitor.remove(); } } else filterHolders.add(_filters[i]); //only retain embedded } } _filters = (FilterHolder[]) LazyList.toArray(filterHolders, FilterHolder.class); _filterMappings = (FilterMapping[]) LazyList.toArray(filterMappings, FilterMapping.class); _matchAfterIndex = (_filterMappings == null || _filterMappings.length == 0 ? -1 : _filterMappings.length-1); _matchBeforeIndex = -1; // Stop servlets List servletHolders = new ArrayList(); //will be remaining servlets List servletMappings = LazyList.array2List(_servletMappings); //will be remaining mappings if (_servlets!=null) { for (int i=_servlets.length; i-->0;) { try { _servlets[i].stop(); }catch(Exception e){LOG.warn(Log.EXCEPTION,e);} if (_servlets[i].getSource() != Source.EMBEDDED) { //remove from servlet name map _servletNameMap.remove(_servlets[i].getName()); //remove any mappings associated with this servlet ListIterator smitor = servletMappings.listIterator(); while (smitor.hasNext()) { ServletMapping sm = smitor.next(); if (sm.getServletName().equals(_servlets[i].getName())) smitor.remove(); } } else servletHolders.add(_servlets[i]); //only retain embedded } } _servlets = (ServletHolder[]) LazyList.toArray(servletHolders, ServletHolder.class); _servletMappings = (ServletMapping[])LazyList.toArray(servletMappings, ServletMapping.class); //will be regenerated on next start _filterPathMappings=null; _filterNameMappings=null; _servletPathMap=null; } /* ------------------------------------------------------------ */ protected IdentityService getIdentityService() { return _identityService; } /* ------------------------------------------------------------ */ /** * @return Returns the contextLog. */ public Object getContextLog() { return null; } /* ------------------------------------------------------------ */ /** * @return Returns the filterMappings. */ public FilterMapping[] getFilterMappings() { return _filterMappings; } /* ------------------------------------------------------------ */ /** Get Filters. * @return Array of defined servlets */ public FilterHolder[] getFilters() { return _filters; } /* ------------------------------------------------------------ */ /** ServletHolder matching path. * @param pathInContext Path within _context. * @return PathMap Entries pathspec to ServletHolder */ public PathMap.Entry getHolderEntry(String pathInContext) { if (_servletPathMap==null) return null; return _servletPathMap.getMatch(pathInContext); } /* ------------------------------------------------------------ */ public ServletContext getServletContext() { return _servletContext; } /* ------------------------------------------------------------ */ /** * @return Returns the servletMappings. */ public ServletMapping[] getServletMappings() { return _servletMappings; } /* ------------------------------------------------------------ */ /** * @return Returns the servletMappings. */ public ServletMapping getServletMapping(String pattern) { ServletMapping theMapping = null; if (_servletMappings!=null) { for (ServletMapping m:_servletMappings) { String[] paths=m.getPathSpecs(); if (paths!=null) { for (String path:paths) { if (pattern.equals(path)) theMapping = m; } } } } return theMapping; } /* ------------------------------------------------------------ */ /** Get Servlets. * @return Array of defined servlets */ public ServletHolder[] getServlets() { return _servlets; } /* ------------------------------------------------------------ */ public ServletHolder getServlet(String name) { return (ServletHolder)_servletNameMap.get(name); } /* ------------------------------------------------------------ */ @Override public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Get the base requests final String old_servlet_path=baseRequest.getServletPath(); final String old_path_info=baseRequest.getPathInfo(); DispatcherType type = baseRequest.getDispatcherType(); ServletHolder servlet_holder=null; UserIdentity.Scope old_scope=null; // find the servlet if (target.startsWith("/")) { // Look for the servlet by path PathMap.Entry entry=getHolderEntry(target); if (entry!=null) { servlet_holder=(ServletHolder)entry.getValue(); String servlet_path_spec=(String)entry.getKey(); String servlet_path=entry.getMapped()!=null?entry.getMapped():PathMap.pathMatch(servlet_path_spec,target); String path_info=PathMap.pathInfo(servlet_path_spec,target); if (DispatcherType.INCLUDE.equals(type)) { baseRequest.setAttribute(Dispatcher.INCLUDE_SERVLET_PATH,servlet_path); baseRequest.setAttribute(Dispatcher.INCLUDE_PATH_INFO, path_info); } else { baseRequest.setServletPath(servlet_path); baseRequest.setPathInfo(path_info); } } } else { // look for a servlet by name! servlet_holder=(ServletHolder)_servletNameMap.get(target); } if (LOG.isDebugEnabled()) LOG.debug("servlet {}|{}|{} -> {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),servlet_holder); try { // Do the filter/handling thang old_scope=baseRequest.getUserIdentityScope(); baseRequest.setUserIdentityScope(servlet_holder); // start manual inline of nextScope(target,baseRequest,request,response); if (never()) nextScope(target,baseRequest,request,response); else if (_nextScope!=null) _nextScope.doScope(target,baseRequest,request, response); else if (_outerScope!=null) _outerScope.doHandle(target,baseRequest,request, response); else doHandle(target,baseRequest,request, response); // end manual inline (pathentic attempt to reduce stack depth) } finally { if (old_scope!=null) baseRequest.setUserIdentityScope(old_scope); if (!(DispatcherType.INCLUDE.equals(type))) { baseRequest.setServletPath(old_servlet_path); baseRequest.setPathInfo(old_path_info); } } } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) */ @Override public void doHandle(String target, Request baseRequest,HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { DispatcherType type = baseRequest.getDispatcherType(); ServletHolder servlet_holder=(ServletHolder) baseRequest.getUserIdentityScope(); FilterChain chain=null; // find the servlet if (target.startsWith("/")) { if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0) chain=getFilterChain(baseRequest, target, servlet_holder); } else { if (servlet_holder!=null) { if (_filterMappings!=null && _filterMappings.length>0) { chain=getFilterChain(baseRequest, null,servlet_holder); } } } LOG.debug("chain={}",chain); Throwable th=null; try { if (servlet_holder==null) { if (getHandler()==null) notFound(request, response); else nextHandle(target,baseRequest,request,response); } else { // unwrap any tunnelling of base Servlet request/responses ServletRequest req = request; if (req instanceof ServletRequestHttpWrapper) req = ((ServletRequestHttpWrapper)req).getRequest(); ServletResponse res = response; if (res instanceof ServletResponseHttpWrapper) res = ((ServletResponseHttpWrapper)res).getResponse(); // Do the filter/handling thang if (chain!=null) chain.doFilter(req, res); else servlet_holder.handle(baseRequest,req,res); } } catch(EofException e) { throw e; } catch(RuntimeIOException e) { throw e; } catch(ContinuationThrowable e) { throw e; } catch(Exception e) { if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type))) { if (e instanceof IOException) throw (IOException)e; if (e instanceof RuntimeException) throw (RuntimeException)e; if (e instanceof ServletException) throw (ServletException)e; } // unwrap cause th=e; if (th instanceof UnavailableException) { LOG.debug(th); } else if (th instanceof ServletException) { LOG.warn(th); Throwable cause=((ServletException)th).getRootCause(); if (cause!=null) th=cause; } // handle or log exception if (th instanceof HttpException) throw (HttpException)th; else if (th instanceof RuntimeIOException) throw (RuntimeIOException)th; else if (th instanceof EofException) throw (EofException)th; else if (LOG.isDebugEnabled()) { LOG.warn(request.getRequestURI(), th); LOG.debug(request.toString()); } else if (th instanceof IOException || th instanceof UnavailableException) { LOG.debug(request.getRequestURI(),th); } else { LOG.warn(request.getRequestURI(),th); } request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass()); request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th); if (!response.isCommitted()) { if (th instanceof UnavailableException) { UnavailableException ue = (UnavailableException)th; if (ue.isPermanent()) response.sendError(HttpServletResponse.SC_NOT_FOUND); else response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); } else response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } else LOG.debug("Response already committed for handling "+th); } catch(Error e) { if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type))) throw e; th=e; LOG.warn("Error for "+request.getRequestURI(),e); if(LOG.isDebugEnabled())LOG.debug(request.toString()); request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass()); request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e); if (!response.isCommitted()) response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); else LOG.debug("Response already committed for handling ",e); } finally { if (servlet_holder!=null) baseRequest.setHandled(true); // Complete async requests if (th!=null && request.isAsyncStarted()) ((AsyncContinuation)request.getAsyncContext()).errorComplete(); } } /* ------------------------------------------------------------ */ protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder) { String key=pathInContext==null?servletHolder.getName():pathInContext; int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType()); if (_filterChainsCached && _chainCache!=null) { FilterChain chain = (FilterChain)_chainCache[dispatch].get(key); if (chain!=null) return chain; } // Build list of filters Object filters= null; // Path filters if (pathInContext!=null && _filterPathMappings!=null) { for (int i= 0; i < _filterPathMappings.size(); i++) { FilterMapping mapping = (FilterMapping)_filterPathMappings.get(i); if (mapping.appliesTo(pathInContext, dispatch)) filters= LazyList.add(filters, mapping.getFilterHolder()); } } // Servlet name filters if (servletHolder != null && _filterNameMappings!=null && _filterNameMappings.size() > 0) { // Servlet name filters if (_filterNameMappings.size() > 0) { Object o= _filterNameMappings.get(servletHolder.getName()); for (int i=0; i 0) chain= new CachedChain(filters, servletHolder); final Map cache=_chainCache[dispatch]; final Queue lru=_chainLRU[dispatch]; // Do we have too many cached chains? while (_maxFilterChainsCacheSize>0 && cache.size()>=_maxFilterChainsCacheSize) { // The LRU list is not atomic with the cache map, so be prepared to invalidate if // a key is not found to delete. // Delete by LRU (where U==created) String k=lru.poll(); if (k==null) { cache.clear(); break; } cache.remove(k); } cache.put(key,chain); lru.add(key); } else if (LazyList.size(filters) > 0) chain = new Chain(baseRequest,filters, servletHolder); return chain; } /* ------------------------------------------------------------ */ protected void invalidateChainsCache() { if (_chainLRU[FilterMapping.REQUEST]!=null) { _chainLRU[FilterMapping.REQUEST].clear(); _chainLRU[FilterMapping.FORWARD].clear(); _chainLRU[FilterMapping.INCLUDE].clear(); _chainLRU[FilterMapping.ERROR].clear(); _chainLRU[FilterMapping.ASYNC].clear(); _chainCache[FilterMapping.REQUEST].clear(); _chainCache[FilterMapping.FORWARD].clear(); _chainCache[FilterMapping.INCLUDE].clear(); _chainCache[FilterMapping.ERROR].clear(); _chainCache[FilterMapping.ASYNC].clear(); } } /* ------------------------------------------------------------ */ /** * @return true if the handler is started and there are no unavailable servlets */ public boolean isAvailable() { if (!isStarted()) return false; ServletHolder[] holders = getServlets(); for (int i=0;i servlet) { return new ServletHolder(servlet); } /* ------------------------------------------------------------ */ /** Convenience method to add a servlet. * @return The servlet holder. */ public ServletHolder addServletWithMapping (String className,String pathSpec) { ServletHolder holder = newServletHolder(Holder.Source.EMBEDDED); holder.setClassName(className); addServletWithMapping(holder,pathSpec); return holder; } /* ------------------------------------------------------------ */ /** conveniance method to add a servlet. * @return The servlet holder. */ public ServletHolder addServletWithMapping (Class servlet,String pathSpec) { ServletHolder holder = newServletHolder(Holder.Source.EMBEDDED); holder.setHeldClass(servlet); //DUPLICATES adding servlet from addServletWithMapping(holder, pathSpec)? //setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class)); addServletWithMapping(holder,pathSpec); return holder; } /* ------------------------------------------------------------ */ /** conveniance method to add a servlet. * @param servlet servlet holder to add * @param pathSpec servlet mappings for the servletHolder */ public void addServletWithMapping (ServletHolder servlet,String pathSpec) { ServletHolder[] holders=getServlets(); if (holders!=null) holders = holders.clone(); try { setServlets((ServletHolder[])LazyList.addToArray(holders, servlet, ServletHolder.class)); ServletMapping mapping = new ServletMapping(); mapping.setServletName(servlet.getName()); mapping.setPathSpec(pathSpec); setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class)); } catch (Exception e) { setServlets(holders); if (e instanceof RuntimeException) throw (RuntimeException)e; throw new RuntimeException(e); } } /* ------------------------------------------------------------ */ /**Convenience method to add a pre-constructed ServletHolder. * @param holder */ public void addServlet(ServletHolder holder) { setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class)); } /* ------------------------------------------------------------ */ /** Convenience method to add a pre-constructed ServletMapping. * @param mapping */ public void addServletMapping (ServletMapping mapping) { setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class)); } public Set setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement) { if (_contextHandler != null) { return _contextHandler.setServletSecurity(registration, servletSecurityElement); } return Collections.emptySet(); } /* ------------------------------------------------------------ */ /** * @see #newFilterHolder(Class) */ public FilterHolder newFilterHolder(Holder.Source source) { return new FilterHolder(source); } /* ------------------------------------------------------------ */ public FilterHolder getFilter(String name) { return (FilterHolder)_filterNameMap.get(name); } /* ------------------------------------------------------------ */ /** Convenience method to add a filter. * @param filter class of filter to create * @param pathSpec filter mappings for filter * @param dispatches see {@link FilterMapping#setDispatches(int)} * @return The filter holder. */ public FilterHolder addFilterWithMapping (Class filter,String pathSpec,EnumSet dispatches) { FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED); holder.setHeldClass(filter); addFilterWithMapping(holder,pathSpec,dispatches); return holder; } /* ------------------------------------------------------------ */ /** Convenience method to add a filter. * @param className of filter * @param pathSpec filter mappings for filter * @param dispatches see {@link FilterMapping#setDispatches(int)} * @return The filter holder. */ public FilterHolder addFilterWithMapping (String className,String pathSpec,EnumSet dispatches) { FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED); holder.setClassName(className); addFilterWithMapping(holder,pathSpec,dispatches); return holder; } /* ------------------------------------------------------------ */ /** Convenience method to add a filter. * @param holder filter holder to add * @param pathSpec filter mappings for filter * @param dispatches see {@link FilterMapping#setDispatches(int)} */ public void addFilterWithMapping (FilterHolder holder,String pathSpec,EnumSet dispatches) { FilterHolder[] holders = getFilters(); if (holders!=null) holders = (FilterHolder[])holders.clone(); try { setFilters((FilterHolder[])LazyList.addToArray(holders, holder, FilterHolder.class)); FilterMapping mapping = new FilterMapping(); mapping.setFilterName(holder.getName()); mapping.setPathSpec(pathSpec); mapping.setDispatcherTypes(dispatches); //setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class)); addFilterMapping(mapping); } catch (RuntimeException e) { setFilters(holders); throw e; } catch (Error e) { setFilters(holders); throw e; } } /* ------------------------------------------------------------ */ /** Convenience method to add a filter. * @param filter class of filter to create * @param pathSpec filter mappings for filter * @param dispatches see {@link FilterMapping#setDispatches(int)} * @return The filter holder. */ public FilterHolder addFilterWithMapping (Class filter,String pathSpec,int dispatches) { FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED); holder.setHeldClass(filter); addFilterWithMapping(holder,pathSpec,dispatches); return holder; } /* ------------------------------------------------------------ */ /** Convenience method to add a filter. * @param className of filter * @param pathSpec filter mappings for filter * @param dispatches see {@link FilterMapping#setDispatches(int)} * @return The filter holder. */ public FilterHolder addFilterWithMapping (String className,String pathSpec,int dispatches) { FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED); holder.setClassName(className); addFilterWithMapping(holder,pathSpec,dispatches); return holder; } /* ------------------------------------------------------------ */ /** Convenience method to add a filter. * @param holder filter holder to add * @param pathSpec filter mappings for filter * @param dispatches see {@link FilterMapping#setDispatches(int)} */ public void addFilterWithMapping (FilterHolder holder,String pathSpec,int dispatches) { FilterHolder[] holders = getFilters(); if (holders!=null) holders = (FilterHolder[])holders.clone(); try { setFilters((FilterHolder[])LazyList.addToArray(holders, holder, FilterHolder.class)); FilterMapping mapping = new FilterMapping(); mapping.setFilterName(holder.getName()); mapping.setPathSpec(pathSpec); mapping.setDispatches(dispatches); //setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class)); addFilterMapping(mapping); } catch (RuntimeException e) { setFilters(holders); throw e; } catch (Error e) { setFilters(holders); throw e; } } /* ------------------------------------------------------------ */ /** Convenience method to add a filter with a mapping * @param className * @param pathSpec * @param dispatches * @return the filter holder created * @deprecated use {@link #addFilterWithMapping(Class, String, EnumSet)} instead */ public FilterHolder addFilter (String className,String pathSpec,EnumSet dispatches) { return addFilterWithMapping(className, pathSpec, dispatches); } /* ------------------------------------------------------------ */ /** * convenience method to add a filter and mapping * @param filter * @param filterMapping */ public void addFilter (FilterHolder filter, FilterMapping filterMapping) { if (filter != null) setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class)); if (filterMapping != null) //setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), filterMapping, FilterMapping.class)); addFilterMapping(filterMapping); } /* ------------------------------------------------------------ */ /** Convenience method to add a preconstructed FilterHolder * @param filter */ public void addFilter (FilterHolder filter) { if (filter != null) setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class)); } /* ------------------------------------------------------------ */ /** Convenience method to add a preconstructed FilterMapping * @param mapping */ public void addFilterMapping (FilterMapping mapping) { if (mapping != null) { Source source = (mapping.getFilterHolder()==null?null:mapping.getFilterHolder().getSource()); FilterMapping[] mappings =getFilterMappings(); if (mappings==null || mappings.length==0) { setFilterMappings(insertFilterMapping(mapping,0,false)); if (source != null && source == Source.JAVAX_API) _matchAfterIndex = 0; } else { //there are existing entries. If this is a programmatic filtermapping, it is added at the end of the list. //If this is a normal filtermapping, it is inserted after all the other filtermappings (matchBefores and normals), //but before the first matchAfter filtermapping. if (source != null && Source.JAVAX_API == source) { setFilterMappings(insertFilterMapping(mapping,mappings.length-1, false)); if (_matchAfterIndex < 0) _matchAfterIndex = getFilterMappings().length-1; } else { //insert non-programmatic filter mappings before any matchAfters, if any if (_matchAfterIndex < 0) setFilterMappings(insertFilterMapping(mapping,mappings.length-1, false)); else { FilterMapping[] new_mappings = insertFilterMapping(mapping, _matchAfterIndex, true); ++_matchAfterIndex; setFilterMappings(new_mappings); } } } } } /* ------------------------------------------------------------ */ /** Convenience method to add a preconstructed FilterMapping * @param mapping */ public void prependFilterMapping (FilterMapping mapping) { if (mapping != null) { Source source = mapping.getFilterHolder().getSource(); FilterMapping[] mappings = getFilterMappings(); if (mappings==null || mappings.length==0) { setFilterMappings(insertFilterMapping(mapping, 0, false)); if (source != null && Source.JAVAX_API == source) _matchBeforeIndex = 0; } else { if (source != null && Source.JAVAX_API == source) { //programmatically defined filter mappings are prepended to mapping list in the order //in which they were defined. In other words, insert this mapping at the tail of the //programmatically prepended filter mappings, BEFORE the first web.xml defined filter mapping. if (_matchBeforeIndex < 0) { //no programmatically defined prepended filter mappings yet, prepend this one _matchBeforeIndex = 0; FilterMapping[] new_mappings = insertFilterMapping(mapping, 0, true); setFilterMappings(new_mappings); } else { FilterMapping[] new_mappings = insertFilterMapping(mapping,_matchBeforeIndex, false); ++_matchBeforeIndex; setFilterMappings(new_mappings); } } else { //non programmatically defined, just prepend to list FilterMapping[] new_mappings = insertFilterMapping(mapping, 0, true); setFilterMappings(new_mappings); } //adjust matchAfterIndex ptr to take account of the mapping we just prepended if (_matchAfterIndex >= 0) ++_matchAfterIndex; } } } /** * Insert a filtermapping in the list * @param mapping the FilterMapping to add * @param pos the position in the existing arry at which to add it * @param before if true, insert before pos, if false insert after it * @return */ protected FilterMapping[] insertFilterMapping (FilterMapping mapping, int pos, boolean before) { if (pos < 0) throw new IllegalArgumentException("FilterMapping insertion pos < 0"); FilterMapping[] mappings = getFilterMappings(); if (mappings==null || mappings.length==0) { return new FilterMapping[] {mapping}; } FilterMapping[] new_mappings = new FilterMapping[mappings.length+1]; if (before) { //copy existing filter mappings up to but not including the pos System.arraycopy(mappings,0,new_mappings,0,pos); //add in the new mapping new_mappings[pos] = mapping; //copy the old pos mapping and any remaining existing mappings System.arraycopy(mappings,pos,new_mappings,pos+1, mappings.length-pos); } else { //copy existing filter mappings up to and including the pos System.arraycopy(mappings,0,new_mappings,0,pos+1); //add in the new mapping after the pos new_mappings[pos+1] = mapping; //copy the remaining existing mappings if (mappings.length > pos+1) System.arraycopy(mappings,pos+1,new_mappings,pos+2, mappings.length-(pos+1)); } return new_mappings; } /* ------------------------------------------------------------ */ protected synchronized void updateNameMappings() { // update filter name map _filterNameMap.clear(); if (_filters!=null) { for (int i=0;i<_filters.length;i++) { _filterNameMap.put(_filters[i].getName(),_filters[i]); _filters[i].setServletHandler(this); } } // Map servlet names to holders _servletNameMap.clear(); if (_servlets!=null) { // update the maps for (int i=0;i<_servlets.length;i++) { _servletNameMap.put(_servlets[i].getName(),_servlets[i]); _servlets[i].setServletHandler(this); } } } /* ------------------------------------------------------------ */ protected synchronized void updateMappings() { // update filter mappings if (_filterMappings==null) { _filterPathMappings=null; _filterNameMappings=null; } else { _filterPathMappings=new ArrayList(); _filterNameMappings=new MultiMap(); for (int i=0;i<_filterMappings.length;i++) { FilterHolder filter_holder = (FilterHolder)_filterNameMap.get(_filterMappings[i].getFilterName()); if (filter_holder==null) throw new IllegalStateException("No filter named "+_filterMappings[i].getFilterName()); _filterMappings[i].setFilterHolder(filter_holder); if (_filterMappings[i].getPathSpecs()!=null) _filterPathMappings.add(_filterMappings[i]); if (_filterMappings[i].getServletNames()!=null) { String[] names=_filterMappings[i].getServletNames(); for (int j=0;j0;) { if (_chainCache[i]!=null) _chainCache[i].clear(); } } if (LOG.isDebugEnabled()) { LOG.debug("filterNameMap="+_filterNameMap); LOG.debug("pathFilters="+_filterPathMappings); LOG.debug("servletFilterMap="+_filterNameMappings); LOG.debug("servletPathMap="+_servletPathMap); LOG.debug("servletNameMap="+_servletNameMap); } try { if (_contextHandler!=null && _contextHandler.isStarted() || _contextHandler==null && isStarted()) initialize(); } catch (Exception e) { throw new RuntimeException(e); } } /* ------------------------------------------------------------ */ protected void notFound(HttpServletRequest request, HttpServletResponse response) throws IOException { if(LOG.isDebugEnabled()) LOG.debug("Not Found "+request.getRequestURI()); //Override to send an error back, eg with: response.sendError(HttpServletResponse.SC_NOT_FOUND); } /* ------------------------------------------------------------ */ /** * @param filterChainsCached The filterChainsCached to set. */ public void setFilterChainsCached(boolean filterChainsCached) { _filterChainsCached = filterChainsCached; } /* ------------------------------------------------------------ */ /** * @param filterMappings The filterMappings to set. */ public void setFilterMappings(FilterMapping[] filterMappings) { if (getServer()!=null) getServer().getContainer().update(this,_filterMappings,filterMappings,"filterMapping",true); _filterMappings = filterMappings; updateMappings(); invalidateChainsCache(); } /* ------------------------------------------------------------ */ public synchronized void setFilters(FilterHolder[] holders) { if (getServer()!=null) getServer().getContainer().update(this,_filters,holders,"filter",true); _filters=holders; updateNameMappings(); invalidateChainsCache(); } /* ------------------------------------------------------------ */ /** * @param servletMappings The servletMappings to set. */ public void setServletMappings(ServletMapping[] servletMappings) { if (getServer()!=null) getServer().getContainer().update(this,_servletMappings,servletMappings,"servletMapping",true); _servletMappings = servletMappings; updateMappings(); invalidateChainsCache(); } /* ------------------------------------------------------------ */ /** Set Servlets. * @param holders Array of servletsto define */ public synchronized void setServlets(ServletHolder[] holders) { if (getServer()!=null) getServer().getContainer().update(this,_servlets,holders,"servlet",true); _servlets=holders; updateNameMappings(); invalidateChainsCache(); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class CachedChain implements FilterChain { FilterHolder _filterHolder; CachedChain _next; ServletHolder _servletHolder; /* ------------------------------------------------------------ */ CachedChain(Object filters, ServletHolder servletHolder) { if (LazyList.size(filters)>0) { _filterHolder=(FilterHolder)LazyList.get(filters, 0); filters=LazyList.remove(filters,0); _next=new CachedChain(filters,servletHolder); } else _servletHolder=servletHolder; } /* ------------------------------------------------------------ */ public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { final Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest(); // pass to next filter if (_filterHolder!=null) { if (LOG.isDebugEnabled()) LOG.debug("call filter " + _filterHolder); Filter filter= _filterHolder.getFilter(); if (_filterHolder.isAsyncSupported()) filter.doFilter(request, response, _next); else { final boolean suspendable=baseRequest.isAsyncSupported(); if (suspendable) { try { baseRequest.setAsyncSupported(false); filter.doFilter(request, response, _next); } finally { baseRequest.setAsyncSupported(true); } } else filter.doFilter(request, response, _next); } return; } // Call servlet HttpServletRequest srequest = (HttpServletRequest)request; if (_servletHolder != null) { if (LOG.isDebugEnabled()) LOG.debug("call servlet " + _servletHolder); _servletHolder.handle(baseRequest,request, response); } else if (getHandler()==null) notFound(srequest, (HttpServletResponse)response); else nextHandle(URIUtil.addPaths(srequest.getServletPath(),srequest.getPathInfo()), baseRequest,srequest,(HttpServletResponse)response); } public String toString() { if (_filterHolder!=null) return _filterHolder+"->"+_next.toString(); if (_servletHolder!=null) return _servletHolder.toString(); return "null"; } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class Chain implements FilterChain { final Request _baseRequest; final Object _chain; final ServletHolder _servletHolder; int _filter= 0; /* ------------------------------------------------------------ */ Chain(Request baseRequest, Object filters, ServletHolder servletHolder) { _baseRequest=baseRequest; _chain= filters; _servletHolder= servletHolder; } /* ------------------------------------------------------------ */ public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (LOG.isDebugEnabled()) LOG.debug("doFilter " + _filter); // pass to next filter if (_filter < LazyList.size(_chain)) { FilterHolder holder= (FilterHolder)LazyList.get(_chain, _filter++); if (LOG.isDebugEnabled()) LOG.debug("call filter " + holder); Filter filter= holder.getFilter(); if (holder.isAsyncSupported() || !_baseRequest.isAsyncSupported()) { filter.doFilter(request, response, this); } else { try { _baseRequest.setAsyncSupported(false); filter.doFilter(request, response, this); } finally { _baseRequest.setAsyncSupported(true); } } return; } // Call servlet HttpServletRequest srequest = (HttpServletRequest)request; if (_servletHolder != null) { if (LOG.isDebugEnabled()) LOG.debug("call servlet " + _servletHolder); _servletHolder.handle(_baseRequest,request, response); } else if (getHandler()==null) notFound(srequest, (HttpServletResponse)response); else { Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest(); nextHandle(URIUtil.addPaths(srequest.getServletPath(),srequest.getPathInfo()), baseRequest,srequest,(HttpServletResponse)response); } } /* ------------------------------------------------------------ */ public String toString() { StringBuilder b = new StringBuilder(); for (int i=0; i"); } b.append(_servletHolder); return b.toString(); } } /* ------------------------------------------------------------ */ /** * @return The maximum entries in a filter chain cache. */ public int getMaxFilterChainsCacheSize() { return _maxFilterChainsCacheSize; } /* ------------------------------------------------------------ */ /** Set the maximum filter chain cache size. * Filter chains are cached if {@link #isFilterChainsCached()} is true. If the max cache size * is greater than zero, then the cache is flushed whenever it grows to be this size. * * @param maxFilterChainsCacheSize the maximum number of entries in a filter chain cache. */ public void setMaxFilterChainsCacheSize(int maxFilterChainsCacheSize) { _maxFilterChainsCacheSize = maxFilterChainsCacheSize; } /* ------------------------------------------------------------ */ void destroyServlet(Servlet servlet) { if (_contextHandler!=null) _contextHandler.destroyServlet(servlet); } /* ------------------------------------------------------------ */ void destroyFilter(Filter filter) { if (_contextHandler!=null) _contextHandler.destroyFilter(filter); } /* ------------------------------------------------------------ */ @Override public void dump(Appendable out,String indent) throws IOException { super.dumpThis(out); dump(out,indent, TypeUtil.asList(getHandlers()), getBeans(), TypeUtil.asList(getFilterMappings()), TypeUtil.asList(getFilters()), TypeUtil.asList(getServletMappings()), TypeUtil.asList(getServlets())); } }