View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.File;
22  import java.io.IOException;
23  import java.lang.reflect.Method;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  import java.util.Stack;
34  
35  import javax.servlet.MultipartConfigElement;
36  import javax.servlet.RequestDispatcher;
37  import javax.servlet.Servlet;
38  import javax.servlet.ServletConfig;
39  import javax.servlet.ServletContext;
40  import javax.servlet.ServletException;
41  import javax.servlet.ServletRegistration;
42  import javax.servlet.ServletRequest;
43  import javax.servlet.ServletResponse;
44  import javax.servlet.ServletSecurityElement;
45  import javax.servlet.SingleThreadModel;
46  import javax.servlet.UnavailableException;
47  
48  import org.eclipse.jetty.security.IdentityService;
49  import org.eclipse.jetty.security.RunAsToken;
50  import org.eclipse.jetty.server.MultiPartCleanerListener;
51  import org.eclipse.jetty.server.Request;
52  import org.eclipse.jetty.server.UserIdentity;
53  import org.eclipse.jetty.server.handler.ContextHandler;
54  import org.eclipse.jetty.util.Loader;
55  import org.eclipse.jetty.util.annotation.ManagedAttribute;
56  import org.eclipse.jetty.util.annotation.ManagedObject;
57  import org.eclipse.jetty.util.log.Log;
58  import org.eclipse.jetty.util.log.Logger;
59  
60  /**
61   * Servlet Instance and Context Holder.
62   * <p>
63   * Holds the name, params and some state of a javax.servlet.Servlet
64   * instance. It implements the ServletConfig interface.
65   * This class will organise the loading of the servlet when needed or
66   * requested.
67   */
68  @ManagedObject("Servlet Holder")
69  public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope, Comparable<ServletHolder>
70  {
71  
72      /* ---------------------------------------------------------------- */
73      private static final Logger LOG = Log.getLogger(ServletHolder.class);
74      private int _initOrder = -1;
75      private boolean _initOnStartup=false;
76      private boolean _initialized = false;
77      private Map<String, String> _roleMap;
78      private String _forcedPath;
79      private String _runAsRole;
80      private RunAsToken _runAsToken;
81      private IdentityService _identityService;
82      private ServletRegistration.Dynamic _registration;
83      private JspContainer _jspContainer;
84  
85      private transient Servlet _servlet;
86      private transient Config _config;
87      private transient long _unavailable;
88      private transient boolean _enabled = true;
89      private transient UnavailableException _unavailableEx;
90  
91  
92      public static final String APACHE_SENTINEL_CLASS = "org.apache.tomcat.InstanceManager";
93      public static final  String JSP_GENERATED_PACKAGE_NAME = "org.eclipse.jetty.servlet.jspPackagePrefix";
94      public static final Map<String,String> NO_MAPPED_ROLES = Collections.emptyMap();
95      public static enum JspContainer {APACHE, OTHER};
96  
97      /* ---------------------------------------------------------------- */
98      /** Constructor .
99       */
100     public ServletHolder()
101     {
102         this(Source.EMBEDDED);
103     }
104 
105     /* ---------------------------------------------------------------- */
106     /** Constructor .
107      * @param creator the holder source
108      */
109     public ServletHolder(Holder.Source creator)
110     {
111         super(creator);
112     }
113 
114     /* ---------------------------------------------------------------- */
115     /** Constructor for existing servlet.
116      * @param servlet the servlet
117      */
118     public ServletHolder(Servlet servlet)
119     {
120         this(Source.EMBEDDED);
121         setServlet(servlet);
122     }
123 
124     /* ---------------------------------------------------------------- */
125     /** Constructor for servlet class.
126      * @param name the name of the servlet
127      * @param servlet the servlet class
128      */
129     public ServletHolder(String name, Class<? extends Servlet> servlet)
130     {
131         this(Source.EMBEDDED);
132         setName(name);
133         setHeldClass(servlet);
134     }
135 
136     /* ---------------------------------------------------------------- */
137     /** Constructor for servlet class.
138      * @param name the servlet name
139      * @param servlet the servlet
140      */
141     public ServletHolder(String name, Servlet servlet)
142     {
143         this(Source.EMBEDDED);
144         setName(name);
145         setServlet(servlet);
146     }
147 
148     /* ---------------------------------------------------------------- */
149     /** Constructor for servlet class.
150      * @param servlet the servlet class
151      */
152     public ServletHolder(Class<? extends Servlet> servlet)
153     {
154         this(Source.EMBEDDED);
155         setHeldClass(servlet);
156     }
157 
158     /* ---------------------------------------------------------------- */
159     /**
160      * @return The unavailable exception or null if not unavailable
161      */
162     public UnavailableException getUnavailableException()
163     {
164         return _unavailableEx;
165     }
166 
167     /* ------------------------------------------------------------ */
168     public synchronized void setServlet(Servlet servlet)
169     {
170         if (servlet==null || servlet instanceof SingleThreadModel)
171             throw new IllegalArgumentException();
172 
173         _extInstance=true;
174         _servlet=servlet;
175         setHeldClass(servlet.getClass());
176         if (getName()==null)
177             setName(servlet.getClass().getName()+"-"+super.hashCode());
178     }
179 
180     /* ------------------------------------------------------------ */
181     @ManagedAttribute(value="initialization order", readonly=true)
182     public int getInitOrder()
183     {
184         return _initOrder;
185     }
186 
187     /* ------------------------------------------------------------ */
188     /**
189      * Set the initialize order.
190      * <p>
191      * Holders with order&lt;0, are initialized on use. Those with
192      * order&gt;=0 are initialized in increasing order when the handler
193      * is started.
194      * @param order the servlet init order
195      */
196     public void setInitOrder(int order)
197     {
198         _initOnStartup=order>=0;
199         _initOrder = order;
200     }
201 
202     /* ------------------------------------------------------------ */
203     /**
204      * Comparator by init order.
205      */
206     @Override
207     public int compareTo(ServletHolder sh)
208     {
209         if (sh==this)
210             return 0;
211 
212         if (sh._initOrder<_initOrder)
213             return 1;
214 
215         if (sh._initOrder>_initOrder)
216             return -1;
217 
218         // consider _className, need to position properly when one is configured but not the other
219         int c;
220         if (_className==null && sh._className==null)
221             c=0;
222         else if (_className==null)
223             c=-1;
224         else if (sh._className==null)
225             c=1;
226         else
227             c=_className.compareTo(sh._className);
228 
229         // if _initOrder and _className are the same, consider the _name
230         if (c==0)
231             c=_name.compareTo(sh._name);
232 
233         return c;
234     }
235 
236     /* ------------------------------------------------------------ */
237     public boolean equals(Object o)
238     {
239         return o instanceof ServletHolder && compareTo((ServletHolder)o)==0;
240     }
241 
242     /* ------------------------------------------------------------ */
243     public int hashCode()
244     {
245         return _name==null?System.identityHashCode(this):_name.hashCode();
246     }
247 
248     /* ------------------------------------------------------------ */
249     /** Link a user role.
250      * Translate the role name used by a servlet, to the link name
251      * used by the container.
252      * @param name The role name as used by the servlet
253      * @param link The role name as used by the container.
254      */
255     public synchronized void setUserRoleLink(String name,String link)
256     {
257         if (_roleMap==null)
258             _roleMap=new HashMap<String, String>();
259         _roleMap.put(name,link);
260     }
261 
262     /* ------------------------------------------------------------ */
263     /** get a user role link.
264      * @param name The name of the role
265      * @return The name as translated by the link. If no link exists,
266      * the name is returned.
267      */
268     public String getUserRoleLink(String name)
269     {
270         if (_roleMap==null)
271             return name;
272         String link= _roleMap.get(name);
273         return (link==null)?name:link;
274     }
275 
276     /* ------------------------------------------------------------ */
277     /**
278      * @return Returns the forcedPath.
279      */
280     @ManagedAttribute(value="forced servlet path", readonly=true)
281     public String getForcedPath()
282     {
283         return _forcedPath;
284     }
285 
286     /* ------------------------------------------------------------ */
287     /**
288      * @param forcedPath The forcedPath to set.
289      */
290     public void setForcedPath(String forcedPath)
291     {
292         _forcedPath = forcedPath;
293     }
294 
295     public boolean isEnabled()
296     {
297         return _enabled;
298     }
299 
300 
301     public void setEnabled(boolean enabled)
302     {
303         _enabled = enabled;
304     }
305 
306 
307     /* ------------------------------------------------------------ */
308     public void doStart()
309         throws Exception
310     {
311         _unavailable=0;
312         if (!_enabled)
313             return;
314 
315         // Handle JSP file forced paths
316         if (_forcedPath != null)
317         {
318             // Look for a precompiled JSP Servlet
319             String precompiled=getClassNameForJsp(_forcedPath);
320             if (LOG.isDebugEnabled())
321                 LOG.debug("Checking for precompiled servlet {} for jsp {}", precompiled, _forcedPath);
322             ServletHolder jsp=getServletHandler().getServlet(precompiled);
323             if (jsp!=null && jsp.getClassName() !=  null)
324             {
325                 if (LOG.isDebugEnabled())
326                     LOG.debug("JSP file {} for {} mapped to Servlet {}",_forcedPath, getName(),jsp.getClassName());
327                 // set the className for this servlet to the precompiled one
328                 setClassName(jsp.getClassName());
329             }
330             else
331             {
332                 if (getClassName() == null)
333                 {
334                     // Look for normal JSP servlet
335                     jsp=getServletHandler().getServlet("jsp");
336                     if (jsp!=null)
337                     {
338                         if (LOG.isDebugEnabled())
339                             LOG.debug("JSP file {} for {} mapped to Servlet class {}",_forcedPath, getName(),jsp.getClassName());
340                         setClassName(jsp.getClassName());
341                         //copy jsp init params that don't exist for this servlet
342                         for (Map.Entry<String, String> entry:jsp.getInitParameters().entrySet())
343                         {
344                             if (!_initParams.containsKey(entry.getKey()))
345                                 setInitParameter(entry.getKey(), entry.getValue());
346                         }
347                         //jsp specific: set up the jsp-file on the JspServlet. If load-on-startup is >=0 and the jsp container supports
348                         //precompilation, the jsp will be compiled when this holder is initialized. If not load on startup, or the
349                         //container does not support startup precompilation, it will be compiled at runtime when handling a request for this jsp.
350                         //See also adaptForcedPathToJspContainer
351                         setInitParameter("jspFile", _forcedPath);
352                     }
353                 }
354             }
355         }
356 
357 
358         //check servlet has a class (ie is not a preliminary registration). If preliminary, fail startup.
359         try
360         {
361             super.doStart();
362         }
363         catch (UnavailableException ue)
364         {
365             makeUnavailable(ue);
366             if (_servletHandler.isStartWithUnavailable())
367             {
368                 LOG.ignore(ue);
369                 return;
370             }
371             else
372                 throw ue;
373         }
374 
375 
376         //servlet is not an instance of javax.servlet.Servlet
377         try
378         {
379             checkServletType();
380         }
381         catch (UnavailableException ue)
382         {
383             makeUnavailable(ue);
384             if (_servletHandler.isStartWithUnavailable())
385             {
386                 LOG.ignore(ue);
387                 return;
388             }
389             else
390                 throw ue;
391         }
392 
393         //check if we need to forcibly set load-on-startup
394         checkInitOnStartup();
395 
396         _identityService = _servletHandler.getIdentityService();
397         if (_identityService!=null && _runAsRole!=null)
398             _runAsToken=_identityService.newRunAsToken(_runAsRole);
399 
400         _config=new Config();
401 
402         if (_class!=null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class))
403             _servlet = new SingleThreadedWrapper();
404 
405     }
406 
407 
408     /* ------------------------------------------------------------ */
409     @Override
410     public void initialize ()
411     throws Exception
412     {
413         if(!_initialized){
414             super.initialize();
415             if (_extInstance || _initOnStartup)
416             {
417                 try
418                 {
419                     initServlet();
420                 }
421                 catch(Exception e)
422                 {
423                     if (_servletHandler.isStartWithUnavailable())
424                         LOG.ignore(e);
425                     else
426                         throw e;
427                 }
428             }
429         }
430         _initialized = true;
431     }
432 
433 
434     /* ------------------------------------------------------------ */
435     public void doStop()
436         throws Exception
437     {
438         Object old_run_as = null;
439         if (_servlet!=null)
440         {
441             try
442             {
443                 if (_identityService!=null)
444                     old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
445 
446                 destroyInstance(_servlet);
447             }
448             catch (Exception e)
449             {
450                 LOG.warn(e);
451             }
452             finally
453             {
454                 if (_identityService!=null)
455                     _identityService.unsetRunAs(old_run_as);
456             }
457         }
458 
459         if (!_extInstance)
460             _servlet=null;
461 
462         _config=null;
463         _initialized = false;
464     }
465 
466     /* ------------------------------------------------------------ */
467     @Override
468     public void destroyInstance (Object o)
469     throws Exception
470     {
471         if (o==null)
472             return;
473         Servlet servlet =  ((Servlet)o);
474         getServletHandler().destroyServlet(servlet);
475         servlet.destroy();
476     }
477 
478     /* ------------------------------------------------------------ */
479     /** Get the servlet.
480      * @return The servlet
481      * @throws ServletException if unable to init the servlet on first use
482      */
483     public synchronized Servlet getServlet()
484         throws ServletException
485     {
486         // Handle previous unavailability
487         if (_unavailable!=0)
488         {
489             if (_unavailable<0 || _unavailable>0 && System.currentTimeMillis()<_unavailable)
490                 throw _unavailableEx;
491             _unavailable=0;
492             _unavailableEx=null;
493         }
494 
495         if (_servlet==null)
496             initServlet();
497         return _servlet;
498     }
499 
500     /* ------------------------------------------------------------ */
501     /** Get the servlet instance (no initialization done).
502      * @return The servlet or null
503      */
504     public Servlet getServletInstance()
505     {
506         return _servlet;
507     }
508 
509     /* ------------------------------------------------------------ */
510     /**
511      * Check to ensure class of servlet is acceptable.
512      * @throws UnavailableException if Servlet class is not of type {@link javax.servlet.Servlet}
513      */
514     public void checkServletType ()
515         throws UnavailableException
516     {
517         if (_class==null || !javax.servlet.Servlet.class.isAssignableFrom(_class))
518         {
519             throw new UnavailableException("Servlet "+_class+" is not a javax.servlet.Servlet");
520         }
521     }
522 
523     /* ------------------------------------------------------------ */
524     /**
525      * @return true if the holder is started and is not unavailable
526      */
527     public boolean isAvailable()
528     {
529         if (isStarted()&& _unavailable==0)
530             return true;
531         try
532         {
533             getServlet();
534         }
535         catch(Exception e)
536         {
537             LOG.ignore(e);
538         }
539 
540         return isStarted()&& _unavailable==0;
541     }
542 
543     /* ------------------------------------------------------------ */
544     /**
545      * Check if there is a javax.servlet.annotation.ServletSecurity
546      * annotation on the servlet class. If there is, then we force
547      * it to be loaded on startup, because all of the security
548      * constraints must be calculated as the container starts.
549      *
550      */
551     private void checkInitOnStartup()
552     {
553         if (_class==null)
554             return;
555 
556         if ((_class.getAnnotation(javax.servlet.annotation.ServletSecurity.class) != null) && !_initOnStartup)
557             setInitOrder(Integer.MAX_VALUE);
558     }
559 
560     /* ------------------------------------------------------------ */
561     private void makeUnavailable(UnavailableException e)
562     {
563         if (_unavailableEx==e && _unavailable!=0)
564             return;
565 
566         _servletHandler.getServletContext().log("unavailable",e);
567 
568         _unavailableEx=e;
569         _unavailable=-1;
570         if (e.isPermanent())
571             _unavailable=-1;
572         else
573         {
574             if (_unavailableEx.getUnavailableSeconds()>0)
575                 _unavailable=System.currentTimeMillis()+1000*_unavailableEx.getUnavailableSeconds();
576             else
577                 _unavailable=System.currentTimeMillis()+5000; // TODO configure
578         }
579     }
580 
581     /* ------------------------------------------------------------ */
582 
583     private void makeUnavailable(final Throwable e)
584     {
585         if (e instanceof UnavailableException)
586             makeUnavailable((UnavailableException)e);
587         else
588         {
589             ServletContext ctx = _servletHandler.getServletContext();
590             if (ctx==null)
591                 LOG.info("unavailable",e);
592             else
593                 ctx.log("unavailable",e);
594             _unavailableEx=new UnavailableException(String.valueOf(e),-1)
595             {
596                 {
597                     initCause(e);
598                 }
599             };
600             _unavailable=-1;
601         }
602     }
603 
604     /* ------------------------------------------------------------ */
605     private void initServlet()
606         throws ServletException
607     {
608         Object old_run_as = null;
609         try
610         {
611             if (_servlet==null)
612                 _servlet=newInstance();
613             if (_config==null)
614                 _config=new Config();
615 
616 
617 
618             // Handle run as
619             if (_identityService!=null)
620             {
621                 old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
622             }
623 
624             // Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet
625             if (isJspServlet())
626             {
627                 initJspServlet();
628                 detectJspContainer();
629             }
630 
631             initMultiPart();
632 
633             if (_forcedPath != null && _jspContainer == null)
634             {
635                 detectJspContainer();
636             }
637 
638             if (LOG.isDebugEnabled())
639                 LOG.debug("Servlet.init {} for {}",_servlet,getName());
640             _servlet.init(_config);
641         }
642         catch (UnavailableException e)
643         {
644             makeUnavailable(e);
645             _servlet=null;
646             _config=null;
647             throw e;
648         }
649         catch (ServletException e)
650         {
651             makeUnavailable(e.getCause()==null?e:e.getCause());
652             _servlet=null;
653             _config=null;
654             throw e;
655         }
656         catch (Exception e)
657         {
658             makeUnavailable(e);
659             _servlet=null;
660             _config=null;
661             throw new ServletException(this.toString(),e);
662         }
663         finally
664         {
665             // pop run-as role
666             if (_identityService!=null)
667                 _identityService.unsetRunAs(old_run_as);
668         }
669     }
670 
671 
672     /* ------------------------------------------------------------ */
673     /**
674      * @throws Exception if unable to init the JSP Servlet
675      */
676     protected void initJspServlet () throws Exception
677     {
678         ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
679 
680         /* Set the webapp's classpath for Jasper */
681         ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
682 
683         /* Set up other classpath attribute */
684         if ("?".equals(getInitParameter("classpath")))
685         {
686             String classpath = ch.getClassPath();
687             if (LOG.isDebugEnabled())
688                 LOG.debug("classpath=" + classpath);
689             if (classpath != null)
690                 setInitParameter("classpath", classpath);
691         }
692 
693         /* ensure scratch dir */
694         File scratch = null;
695         if (getInitParameter("scratchdir") == null)
696         {
697             File tmp = (File)getServletHandler().getServletContext().getAttribute(ServletContext.TEMPDIR);
698             scratch = new File(tmp, "jsp");
699             setInitParameter("scratchdir", scratch.getAbsolutePath());
700         }
701 
702         scratch = new File (getInitParameter("scratchdir"));
703         if (!scratch.exists()) scratch.mkdir();
704     }
705 
706     /* ------------------------------------------------------------ */
707     /**
708      * Register a ServletRequestListener that will ensure tmp multipart
709      * files are deleted when the request goes out of scope.
710      *
711      * @throws Exception if unable to init the multipart
712      */
713     protected void initMultiPart () throws Exception
714     {
715         //if this servlet can handle multipart requests, ensure tmp files will be
716         //cleaned up correctly
717         if (((Registration)getRegistration()).getMultipartConfig() != null)
718         {
719             //Register a listener to delete tmp files that are created as a result of this
720             //servlet calling Request.getPart() or Request.getParts()
721 
722             ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
723             ch.addEventListener(MultiPartCleanerListener.INSTANCE);
724         }
725     }
726 
727     /* ------------------------------------------------------------ */
728     /**
729      * @see org.eclipse.jetty.server.UserIdentity.Scope#getContextPath()
730      */
731     @Override
732     public String getContextPath()
733     {
734         return _config.getServletContext().getContextPath();
735     }
736 
737     /* ------------------------------------------------------------ */
738     /**
739      * @see org.eclipse.jetty.server.UserIdentity.Scope#getRoleRefMap()
740      */
741     @Override
742     public Map<String, String> getRoleRefMap()
743     {
744         return _roleMap;
745     }
746 
747     /* ------------------------------------------------------------ */
748     @ManagedAttribute(value="role to run servlet as", readonly=true)
749     public String getRunAsRole()
750     {
751         return _runAsRole;
752     }
753 
754     /* ------------------------------------------------------------ */
755     public void setRunAsRole(String role)
756     {
757         _runAsRole = role;
758     }
759 
760     /* ------------------------------------------------------------ */
761     /**
762      * Prepare to service a request.
763      *
764      * @param baseRequest the base request
765      * @param request the request
766      * @param response the response
767      * @throws ServletException if unable to prepare the servlet
768      * @throws UnavailableException if not available
769      */
770     protected void prepare (Request baseRequest, ServletRequest request, ServletResponse response)
771     throws ServletException, UnavailableException
772     {
773         ensureInstance();
774         MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
775         if (mpce != null)
776             baseRequest.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
777     }
778 
779     public synchronized Servlet ensureInstance()
780     throws ServletException, UnavailableException
781     {
782         if (_class==null)
783             throw new UnavailableException("Servlet Not Initialized");
784         Servlet servlet=_servlet;
785         if (!isStarted())
786             throw new UnavailableException("Servlet not initialized", -1);
787         if (_unavailable!=0 || (!_initOnStartup && servlet==null))
788             servlet=getServlet();
789         if (servlet==null)
790             throw new UnavailableException("Could not instantiate "+_class);
791 
792         return servlet;
793     }
794 
795     /* ------------------------------------------------------------ */
796     /**
797      * Service a request with this servlet.
798      *
799      * @param baseRequest the base request
800      * @param request the request
801      * @param response the response
802      * @throws ServletException if unable to process the servlet
803      * @throws UnavailableException if servlet is unavailable
804      * @throws IOException if unable to process the request or response
805      */
806     public void handle(Request baseRequest,
807                        ServletRequest request,
808                        ServletResponse response)
809         throws ServletException,
810                UnavailableException,
811                IOException
812     {
813         if (_class==null)
814             throw new UnavailableException("Servlet Not Initialized");
815 
816         Servlet servlet = ensureInstance();
817 
818         // Service the request
819         boolean servlet_error=true;
820         Object old_run_as = null;
821         boolean suspendable = baseRequest.isAsyncSupported();
822         try
823         {
824             // Handle aliased path
825             if (_forcedPath!=null)
826                 adaptForcedPathToJspContainer(request);
827 
828             // Handle run as
829             if (_identityService!=null)
830                 old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
831 
832             if (!isAsyncSupported())
833                 baseRequest.setAsyncSupported(false);
834 
835             servlet.service(request,response);
836             servlet_error=false;
837         }
838         catch(UnavailableException e)
839         {
840             makeUnavailable(e);
841             throw _unavailableEx;
842         }
843         finally
844         {
845             baseRequest.setAsyncSupported(suspendable);
846 
847             // pop run-as role
848             if (_identityService!=null)
849                 _identityService.unsetRunAs(old_run_as);
850 
851             // Handle error params.
852             if (servlet_error)
853                 request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,getName());
854         }
855     }
856 
857 
858     /* ------------------------------------------------------------ */
859     private boolean isJspServlet ()
860     {
861         if (_servlet == null)
862             return false;
863 
864         Class<?> c = _servlet.getClass();
865 
866         boolean result = false;
867         while (c != null && !result)
868         {
869             result = isJspServlet(c.getName());
870             c = c.getSuperclass();
871         }
872 
873         return result;
874     }
875 
876 
877     /* ------------------------------------------------------------ */
878     private boolean isJspServlet (String classname)
879     {
880         if (classname == null)
881             return false;
882         return ("org.apache.jasper.servlet.JspServlet".equals(classname));
883     }
884 
885     /* ------------------------------------------------------------ */
886     private void adaptForcedPathToJspContainer (ServletRequest request)
887     {
888         //no-op for apache jsp
889     }
890 
891     /* ------------------------------------------------------------ */
892     private void detectJspContainer ()
893     {
894         if (_jspContainer == null)
895         {
896             try
897             {
898                 //check for apache
899                 Loader.loadClass(Holder.class, APACHE_SENTINEL_CLASS);
900                 if (LOG.isDebugEnabled())LOG.debug("Apache jasper detected");
901                 _jspContainer = JspContainer.APACHE;
902             }
903             catch (ClassNotFoundException x)
904             {
905                 if (LOG.isDebugEnabled())LOG.debug("Other jasper detected");
906                 _jspContainer = JspContainer.OTHER;
907             }
908         }
909     }
910 
911     /* ------------------------------------------------------------ */
912     private String getNameOfJspClass (String jsp)
913     {
914         if (jsp == null)
915             return "";
916 
917         int i = jsp.lastIndexOf('/') + 1;
918         jsp = jsp.substring(i);
919         try
920         {
921             Class<?> jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
922             Method makeJavaIdentifier = jspUtil.getMethod("makeJavaIdentifier", String.class);
923             return (String)makeJavaIdentifier.invoke(null, jsp);
924         }
925         catch (Exception e)
926         {
927             String tmp = jsp.replace('.','_');
928             LOG.warn("Unable to make identifier for jsp "+jsp +" trying "+tmp+" instead");
929             if (LOG.isDebugEnabled())
930                 LOG.warn(e);
931             return tmp;
932         }
933     }
934 
935 
936     /* ------------------------------------------------------------ */
937     private String getPackageOfJspClass (String jsp)
938     {
939         if (jsp == null)
940             return "";
941 
942         int i = jsp.lastIndexOf('/');
943         if (i <= 0)
944             return "";
945         try
946         {
947             Class<?> jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
948             Method makeJavaPackage = jspUtil.getMethod("makeJavaPackage", String.class);
949             return (String)makeJavaPackage.invoke(null, jsp.substring(0,i));
950         }
951         catch (Exception e)
952         {
953             String tmp = jsp.substring(1).replace('/','.');
954             LOG.warn("Unable to make package for jsp "+jsp +" trying "+tmp+" instead");
955             if (LOG.isDebugEnabled())
956                 LOG.warn(e);
957             return tmp;
958         }
959     }
960 
961 
962     /* ------------------------------------------------------------ */
963     private String getJspPackagePrefix ()
964     {
965         String jspPackageName = (String)getServletHandler().getServletContext().getInitParameter(JSP_GENERATED_PACKAGE_NAME );
966         if (jspPackageName == null)
967             jspPackageName = "org.apache.jsp";
968 
969         return jspPackageName;
970     }
971 
972 
973     /* ------------------------------------------------------------ */
974     private String getClassNameForJsp (String jsp)
975     {
976         if (jsp == null)
977             return null;
978 
979         return getJspPackagePrefix() + "." +getPackageOfJspClass(jsp) + "." + getNameOfJspClass(jsp);
980     }
981 
982 
983     /* ------------------------------------------------------------ */
984     /* ------------------------------------------------------------ */
985     /* ------------------------------------------------------------ */
986     protected class Config extends HolderConfig implements ServletConfig
987     {
988         /* -------------------------------------------------------- */
989         @Override
990         public String getServletName()
991         {
992             return getName();
993         }
994 
995     }
996 
997     /* -------------------------------------------------------- */
998     /* -------------------------------------------------------- */
999     /* -------------------------------------------------------- */
1000     public class Registration extends HolderRegistration implements ServletRegistration.Dynamic
1001     {
1002         protected MultipartConfigElement _multipartConfig;
1003 
1004         @Override
1005         public Set<String> addMapping(String... urlPatterns)
1006         {
1007             illegalStateIfContextStarted();
1008             Set<String> clash=null;
1009             for (String pattern : urlPatterns)
1010             {
1011                 ServletMapping mapping = _servletHandler.getServletMapping(pattern);
1012                 if (mapping!=null)
1013                 {
1014                     //if the servlet mapping was from a default descriptor, then allow it to be overridden
1015                     if (!mapping.isDefault())
1016                     {
1017                         if (clash==null)
1018                             clash=new HashSet<String>();
1019                         clash.add(pattern);
1020                     }
1021                 }
1022             }
1023 
1024             //if there were any clashes amongst the urls, return them
1025             if (clash!=null)
1026                 return clash;
1027 
1028             //otherwise apply all of them
1029             ServletMapping mapping = new ServletMapping();
1030             mapping.setServletName(ServletHolder.this.getName());
1031             mapping.setPathSpecs(urlPatterns);
1032             _servletHandler.addServletMapping(mapping);
1033 
1034             return Collections.emptySet();
1035         }
1036 
1037         @Override
1038         public Collection<String> getMappings()
1039         {
1040             ServletMapping[] mappings =_servletHandler.getServletMappings();
1041             List<String> patterns=new ArrayList<String>();
1042             if (mappings!=null)
1043             {
1044                 for (ServletMapping mapping : mappings)
1045                 {
1046                     if (!mapping.getServletName().equals(getName()))
1047                         continue;
1048                     String[] specs=mapping.getPathSpecs();
1049                     if (specs!=null && specs.length>0)
1050                         patterns.addAll(Arrays.asList(specs));
1051                 }
1052             }
1053             return patterns;
1054         }
1055 
1056         @Override
1057         public String getRunAsRole()
1058         {
1059             return _runAsRole;
1060         }
1061 
1062         @Override
1063         public void setLoadOnStartup(int loadOnStartup)
1064         {
1065             illegalStateIfContextStarted();
1066             ServletHolder.this.setInitOrder(loadOnStartup);
1067         }
1068 
1069         public int getInitOrder()
1070         {
1071             return ServletHolder.this.getInitOrder();
1072         }
1073 
1074         @Override
1075         public void setMultipartConfig(MultipartConfigElement element)
1076         {
1077             _multipartConfig = element;
1078         }
1079 
1080         public MultipartConfigElement getMultipartConfig()
1081         {
1082             return _multipartConfig;
1083         }
1084 
1085         @Override
1086         public void setRunAsRole(String role)
1087         {
1088             _runAsRole = role;
1089         }
1090 
1091         @Override
1092         public Set<String> setServletSecurity(ServletSecurityElement securityElement)
1093         {
1094             return _servletHandler.setServletSecurity(this, securityElement);
1095         }
1096     }
1097 
1098     public ServletRegistration.Dynamic getRegistration()
1099     {
1100         if (_registration == null)
1101             _registration = new Registration();
1102         return _registration;
1103     }
1104 
1105     /* -------------------------------------------------------- */
1106     /* -------------------------------------------------------- */
1107     /* -------------------------------------------------------- */
1108     private class SingleThreadedWrapper implements Servlet
1109     {
1110         Stack<Servlet> _stack=new Stack<Servlet>();
1111 
1112         @Override
1113         public void destroy()
1114         {
1115             synchronized(this)
1116             {
1117                 while(_stack.size()>0)
1118                     try { (_stack.pop()).destroy(); } catch (Exception e) { LOG.warn(e); }
1119             }
1120         }
1121 
1122         @Override
1123         public ServletConfig getServletConfig()
1124         {
1125             return _config;
1126         }
1127 
1128         @Override
1129         public String getServletInfo()
1130         {
1131             return null;
1132         }
1133 
1134         @Override
1135         public void init(ServletConfig config) throws ServletException
1136         {
1137             synchronized(this)
1138             {
1139                 if(_stack.size()==0)
1140                 {
1141                     try
1142                     {
1143                         Servlet s = newInstance();
1144                         s.init(config);
1145                         _stack.push(s);
1146                     }
1147                     catch (ServletException e)
1148                     {
1149                         throw e;
1150                     }
1151                     catch (Exception e)
1152                     {
1153                         throw new ServletException(e);
1154                     }
1155                 }
1156             }
1157         }
1158 
1159         @Override
1160         public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
1161         {
1162             Servlet s;
1163             synchronized(this)
1164             {
1165                 if(_stack.size()>0)
1166                     s=(Servlet)_stack.pop();
1167                 else
1168                 {
1169                     try
1170                     {
1171                         s = newInstance();
1172                         s.init(_config);
1173                     }
1174                     catch (ServletException e)
1175                     {
1176                         throw e;
1177                     }
1178                     catch (Exception e)
1179                     {
1180                         throw new ServletException(e);
1181                     }
1182                 }
1183             }
1184 
1185             try
1186             {
1187                 s.service(req,res);
1188             }
1189             finally
1190             {
1191                 synchronized(this)
1192                 {
1193                     _stack.push(s);
1194                 }
1195             }
1196         }
1197     }
1198 
1199     /* ------------------------------------------------------------ */
1200     /**
1201      * @return the newly created Servlet instance
1202      * @throws ServletException if unable to create a new instance
1203      * @throws IllegalAccessException if not allowed to create a new instance
1204      * @throws InstantiationException if creating new instance resulted in error
1205      */
1206     protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException
1207     {
1208         try
1209         {
1210             ServletContext ctx = getServletHandler().getServletContext();
1211             if (ctx instanceof ServletContextHandler.Context)
1212                 return ((ServletContextHandler.Context)ctx).createServlet(getHeldClass());
1213             return getHeldClass().newInstance();
1214         }
1215         catch (ServletException se)
1216         {
1217             Throwable cause = se.getRootCause();
1218             if (cause instanceof InstantiationException)
1219                 throw (InstantiationException)cause;
1220             if (cause instanceof IllegalAccessException)
1221                 throw (IllegalAccessException)cause;
1222             throw se;
1223         }
1224     }
1225 
1226 
1227     /* ------------------------------------------------------------ */
1228     @Override
1229     public String toString()
1230     {
1231         return String.format("%s@%x==%s,%d,%b",_name,hashCode(),_className,_initOrder,_servlet!=null);
1232     }
1233 }