View Javadoc

1   // ========================================================================
2   // Copyright (c) 1999-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.servlet;
15  
16  import java.io.IOException;
17  import java.util.ArrayList;
18  import java.util.Arrays;
19  import java.util.HashMap;
20  import java.util.HashSet;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.concurrent.ConcurrentHashMap;
24  
25  import javax.servlet.Filter;
26  import javax.servlet.FilterChain;
27  import javax.servlet.RequestDispatcher;
28  import javax.servlet.Servlet;
29  import javax.servlet.ServletContext;
30  import javax.servlet.ServletException;
31  import javax.servlet.ServletRequest;
32  import javax.servlet.ServletResponse;
33  import javax.servlet.UnavailableException;
34  import javax.servlet.http.HttpServletRequest;
35  import javax.servlet.http.HttpServletResponse;
36  
37  import org.eclipse.jetty.continuation.ContinuationThrowable;
38  import org.eclipse.jetty.http.HttpException;
39  import org.eclipse.jetty.http.PathMap;
40  import org.eclipse.jetty.io.EofException;
41  import org.eclipse.jetty.security.IdentityService;
42  import org.eclipse.jetty.security.SecurityHandler;
43  import org.eclipse.jetty.server.Dispatcher;
44  import org.eclipse.jetty.server.DispatcherType;
45  import org.eclipse.jetty.server.HttpConnection;
46  import org.eclipse.jetty.server.Request;
47  import org.eclipse.jetty.server.Server;
48  import org.eclipse.jetty.server.UserIdentity;
49  import org.eclipse.jetty.server.handler.ContextHandler;
50  import org.eclipse.jetty.server.handler.ScopedHandler;
51  import org.eclipse.jetty.util.LazyList;
52  import org.eclipse.jetty.util.MultiException;
53  import org.eclipse.jetty.util.MultiMap;
54  import org.eclipse.jetty.util.URIUtil;
55  import org.eclipse.jetty.util.log.Log;
56  
57  /* --------------------------------------------------------------------- */
58  /** Servlet HttpHandler.
59   * This handler maps requests to servlets that implement the
60   * javax.servlet.http.HttpServlet API.
61   * <P>
62   * This handler does not implement the full J2EE features and is intended to
63   * be used when a full web application is not required.  Specifically filters
64   * and request wrapping are not supported.
65   * 
66   * Unless run as part of a {@link ServletContextHandler} or derivative, the {@link #initialize()}
67   * method must be called manually after start().
68   * 
69   * @see org.eclipse.jetty.webapp.WebAppContext
70   * 
71   */
72  public class ServletHandler extends ScopedHandler
73  {
74      /* ------------------------------------------------------------ */
75      public static final String __DEFAULT_SERVLET="default";
76          
77      /* ------------------------------------------------------------ */
78      private ContextHandler _contextHandler;
79      private ContextHandler.Context _servletContext;
80      private FilterHolder[] _filters;
81      private FilterMapping[] _filterMappings;
82      private boolean _filterChainsCached=true;
83      private int _maxFilterChainsCacheSize=1000;
84      private boolean _startWithUnavailable=true;
85      private IdentityService _identityService;
86      
87      private ServletHolder[] _servlets;
88      private ServletMapping[] _servletMappings;
89      
90      private transient Map<String,FilterHolder> _filterNameMap= new HashMap<String,FilterHolder>();
91      private transient List<FilterMapping> _filterPathMappings;
92      private transient MultiMap<String> _filterNameMappings;
93      
94      private transient Map<String,ServletHolder> _servletNameMap=new HashMap();
95      private transient PathMap _servletPathMap;
96      
97      protected transient ConcurrentHashMap _chainCache[];
98  
99  
100     /* ------------------------------------------------------------ */
101     /** Constructor. 
102      */
103     public ServletHandler()
104     {
105     }
106 
107     /* ------------------------------------------------------------ */
108     /* 
109      * @see org.eclipse.jetty.server.handler.AbstractHandler#setServer(org.eclipse.jetty.server.Server)
110      */
111     public void setServer(Server server)
112     {
113         if (getServer()!=null && getServer()!=server)
114         {
115             getServer().getContainer().update(this, _filters, null, "filter",true);
116             getServer().getContainer().update(this, _filterMappings, null, "filterMapping",true);
117             getServer().getContainer().update(this, _servlets, null, "servlet",true);
118             getServer().getContainer().update(this, _servletMappings, null, "servletMapping",true);
119         }
120         if (server!=null && getServer()!=server)
121         {
122             server.getContainer().update(this, null, _filters, "filter",true);
123             server.getContainer().update(this, null, _filterMappings, "filterMapping",true);
124             server.getContainer().update(this, null, _servlets, "servlet",true);
125             server.getContainer().update(this, null, _servletMappings, "servletMapping",true);
126         }
127         super.setServer(server);
128     }
129 
130     /* ----------------------------------------------------------------- */
131     protected synchronized void doStart()
132         throws Exception
133     {
134         _servletContext=ContextHandler.getCurrentContext();
135         _contextHandler=_servletContext==null?null:_servletContext.getContextHandler();
136 
137         if (_contextHandler!=null)
138         {
139             SecurityHandler security_handler = (SecurityHandler)_contextHandler.getChildHandlerByClass(SecurityHandler.class);
140             if (security_handler!=null)
141                 _identityService=security_handler.getIdentityService();
142         }
143         
144         updateNameMappings();
145         updateMappings();
146         
147         if(_filterChainsCached)
148             _chainCache= new ConcurrentHashMap[]{null,new ConcurrentHashMap(),new ConcurrentHashMap(),null,new ConcurrentHashMap(),null,null,null,new ConcurrentHashMap(),null,null,null,null,null,null,null,new ConcurrentHashMap()};
149 
150         super.doStart();
151         
152         if (_contextHandler==null || !(_contextHandler instanceof ServletContextHandler))
153             initialize();
154     }   
155     
156     /* ----------------------------------------------------------------- */
157     protected synchronized void doStop()
158         throws Exception
159     {
160         super.doStop();
161         
162         // Stop filters
163         if (_filters!=null)
164         {
165             for (int i=_filters.length; i-->0;)
166             {
167                 try { _filters[i].stop(); }catch(Exception e){Log.warn(Log.EXCEPTION,e);}
168             }
169         }
170         
171         // Stop servlets
172         if (_servlets!=null)
173         {
174             for (int i=_servlets.length; i-->0;)
175             {
176                 try { _servlets[i].stop(); }catch(Exception e){Log.warn(Log.EXCEPTION,e);}
177             }
178         }
179 
180         _filterPathMappings=null;
181         _filterNameMappings=null;
182         
183         _servletPathMap=null;
184         _chainCache=null;
185     }
186 
187     /* ------------------------------------------------------------ */
188     IdentityService getIdentityService()
189     {
190         return _identityService;
191     }
192     
193     /* ------------------------------------------------------------ */
194     /**
195      * @return Returns the contextLog.
196      */
197     public Object getContextLog()
198     {
199         return null;
200     }
201     
202     /* ------------------------------------------------------------ */
203     /**
204      * @return Returns the filterMappings.
205      */
206     public FilterMapping[] getFilterMappings()
207     {
208         return _filterMappings;
209     }
210     
211     /* ------------------------------------------------------------ */
212     /** Get Filters.
213      * @return Array of defined servlets
214      */
215     public FilterHolder[] getFilters()
216     {
217         return _filters;
218     }
219     
220     /* ------------------------------------------------------------ */
221     /** ServletHolder matching path.
222      * @param pathInContext Path within _context.
223      * @return PathMap Entries pathspec to ServletHolder
224      */
225     public PathMap.Entry getHolderEntry(String pathInContext)
226     {
227         if (_servletPathMap==null)
228             return null;
229         return _servletPathMap.getMatch(pathInContext);
230     }
231     
232     /* ------------------------------------------------------------ */
233 	/** Whether there is a ServletHolder that matches this path
234  	* @param pathInContext Path within _context.
235  	* @return whether there is a ServletHolder that matches this path
236  	*/
237  	public boolean matchesPath(String pathInContext)
238  	{
239  	return _servletPathMap.containsMatch(pathInContext);
240  	}
241 
242  	/* ------------------------------------------------------------ */
243     /**
244      * @param uriInContext uri to get dispatcher for
245      * @return A {@link RequestDispatcher dispatcher} wrapping the resource at <code>uriInContext</code>,
246      *  or <code>null</code> if the specified uri cannot be dispatched to.
247      */
248     public RequestDispatcher getRequestDispatcher(String uriInContext)
249     {
250         if (uriInContext == null)
251             return null;
252 
253         if (!uriInContext.startsWith("/"))
254             return null;
255         
256         try
257         {
258             String query=null;
259             int q;
260             if ((q=uriInContext.indexOf('?'))>0)
261             {
262                 query=uriInContext.substring(q+1);
263                 uriInContext=uriInContext.substring(0,q);
264             }
265             if ((q=uriInContext.indexOf(';'))>0)
266                 uriInContext=uriInContext.substring(0,q);
267 
268             String pathInContext=URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
269             String uri=URIUtil.addPaths(_contextHandler.getContextPath(), uriInContext);
270             return new Dispatcher(_contextHandler, uri, pathInContext, query);
271         }
272         catch(Exception e)
273         {
274             Log.ignore(e);
275         }
276         return null;
277     }
278 
279     /* ------------------------------------------------------------ */
280     public ServletContext getServletContext()
281     {
282         return _servletContext;
283     }
284     /* ------------------------------------------------------------ */
285     /**
286      * @return Returns the servletMappings.
287      */
288     public ServletMapping[] getServletMappings()
289     {
290         return _servletMappings;
291     }
292         
293     /* ------------------------------------------------------------ */
294     /** Get Servlets.
295      * @return Array of defined servlets
296      */
297     public ServletHolder[] getServlets()
298     {
299         return _servlets;
300     }
301 
302     /* ------------------------------------------------------------ */
303     public ServletHolder getServlet(String name)
304     {
305         return (ServletHolder)_servletNameMap.get(name);
306     }
307 
308     /* ------------------------------------------------------------ */
309     @Override
310     public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
311     {
312         // Get the base requests
313         final String old_servlet_path=baseRequest.getServletPath();
314         final String old_path_info=baseRequest.getPathInfo();
315 
316         DispatcherType type = baseRequest.getDispatcherType();
317        
318         ServletHolder servlet_holder=null;
319         UserIdentity.Scope old_scope=null;
320 
321         // find the servlet
322         if (target.startsWith("/"))
323         {
324             // Look for the servlet by path
325             PathMap.Entry entry=getHolderEntry(target);
326             if (entry!=null)
327             {
328                 servlet_holder=(ServletHolder)entry.getValue();
329 
330                 if(Log.isDebugEnabled())Log.debug("servlet="+servlet_holder);
331 
332                 String servlet_path_spec=(String)entry.getKey(); 
333                 String servlet_path=entry.getMapped()!=null?entry.getMapped():PathMap.pathMatch(servlet_path_spec,target);
334                 String path_info=PathMap.pathInfo(servlet_path_spec,target);
335 
336                 if (DispatcherType.INCLUDE.equals(type))
337                 {
338                     baseRequest.setAttribute(Dispatcher.INCLUDE_SERVLET_PATH,servlet_path);
339                     baseRequest.setAttribute(Dispatcher.INCLUDE_PATH_INFO, path_info);
340                 }
341                 else
342                 {
343                     baseRequest.setServletPath(servlet_path);
344                     baseRequest.setPathInfo(path_info);
345                 }
346             }      
347         }
348         else
349         {
350             // look for a servlet by name!
351             servlet_holder=(ServletHolder)_servletNameMap.get(target);
352         }
353 
354         Log.debug("servlet holder=",servlet_holder);
355 
356         try
357         {
358             // Do the filter/handling thang
359             if (servlet_holder!=null)
360             {
361                 old_scope=baseRequest.getUserIdentityScope();
362                 baseRequest.setUserIdentityScope(servlet_holder);
363 
364                 // start manual inline of nextScope(target,baseRequest,request,response);
365                 if (false)
366                     nextScope(target,baseRequest,request,response);
367                 else if (_nextScope!=null)
368                     _nextScope.doScope(target,baseRequest,request, response);
369                 else if (_outerScope!=null)
370                     _outerScope.doHandle(target,baseRequest,request, response);
371                 else 
372                     doHandle(target,baseRequest,request, response);
373                 // end manual inline (pathentic attempt to reduce stack depth)
374             }
375         }
376         finally
377         {
378             if (old_scope!=null)
379                 baseRequest.setUserIdentityScope(old_scope);
380 
381             if (!(DispatcherType.INCLUDE.equals(type)))
382             {
383                 baseRequest.setServletPath(old_servlet_path);
384                 baseRequest.setPathInfo(old_path_info); 
385             }
386         }
387         
388     }
389     
390     /* ------------------------------------------------------------ */
391     /* 
392      * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
393      */
394     public void doHandle(String target, Request baseRequest,HttpServletRequest request, HttpServletResponse response)
395         throws IOException, ServletException
396     {
397         DispatcherType type = baseRequest.getDispatcherType();
398         
399         ServletHolder servlet_holder=(ServletHolder) baseRequest.getUserIdentityScope();
400         FilterChain chain=null;
401 
402         // find the servlet
403         if (target.startsWith("/"))
404         {
405             if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0)
406                 chain=getFilterChain(baseRequest, target, servlet_holder);
407         }
408         else
409         {
410             if (servlet_holder!=null)
411             {
412                 if (_filterMappings!=null && _filterMappings.length>0)
413                 {
414                     chain=getFilterChain(baseRequest, null,servlet_holder);
415                 }
416             }
417         }
418 
419         Log.debug("chain=",chain);
420         
421         try
422         {
423             // Do the filter/handling thang
424             if (servlet_holder==null)
425             {
426                 notFound(request, response);
427             }
428             else
429             {
430                 baseRequest.setHandled(true);
431 
432                 if (chain!=null)
433                     chain.doFilter(request, response);
434                 else 
435                     servlet_holder.handle(baseRequest,request,response);
436             }
437         }
438         catch(EofException e)
439         {
440             throw e;
441         }
442         catch(Exception e)
443         {
444             if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
445             {
446                 if (e instanceof IOException)
447                     throw (IOException)e;
448                 if (e instanceof RuntimeException)
449                     throw (RuntimeException)e;
450                 if (e instanceof ServletException)
451                     throw (ServletException)e;
452             }
453 
454             // unwrap cause
455             Throwable th=e;
456             if (th instanceof UnavailableException)
457             {
458                 Log.debug(th); 
459             }
460             else if (th instanceof ServletException)
461             {
462                 Log.debug(th);
463                 Throwable cause=((ServletException)th).getRootCause();
464                 if (cause!=th && cause!=null)
465                     th=cause;
466             }
467 
468             // handle or log exception
469             if (th instanceof HttpException)
470             {
471                 throw (HttpException)th;
472             }
473             else if (Log.isDebugEnabled())
474             {
475                 Log.warn(request.getRequestURI(), th); 
476                 Log.debug(request.toString()); 
477             }
478             else if (th instanceof IOException || th instanceof UnavailableException)
479             {
480                 Log.warn(request.getRequestURI()+": "+th);
481             }
482             else
483             {
484                 Log.warn(request.getRequestURI(),th);
485             }
486 
487             // TODO httpResponse.getHttpConnection().forceClose();
488             if (!response.isCommitted())
489             {
490                 request.setAttribute(Dispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
491                 request.setAttribute(Dispatcher.ERROR_EXCEPTION,th);
492                 if (th instanceof UnavailableException)
493                 {
494                     UnavailableException ue = (UnavailableException)th;
495                     if (ue.isPermanent())
496                         response.sendError(HttpServletResponse.SC_NOT_FOUND,th.getMessage());
497                     else
498                         response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,th.getMessage());
499                 }
500                 else
501                     response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,th.getMessage());
502             }
503             else
504                 if(Log.isDebugEnabled())Log.debug("Response already committed for handling "+th);
505         }
506         catch(ContinuationThrowable e)
507         {   
508             throw e;
509         }
510         catch(Error e)
511         {   
512             if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
513                 throw e;
514             Log.warn("Error for "+request.getRequestURI(),e);
515             if(Log.isDebugEnabled())Log.debug(request.toString());
516 
517             // TODO httpResponse.getHttpConnection().forceClose();
518             if (!response.isCommitted())
519             {
520                 request.setAttribute(Dispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
521                 request.setAttribute(Dispatcher.ERROR_EXCEPTION,e);
522                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,e.getMessage());
523             }
524             else
525                 if(Log.isDebugEnabled())Log.debug("Response already committed for handling ",e);
526         }
527     }
528 
529     /* ------------------------------------------------------------ */
530     private FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder) 
531     {
532         String key=pathInContext==null?servletHolder.getName():pathInContext;
533         int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType());
534         
535         if (_filterChainsCached && _chainCache!=null)
536         {
537             FilterChain chain = (FilterChain)_chainCache[dispatch].get(key);
538             if (chain!=null)
539                 return chain;
540         }
541         
542         // Build list of filters
543         Object filters= null;
544         // Path filters
545         if (pathInContext!=null && _filterPathMappings!=null)
546         {
547             for (int i= 0; i < _filterPathMappings.size(); i++)
548             {
549                 FilterMapping mapping = (FilterMapping)_filterPathMappings.get(i);
550                 if (mapping.appliesTo(pathInContext, dispatch))
551                     filters= LazyList.add(filters, mapping.getFilterHolder());
552             }
553         }
554 
555         // Servlet name filters
556         if (servletHolder != null && _filterNameMappings!=null && _filterNameMappings.size() > 0)
557         {
558             // Servlet name filters
559             if (_filterNameMappings.size() > 0)
560             {
561                 Object o= _filterNameMappings.get(servletHolder.getName());
562                 for (int i=0; i<LazyList.size(o);i++)
563                 {
564                     FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
565                     if (mapping.appliesTo(dispatch))
566                         filters=LazyList.add(filters,mapping.getFilterHolder());
567                 }
568                 
569                 o= _filterNameMappings.get("*");
570                 for (int i=0; i<LazyList.size(o);i++)
571                 {
572                     FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
573                     if (mapping.appliesTo(dispatch))
574                         filters=LazyList.add(filters,mapping.getFilterHolder());
575                 }
576             }
577         }
578         
579         if (filters==null)
580             return null;
581         
582         FilterChain chain = null;
583         if (_filterChainsCached)
584         {
585         	if (LazyList.size(filters) > 0)
586         		chain= new CachedChain(filters, servletHolder);
587         	if (_maxFilterChainsCacheSize>0 && _chainCache[dispatch].size()>_maxFilterChainsCacheSize)
588         		_chainCache[dispatch].clear();
589         	_chainCache[dispatch].put(key,chain);
590         }
591         else if (LazyList.size(filters) > 0)
592             chain = new Chain(baseRequest,filters, servletHolder);
593     
594         return chain;
595     }
596 
597     /* ------------------------------------------------------------ */
598     /**
599      * @return Returns the initializeAtStart.
600      * @deprecated
601      */
602     public boolean isInitializeAtStart()
603     {
604         return false;
605     }
606     
607     /* ------------------------------------------------------------ */
608     /**
609      * @param initializeAtStart The initializeAtStart to set.
610      * @deprecated
611      */
612     public void setInitializeAtStart(boolean initializeAtStart)
613     {
614     }
615 
616     /* ------------------------------------------------------------ */
617     /**
618      * @return true if the handler is started and there are no unavailable servlets 
619      */
620     public boolean isAvailable()
621     {
622         if (!isStarted())
623             return false;
624         ServletHolder[] holders = getServlets();
625         for (int i=0;i<holders.length;i++)
626         {
627             ServletHolder holder = holders[i];
628             if (holder!=null && !holder.isAvailable())
629                 return false;
630         }
631         return true;
632     }
633     
634     /* ------------------------------------------------------------ */
635     /**
636      * @param start True if this handler will start with unavailable servlets
637      */
638     public void setStartWithUnavailable(boolean start)
639     {
640         _startWithUnavailable=start;
641     }
642     
643     /* ------------------------------------------------------------ */
644     /**
645      * @return True if this handler will start with unavailable servlets
646      */
647     public boolean isStartWithUnavailable()
648     {
649         return _startWithUnavailable;
650     }
651     
652     
653     
654     /* ------------------------------------------------------------ */
655     /** Initialize filters and load-on-startup servlets.
656      * Called automatically from start if autoInitializeServlet is true.
657      */
658     public void initialize()
659         throws Exception
660     {
661         MultiException mx = new MultiException();
662 
663         // Start filters
664         if (_filters!=null)
665         {
666             for (int i=0;i<_filters.length; i++)
667                 _filters[i].start();
668         }
669         
670         if (_servlets!=null)
671         {
672             // Sort and Initialize servlets
673             ServletHolder[] servlets = (ServletHolder[])_servlets.clone();
674             Arrays.sort(servlets);
675             for (int i=0; i<servlets.length; i++)
676             {
677                 try
678                 {
679                     if (servlets[i].getClassName()==null && servlets[i].getForcedPath()!=null)
680                     {
681                         ServletHolder forced_holder = (ServletHolder)_servletPathMap.match(servlets[i].getForcedPath());
682                         if (forced_holder==null || forced_holder.getClassName()==null)
683                         {    
684                             mx.add(new IllegalStateException("No forced path servlet for "+servlets[i].getForcedPath()));
685                             continue;
686                         }
687                         servlets[i].setClassName(forced_holder.getClassName());
688                     }
689                     
690                     servlets[i].start();
691                 }
692                 catch(Throwable e)
693                 {
694                     Log.debug(Log.EXCEPTION,e);
695                     mx.add(e);
696                 }
697             } 
698             mx.ifExceptionThrow();  
699         }
700     }
701     
702     /* ------------------------------------------------------------ */
703     /**
704      * @return Returns the filterChainsCached.
705      */
706     public boolean isFilterChainsCached()
707     {
708         return _filterChainsCached;
709     }
710 
711     /* ------------------------------------------------------------ */
712     /**
713      * see also newServletHolder(Class)
714      */
715     public ServletHolder newServletHolder()
716     {
717         return new ServletHolder();
718     }
719     
720     /* ------------------------------------------------------------ */
721     public ServletHolder newServletHolder(Class servlet)
722     {
723         return new ServletHolder(servlet);
724     }
725     
726     /* ------------------------------------------------------------ */
727     /** conveniance method to add a servlet.
728      * @return The servlet holder.
729      */
730     public ServletHolder addServletWithMapping (String className,String pathSpec)
731     {
732         ServletHolder holder = newServletHolder(null);
733         holder.setName(className+"-"+holder.hashCode());
734         holder.setClassName(className);
735         
736         addServletWithMapping(holder,pathSpec);
737         
738         return holder;
739     }   
740     
741     /* ------------------------------------------------------------ */
742     /** conveniance method to add a servlet.
743      * @return The servlet holder.
744      */
745     public ServletHolder addServletWithMapping (Class<? extends Servlet> servlet,String pathSpec)
746     {
747         ServletHolder holder = newServletHolder(servlet);
748         setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
749         
750         addServletWithMapping(holder,pathSpec);
751         
752         return holder;
753     }   
754     
755     /* ------------------------------------------------------------ */
756     /** conveniance method to add a servlet.
757      * @param servlet servlet holder to add
758      * @param pathSpec servlet mappings for the servletHolder
759      */
760     public void addServletWithMapping (ServletHolder servlet,String pathSpec)
761     {
762         ServletHolder[] holders=getServlets();
763         if (holders!=null)
764             holders = holders.clone();
765         
766         try
767         {
768             setServlets((ServletHolder[])LazyList.addToArray(holders, servlet, ServletHolder.class));
769             
770             ServletMapping mapping = new ServletMapping();
771             mapping.setServletName(servlet.getName());
772             mapping.setPathSpec(pathSpec);
773             setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class));
774         }
775         catch (Exception e)
776         {
777             setServlets(holders);
778             if (e instanceof RuntimeException)
779                 throw (RuntimeException)e;
780             throw new RuntimeException(e);
781         }
782     }
783 
784     /* ------------------------------------------------------------ */
785     /** Convenience method to add a servlet with a servlet mapping.
786      * @param className
787      * @param pathSpec
788      * @return
789      * @deprecated
790      */
791     public ServletHolder addServlet (String className, String pathSpec)
792     {
793         return addServletWithMapping (className, pathSpec);
794     }
795 
796     
797     /* ------------------------------------------------------------ */    
798     /**Convenience method to add a pre-constructed ServletHolder.
799      * @param holder
800      */
801     public void addServlet(ServletHolder holder)
802     {
803         setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
804     }
805     
806     /* ------------------------------------------------------------ */    
807     /** Convenience method to add a pre-constructed ServletMapping.
808      * @param mapping
809      */
810     public void addServletMapping (ServletMapping mapping)
811     {
812         setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class));
813     }
814     
815     /* ------------------------------------------------------------ */
816     public FilterHolder newFilterHolder(Class<? extends Filter> filter)
817     {
818         return new FilterHolder(filter);
819     }
820     
821     /* ------------------------------------------------------------ */
822     /** 
823      * @see {@link #newFilterHolder(Class)}
824      */
825     public FilterHolder newFilterHolder()
826     {
827         return new FilterHolder();
828     }
829 
830     /* ------------------------------------------------------------ */
831     public FilterHolder getFilter(String name)
832     {
833         return (FilterHolder)_filterNameMap.get(name);
834     }
835     
836     /* ------------------------------------------------------------ */
837     /** conveniance method to add a filter.
838      * @param filter  class of filter to create
839      * @param pathSpec filter mappings for filter
840      * @param dispatches see {@link FilterMapping#setDispatches(int)}
841      * @return The filter holder.
842      */
843     public FilterHolder addFilterWithMapping (Class<? extends Filter> filter,String pathSpec,int dispatches)
844     {
845         FilterHolder holder = newFilterHolder(filter);
846         addFilterWithMapping(holder,pathSpec,dispatches);
847         
848         return holder;
849     }
850     
851     /* ------------------------------------------------------------ */
852     /** conveniance method to add a filter.
853      * @param className of filter
854      * @param pathSpec filter mappings for filter
855      * @param dispatches see {@link FilterMapping#setDispatches(int)}
856      * @return The filter holder.
857      */
858     public FilterHolder addFilterWithMapping (String className,String pathSpec,int dispatches)
859     {
860         FilterHolder holder = newFilterHolder(null);
861         holder.setName(className+"-"+holder.hashCode());
862         holder.setClassName(className);
863         
864         addFilterWithMapping(holder,pathSpec,dispatches);
865         return holder;
866     }
867     
868     /* ------------------------------------------------------------ */
869     /** conveniance method to add a filter.
870      * @param holder filter holder to add
871      * @param pathSpec filter mappings for filter
872      * @param dispatches see {@link FilterMapping#setDispatches(int)}
873      */
874     public void addFilterWithMapping (FilterHolder holder,String pathSpec,int dispatches)
875     {
876         FilterHolder[] holders = getFilters();
877         if (holders!=null)
878             holders = (FilterHolder[])holders.clone();
879         
880         try
881         {
882             setFilters((FilterHolder[])LazyList.addToArray(holders, holder, FilterHolder.class));
883             
884             FilterMapping mapping = new FilterMapping();
885             mapping.setFilterName(holder.getName());
886             mapping.setPathSpec(pathSpec);
887             mapping.setDispatches(dispatches);
888             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
889         }
890         catch (RuntimeException e)
891         {
892             setFilters(holders);
893             throw e;
894         }
895         catch (Error e)
896         {
897             setFilters(holders);
898             throw e;
899         }
900             
901     }
902     
903     /* ------------------------------------------------------------ */
904     /** Convenience method to add a filter with a mapping
905      * @param className
906      * @param pathSpec
907      * @param dispatches
908      * @return
909      * @deprecated
910      */
911     public FilterHolder addFilter (String className,String pathSpec,int dispatches)
912     {
913         return addFilterWithMapping(className, pathSpec, dispatches);
914     }
915     
916     /* ------------------------------------------------------------ */
917     /**
918      * convenience method to add a filter and mapping
919      * @param filter
920      * @param filterMapping
921      */
922     public void addFilter (FilterHolder filter, FilterMapping filterMapping)
923     {
924         if (filter != null)
925             setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class));
926         if (filterMapping != null)
927             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), filterMapping, FilterMapping.class));
928     }
929     
930     /* ------------------------------------------------------------ */  
931     /** Convenience method to add a preconstructed FilterHolder
932      * @param filter
933      */
934     public void addFilter (FilterHolder filter)
935     {
936         if (filter != null)
937             setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class));
938     }
939     
940     /* ------------------------------------------------------------ */
941     /** Convenience method to add a preconstructed FilterMapping
942      * @param mapping
943      */
944     public void addFilterMapping (FilterMapping mapping)
945     {
946         if (mapping != null)
947             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
948     }
949     
950     /* ------------------------------------------------------------ */
951     /** Convenience method to add a preconstructed FilterMapping
952      * @param mapping
953      */
954     public void prependFilterMapping (FilterMapping mapping)
955     {
956         if (mapping != null)
957         {
958             FilterMapping[] mappings =getFilterMappings();
959             if (mappings==null || mappings.length==0)
960                 setFilterMappings(new FilterMapping[] {mapping});
961             else
962             {
963 
964                 FilterMapping[] new_mappings=new FilterMapping[mappings.length+1];
965                 System.arraycopy(mappings,0,new_mappings,1,mappings.length);
966                 new_mappings[0]=mapping;
967                 setFilterMappings(new_mappings);
968             }
969         }
970     }
971 
972     /* ------------------------------------------------------------ */
973     protected synchronized void updateNameMappings()
974     {   
975         // update filter name map
976         _filterNameMap.clear();
977         if (_filters!=null)
978         {   
979             for (int i=0;i<_filters.length;i++)
980             {
981                 _filterNameMap.put(_filters[i].getName(),_filters[i]);
982                 _filters[i].setServletHandler(this);
983             }
984         }
985 
986         // Map servlet names to holders
987         _servletNameMap.clear();
988         if (_servlets!=null)
989         {   
990             // update the maps
991             for (int i=0;i<_servlets.length;i++)
992             {
993                 _servletNameMap.put(_servlets[i].getName(),_servlets[i]);
994                 _servlets[i].setServletHandler(this);
995             }
996         }
997     }
998     
999     /* ------------------------------------------------------------ */
1000     protected synchronized void updateMappings()
1001     {   
1002         // update filter mappings
1003         if (_filterMappings==null)
1004         {
1005             _filterPathMappings=null;
1006             _filterNameMappings=null;
1007         }
1008         else 
1009         {
1010             _filterPathMappings=new ArrayList();
1011             _filterNameMappings=new MultiMap();
1012             for (int i=0;i<_filterMappings.length;i++)
1013             {
1014                 FilterHolder filter_holder = (FilterHolder)_filterNameMap.get(_filterMappings[i].getFilterName());
1015                 if (filter_holder==null)
1016                     throw new IllegalStateException("No filter named "+_filterMappings[i].getFilterName());
1017                 _filterMappings[i].setFilterHolder(filter_holder);    
1018                 if (_filterMappings[i].getPathSpecs()!=null)
1019                     _filterPathMappings.add(_filterMappings[i]);
1020                 
1021                 if (_filterMappings[i].getServletNames()!=null)
1022                 {
1023                     String[] names=_filterMappings[i].getServletNames();
1024                     for (int j=0;j<names.length;j++)
1025                     {
1026                         if (names[j]!=null)
1027                             _filterNameMappings.add(names[j], _filterMappings[i]);  
1028                     }
1029                 }
1030             }
1031         }
1032 
1033         // Map servlet paths to holders
1034         if (_servletMappings==null || _servletNameMap==null)
1035         {
1036             _servletPathMap=null;
1037         }
1038         else
1039         {
1040             PathMap pm = new PathMap();
1041             
1042             // update the maps
1043             for (int i=0;i<_servletMappings.length;i++)
1044             {
1045                 ServletHolder servlet_holder = (ServletHolder)_servletNameMap.get(_servletMappings[i].getServletName());
1046                 if (servlet_holder==null)
1047                     throw new IllegalStateException("No such servlet: "+_servletMappings[i].getServletName());
1048                 else if (_servletMappings[i].getPathSpecs()!=null)
1049                 {
1050                     String[] pathSpecs = _servletMappings[i].getPathSpecs();
1051                     for (int j=0;j<pathSpecs.length;j++)
1052                         if (pathSpecs[j]!=null)
1053                             pm.put(pathSpecs[j],servlet_holder);
1054                 }
1055             }
1056             
1057             _servletPathMap=pm;
1058         }
1059         
1060         
1061 
1062         if (Log.isDebugEnabled()) 
1063         {
1064             Log.debug("filterNameMap="+_filterNameMap);
1065             Log.debug("pathFilters="+_filterPathMappings);
1066             Log.debug("servletFilterMap="+_filterNameMappings);
1067             Log.debug("servletPathMap="+_servletPathMap);
1068             Log.debug("servletNameMap="+_servletNameMap);
1069         }
1070         
1071         try
1072         {
1073             if (isStarted())
1074                 initialize();
1075         }
1076         catch (Exception e)
1077         {
1078             throw new RuntimeException(e);
1079         }
1080     }
1081 
1082     /* ------------------------------------------------------------ */
1083     protected void notFound(HttpServletRequest request,
1084                   HttpServletResponse response)
1085         throws IOException
1086     {
1087         if(Log.isDebugEnabled())Log.debug("Not Found "+request.getRequestURI());
1088         response.sendError(HttpServletResponse.SC_NOT_FOUND);
1089     }
1090     
1091     /* ------------------------------------------------------------ */
1092     /**
1093      * @param filterChainsCached The filterChainsCached to set.
1094      */
1095     public void setFilterChainsCached(boolean filterChainsCached)
1096     {
1097         _filterChainsCached = filterChainsCached;
1098     }
1099     
1100     /* ------------------------------------------------------------ */
1101     /**
1102      * @param filterMappings The filterMappings to set.
1103      */
1104     public void setFilterMappings(FilterMapping[] filterMappings)
1105     {
1106         if (getServer()!=null)
1107             getServer().getContainer().update(this,_filterMappings,filterMappings,"filterMapping",true);
1108         _filterMappings = filterMappings;
1109         updateMappings();
1110     }
1111     
1112     /* ------------------------------------------------------------ */
1113     public synchronized void setFilters(FilterHolder[] holders)
1114     {
1115         if (getServer()!=null)
1116             getServer().getContainer().update(this,_filters,holders,"filter",true);
1117         _filters=holders;
1118         updateNameMappings();
1119     }
1120     
1121     /* ------------------------------------------------------------ */
1122     /**
1123      * @param servletMappings The servletMappings to set.
1124      */
1125     public void setServletMappings(ServletMapping[] servletMappings)
1126     {
1127         if (getServer()!=null)
1128             getServer().getContainer().update(this,_servletMappings,servletMappings,"servletMapping",true);
1129         _servletMappings = servletMappings;
1130         updateMappings();
1131     }
1132     
1133     /* ------------------------------------------------------------ */
1134     /** Set Servlets.
1135      * @param holders Array of servletsto define
1136      */
1137     public synchronized void setServlets(ServletHolder[] holders)
1138     {
1139         if (getServer()!=null)
1140             getServer().getContainer().update(this,_servlets,holders,"servlet",true);
1141         _servlets=holders;
1142         updateNameMappings();
1143     }
1144 
1145 
1146     /* ------------------------------------------------------------ */
1147     /* ------------------------------------------------------------ */
1148     private class CachedChain implements FilterChain
1149     {
1150         FilterHolder _filterHolder;
1151         CachedChain _next;
1152         ServletHolder _servletHolder;
1153 
1154         /* ------------------------------------------------------------ */
1155         CachedChain(Object filters, ServletHolder servletHolder)
1156         {
1157             if (LazyList.size(filters)>0)
1158             {
1159                 _filterHolder=(FilterHolder)LazyList.get(filters, 0);
1160                 filters=LazyList.remove(filters,0);
1161                 _next=new CachedChain(filters,servletHolder);
1162             }
1163             else
1164                 _servletHolder=servletHolder;
1165         }
1166 
1167         /* ------------------------------------------------------------ */
1168         public void doFilter(ServletRequest request, ServletResponse response) 
1169             throws IOException, ServletException
1170         {
1171             // pass to next filter
1172             if (_filterHolder!=null)
1173             {
1174                 if (Log.isDebugEnabled())
1175                     Log.debug("call filter " + _filterHolder);
1176                 Filter filter= _filterHolder.getFilter();
1177                 if (_filterHolder.isAsyncSupported())
1178                     filter.doFilter(request, response, _next);
1179                 else
1180                 {
1181                     final Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
1182                     final boolean suspendable=baseRequest.isAsyncSupported();
1183                     if (suspendable)
1184                     {
1185                         try
1186                         {
1187                             baseRequest.setAsyncSupported(false);
1188                             filter.doFilter(request, response, _next);
1189                         }
1190                         finally
1191                         {
1192                             baseRequest.setAsyncSupported(true);
1193                         }
1194                     }
1195                     else
1196                         filter.doFilter(request, response, _next);
1197                 }
1198                 return;
1199             }
1200 
1201             // Call servlet
1202             if (_servletHolder != null)
1203             {
1204                 if (Log.isDebugEnabled())
1205                     Log.debug("call servlet " + _servletHolder);
1206                 final Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
1207                 _servletHolder.handle(baseRequest,request, response);
1208             }
1209             else // Not found
1210                 notFound((HttpServletRequest)request, (HttpServletResponse)response);
1211         }
1212         
1213         public String toString()
1214         {
1215             if (_filterHolder!=null)
1216                 return _filterHolder+"->"+_next.toString();
1217             if (_servletHolder!=null)
1218                 return _servletHolder.toString();
1219             return "null";
1220         }
1221     }  
1222     
1223     /* ------------------------------------------------------------ */
1224     /* ------------------------------------------------------------ */
1225     private class Chain implements FilterChain
1226     {
1227         final Request _baseRequest;
1228         final Object _chain;
1229         final ServletHolder _servletHolder;
1230         int _filter= 0;
1231 
1232         /* ------------------------------------------------------------ */
1233         Chain(Request baseRequest, Object filters, ServletHolder servletHolder)
1234         {
1235             _baseRequest=baseRequest;
1236             _chain= filters;
1237             _servletHolder= servletHolder;
1238         }
1239 
1240         /* ------------------------------------------------------------ */
1241         public void doFilter(ServletRequest request, ServletResponse response)
1242             throws IOException, ServletException
1243         {
1244             if (Log.isDebugEnabled()) Log.debug("doFilter " + _filter);
1245 
1246             // pass to next filter
1247             if (_filter < LazyList.size(_chain))
1248             {
1249                 FilterHolder holder= (FilterHolder)LazyList.get(_chain, _filter++);
1250                 if (Log.isDebugEnabled()) Log.debug("call filter " + holder);
1251                 Filter filter= holder.getFilter();
1252                 
1253                 if (holder.isAsyncSupported() || !_baseRequest.isAsyncSupported())
1254                 {
1255                     filter.doFilter(request, response, this);
1256                 }
1257                 else
1258                 {
1259                     try
1260                     {
1261                         _baseRequest.setAsyncSupported(false);
1262                         filter.doFilter(request, response, this);
1263                     }
1264                     finally
1265                     {
1266                         _baseRequest.setAsyncSupported(true);
1267                     }
1268                 }
1269                     
1270                 return;
1271             }
1272 
1273             // Call servlet
1274             if (_servletHolder != null)
1275             {
1276                 if (Log.isDebugEnabled()) Log.debug("call servlet " + _servletHolder);
1277                 _servletHolder.handle(_baseRequest,request, response);
1278             }
1279             else // Not found
1280                 notFound((HttpServletRequest)request, (HttpServletResponse)response);
1281         }
1282 
1283         /* ------------------------------------------------------------ */
1284         public String toString()
1285         {
1286             StringBuilder b = new StringBuilder();
1287             for (int i=0; i<LazyList.size(_chain);i++)
1288             {
1289                 Object o=LazyList.get(_chain, i);
1290                 b.append(o.toString());
1291                 b.append("->");
1292             }
1293             b.append(_servletHolder);
1294             return b.toString();
1295         }
1296     }
1297 
1298     /* ------------------------------------------------------------ */
1299     /**
1300      * @return The maximum entries in a filter chain cache.
1301      */
1302     public int getMaxFilterChainsCacheSize()
1303     {
1304         return _maxFilterChainsCacheSize;
1305     }
1306 
1307     /* ------------------------------------------------------------ */
1308     /** Set the maximum filter chain cache size.
1309      * Filter chains are cached if {@link #isFilterChainsCached()} is true. If the max cache size
1310      * is greater than zero, then the cache is flushed whenever it grows to be this size.
1311      * 
1312      * @param maxFilterChainsCacheSize  the maximum number of entries in a filter chain cache.
1313      */
1314     public void setMaxFilterChainsCacheSize(int maxFilterChainsCacheSize)
1315     {
1316         _maxFilterChainsCacheSize = maxFilterChainsCacheSize;
1317     }
1318     
1319     /**
1320      * Customize a servlet.
1321      * 
1322      * Called before the servlet goes into service.
1323      * Subclasses of ServletHandler should override
1324      * this method.
1325      * 
1326      * @param servlet
1327      * @return
1328      * @throws Exception
1329      */
1330     public Servlet customizeServlet (Servlet servlet)
1331     throws Exception
1332     {
1333         return servlet;
1334     }
1335     
1336     
1337     public Servlet customizeServletDestroy (Servlet servlet)
1338     throws Exception
1339     {
1340         return servlet;
1341     }
1342     
1343     
1344     /**
1345      * Customize a Filter.
1346      * 
1347      * Called before the Filter goes into service.
1348      * Subclasses of ServletHandler should override
1349      * this method.
1350      * 
1351      * @param filter
1352      * @return
1353      * @throws Exception
1354      */
1355     public Filter customizeFilter (Filter filter)
1356     throws Exception
1357     {
1358         return filter;
1359     }
1360     
1361     
1362     public Filter customizeFilterDestroy (Filter filter)
1363     throws Exception
1364     {
1365         return filter;
1366     }
1367     
1368 
1369     
1370     /* ------------------------------------------------------------ */
1371     protected void dump(StringBuilder b,String indent)
1372     {
1373         super.dump(b,indent);
1374 
1375         if (getFilterMappings()!=null)
1376         {
1377             for (FilterMapping f : getFilterMappings())
1378             {
1379                 b.append(indent);
1380                 b.append(" +-");
1381                 b.append(f);
1382                 b.append('\n');
1383             }
1384         }
1385         HashSet<String> servlets = new HashSet<String>();
1386         if (getServletMappings()!=null)
1387         {
1388             for (ServletMapping m : getServletMappings())
1389             {
1390                 servlets.add(m.getServletName());
1391                 b.append(indent);
1392                 b.append(" +-");
1393                 b.append(m);
1394                 b.append('\n');
1395             }
1396         }
1397 
1398         if (getServlets()!=null)
1399         {
1400             for (ServletHolder h : getServlets())
1401             {
1402                 if (servlets.contains(h.getName()))
1403                     continue;
1404                 b.append(indent);
1405                 b.append(" +-[]==>");
1406                 b.append(h.getName());
1407                 b.append('\n');
1408             }
1409         }
1410 
1411     }
1412 
1413     
1414 }