View Javadoc

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