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