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 (th instanceof RuntimeIOException)
444                 throw (RuntimeIOException)th;
445             else if (th instanceof EofException)
446                 throw (EofException)th;
447             else if (Log.isDebugEnabled())
448             {
449                 Log.warn(request.getRequestURI(), th); 
450                 Log.debug(request.toString()); 
451             }
452             else if (th instanceof IOException || th instanceof UnavailableException)
453             {
454                 Log.warn(request.getRequestURI()+": "+th);
455             }
456             else
457             {
458                 Log.warn(request.getRequestURI(),th);
459             }
460             
461             // TODO httpResponse.getHttpConnection().forceClose();
462             if (!response.isCommitted())
463             {
464                 request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE,th.getClass());
465                 request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION,th);
466                 if (th instanceof UnavailableException)
467                 {
468                     UnavailableException ue = (UnavailableException)th;
469                     if (ue.isPermanent())
470                         response.sendError(HttpServletResponse.SC_NOT_FOUND,th.getMessage());
471                     else
472                         response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,th.getMessage());
473                 }
474                 else
475                     response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,th.getMessage());
476             }
477             else
478                 if(Log.isDebugEnabled())Log.debug("Response already committed for handling "+th);
479         }
480         catch(Error e)
481         {   
482             if (type!=REQUEST)
483                 throw e;
484             Log.warn("Error for "+request.getRequestURI(),e);
485             if(Log.isDebugEnabled())Log.debug(request.toString());
486             
487             // TODO httpResponse.getHttpConnection().forceClose();
488             if (!response.isCommitted())
489             {
490                 request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE,e.getClass());
491                 request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION,e);
492                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,e.getMessage());
493             }
494             else
495                 if(Log.isDebugEnabled())Log.debug("Response already committed for handling ",e);
496         }
497         finally
498         {
499             if (request_listeners!=null)
500             {
501                 for(int i=LazyList.size(request_listeners);i-->0;)
502                 {
503                     final ServletRequestListener listener = (ServletRequestListener)LazyList.get(request_listeners,i);
504                     listener.requestDestroyed(request_event);
505                 }
506             }
507             
508             base_request.setServletName(old_servlet_name);
509             base_request.setRoleMap(old_role_map);
510             if (type!=INCLUDE)
511             {
512                 base_request.setServletPath(old_servlet_path);
513                 base_request.setPathInfo(old_path_info); 
514             }
515         }
516         return;
517     }
518 
519     /* ------------------------------------------------------------ */
520     private FilterChain getFilterChain(int requestType, String pathInContext, ServletHolder servletHolder) 
521     {
522         String key=pathInContext==null?servletHolder.getName():pathInContext;
523         
524         if (_filterChainsCached && _chainCache!=null)
525         {
526             synchronized(this)
527             {
528                 if(_chainCache[requestType].containsKey(key))
529                     return (FilterChain)_chainCache[requestType].get(key);
530             }
531         }
532         
533         // Build list of filters
534         Object filters= null;
535     
536         // Path filters
537         if (pathInContext!=null && _filterPathMappings!=null)
538         {
539             for (int i= 0; i < _filterPathMappings.size(); i++)
540             {
541                 FilterMapping mapping = (FilterMapping)_filterPathMappings.get(i);
542                 if (mapping.appliesTo(pathInContext, requestType))
543                     filters= LazyList.add(filters, mapping.getFilterHolder());
544             }
545         }
546 
547         // Servlet name filters
548         if (servletHolder != null && _filterNameMappings!=null && _filterNameMappings.size() > 0)
549         {
550             // Servlet name filters
551             if (_filterNameMappings.size() > 0)
552             {
553                 Object o= _filterNameMappings.get(servletHolder.getName());
554                 for (int i=0; i<LazyList.size(o);i++)
555                 {
556                     FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
557                     if (mapping.appliesTo(requestType))
558                         filters=LazyList.add(filters,mapping.getFilterHolder());
559                 }
560                 
561                 o= _filterNameMappings.get("*");
562                 for (int i=0; i<LazyList.size(o);i++)
563                 {
564                     FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
565                     if (mapping.appliesTo(requestType))
566                         filters=LazyList.add(filters,mapping.getFilterHolder());
567                 }
568             }
569         }
570         
571         if (filters==null)
572             return null;
573         
574         FilterChain chain = null;
575         if (_filterChainsCached)
576         {
577             if (LazyList.size(filters) > 0)
578                 chain= new CachedChain(filters, servletHolder);
579             synchronized(this)
580             {
581                 if (_maxFilterChainsCacheSize>0 && _chainCache[requestType].size()>_maxFilterChainsCacheSize)
582                     _chainCache[requestType].clear();
583                 _chainCache[requestType].put(key,chain);
584             }
585         }
586         else if (LazyList.size(filters) > 0)
587             chain = new Chain(filters, servletHolder);
588     
589         return chain;
590     }
591 
592     /* ------------------------------------------------------------ */
593     /**
594      * @return Returns the initializeAtStart.
595      * @deprecated
596      */
597     public boolean isInitializeAtStart()
598     {
599         return false;
600     }
601     
602     /* ------------------------------------------------------------ */
603     /**
604      * @param initializeAtStart The initializeAtStart to set.
605      * @deprecated
606      */
607     public void setInitializeAtStart(boolean initializeAtStart)
608     {
609     }
610 
611     /* ------------------------------------------------------------ */
612     /**
613      * @return true if the handler is started and there are no unavailable servlets 
614      */
615     public boolean isAvailable()
616     {
617         if (!isStarted())
618             return false;
619         ServletHolder[] holders = getServlets();
620         for (int i=0;i<holders.length;i++)
621         {
622             ServletHolder holder = holders[i];
623             if (holder!=null && !holder.isAvailable())
624                 return false;
625         }
626         return true;
627     }
628     
629     /* ------------------------------------------------------------ */
630     /**
631      * @param start True if this handler will start with unavailable servlets
632      */
633     public void setStartWithUnavailable(boolean start)
634     {
635         _startWithUnavailable=start;
636     }
637     
638     /* ------------------------------------------------------------ */
639     /**
640      * @return True if this handler will start with unavailable servlets
641      */
642     public boolean isStartWithUnavailable()
643     {
644         return _startWithUnavailable;
645     }
646     
647     
648     
649     /* ------------------------------------------------------------ */
650     /** Initialize filters and load-on-startup servlets.
651      * Called automatically from start if autoInitializeServlet is true.
652      */
653     public void initialize()
654         throws Exception
655     {
656         MultiException mx = new MultiException();
657 
658         // Start filters
659         if (_filters!=null)
660         {
661             for (int i=0;i<_filters.length; i++)
662                 _filters[i].start();
663         }
664         
665         if (_servlets!=null)
666         {
667             // Sort and Initialize servlets
668             ServletHolder[] servlets = (ServletHolder[])_servlets.clone();
669             Arrays.sort(servlets);
670             for (int i=0; i<servlets.length; i++)
671             {
672                 try
673                 {
674                     if (servlets[i].getClassName()==null && servlets[i].getForcedPath()!=null)
675                     {
676                         ServletHolder forced_holder = (ServletHolder)_servletPathMap.match(servlets[i].getForcedPath());
677                         if (forced_holder==null || forced_holder.getClassName()==null)
678                         {    
679                             mx.add(new IllegalStateException("No forced path servlet for "+servlets[i].getForcedPath()));
680                             continue;
681                         }
682                         servlets[i].setClassName(forced_holder.getClassName());
683                     }
684                     
685                     servlets[i].start();
686                 }
687                 catch(Throwable e)
688                 {
689                     Log.debug(Log.EXCEPTION,e);
690                     mx.add(e);
691                 }
692             } 
693             mx.ifExceptionThrow();  
694         }
695     }
696     
697     /* ------------------------------------------------------------ */
698     /**
699      * @return Returns the filterChainsCached.
700      */
701     public boolean isFilterChainsCached()
702     {
703         return _filterChainsCached;
704     }
705 
706     /* ------------------------------------------------------------ */
707     /**
708      * @see also newServletHolder(Class)
709      */
710     public ServletHolder newServletHolder()
711     {
712         return new ServletHolder();
713     }
714     
715     /* ------------------------------------------------------------ */
716     public ServletHolder newServletHolder(Class servlet)
717     {
718         return new ServletHolder(servlet);
719     }
720     
721     /* ------------------------------------------------------------ */
722     /** conveniance method to add a servlet.
723      * @return The servlet holder.
724      */
725     public ServletHolder addServletWithMapping (String className,String pathSpec)
726     {
727         ServletHolder holder = newServletHolder(null);
728         holder.setName(className+"-"+holder.hashCode());
729         holder.setClassName(className);
730         
731         addServletWithMapping(holder,pathSpec);
732         
733         return holder;
734     }   
735     
736     /* ------------------------------------------------------------ */
737     /** conveniance method to add a servlet.
738      * @return The servlet holder.
739      */
740     public ServletHolder addServletWithMapping (Class servlet,String pathSpec)
741     {
742         ServletHolder holder = newServletHolder(servlet);
743         setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
744         
745         addServletWithMapping(holder,pathSpec);
746         
747         return holder;
748     }   
749     
750     /* ------------------------------------------------------------ */
751     /** conveniance method to add a servlet.
752      * @param name
753      * @param className
754      * @param pathSpec
755      * @return The servlet holder.
756      */
757     public void addServletWithMapping (ServletHolder servlet,String pathSpec)
758     {
759         ServletHolder[] holders=getServlets();
760         if (holders!=null)
761             holders = (ServletHolder[])holders.clone();
762         
763         try
764         {
765             setServlets((ServletHolder[])LazyList.addToArray(holders, servlet, ServletHolder.class));
766             
767             ServletMapping mapping = new ServletMapping();
768             mapping.setServletName(servlet.getName());
769             mapping.setPathSpec(pathSpec);
770             setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class));
771         }
772         catch (Exception e)
773         {
774             setServlets(holders);
775             if (e instanceof RuntimeException)
776                 throw (RuntimeException)e;
777             throw new RuntimeException(e);
778         }
779     }
780 
781     /* ------------------------------------------------------------ */
782     /** Convenience method to add a servlet with a servlet mapping.
783      * @param className
784      * @param pathSpec
785      * @return
786      * @deprecated
787      */
788     public ServletHolder addServlet (String className, String pathSpec)
789     {
790         return addServletWithMapping (className, pathSpec);
791     }
792 
793     
794     /* ------------------------------------------------------------ */    
795     /**Convenience method to add a pre-constructed ServletHolder.
796      * @param holder
797      */
798     public void addServlet(ServletHolder holder)
799     {
800         setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
801     }
802     
803     /* ------------------------------------------------------------ */    
804     /** Convenience method to add a pre-constructed ServletMapping.
805      * @param mapping
806      */
807     public void addServletMapping (ServletMapping mapping)
808     {
809         setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class));
810     }
811     
812     /* ------------------------------------------------------------ */
813     public FilterHolder newFilterHolder(Class filter)
814     {
815         return new FilterHolder(filter);
816     }
817     
818     /* ------------------------------------------------------------ */
819     /** 
820      * @see {@link #newFilterHolder(Class)}
821      */
822     public FilterHolder newFilterHolder()
823     {
824         return new FilterHolder();
825     }
826 
827     /* ------------------------------------------------------------ */
828     public FilterHolder getFilter(String name)
829     {
830         return (FilterHolder)_filterNameMap.get(name);
831     }
832     
833     /* ------------------------------------------------------------ */
834     /** conveniance method to add a filter.
835      * @param name
836      * @param className
837      * @param pathSpec
838      * @param dispatches see {@link FilterMapping#setDispatches(int)}
839      * @return The filter holder.
840      */
841     public FilterHolder addFilterWithMapping (Class filter,String pathSpec,int dispatches)
842     {
843         FilterHolder holder = newFilterHolder(filter);
844         addFilterWithMapping(holder,pathSpec,dispatches);
845         
846         return holder;
847     }
848     
849     /* ------------------------------------------------------------ */
850     /** conveniance method to add a filter.
851      * @param name
852      * @param className
853      * @param pathSpec
854      * @param dispatches see {@link FilterMapping#setDispatches(int)}
855      * @return The filter holder.
856      */
857     public FilterHolder addFilterWithMapping (String className,String pathSpec,int dispatches)
858     {
859         FilterHolder holder = newFilterHolder(null);
860         holder.setName(className+"-"+holder.hashCode());
861         holder.setClassName(className);
862         
863         addFilterWithMapping(holder,pathSpec,dispatches);
864         return holder;
865     }
866     
867     /* ------------------------------------------------------------ */
868     /** conveniance method to add a filter.
869      * @param name
870      * @param className
871      * @param pathSpec
872      * @param dispatches see {@link FilterMapping#setDispatches(int)}
873      * @return The filter holder.
874      */
875     public void addFilterWithMapping (FilterHolder holder,String pathSpec,int dispatches)
876     {
877         FilterHolder[] holders = getFilters();
878         if (holders!=null)
879             holders = (FilterHolder[])holders.clone();
880         
881         try
882         {
883             setFilters((FilterHolder[])LazyList.addToArray(holders, holder, FilterHolder.class));
884             
885             FilterMapping mapping = new FilterMapping();
886             mapping.setFilterName(holder.getName());
887             mapping.setPathSpec(pathSpec);
888             mapping.setDispatches(dispatches);
889             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
890         }
891         catch (RuntimeException e)
892         {
893             setFilters(holders);
894             throw e;
895         }
896         catch (Error e)
897         {
898             setFilters(holders);
899             throw e;
900         }
901             
902     }
903     
904     /* ------------------------------------------------------------ */
905     /** Convenience method to add a filter with a mapping
906      * @param className
907      * @param pathSpec
908      * @param dispatches
909      * @return
910      * @deprecated
911      */
912     public FilterHolder addFilter (String className,String pathSpec,int dispatches)
913     {
914         return addFilterWithMapping(className, pathSpec, dispatches);
915     }
916     
917     /* ------------------------------------------------------------ */
918     /**
919      * convenience method to add a filter and mapping
920      * @param filter
921      * @param filterMapping
922      */
923     public void addFilter (FilterHolder filter, FilterMapping filterMapping)
924     {
925         if (filter != null)
926             setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class));
927         if (filterMapping != null)
928             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), filterMapping, FilterMapping.class));
929     }
930     
931     /* ------------------------------------------------------------ */  
932     /** Convenience method to add a preconstructed FilterHolder
933      * @param filter
934      */
935     public void addFilter (FilterHolder filter)
936     {
937         if (filter != null)
938             setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class));
939     }
940     
941     /* ------------------------------------------------------------ */
942     /** Convenience method to add a preconstructed FilterMapping
943      * @param mapping
944      */
945     public void addFilterMapping (FilterMapping mapping)
946     {
947         if (mapping != null)
948             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
949     }
950 
951     /* ------------------------------------------------------------ */
952     protected synchronized void updateNameMappings()
953     {   
954         // update filter name map
955         _filterNameMap.clear();
956         if (_filters!=null)
957         {   
958             for (int i=0;i<_filters.length;i++)
959             {
960                 _filterNameMap.put(_filters[i].getName(),_filters[i]);
961                 _filters[i].setServletHandler(this);
962             }
963         }
964 
965         // Map servlet names to holders
966         _servletNameMap.clear();
967         if (_servlets!=null)
968         {   
969             // update the maps
970             for (int i=0;i<_servlets.length;i++)
971             {
972                 _servletNameMap.put(_servlets[i].getName(),_servlets[i]);
973                 _servlets[i].setServletHandler(this);
974             }
975         }
976     }
977     
978     /* ------------------------------------------------------------ */
979     protected synchronized void updateMappings()
980     {   
981         // update filter mappings
982         if (_filterMappings==null)
983         {
984             _filterPathMappings=null;
985             _filterNameMappings=null;
986         }
987         else 
988         {
989             _filterPathMappings=new ArrayList();
990             _filterNameMappings=new MultiMap();
991             for (int i=0;i<_filterMappings.length;i++)
992             {
993                 FilterHolder filter_holder = (FilterHolder)_filterNameMap.get(_filterMappings[i].getFilterName());
994                 if (filter_holder==null)
995                     throw new IllegalStateException("No filter named "+_filterMappings[i].getFilterName());
996                 _filterMappings[i].setFilterHolder(filter_holder);    
997                 if (_filterMappings[i].getPathSpecs()!=null)
998                     _filterPathMappings.add(_filterMappings[i]);
999                 
1000                 if (_filterMappings[i].getServletNames()!=null)
1001                 {
1002                     String[] names=_filterMappings[i].getServletNames();
1003                     for (int j=0;j<names.length;j++)
1004                     {
1005                         if (names[j]!=null)
1006                             _filterNameMappings.add(names[j], _filterMappings[i]);  
1007                     }
1008                 }
1009             }
1010         }
1011 
1012         // Map servlet paths to holders
1013         if (_servletMappings==null || _servletNameMap==null)
1014         {
1015             _servletPathMap=null;
1016         }
1017         else
1018         {
1019             PathMap pm = new PathMap();
1020             
1021             // update the maps
1022             for (int i=0;i<_servletMappings.length;i++)
1023             {
1024                 ServletHolder servlet_holder = (ServletHolder)_servletNameMap.get(_servletMappings[i].getServletName());
1025                 if (servlet_holder==null)
1026                     throw new IllegalStateException("No such servlet: "+_servletMappings[i].getServletName());
1027                 else if (_servletMappings[i].getPathSpecs()!=null)
1028                 {
1029                     String[] pathSpecs = _servletMappings[i].getPathSpecs();
1030                     for (int j=0;j<pathSpecs.length;j++)
1031                         if (pathSpecs[j]!=null)
1032                             pm.put(pathSpecs[j],servlet_holder);
1033                 }
1034             }
1035             
1036             _servletPathMap=pm;
1037         }
1038         
1039         
1040 
1041         if (Log.isDebugEnabled()) 
1042         {
1043             Log.debug("filterNameMap="+_filterNameMap);
1044             Log.debug("pathFilters="+_filterPathMappings);
1045             Log.debug("servletFilterMap="+_filterNameMappings);
1046             Log.debug("servletPathMap="+_servletPathMap);
1047             Log.debug("servletNameMap="+_servletNameMap);
1048         }
1049         
1050         try
1051         {
1052             if (isStarted())
1053                 initialize();
1054         }
1055         catch (Exception e)
1056         {
1057             throw new RuntimeException(e);
1058         }
1059     }
1060 
1061 
1062     /* ------------------------------------------------------------ */
1063     protected void notFound(HttpServletRequest request,
1064                   HttpServletResponse response)
1065         throws IOException
1066     {
1067         if(Log.isDebugEnabled())Log.debug("Not Found "+request.getRequestURI());
1068         response.sendError(HttpServletResponse.SC_NOT_FOUND);
1069     }
1070     
1071     /* ------------------------------------------------------------ */
1072     /**
1073      * @param filterChainsCached The filterChainsCached to set.
1074      */
1075     public void setFilterChainsCached(boolean filterChainsCached)
1076     {
1077         _filterChainsCached = filterChainsCached;
1078     }
1079     
1080     /* ------------------------------------------------------------ */
1081     /**
1082      * @param filterMappings The filterMappings to set.
1083      */
1084     public void setFilterMappings(FilterMapping[] filterMappings)
1085     {
1086         if (getServer()!=null)
1087             getServer().getContainer().update(this,_filterMappings,filterMappings,"filterMapping",true);
1088         _filterMappings = filterMappings;
1089         updateMappings();
1090     }
1091     
1092     /* ------------------------------------------------------------ */
1093     public synchronized void setFilters(FilterHolder[] holders)
1094     {
1095         if (getServer()!=null)
1096             getServer().getContainer().update(this,_filters,holders,"filter",true);
1097         _filters=holders;
1098         updateNameMappings();
1099     }
1100     
1101     /* ------------------------------------------------------------ */
1102     /**
1103      * @param servletMappings The servletMappings to set.
1104      */
1105     public void setServletMappings(ServletMapping[] servletMappings)
1106     {
1107         if (getServer()!=null)
1108             getServer().getContainer().update(this,_servletMappings,servletMappings,"servletMapping",true);
1109         _servletMappings = servletMappings;
1110         updateMappings();
1111     }
1112     
1113     /* ------------------------------------------------------------ */
1114     /** Set Servlets.
1115      * @param holders Array of servletsto define
1116      */
1117     public synchronized void setServlets(ServletHolder[] holders)
1118     {
1119         if (getServer()!=null)
1120             getServer().getContainer().update(this,_servlets,holders,"servlet",true);
1121         _servlets=holders;
1122         updateNameMappings();
1123     }
1124 
1125 
1126     /* ------------------------------------------------------------ */
1127     /* ------------------------------------------------------------ */
1128     private class CachedChain implements FilterChain
1129     {
1130         FilterHolder _filterHolder;
1131         CachedChain _next;
1132         ServletHolder _servletHolder;
1133 
1134         /* ------------------------------------------------------------ */
1135         CachedChain(Object filters, ServletHolder servletHolder)
1136         {
1137             if (LazyList.size(filters)>0)
1138             {
1139                 _filterHolder=(FilterHolder)LazyList.get(filters, 0);
1140                 filters=LazyList.remove(filters,0);
1141                 _next=new CachedChain(filters,servletHolder);
1142             }
1143             else
1144                 _servletHolder=servletHolder;
1145         }
1146 
1147         /* ------------------------------------------------------------ */
1148         public void doFilter(ServletRequest request, ServletResponse response) 
1149             throws IOException, ServletException
1150         {
1151             // pass to next filter
1152             if (_filterHolder!=null)
1153             {
1154                 if (Log.isDebugEnabled())
1155                     Log.debug("call filter " + _filterHolder);
1156                 Filter filter= _filterHolder.getFilter();
1157                 filter.doFilter(request, response, _next);
1158                 return;
1159             }
1160 
1161             // Call servlet
1162             if (_servletHolder != null)
1163             {
1164                 if (Log.isDebugEnabled())
1165                     Log.debug("call servlet " + _servletHolder);
1166                 _servletHolder.handle(request, response);
1167             }
1168             else // Not found
1169                 notFound((HttpServletRequest)request, (HttpServletResponse)response);
1170         }
1171         
1172         public String toString()
1173         {
1174             if (_filterHolder!=null)
1175                 return _filterHolder+"->"+_next.toString();
1176             if (_servletHolder!=null)
1177                 return _servletHolder.toString();
1178             return "null";
1179         }
1180     }  
1181     
1182     /* ------------------------------------------------------------ */
1183     /* ------------------------------------------------------------ */
1184     private class Chain implements FilterChain
1185     {
1186         int _filter= 0;
1187         Object _chain;
1188         ServletHolder _servletHolder;
1189 
1190         /* ------------------------------------------------------------ */
1191         Chain(Object filters, ServletHolder servletHolder)
1192         {
1193             _chain= filters;
1194             _servletHolder= servletHolder;
1195         }
1196 
1197         /* ------------------------------------------------------------ */
1198         public void doFilter(ServletRequest request, ServletResponse response)
1199             throws IOException, ServletException
1200         {
1201             if (Log.isDebugEnabled()) Log.debug("doFilter " + _filter);
1202 
1203             // pass to next filter
1204             if (_filter < LazyList.size(_chain))
1205             {
1206                 FilterHolder holder= (FilterHolder)LazyList.get(_chain, _filter++);
1207                 if (Log.isDebugEnabled()) Log.debug("call filter " + holder);
1208                 Filter filter= holder.getFilter();
1209                 filter.doFilter(request, response, this);
1210                 return;
1211             }
1212 
1213             // Call servlet
1214             if (_servletHolder != null)
1215             {
1216                 if (Log.isDebugEnabled()) Log.debug("call servlet " + _servletHolder);
1217                 _servletHolder.handle(request, response);
1218             }
1219             else // Not found
1220                 notFound((HttpServletRequest)request, (HttpServletResponse)response);
1221         }
1222 
1223         /* ------------------------------------------------------------ */
1224         public String toString()
1225         {
1226             StringBuffer b = new StringBuffer();
1227             for (int i=0; i<LazyList.size(_chain);i++)
1228             {
1229                 b.append(LazyList.get(_chain, i).toString());
1230                 b.append("->");
1231             }
1232             b.append(_servletHolder);
1233             return b.toString();
1234         }
1235     }
1236 
1237     /* ------------------------------------------------------------ */
1238     /**
1239      * @return The maximum entries in a filter chain cache.
1240      */
1241     public int getMaxFilterChainsCacheSize()
1242     {
1243         return _maxFilterChainsCacheSize;
1244     }
1245 
1246     /* ------------------------------------------------------------ */
1247     /** Set the maximum filter chain cache size.
1248      * Filter chains are cached if {@link #isFilterChainsCached()} is true. If the max cache size
1249      * is greater than zero, then the cache is flushed whenever it grows to be this size.
1250      * 
1251      * @param maxFilterChainsCacheSize  the maximum number of entries in a filter chain cache.
1252      */
1253     public void setMaxFilterChainsCacheSize(int maxFilterChainsCacheSize)
1254     {
1255         _maxFilterChainsCacheSize = maxFilterChainsCacheSize;
1256     }
1257     
1258     /**
1259      * Customize a servlet.
1260      * 
1261      * Called before the servlet goes into service.
1262      * Subclasses of ServletHandler should override
1263      * this method.
1264      * 
1265      * @param servlet
1266      * @return
1267      * @throws Exception
1268      */
1269     public Servlet customizeServlet (Servlet servlet)
1270     throws Exception
1271     {
1272         return servlet;
1273     }
1274     
1275     
1276     public Servlet customizeServletDestroy (Servlet servlet)
1277     throws Exception
1278     {
1279         return servlet;
1280     }
1281     
1282     
1283     /**
1284      * Customize a Filter.
1285      * 
1286      * Called before the Filter goes into service.
1287      * Subclasses of ServletHandler should override
1288      * this method.
1289      * 
1290      * @param filter
1291      * @return
1292      * @throws Exception
1293      */
1294     public Filter customizeFilter (Filter filter)
1295     throws Exception
1296     {
1297         return filter;
1298     }
1299     
1300     
1301     public Filter customizeFilterDestroy (Filter filter)
1302     throws Exception
1303     {
1304         return filter;
1305     }
1306 }