View Javadoc

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