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