View Javadoc

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