View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.servlet;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.Stack;
32  
33  import javax.servlet.MultipartConfigElement;
34  import javax.servlet.Servlet;
35  import javax.servlet.ServletConfig;
36  import javax.servlet.ServletException;
37  import javax.servlet.ServletRegistration;
38  import javax.servlet.ServletContext;
39  import javax.servlet.ServletRequest;
40  import javax.servlet.ServletResponse;
41  import javax.servlet.ServletSecurityElement;
42  import javax.servlet.SingleThreadModel;
43  import javax.servlet.UnavailableException;
44  
45  import org.eclipse.jetty.security.IdentityService;
46  import org.eclipse.jetty.security.RunAsToken;
47  import org.eclipse.jetty.server.Request;
48  import org.eclipse.jetty.server.UserIdentity;
49  import org.eclipse.jetty.server.handler.ContextHandler;
50  import org.eclipse.jetty.util.Loader;
51  import org.eclipse.jetty.util.log.Log;
52  import org.eclipse.jetty.util.log.Logger;
53  
54  
55  
56  
57  /* --------------------------------------------------------------------- */
58  /** Servlet Instance and Context Holder.
59   * Holds the name, params and some state of a javax.servlet.Servlet
60   * instance. It implements the ServletConfig interface.
61   * This class will organise the loading of the servlet when needed or
62   * requested.
63   *
64   * 
65   */
66  public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope, Comparable
67  {
68      private static final Logger LOG = Log.getLogger(ServletHolder.class);
69  
70      /* ---------------------------------------------------------------- */
71      private int _initOrder;
72      private boolean _initOnStartup=false;
73      private Map<String, String> _roleMap;
74      private String _forcedPath;
75      private String _runAsRole;
76      private RunAsToken _runAsToken;
77      private IdentityService _identityService;
78      private ServletRegistration.Dynamic _registration;
79      
80      
81      private transient Servlet _servlet;
82      private transient Config _config;
83      private transient long _unavailable;
84      private transient boolean _enabled = true;
85      private transient UnavailableException _unavailableEx;
86      public static final Map<String,String> NO_MAPPED_ROLES = Collections.emptyMap();
87  
88      /* ---------------------------------------------------------------- */
89      /** Constructor .
90       */
91      public ServletHolder()
92      {
93          super (Source.EMBEDDED);
94      }
95      
96      /* ---------------------------------------------------------------- */
97      /** Constructor .
98       */
99      public ServletHolder(Holder.Source creator)
100     {
101         super (creator);
102     }
103     
104     /* ---------------------------------------------------------------- */
105     /** Constructor for existing servlet.
106      */
107     public ServletHolder(Servlet servlet)
108     {
109         super (Source.EMBEDDED);
110         setServlet(servlet);
111     }
112 
113     /* ---------------------------------------------------------------- */
114     /** Constructor for servlet class.
115      */
116     public ServletHolder(String name, Class<? extends Servlet> servlet)
117     {
118         super (Source.EMBEDDED);
119         setName(name);
120         setHeldClass(servlet);
121     }
122     
123     /* ---------------------------------------------------------------- */
124     /** Constructor for servlet class.
125      */
126     public ServletHolder(String name, Servlet servlet)
127     {
128         super (Source.EMBEDDED);
129         setName(name);
130         setServlet(servlet);
131     }
132     
133     /* ---------------------------------------------------------------- */
134     /** Constructor for servlet class.
135      */
136     public ServletHolder(Class<? extends Servlet> servlet)
137     {
138         super (Source.EMBEDDED);
139         setHeldClass(servlet);
140     }
141 
142     /* ---------------------------------------------------------------- */
143     /**
144      * @return The unavailable exception or null if not unavailable
145      */
146     public UnavailableException getUnavailableException()
147     {
148         return _unavailableEx;
149     }
150     
151     /* ------------------------------------------------------------ */
152     public synchronized void setServlet(Servlet servlet)
153     {
154         if (servlet==null || servlet instanceof SingleThreadModel)
155             throw new IllegalArgumentException();
156 
157         _extInstance=true;
158         _servlet=servlet;
159         setHeldClass(servlet.getClass());
160         if (getName()==null)
161             setName(servlet.getClass().getName()+"-"+super.hashCode());
162     }
163     
164     /* ------------------------------------------------------------ */
165     public int getInitOrder()
166     {
167         return _initOrder;
168     }
169 
170     /* ------------------------------------------------------------ */
171     /** Set the initialize order.
172      * Holders with order<0, are initialized on use. Those with
173      * order>=0 are initialized in increasing order when the handler
174      * is started.
175      */
176     public void setInitOrder(int order)
177     {
178         _initOnStartup=true;
179         _initOrder = order;
180     }
181     
182     public boolean isSetInitOrder()
183     {
184         return _initOnStartup;
185     }
186 
187     /* ------------------------------------------------------------ */
188     /** Comparitor by init order.
189      */
190     public int compareTo(Object o)
191     {
192         if (o instanceof ServletHolder)
193         {
194             ServletHolder sh= (ServletHolder)o;
195             if (sh==this)
196                 return 0;
197             if (sh._initOrder<_initOrder)
198                 return 1;
199             if (sh._initOrder>_initOrder)
200                 return -1;
201             
202             int c=(_className!=null && sh._className!=null)?_className.compareTo(sh._className):0;
203             if (c==0)
204                 c=_name.compareTo(sh._name);
205             if (c==0)
206                 c=this.hashCode()>o.hashCode()?1:-1;
207             return c;
208         }
209         return 1;
210     }
211 
212     /* ------------------------------------------------------------ */
213     public boolean equals(Object o)
214     {
215         return compareTo(o)==0;
216     }
217 
218     /* ------------------------------------------------------------ */
219     public int hashCode()
220     {
221         return _name==null?System.identityHashCode(this):_name.hashCode();
222     }
223 
224     /* ------------------------------------------------------------ */
225     /** Link a user role.
226      * Translate the role name used by a servlet, to the link name
227      * used by the container.
228      * @param name The role name as used by the servlet
229      * @param link The role name as used by the container.
230      */
231     public synchronized void setUserRoleLink(String name,String link)
232     {
233         if (_roleMap==null)
234             _roleMap=new HashMap<String, String>();
235         _roleMap.put(name,link);
236     }
237     
238     /* ------------------------------------------------------------ */
239     /** get a user role link.
240      * @param name The name of the role
241      * @return The name as translated by the link. If no link exists,
242      * the name is returned.
243      */
244     public String getUserRoleLink(String name)
245     {
246         if (_roleMap==null)
247             return name;
248         String link= _roleMap.get(name);
249         return (link==null)?name:link;
250     }
251 
252     /* ------------------------------------------------------------ */
253     public Map<String, String> getRoleMap()
254     {
255         return _roleMap == null? NO_MAPPED_ROLES : _roleMap;
256     }
257     
258     /* ------------------------------------------------------------ */
259     /**
260      * @return Returns the forcedPath.
261      */
262     public String getForcedPath()
263     {
264         return _forcedPath;
265     }
266     
267     /* ------------------------------------------------------------ */
268     /**
269      * @param forcedPath The forcedPath to set.
270      */
271     public void setForcedPath(String forcedPath)
272     {
273         _forcedPath = forcedPath;
274     }
275     
276     public boolean isEnabled()
277     {
278         return _enabled;
279     }
280 
281 
282     public void setEnabled(boolean enabled)
283     {
284         _enabled = enabled;
285     }
286 
287 
288     /* ------------------------------------------------------------ */
289     public void doStart()
290         throws Exception
291     {
292         _unavailable=0;
293         if (!_enabled)
294             return;
295         //check servlet has a class (ie is not a preliminary registration). If preliminary, fail startup.
296         try
297         {
298             super.doStart();
299         } 
300         catch (UnavailableException ue)
301         {
302             makeUnavailable(ue);
303             throw ue;
304         }
305         
306         try
307         {
308             checkServletType();
309         }
310         catch (UnavailableException ue)
311         {
312             makeUnavailable(ue);
313             if (!_servletHandler.isStartWithUnavailable())
314                 throw ue; //servlet is not an instance of javax.servlet.Servlet
315         }
316         
317 
318         _identityService = _servletHandler.getIdentityService();
319         if (_identityService!=null && _runAsRole!=null)
320             _runAsToken=_identityService.newRunAsToken(_runAsRole);
321         
322         _config=new Config();
323 
324         if (_class!=null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class))
325             _servlet = new SingleThreadedWrapper();
326 
327         if (_extInstance || _initOnStartup)
328         {
329             try
330             {
331                 initServlet();
332             }
333             catch(Exception e)
334             {
335                 if (_servletHandler.isStartWithUnavailable())
336                     LOG.ignore(e);
337                 else
338                     throw e;
339             }
340         }  
341     }
342 
343     /* ------------------------------------------------------------ */
344     public void doStop()
345         throws Exception
346     {
347         Object old_run_as = null;
348         if (_servlet!=null)
349         {       
350             try
351             {
352                 if (_identityService!=null)
353                     old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
354 
355                 destroyInstance(_servlet);
356             }
357             catch (Exception e)
358             {
359                 LOG.warn(e);
360             }
361             finally
362             {
363                 if (_identityService!=null)
364                     _identityService.unsetRunAs(old_run_as);
365             }
366         }
367 
368         if (!_extInstance)
369             _servlet=null;
370 
371         _config=null;
372     }
373 
374     /* ------------------------------------------------------------ */
375     public void destroyInstance (Object o)
376     throws Exception
377     {
378         if (o==null)
379             return;
380         Servlet servlet =  ((Servlet)o);
381         servlet.destroy();
382         getServletHandler().destroyServlet(servlet);
383     }
384 
385     /* ------------------------------------------------------------ */
386     /** Get the servlet.
387      * @return The servlet
388      */
389     public synchronized Servlet getServlet()
390         throws ServletException
391     {
392         // Handle previous unavailability
393         if (_unavailable!=0)
394         {
395             if (_unavailable<0 || _unavailable>0 && System.currentTimeMillis()<_unavailable)
396                 throw _unavailableEx;
397             _unavailable=0;
398             _unavailableEx=null;
399         }
400 
401         if (_servlet==null)
402             initServlet();
403         return _servlet;
404     }
405 
406     /* ------------------------------------------------------------ */
407     /** Get the servlet instance (no initialization done).
408      * @return The servlet or null
409      */
410     public Servlet getServletInstance()
411     {
412         return _servlet;
413     }
414         
415     /* ------------------------------------------------------------ */
416     /**
417      * Check to ensure class of servlet is acceptable.
418      * @throws UnavailableException
419      */
420     public void checkServletType ()
421         throws UnavailableException
422     {
423         if (_class==null || !javax.servlet.Servlet.class.isAssignableFrom(_class))
424         {
425             throw new UnavailableException("Servlet "+_class+" is not a javax.servlet.Servlet");
426         }
427     }
428 
429     /* ------------------------------------------------------------ */
430     /** 
431      * @return true if the holder is started and is not unavailable
432      */
433     public boolean isAvailable()
434     {
435         if (isStarted()&& _unavailable==0)
436             return true;
437         try 
438         {
439             getServlet();
440         }
441         catch(Exception e)
442         {
443             LOG.ignore(e);
444         }
445 
446         return isStarted()&& _unavailable==0;
447     }
448     
449     /* ------------------------------------------------------------ */
450     private void makeUnavailable(UnavailableException e)
451     {
452         if (_unavailableEx==e && _unavailable!=0)
453             return;
454 
455         _servletHandler.getServletContext().log("unavailable",e);
456 
457         _unavailableEx=e;
458         _unavailable=-1;
459         if (e.isPermanent())   
460             _unavailable=-1;
461         else
462         {
463             if (_unavailableEx.getUnavailableSeconds()>0)
464                 _unavailable=System.currentTimeMillis()+1000*_unavailableEx.getUnavailableSeconds();
465             else
466                 _unavailable=System.currentTimeMillis()+5000; // TODO configure
467         }
468     }
469 
470     /* ------------------------------------------------------------ */
471 
472     private void makeUnavailable(final Throwable e)
473     {
474         if (e instanceof UnavailableException)
475             makeUnavailable((UnavailableException)e);
476         else
477         {
478             ServletContext ctx = _servletHandler.getServletContext();
479             if (ctx==null)
480                 LOG.info("unavailable",e);
481             else
482                 ctx.log("unavailable",e);
483             _unavailableEx=new UnavailableException(String.valueOf(e),-1)
484             {
485                 {
486                     initCause(e);
487                 }
488             };
489             _unavailable=-1;
490         }
491     }
492 
493     /* ------------------------------------------------------------ */
494     private void initServlet()
495     	throws ServletException
496     {
497         Object old_run_as = null;
498         try
499         {
500             if (_servlet==null)
501                 _servlet=newInstance();
502             if (_config==null)
503                 _config=new Config();
504             
505             // Handle run as
506             if (_identityService!=null)
507             {
508                 old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
509             }
510             
511             // Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet
512             if (isJspServlet())
513             {
514                 initJspServlet();
515             }
516 
517             initMultiPart();
518             
519             _servlet.init(_config);
520         }
521         catch (UnavailableException e)
522         {
523             makeUnavailable(e);
524             _servlet=null;
525             _config=null;
526             throw e;
527         }
528         catch (ServletException e)
529         {
530             makeUnavailable(e.getCause()==null?e:e.getCause());
531             _servlet=null;
532             _config=null;
533             throw e;
534         }
535         catch (Exception e)
536         {
537             makeUnavailable(e);
538             _servlet=null;
539             _config=null;
540             throw new ServletException(this.toString(),e);
541         }
542         finally
543         {
544             // pop run-as role
545             if (_identityService!=null)
546                 _identityService.unsetRunAs(old_run_as);
547         }
548     }
549     
550     
551     /* ------------------------------------------------------------ */
552     /**
553      * @throws Exception
554      */
555     protected void initJspServlet () throws Exception
556     {
557         ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler();
558         
559         /* Set the webapp's classpath for Jasper */
560         ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
561 
562         /* Set the system classpath for Jasper */
563         setInitParameter("com.sun.appserv.jsp.classpath", Loader.getClassPath(ch.getClassLoader().getParent())); 
564         
565         /* Set up other classpath attribute */
566         if ("?".equals(getInitParameter("classpath")))
567         {
568             String classpath = ch.getClassPath();
569             LOG.debug("classpath=" + classpath);
570             if (classpath != null) 
571                 setInitParameter("classpath", classpath);
572         }
573     }
574     
575     /* ------------------------------------------------------------ */
576     /**
577      * Register a ServletRequestListener that will ensure tmp multipart
578      * files are deleted when the request goes out of scope.
579      * 
580      * @throws Exception
581      */
582     protected void initMultiPart () throws Exception
583     {
584         //if this servlet can handle multipart requests, ensure tmp files will be
585         //cleaned up correctly
586         if (((Registration)getRegistration()).getMultipartConfig() != null)
587         {
588             //Register a listener to delete tmp files that are created as a result of this
589             //servlet calling Request.getPart() or Request.getParts()
590             ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler();
591             ch.addEventListener(new Request.MultiPartCleanerListener());
592         }
593     }
594     
595     /* ------------------------------------------------------------ */
596     /**
597      * @see org.eclipse.jetty.server.UserIdentity.Scope#getContextPath()
598      */
599     public String getContextPath()
600     {
601         return _config.getServletContext().getContextPath();
602     }
603 
604     /* ------------------------------------------------------------ */
605     /**
606      * @see org.eclipse.jetty.server.UserIdentity.Scope#getRoleRefMap()
607      */
608     public Map<String, String> getRoleRefMap()
609     {
610         return _roleMap;
611     }
612 
613     /* ------------------------------------------------------------ */
614     public String getRunAsRole() 
615     {
616         return _runAsRole;
617     }
618     
619     /* ------------------------------------------------------------ */
620     public void setRunAsRole(String role) 
621     {
622         _runAsRole = role;
623     }
624     
625     /* ------------------------------------------------------------ */
626     /** Service a request with this servlet.
627      */
628     public void handle(Request baseRequest,
629                        ServletRequest request,
630                        ServletResponse response)
631         throws ServletException,
632                UnavailableException,
633                IOException
634     {
635         if (_class==null)
636             throw new UnavailableException("Servlet Not Initialized");
637         
638         Servlet servlet=_servlet;
639         synchronized(this)
640         {
641             if (_unavailable!=0 || !_initOnStartup)
642                 servlet=getServlet();
643             if (servlet==null)
644                 throw new UnavailableException("Could not instantiate "+_class);
645         }
646         
647         // Service the request
648         boolean servlet_error=true;
649         Object old_run_as = null;
650         boolean suspendable = baseRequest.isAsyncSupported();
651         try
652         {
653             // Handle aliased path
654             if (_forcedPath!=null)
655                 // TODO complain about poor naming to the Jasper folks
656                 request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
657 
658             // Handle run as
659             if (_identityService!=null)
660                 old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
661 
662             if (!isAsyncSupported())
663                 baseRequest.setAsyncSupported(false);
664 
665             MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
666             if (mpce != null)
667                 request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
668 
669             servlet.service(request,response);
670             servlet_error=false;
671         }
672         catch(UnavailableException e)
673         {
674             makeUnavailable(e);
675             throw _unavailableEx;
676         }
677         finally
678         {
679             baseRequest.setAsyncSupported(suspendable);
680             
681             // pop run-as role
682             if (_identityService!=null)
683                 _identityService.unsetRunAs(old_run_as);
684 
685             // Handle error params.
686             if (servlet_error)
687                 request.setAttribute("javax.servlet.error.servlet_name",getName());
688         }
689     }
690     
691     
692     /* ------------------------------------------------------------ */
693     private boolean isJspServlet ()
694     {
695         if (_servlet == null)
696             return false;
697         
698         Class c = _servlet.getClass();
699         
700         boolean result = false;
701         while (c != null && !result)
702         {
703             result = isJspServlet(c.getName());
704             c = c.getSuperclass();
705         }
706         
707         return result;
708     }
709     
710     
711     /* ------------------------------------------------------------ */
712     private boolean isJspServlet (String classname)
713     {
714         if (classname == null)
715             return false;
716         return ("org.apache.jasper.servlet.JspServlet".equals(classname));
717     }
718 
719  
720     /* ------------------------------------------------------------ */
721     /* ------------------------------------------------------------ */
722     /* ------------------------------------------------------------ */
723     protected class Config extends HolderConfig implements ServletConfig
724     {   
725         /* -------------------------------------------------------- */
726         public String getServletName()
727         {
728             return getName();
729         }
730         
731     }
732 
733     /* -------------------------------------------------------- */
734     /* -------------------------------------------------------- */
735     /* -------------------------------------------------------- */
736     public class Registration extends HolderRegistration implements ServletRegistration.Dynamic
737     {
738         protected MultipartConfigElement _multipartConfig;       
739         
740         public Set<String> addMapping(String... urlPatterns)
741         {
742             illegalStateIfContextStarted();
743             Set<String> clash=null;
744             for (String pattern : urlPatterns)
745             {
746                 ServletMapping mapping = _servletHandler.getServletMapping(pattern);
747                 if (mapping!=null)
748                 {
749                     //if the servlet mapping was from a default descriptor, then allow it to be overridden
750                     if (!mapping.isDefault())
751                     {
752                         if (clash==null)
753                             clash=new HashSet<String>();
754                         clash.add(pattern);
755                     }
756                 }
757             }
758             
759             //if there were any clashes amongst the urls, return them
760             if (clash!=null)
761                 return clash;
762             
763             //otherwise apply all of them
764             ServletMapping mapping = new ServletMapping();
765             mapping.setServletName(ServletHolder.this.getName());
766             mapping.setPathSpecs(urlPatterns);
767             _servletHandler.addServletMapping(mapping);
768             
769             return Collections.emptySet();
770         }
771 
772         public Collection<String> getMappings()
773         {
774             ServletMapping[] mappings =_servletHandler.getServletMappings();
775             List<String> patterns=new ArrayList<String>();
776             if (mappings!=null)
777             {
778                 for (ServletMapping mapping : mappings)
779                 {
780                     if (!mapping.getServletName().equals(getName()))
781                         continue;
782                     String[] specs=mapping.getPathSpecs();
783                     if (specs!=null && specs.length>0)
784                         patterns.addAll(Arrays.asList(specs));
785                 }
786             }
787             return patterns;
788         }
789 
790         @Override
791         public String getRunAsRole() 
792         {
793             return _runAsRole;
794         }
795 
796         @Override
797         public void setLoadOnStartup(int loadOnStartup)
798         {
799             illegalStateIfContextStarted();
800             ServletHolder.this.setInitOrder(loadOnStartup);
801         }
802         
803         public int getInitOrder()
804         {
805             return ServletHolder.this.getInitOrder();
806         }
807 
808         @Override
809         public void setMultipartConfig(MultipartConfigElement element) 
810         {
811             _multipartConfig = element;
812         }
813         
814         public MultipartConfigElement getMultipartConfig()
815         {
816             return _multipartConfig;
817         }
818 
819         @Override
820         public void setRunAsRole(String role) 
821         {
822             _runAsRole = role;
823         }
824 
825         @Override
826         public Set<String> setServletSecurity(ServletSecurityElement securityElement) 
827         {
828             return _servletHandler.setServletSecurity(this, securityElement);
829         }
830     }
831     
832     public ServletRegistration.Dynamic getRegistration()
833     {
834         if (_registration == null)
835             _registration = new Registration();
836         return _registration;
837     }
838     
839     /* -------------------------------------------------------- */
840     /* -------------------------------------------------------- */
841     /* -------------------------------------------------------- */
842     private class SingleThreadedWrapper implements Servlet
843     {
844         Stack<Servlet> _stack=new Stack<Servlet>();
845         
846         public void destroy()
847         {
848             synchronized(this)
849             {
850                 while(_stack.size()>0)
851                     try { (_stack.pop()).destroy(); } catch (Exception e) { LOG.warn(e); }
852             }
853         }
854 
855         public ServletConfig getServletConfig()
856         {
857             return _config;
858         }
859 
860         public String getServletInfo()
861         {
862             return null;
863         }
864 
865         public void init(ServletConfig config) throws ServletException
866         {
867             synchronized(this)
868             {
869                 if(_stack.size()==0)
870                 {
871                     try
872                     {
873                         Servlet s = newInstance();
874                         s.init(config);
875                         _stack.push(s);
876                     }
877                     catch (ServletException e)
878                     {
879                         throw e;
880                     }
881                     catch (Exception e)
882                     {
883                         throw new ServletException(e);
884                     }
885                 }
886             }
887         }
888 
889         public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
890         {
891             Servlet s;
892             synchronized(this)
893             {
894                 if(_stack.size()>0)
895                     s=(Servlet)_stack.pop();
896                 else
897                 {
898                     try
899                     {
900                         s = newInstance();
901                         s.init(_config);
902                     }
903                     catch (ServletException e)
904                     {
905                         throw e;
906                     }
907                     catch (Exception e)
908                     {
909                         throw new ServletException(e);
910                     }
911                 }
912             }
913             
914             try
915             {
916                 s.service(req,res);
917             }
918             finally
919             {
920                 synchronized(this)
921                 {
922                     _stack.push(s);
923                 }
924             }
925         }
926     }
927     
928     /* ------------------------------------------------------------ */
929     /**
930      * @return the newly created Servlet instance
931      * @throws ServletException
932      * @throws IllegalAccessException
933      * @throws InstantiationException
934      */
935     protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException
936     {
937         try
938         {
939             ServletContext ctx = getServletHandler().getServletContext();
940             if (ctx==null)
941                 return getHeldClass().newInstance();
942             return ((ServletContextHandler.Context)ctx).createServlet(getHeldClass());
943         }
944         catch (ServletException se)
945         {
946             Throwable cause = se.getRootCause();
947             if (cause instanceof InstantiationException)
948                 throw (InstantiationException)cause;
949             if (cause instanceof IllegalAccessException)
950                 throw (IllegalAccessException)cause;
951             throw se;
952         }
953     }
954 }