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