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