View Javadoc

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