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          super (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         super (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         super (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         super (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         super (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         if (c==0)
203             c=this.hashCode()>sh.hashCode()?1:-1;
204             return c;
205     }
206 
207     /* ------------------------------------------------------------ */
208     public boolean equals(Object o)
209     {
210         return o instanceof ServletHolder && compareTo((ServletHolder)o)==0;
211     }
212 
213     /* ------------------------------------------------------------ */
214     public int hashCode()
215     {
216         return _name==null?System.identityHashCode(this):_name.hashCode();
217     }
218 
219     /* ------------------------------------------------------------ */
220     /** Link a user role.
221      * Translate the role name used by a servlet, to the link name
222      * used by the container.
223      * @param name The role name as used by the servlet
224      * @param link The role name as used by the container.
225      */
226     public synchronized void setUserRoleLink(String name,String link)
227     {
228         if (_roleMap==null)
229             _roleMap=new HashMap<String, String>();
230         _roleMap.put(name,link);
231     }
232 
233     /* ------------------------------------------------------------ */
234     /** get a user role link.
235      * @param name The name of the role
236      * @return The name as translated by the link. If no link exists,
237      * the name is returned.
238      */
239     public String getUserRoleLink(String name)
240     {
241         if (_roleMap==null)
242             return name;
243         String link= _roleMap.get(name);
244         return (link==null)?name:link;
245     }
246 
247     /* ------------------------------------------------------------ */
248     public Map<String, String> getRoleMap()
249     {
250         return _roleMap == null? NO_MAPPED_ROLES : _roleMap;
251     }
252 
253     /* ------------------------------------------------------------ */
254     /**
255      * @return Returns the forcedPath.
256      */
257     @ManagedAttribute(value="forced servlet path", readonly=true)
258     public String getForcedPath()
259     {
260         return _forcedPath;
261     }
262 
263     /* ------------------------------------------------------------ */
264     /**
265      * @param forcedPath The forcedPath to set.
266      */
267     public void setForcedPath(String forcedPath)
268     {
269         _forcedPath = forcedPath;
270     }
271 
272     public boolean isEnabled()
273     {
274         return _enabled;
275     }
276 
277 
278     public void setEnabled(boolean enabled)
279     {
280         _enabled = enabled;
281     }
282 
283     
284     /* ------------------------------------------------------------ */
285     public void doStart()
286         throws Exception
287     {
288         _unavailable=0;
289         if (!_enabled)
290             return;
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             throw ue;
300         }
301 
302         try
303         {
304             checkServletType();
305         }
306         catch (UnavailableException ue)
307         {
308             makeUnavailable(ue);
309             if (!_servletHandler.isStartWithUnavailable())
310                 throw ue; //servlet is not an instance of javax.servlet.Servlet
311         }
312 
313 
314         _identityService = _servletHandler.getIdentityService();
315         if (_identityService!=null && _runAsRole!=null)
316             _runAsToken=_identityService.newRunAsToken(_runAsRole);
317 
318         _config=new Config();
319 
320         if (_class!=null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class))
321             _servlet = new SingleThreadedWrapper();
322      
323     }
324     
325     
326     /* ------------------------------------------------------------ */
327     @Override
328     public void initialize ()
329     throws Exception
330     {
331         super.initialize();
332         if (_extInstance || _initOnStartup)
333         {
334             try
335             {
336                 initServlet();
337             }
338             catch(Exception e)
339             {
340                 if (_servletHandler.isStartWithUnavailable())
341                     LOG.ignore(e);
342                 else
343                     throw e;
344             }
345         }
346     }
347     
348     
349     /* ------------------------------------------------------------ */
350     public void doStop()
351         throws Exception
352     {
353         Object old_run_as = null;
354         if (_servlet!=null)
355         {
356             try
357             {
358                 if (_identityService!=null)
359                     old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
360 
361                 destroyInstance(_servlet);
362             }
363             catch (Exception e)
364             {
365                 LOG.warn(e);
366             }
367             finally
368             {
369                 if (_identityService!=null)
370                     _identityService.unsetRunAs(old_run_as);
371             }
372         }
373 
374         if (!_extInstance)
375             _servlet=null;
376 
377         _config=null;
378     }
379 
380     /* ------------------------------------------------------------ */
381     @Override
382     public void destroyInstance (Object o)
383     throws Exception
384     {
385         if (o==null)
386             return;
387         Servlet servlet =  ((Servlet)o);
388         servlet.destroy();
389         getServletHandler().destroyServlet(servlet);
390     }
391 
392     /* ------------------------------------------------------------ */
393     /** Get the servlet.
394      * @return The servlet
395      */
396     public synchronized Servlet getServlet()
397         throws ServletException
398     {
399         // Handle previous unavailability
400         if (_unavailable!=0)
401         {
402             if (_unavailable<0 || _unavailable>0 && System.currentTimeMillis()<_unavailable)
403                 throw _unavailableEx;
404             _unavailable=0;
405             _unavailableEx=null;
406         }
407 
408         if (_servlet==null)
409             initServlet();
410         return _servlet;
411     }
412 
413     /* ------------------------------------------------------------ */
414     /** Get the servlet instance (no initialization done).
415      * @return The servlet or null
416      */
417     public Servlet getServletInstance()
418     {
419         return _servlet;
420     }
421 
422     /* ------------------------------------------------------------ */
423     /**
424      * Check to ensure class of servlet is acceptable.
425      * @throws UnavailableException
426      */
427     public void checkServletType ()
428         throws UnavailableException
429     {
430         if (_class==null || !javax.servlet.Servlet.class.isAssignableFrom(_class))
431         {
432             throw new UnavailableException("Servlet "+_class+" is not a javax.servlet.Servlet");
433         }
434     }
435 
436     /* ------------------------------------------------------------ */
437     /**
438      * @return true if the holder is started and is not unavailable
439      */
440     public boolean isAvailable()
441     {
442         if (isStarted()&& _unavailable==0)
443             return true;
444         try
445         {
446             getServlet();
447         }
448         catch(Exception e)
449         {
450             LOG.ignore(e);
451         }
452 
453         return isStarted()&& _unavailable==0;
454     }
455 
456     /* ------------------------------------------------------------ */
457     private void makeUnavailable(UnavailableException e)
458     {
459         if (_unavailableEx==e && _unavailable!=0)
460             return;
461 
462         _servletHandler.getServletContext().log("unavailable",e);
463 
464         _unavailableEx=e;
465         _unavailable=-1;
466         if (e.isPermanent())
467             _unavailable=-1;
468         else
469         {
470             if (_unavailableEx.getUnavailableSeconds()>0)
471                 _unavailable=System.currentTimeMillis()+1000*_unavailableEx.getUnavailableSeconds();
472             else
473                 _unavailable=System.currentTimeMillis()+5000; // TODO configure
474         }
475     }
476 
477     /* ------------------------------------------------------------ */
478 
479     private void makeUnavailable(final Throwable e)
480     {
481         if (e instanceof UnavailableException)
482             makeUnavailable((UnavailableException)e);
483         else
484         {
485             ServletContext ctx = _servletHandler.getServletContext();
486             if (ctx==null)
487                 LOG.info("unavailable",e);
488             else
489                 ctx.log("unavailable",e);
490             _unavailableEx=new UnavailableException(String.valueOf(e),-1)
491             {
492                 {
493                     initCause(e);
494                 }
495             };
496             _unavailable=-1;
497         }
498     }
499 
500     /* ------------------------------------------------------------ */
501     private void initServlet()
502     	throws ServletException
503     {
504         Object old_run_as = null;
505         try
506         {
507             if (_servlet==null)
508                 _servlet=newInstance();
509             if (_config==null)
510                 _config=new Config();
511 
512             // Handle run as
513             if (_identityService!=null)
514             {
515                 old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
516             }
517 
518             // Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet
519             if (isJspServlet())
520             {
521                 initJspServlet();
522             }
523 
524             initMultiPart();
525 
526             LOG.debug("Filter.init {}",_servlet);
527             _servlet.init(_config);
528         }
529         catch (UnavailableException e)
530         {
531             makeUnavailable(e);
532             _servlet=null;
533             _config=null;
534             throw e;
535         }
536         catch (ServletException e)
537         {
538             makeUnavailable(e.getCause()==null?e:e.getCause());
539             _servlet=null;
540             _config=null;
541             throw e;
542         }
543         catch (Exception e)
544         {
545             makeUnavailable(e);
546             _servlet=null;
547             _config=null;
548             throw new ServletException(this.toString(),e);
549         }
550         finally
551         {
552             // pop run-as role
553             if (_identityService!=null)
554                 _identityService.unsetRunAs(old_run_as);
555         }
556     }
557 
558 
559     /* ------------------------------------------------------------ */
560     /**
561      * @throws Exception
562      */
563     protected void initJspServlet () throws Exception
564     {
565         ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
566             
567         /* Set the webapp's classpath for Jasper */
568         ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
569 
570         /* Set the system classpath for Jasper */
571         setInitParameter("com.sun.appserv.jsp.classpath", Loader.getClassPath(ch.getClassLoader().getParent()));
572 
573         /* Set up other classpath attribute */
574         if ("?".equals(getInitParameter("classpath")))
575         {
576             String classpath = ch.getClassPath();
577             LOG.debug("classpath=" + classpath);
578             if (classpath != null)
579                 setInitParameter("classpath", classpath);
580         }
581     }
582 
583     /* ------------------------------------------------------------ */
584     /**
585      * Register a ServletRequestListener that will ensure tmp multipart
586      * files are deleted when the request goes out of scope.
587      * 
588      * @throws Exception
589      */
590     protected void initMultiPart () throws Exception
591     {
592         //if this servlet can handle multipart requests, ensure tmp files will be
593         //cleaned up correctly
594         if (((Registration)getRegistration()).getMultipartConfig() != null)
595         {
596             //Register a listener to delete tmp files that are created as a result of this
597             //servlet calling Request.getPart() or Request.getParts()
598 
599             ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
600             ch.addEventListener(new Request.MultiPartCleanerListener());
601         }
602     }
603 
604     /* ------------------------------------------------------------ */
605     /**
606      * @see org.eclipse.jetty.server.UserIdentity.Scope#getContextPath()
607      */
608     @Override
609     public String getContextPath()
610     {
611         return _config.getServletContext().getContextPath();
612     }
613 
614     /* ------------------------------------------------------------ */
615     /**
616      * @see org.eclipse.jetty.server.UserIdentity.Scope#getRoleRefMap()
617      */
618     @Override
619     public Map<String, String> getRoleRefMap()
620     {
621         return _roleMap;
622     }
623 
624     /* ------------------------------------------------------------ */
625     @ManagedAttribute(value="role to run servlet as", readonly=true)
626     public String getRunAsRole()
627     {
628         return _runAsRole;
629     }
630 
631     /* ------------------------------------------------------------ */
632     public void setRunAsRole(String role)
633     {
634         _runAsRole = role;
635     }
636 
637     /* ------------------------------------------------------------ */
638     /** Service a request with this servlet.
639      */
640     public void handle(Request baseRequest,
641                        ServletRequest request,
642                        ServletResponse response)
643         throws ServletException,
644                UnavailableException,
645                IOException
646     {
647         if (_class==null)
648             throw new UnavailableException("Servlet Not Initialized");
649 
650         Servlet servlet=_servlet;
651         synchronized(this)
652         {
653             if (_unavailable!=0 || !_initOnStartup)
654                 servlet=getServlet();
655             if (servlet==null)
656                 throw new UnavailableException("Could not instantiate "+_class);
657         }
658 
659         // Service the request
660         boolean servlet_error=true;
661         Object old_run_as = null;
662         boolean suspendable = baseRequest.isAsyncSupported();
663         try
664         {
665             // Handle aliased path
666             if (_forcedPath!=null)
667                 // TODO complain about poor naming to the Jasper folks
668                 request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
669 
670             // Handle run as
671             if (_identityService!=null)
672                 old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
673 
674             if (!isAsyncSupported())
675                 baseRequest.setAsyncSupported(false);
676 
677             MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
678             if (mpce != null)
679                 request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
680 
681             servlet.service(request,response);
682             servlet_error=false;
683         }
684         catch(UnavailableException e)
685         {
686             makeUnavailable(e);
687             throw _unavailableEx;
688         }
689         finally
690         {
691             baseRequest.setAsyncSupported(suspendable);
692 
693             // pop run-as role
694             if (_identityService!=null)
695                 _identityService.unsetRunAs(old_run_as);
696 
697             // Handle error params.
698             if (servlet_error)
699                 request.setAttribute("javax.servlet.error.servlet_name",getName());
700         }
701     }
702 
703 
704     /* ------------------------------------------------------------ */
705     private boolean isJspServlet ()
706     {
707         if (_servlet == null)
708             return false;
709 
710         Class c = _servlet.getClass();
711 
712         boolean result = false;
713         while (c != null && !result)
714         {
715             result = isJspServlet(c.getName());
716             c = c.getSuperclass();
717         }
718 
719         return result;
720     }
721 
722 
723     /* ------------------------------------------------------------ */
724     private boolean isJspServlet (String classname)
725     {
726         if (classname == null)
727             return false;
728         return ("org.apache.jasper.servlet.JspServlet".equals(classname));
729     }
730 
731 
732     /* ------------------------------------------------------------ */
733     /* ------------------------------------------------------------ */
734     /* ------------------------------------------------------------ */
735     protected class Config extends HolderConfig implements ServletConfig
736     {
737         /* -------------------------------------------------------- */
738         @Override
739         public String getServletName()
740         {
741             return getName();
742         }
743 
744     }
745 
746     /* -------------------------------------------------------- */
747     /* -------------------------------------------------------- */
748     /* -------------------------------------------------------- */
749     public class Registration extends HolderRegistration implements ServletRegistration.Dynamic
750     {
751         protected MultipartConfigElement _multipartConfig;
752 
753         @Override
754         public Set<String> addMapping(String... urlPatterns)
755         {
756             illegalStateIfContextStarted();
757             Set<String> clash=null;
758             for (String pattern : urlPatterns)
759             {
760                 ServletMapping mapping = _servletHandler.getServletMapping(pattern);
761                 if (mapping!=null)
762                 {
763                     //if the servlet mapping was from a default descriptor, then allow it to be overridden
764                     if (!mapping.isDefault())
765                     {
766                         if (clash==null)
767                             clash=new HashSet<String>();
768                         clash.add(pattern);
769                     }
770                 }
771             }
772 
773             //if there were any clashes amongst the urls, return them
774             if (clash!=null)
775                 return clash;
776 
777             //otherwise apply all of them
778             ServletMapping mapping = new ServletMapping();
779             mapping.setServletName(ServletHolder.this.getName());
780             mapping.setPathSpecs(urlPatterns);
781             _servletHandler.addServletMapping(mapping);
782 
783             return Collections.emptySet();
784         }
785 
786         @Override
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         @Override
862         public void destroy()
863         {
864             synchronized(this)
865             {
866                 while(_stack.size()>0)
867                     try { (_stack.pop()).destroy(); } catch (Exception e) { LOG.warn(e); }
868             }
869         }
870 
871         @Override
872         public ServletConfig getServletConfig()
873         {
874             return _config;
875         }
876 
877         @Override
878         public String getServletInfo()
879         {
880             return null;
881         }
882 
883         @Override
884         public void init(ServletConfig config) throws ServletException
885         {
886             synchronized(this)
887             {
888                 if(_stack.size()==0)
889                 {
890                     try
891                     {
892                         Servlet s = newInstance();
893                         s.init(config);
894                         _stack.push(s);
895                     }
896                     catch (ServletException e)
897                     {
898                         throw e;
899                     }
900                     catch (Exception e)
901                     {
902                         throw new ServletException(e);
903                     }
904                 }
905             }
906         }
907 
908         @Override
909         public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
910         {
911             Servlet s;
912             synchronized(this)
913             {
914                 if(_stack.size()>0)
915                     s=(Servlet)_stack.pop();
916                 else
917                 {
918                     try
919                     {
920                         s = newInstance();
921                         s.init(_config);
922                     }
923                     catch (ServletException e)
924                     {
925                         throw e;
926                     }
927                     catch (Exception e)
928                     {
929                         throw new ServletException(e);
930                     }
931                 }
932             }
933 
934             try
935             {
936                 s.service(req,res);
937             }
938             finally
939             {
940                 synchronized(this)
941                 {
942                     _stack.push(s);
943                 }
944             }
945         }
946     }
947 
948     /* ------------------------------------------------------------ */
949     /**
950      * @return the newly created Servlet instance
951      * @throws ServletException
952      * @throws IllegalAccessException
953      * @throws InstantiationException
954      */
955     protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException
956     {
957         try
958         {
959             ServletContext ctx = getServletHandler().getServletContext();
960             if (ctx instanceof ServletContextHandler.Context)
961                 return ((ServletContextHandler.Context)ctx).createServlet(getHeldClass());
962             return getHeldClass().newInstance();
963         }
964         catch (ServletException se)
965         {
966             Throwable cause = se.getRootCause();
967             if (cause instanceof InstantiationException)
968                 throw (InstantiationException)cause;
969             if (cause instanceof IllegalAccessException)
970                 throw (IllegalAccessException)cause;
971             throw se;
972         }
973     }
974     
975 
976     /* ------------------------------------------------------------ */
977     @Override
978     public String toString()
979     {
980         return String.format("%s@%x==%s,%d,%b",_name,hashCode(),_className,_initOrder,_servlet!=null);
981     }
982 }