View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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         
296         
297         //check servlet has a class (ie is not a preliminary registration). If preliminary, fail startup.
298         try
299         {
300             super.doStart();
301         } 
302         catch (UnavailableException ue)
303         {
304             makeUnavailable(ue);
305             if (_servletHandler.isStartWithUnavailable())
306             {
307                 LOG.ignore(ue);
308                 return;
309             }
310             else
311                 throw ue;
312         }
313 
314 
315         //servlet is not an instance of javax.servlet.Servlet
316         try
317         {
318             checkServletType();
319         }
320         catch (UnavailableException ue)
321         {
322             makeUnavailable(ue);
323             if (_servletHandler.isStartWithUnavailable())
324             {
325                 LOG.ignore(ue);
326                 return;
327             }
328             else
329                 throw ue;
330         }
331         
332 
333         _identityService = _servletHandler.getIdentityService();
334         if (_identityService!=null && _runAsRole!=null)
335             _runAsToken=_identityService.newRunAsToken(_runAsRole);
336         
337         _config=new Config();
338 
339         if (_class!=null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class))
340             _servlet = new SingleThreadedWrapper();
341 
342         if (_extInstance || _initOnStartup)
343         {
344             try
345             {
346                 initServlet();
347             }
348             catch(Exception e)
349             {
350                 if (_servletHandler.isStartWithUnavailable())
351                     LOG.ignore(e);
352                 else
353                     throw e;
354             }
355         }  
356     }
357 
358     /* ------------------------------------------------------------ */
359     public void doStop()
360         throws Exception
361     {
362         Object old_run_as = null;
363         if (_servlet!=null)
364         {       
365             try
366             {
367                 if (_identityService!=null)
368                     old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
369 
370                 destroyInstance(_servlet);
371             }
372             catch (Exception e)
373             {
374                 LOG.warn(e);
375             }
376             finally
377             {
378                 if (_identityService!=null)
379                     _identityService.unsetRunAs(old_run_as);
380             }
381         }
382 
383         if (!_extInstance)
384             _servlet=null;
385 
386         _config=null;
387     }
388 
389     /* ------------------------------------------------------------ */
390     public void destroyInstance (Object o)
391     throws Exception
392     {
393         if (o==null)
394             return;
395         Servlet servlet =  ((Servlet)o);
396         getServletHandler().destroyServlet(servlet);
397         servlet.destroy();
398     }
399 
400     /* ------------------------------------------------------------ */
401     /** Get the servlet.
402      * @return The servlet
403      */
404     public synchronized Servlet getServlet()
405         throws ServletException
406     {
407         // Handle previous unavailability
408         if (_unavailable!=0)
409         {
410             if (_unavailable<0 || _unavailable>0 && System.currentTimeMillis()<_unavailable)
411                 throw _unavailableEx;
412             _unavailable=0;
413             _unavailableEx=null;
414         }
415 
416         if (_servlet==null)
417             initServlet();
418         return _servlet;
419     }
420 
421     /* ------------------------------------------------------------ */
422     /** Get the servlet instance (no initialization done).
423      * @return The servlet or null
424      */
425     public Servlet getServletInstance()
426     {
427         return _servlet;
428     }
429         
430     /* ------------------------------------------------------------ */
431     /**
432      * Check to ensure class of servlet is acceptable.
433      * @throws UnavailableException
434      */
435     public void checkServletType ()
436         throws UnavailableException
437     {
438         if (_class==null || !javax.servlet.Servlet.class.isAssignableFrom(_class))
439         {
440             throw new UnavailableException("Servlet "+_class+" is not a javax.servlet.Servlet");
441         }
442     }
443 
444     /* ------------------------------------------------------------ */
445     /** 
446      * @return true if the holder is started and is not unavailable
447      */
448     public boolean isAvailable()
449     {
450         if (isStarted()&& _unavailable==0)
451             return true;
452         try 
453         {
454             getServlet();
455         }
456         catch(Exception e)
457         {
458             LOG.ignore(e);
459         }
460 
461         return isStarted()&& _unavailable==0;
462     }
463     
464     /* ------------------------------------------------------------ */
465     private void makeUnavailable(UnavailableException e)
466     {
467         if (_unavailableEx==e && _unavailable!=0)
468             return;
469 
470         _servletHandler.getServletContext().log("unavailable",e);
471 
472         _unavailableEx=e;
473         _unavailable=-1;
474         if (e.isPermanent())   
475             _unavailable=-1;
476         else
477         {
478             if (_unavailableEx.getUnavailableSeconds()>0)
479                 _unavailable=System.currentTimeMillis()+1000*_unavailableEx.getUnavailableSeconds();
480             else
481                 _unavailable=System.currentTimeMillis()+5000; // TODO configure
482         }
483     }
484 
485     /* ------------------------------------------------------------ */
486 
487     private void makeUnavailable(final Throwable e)
488     {
489         if (e instanceof UnavailableException)
490             makeUnavailable((UnavailableException)e);
491         else
492         {
493             ServletContext ctx = _servletHandler.getServletContext();
494             if (ctx==null)
495                 LOG.info("unavailable",e);
496             else
497                 ctx.log("unavailable",e);
498             _unavailableEx=new UnavailableException(String.valueOf(e),-1)
499             {
500                 {
501                     initCause(e);
502                 }
503             };
504             _unavailable=-1;
505         }
506     }
507 
508     /* ------------------------------------------------------------ */
509     private void initServlet()
510     	throws ServletException
511     {
512         Object old_run_as = null;
513         try
514         {
515             if (_servlet==null)
516                 _servlet=newInstance();
517             if (_config==null)
518                 _config=new Config();
519             
520             // Handle run as
521             if (_identityService!=null)
522             {
523                 old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
524             }
525             
526             // Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet
527             if (isJspServlet())
528             {
529                 initJspServlet();
530             }
531 
532             initMultiPart();
533             
534             _servlet.init(_config);
535         }
536         catch (UnavailableException e)
537         {
538             makeUnavailable(e);
539             _servlet=null;
540             _config=null;
541             throw e;
542         }
543         catch (ServletException e)
544         {
545             makeUnavailable(e.getCause()==null?e:e.getCause());
546             _servlet=null;
547             _config=null;
548             throw e;
549         }
550         catch (Exception e)
551         {
552             makeUnavailable(e);
553             _servlet=null;
554             _config=null;
555             throw new ServletException(this.toString(),e);
556         }
557         finally
558         {
559             // pop run-as role
560             if (_identityService!=null)
561                 _identityService.unsetRunAs(old_run_as);
562         }
563     }
564     
565     
566     /* ------------------------------------------------------------ */
567     /**
568      * @throws Exception
569      */
570     protected void initJspServlet () throws Exception
571     {
572         ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler();
573         
574         /* Set the webapp's classpath for Jasper */
575         ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
576 
577         /* Set the system classpath for Jasper */
578         setInitParameter("com.sun.appserv.jsp.classpath", Loader.getClassPath(ch.getClassLoader().getParent())); 
579         
580         /* Set up other classpath attribute */
581         if ("?".equals(getInitParameter("classpath")))
582         {
583             String classpath = ch.getClassPath();
584             LOG.debug("classpath=" + classpath);
585             if (classpath != null) 
586                 setInitParameter("classpath", classpath);
587         }
588     }
589     
590     /* ------------------------------------------------------------ */
591     /**
592      * Register a ServletRequestListener that will ensure tmp multipart
593      * files are deleted when the request goes out of scope.
594      * 
595      * @throws Exception
596      */
597     protected void initMultiPart () throws Exception
598     {
599         //if this servlet can handle multipart requests, ensure tmp files will be
600         //cleaned up correctly
601         if (((Registration)getRegistration()).getMultipartConfig() != null)
602         {
603             //Register a listener to delete tmp files that are created as a result of this
604             //servlet calling Request.getPart() or Request.getParts()
605             ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler();
606             ch.addEventListener(new Request.MultiPartCleanerListener());
607         }
608     }
609     
610     /* ------------------------------------------------------------ */
611     /**
612      * @see org.eclipse.jetty.server.UserIdentity.Scope#getContextPath()
613      */
614     public String getContextPath()
615     {
616         return _config.getServletContext().getContextPath();
617     }
618 
619     /* ------------------------------------------------------------ */
620     /**
621      * @see org.eclipse.jetty.server.UserIdentity.Scope#getRoleRefMap()
622      */
623     public Map<String, String> getRoleRefMap()
624     {
625         return _roleMap;
626     }
627 
628     /* ------------------------------------------------------------ */
629     public String getRunAsRole() 
630     {
631         return _runAsRole;
632     }
633     
634     /* ------------------------------------------------------------ */
635     public void setRunAsRole(String role) 
636     {
637         _runAsRole = role;
638     }
639     
640     /* ------------------------------------------------------------ */
641     /** Service a request with this servlet.
642      */
643     public void handle(Request baseRequest,
644                        ServletRequest request,
645                        ServletResponse response)
646         throws ServletException,
647                UnavailableException,
648                IOException
649     {
650         if (_class==null)
651             throw new UnavailableException("Servlet Not Initialized");
652         
653         Servlet servlet=_servlet;
654         synchronized(this)
655         {
656             if (_unavailable!=0 || !_initOnStartup)
657                 servlet=getServlet();
658             if (servlet==null)
659                 throw new UnavailableException("Could not instantiate "+_class);
660         }
661         
662         // Service the request
663         boolean servlet_error=true;
664         Object old_run_as = null;
665         boolean suspendable = baseRequest.isAsyncSupported();
666         try
667         {
668             // Handle aliased path
669             if (_forcedPath!=null)
670                 // TODO complain about poor naming to the Jasper folks
671                 request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
672 
673             // Handle run as
674             if (_identityService!=null)
675                 old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
676 
677             if (!isAsyncSupported())
678                 baseRequest.setAsyncSupported(false);
679 
680             MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
681             if (mpce != null)
682                 request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
683 
684             servlet.service(request,response);
685             servlet_error=false;
686         }
687         catch(UnavailableException e)
688         {
689             makeUnavailable(e);
690             throw _unavailableEx;
691         }
692         finally
693         {
694             baseRequest.setAsyncSupported(suspendable);
695             
696             // pop run-as role
697             if (_identityService!=null)
698                 _identityService.unsetRunAs(old_run_as);
699 
700             // Handle error params.
701             if (servlet_error)
702                 request.setAttribute("javax.servlet.error.servlet_name",getName());
703         }
704     }
705     
706     
707     /* ------------------------------------------------------------ */
708     private boolean isJspServlet ()
709     {
710         if (_servlet == null)
711             return false;
712         
713         Class c = _servlet.getClass();
714         
715         boolean result = false;
716         while (c != null && !result)
717         {
718             result = isJspServlet(c.getName());
719             c = c.getSuperclass();
720         }
721         
722         return result;
723     }
724     
725     
726     /* ------------------------------------------------------------ */
727     private boolean isJspServlet (String classname)
728     {
729         if (classname == null)
730             return false;
731         return ("org.apache.jasper.servlet.JspServlet".equals(classname));
732     }
733 
734  
735     /* ------------------------------------------------------------ */
736     /* ------------------------------------------------------------ */
737     /* ------------------------------------------------------------ */
738     protected class Config extends HolderConfig implements ServletConfig
739     {   
740         /* -------------------------------------------------------- */
741         public String getServletName()
742         {
743             return getName();
744         }
745         
746     }
747 
748     /* -------------------------------------------------------- */
749     /* -------------------------------------------------------- */
750     /* -------------------------------------------------------- */
751     public class Registration extends HolderRegistration implements ServletRegistration.Dynamic
752     {
753         protected MultipartConfigElement _multipartConfig;       
754         
755         public Set<String> addMapping(String... urlPatterns)
756         {
757             illegalStateIfContextStarted();
758             Set<String> clash=null;
759             for (String pattern : urlPatterns)
760             {
761                 ServletMapping mapping = _servletHandler.getServletMapping(pattern);
762                 if (mapping!=null)
763                 {
764                     //if the servlet mapping was from a default descriptor, then allow it to be overridden
765                     if (!mapping.isDefault())
766                     {
767                         if (clash==null)
768                             clash=new HashSet<String>();
769                         clash.add(pattern);
770                     }
771                 }
772             }
773             
774             //if there were any clashes amongst the urls, return them
775             if (clash!=null)
776                 return clash;
777             
778             //otherwise apply all of them
779             ServletMapping mapping = new ServletMapping();
780             mapping.setServletName(ServletHolder.this.getName());
781             mapping.setPathSpecs(urlPatterns);
782             _servletHandler.addServletMapping(mapping);
783             
784             return Collections.emptySet();
785         }
786 
787         public Collection<String> getMappings()
788         {
789             ServletMapping[] mappings =_servletHandler.getServletMappings();
790             List<String> patterns=new ArrayList<String>();
791             if (mappings!=null)
792             {
793                 for (ServletMapping mapping : mappings)
794                 {
795                     if (!mapping.getServletName().equals(getName()))
796                         continue;
797                     String[] specs=mapping.getPathSpecs();
798                     if (specs!=null && specs.length>0)
799                         patterns.addAll(Arrays.asList(specs));
800                 }
801             }
802             return patterns;
803         }
804 
805         @Override
806         public String getRunAsRole() 
807         {
808             return _runAsRole;
809         }
810 
811         @Override
812         public void setLoadOnStartup(int loadOnStartup)
813         {
814             illegalStateIfContextStarted();
815             ServletHolder.this.setInitOrder(loadOnStartup);
816         }
817         
818         public int getInitOrder()
819         {
820             return ServletHolder.this.getInitOrder();
821         }
822 
823         @Override
824         public void setMultipartConfig(MultipartConfigElement element) 
825         {
826             _multipartConfig = element;
827         }
828         
829         public MultipartConfigElement getMultipartConfig()
830         {
831             return _multipartConfig;
832         }
833 
834         @Override
835         public void setRunAsRole(String role) 
836         {
837             _runAsRole = role;
838         }
839 
840         @Override
841         public Set<String> setServletSecurity(ServletSecurityElement securityElement) 
842         {
843             return _servletHandler.setServletSecurity(this, securityElement);
844         }
845     }
846     
847     public ServletRegistration.Dynamic getRegistration()
848     {
849         if (_registration == null)
850             _registration = new Registration();
851         return _registration;
852     }
853     
854     /* -------------------------------------------------------- */
855     /* -------------------------------------------------------- */
856     /* -------------------------------------------------------- */
857     private class SingleThreadedWrapper implements Servlet
858     {
859         Stack<Servlet> _stack=new Stack<Servlet>();
860         
861         public void destroy()
862         {
863             synchronized(this)
864             {
865                 while(_stack.size()>0)
866                     try { (_stack.pop()).destroy(); } catch (Exception e) { LOG.warn(e); }
867             }
868         }
869 
870         public ServletConfig getServletConfig()
871         {
872             return _config;
873         }
874 
875         public String getServletInfo()
876         {
877             return null;
878         }
879 
880         public void init(ServletConfig config) throws ServletException
881         {
882             synchronized(this)
883             {
884                 if(_stack.size()==0)
885                 {
886                     try
887                     {
888                         Servlet s = newInstance();
889                         s.init(config);
890                         _stack.push(s);
891                     }
892                     catch (ServletException e)
893                     {
894                         throw e;
895                     }
896                     catch (Exception e)
897                     {
898                         throw new ServletException(e);
899                     }
900                 }
901             }
902         }
903 
904         public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
905         {
906             Servlet s;
907             synchronized(this)
908             {
909                 if(_stack.size()>0)
910                     s=(Servlet)_stack.pop();
911                 else
912                 {
913                     try
914                     {
915                         s = newInstance();
916                         s.init(_config);
917                     }
918                     catch (ServletException e)
919                     {
920                         throw e;
921                     }
922                     catch (Exception e)
923                     {
924                         throw new ServletException(e);
925                     }
926                 }
927             }
928             
929             try
930             {
931                 s.service(req,res);
932             }
933             finally
934             {
935                 synchronized(this)
936                 {
937                     _stack.push(s);
938                 }
939             }
940         }
941     }
942     
943     /* ------------------------------------------------------------ */
944     /**
945      * @return the newly created Servlet instance
946      * @throws ServletException
947      * @throws IllegalAccessException
948      * @throws InstantiationException
949      */
950     protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException
951     {
952         try
953         {
954             ServletContext ctx = getServletHandler().getServletContext();
955             if (ctx==null)
956                 return getHeldClass().newInstance();
957             return ((ServletContextHandler.Context)ctx).createServlet(getHeldClass());
958         }
959         catch (ServletException se)
960         {
961             Throwable cause = se.getRootCause();
962             if (cause instanceof InstantiationException)
963                 throw (InstantiationException)cause;
964             if (cause instanceof IllegalAccessException)
965                 throw (IllegalAccessException)cause;
966             throw se;
967         }
968     }
969 }