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