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