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.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      private static final Logger LOG = Log.getLogger(ServletHolder.class);
74  
75      /* ---------------------------------------------------------------- */
76      private int _initOrder = -1;
77      private boolean _initOnStartup=false;
78      private Map<String, String> _roleMap;
79      private String _forcedPath;
80      private String _runAsRole;
81      private RunAsToken _runAsToken;
82      private IdentityService _identityService;
83      private ServletRegistration.Dynamic _registration;
84      private JspContainer _jspContainer;
85  
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)
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         super.initialize();
391         if (_extInstance || _initOnStartup)
392         {
393             try
394             {
395                 initServlet();
396             }
397             catch(Exception e)
398             {
399                 if (_servletHandler.isStartWithUnavailable())
400                     LOG.ignore(e);
401                 else
402                     throw e;
403             }
404         }
405     }
406     
407     
408     /* ------------------------------------------------------------ */
409     public void doStop()
410         throws Exception
411     {
412         Object old_run_as = null;
413         if (_servlet!=null)
414         {
415             try
416             {
417                 if (_identityService!=null)
418                     old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
419 
420                 destroyInstance(_servlet);
421             }
422             catch (Exception e)
423             {
424                 LOG.warn(e);
425             }
426             finally
427             {
428                 if (_identityService!=null)
429                     _identityService.unsetRunAs(old_run_as);
430             }
431         }
432 
433         if (!_extInstance)
434             _servlet=null;
435 
436         _config=null;
437     }
438 
439     /* ------------------------------------------------------------ */
440     @Override
441     public void destroyInstance (Object o)
442     throws Exception
443     {
444         if (o==null)
445             return;
446         Servlet servlet =  ((Servlet)o);
447         getServletHandler().destroyServlet(servlet);
448         servlet.destroy();
449     }
450 
451     /* ------------------------------------------------------------ */
452     /** Get the servlet.
453      * @return The servlet
454      */
455     public synchronized Servlet getServlet()
456         throws ServletException
457     {
458         // Handle previous unavailability
459         if (_unavailable!=0)
460         {
461             if (_unavailable<0 || _unavailable>0 && System.currentTimeMillis()<_unavailable)
462                 throw _unavailableEx;
463             _unavailable=0;
464             _unavailableEx=null;
465         }
466 
467         if (_servlet==null)
468             initServlet();
469         return _servlet;
470     }
471 
472     /* ------------------------------------------------------------ */
473     /** Get the servlet instance (no initialization done).
474      * @return The servlet or null
475      */
476     public Servlet getServletInstance()
477     {
478         return _servlet;
479     }
480 
481     /* ------------------------------------------------------------ */
482     /**
483      * Check to ensure class of servlet is acceptable.
484      * @throws UnavailableException
485      */
486     public void checkServletType ()
487         throws UnavailableException
488     {
489         if (_class==null || !javax.servlet.Servlet.class.isAssignableFrom(_class))
490         {
491             throw new UnavailableException("Servlet "+_class+" is not a javax.servlet.Servlet");
492         }
493     }
494 
495     /* ------------------------------------------------------------ */
496     /**
497      * @return true if the holder is started and is not unavailable
498      */
499     public boolean isAvailable()
500     {
501         if (isStarted()&& _unavailable==0)
502             return true;
503         try
504         {
505             getServlet();
506         }
507         catch(Exception e)
508         {
509             LOG.ignore(e);
510         }
511 
512         return isStarted()&& _unavailable==0;
513     }
514     
515     /* ------------------------------------------------------------ */
516     /**
517      * Check if there is a javax.servlet.annotation.ServletSecurity
518      * annotation on the servlet class. If there is, then we force
519      * it to be loaded on startup, because all of the security 
520      * constraints must be calculated as the container starts.
521      * 
522      */
523     private void checkInitOnStartup()
524     {
525         if (_class==null)
526             return;
527         
528         if ((_class.getAnnotation(javax.servlet.annotation.ServletSecurity.class) != null) && !_initOnStartup)
529             setInitOrder(Integer.MAX_VALUE);    
530     }
531 
532     /* ------------------------------------------------------------ */
533     private void makeUnavailable(UnavailableException e)
534     {
535         if (_unavailableEx==e && _unavailable!=0)
536             return;
537 
538         _servletHandler.getServletContext().log("unavailable",e);
539 
540         _unavailableEx=e;
541         _unavailable=-1;
542         if (e.isPermanent())
543             _unavailable=-1;
544         else
545         {
546             if (_unavailableEx.getUnavailableSeconds()>0)
547                 _unavailable=System.currentTimeMillis()+1000*_unavailableEx.getUnavailableSeconds();
548             else
549                 _unavailable=System.currentTimeMillis()+5000; // TODO configure
550         }
551     }
552 
553     /* ------------------------------------------------------------ */
554 
555     private void makeUnavailable(final Throwable e)
556     {
557         if (e instanceof UnavailableException)
558             makeUnavailable((UnavailableException)e);
559         else
560         {
561             ServletContext ctx = _servletHandler.getServletContext();
562             if (ctx==null)
563                 LOG.info("unavailable",e);
564             else
565                 ctx.log("unavailable",e);
566             _unavailableEx=new UnavailableException(String.valueOf(e),-1)
567             {
568                 {
569                     initCause(e);
570                 }
571             };
572             _unavailable=-1;
573         }
574     }
575 
576     /* ------------------------------------------------------------ */
577     private void initServlet()
578         throws ServletException
579     {
580         Object old_run_as = null;
581         try
582         {
583             if (_servlet==null)
584                 _servlet=newInstance();
585             if (_config==null)
586                 _config=new Config();
587             
588           
589 
590             // Handle run as
591             if (_identityService!=null)
592             {
593                 old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
594             }
595 
596             // Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet
597             if (isJspServlet())
598             {
599                 initJspServlet();
600                 detectJspContainer();
601             }
602 
603             initMultiPart();
604 
605             if (_forcedPath != null && _jspContainer == null)
606             {
607                 detectJspContainer();
608             }
609 
610             if (LOG.isDebugEnabled())
611                 LOG.debug("Servlet.init {} for {}",_servlet,getName());
612             _servlet.init(_config);
613         }
614         catch (UnavailableException e)
615         {
616             makeUnavailable(e);
617             _servlet=null;
618             _config=null;
619             throw e;
620         }
621         catch (ServletException e)
622         {
623             makeUnavailable(e.getCause()==null?e:e.getCause());
624             _servlet=null;
625             _config=null;
626             throw e;
627         }
628         catch (Exception e)
629         {
630             makeUnavailable(e);
631             _servlet=null;
632             _config=null;
633             throw new ServletException(this.toString(),e);
634         }
635         finally
636         {
637             // pop run-as role
638             if (_identityService!=null)
639                 _identityService.unsetRunAs(old_run_as);
640         }
641     }
642 
643 
644     /* ------------------------------------------------------------ */
645     /**
646      * @throws Exception
647      */
648     protected void initJspServlet () throws Exception
649     {
650         ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
651             
652         /* Set the webapp's classpath for Jasper */
653         ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
654 
655         /* Set the system classpath for Jasper */
656         setInitParameter("com.sun.appserv.jsp.classpath", Loader.getClassPath(ch.getClassLoader().getParent()));
657 
658         /* Set up other classpath attribute */
659         if ("?".equals(getInitParameter("classpath")))
660         {
661             String classpath = ch.getClassPath();
662             if (LOG.isDebugEnabled())
663                 LOG.debug("classpath=" + classpath);
664             if (classpath != null)
665                 setInitParameter("classpath", classpath);
666         }
667         
668         /* ensure scratch dir */
669         File scratch = null;
670         if (getInitParameter("scratchdir") == null)
671         {
672             File tmp = (File)getServletHandler().getServletContext().getAttribute(ServletContext.TEMPDIR);
673             scratch = new File(tmp, "jsp");
674             setInitParameter("scratchdir", scratch.getAbsolutePath());
675         }
676      
677         scratch = new File (getInitParameter("scratchdir"));
678         if (!scratch.exists()) scratch.mkdir();
679     }
680 
681     /* ------------------------------------------------------------ */
682     /**
683      * Register a ServletRequestListener that will ensure tmp multipart
684      * files are deleted when the request goes out of scope.
685      * 
686      * @throws Exception
687      */
688     protected void initMultiPart () throws Exception
689     {
690         //if this servlet can handle multipart requests, ensure tmp files will be
691         //cleaned up correctly
692         if (((Registration)getRegistration()).getMultipartConfig() != null)
693         {
694             //Register a listener to delete tmp files that are created as a result of this
695             //servlet calling Request.getPart() or Request.getParts()
696 
697             ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
698             ch.addEventListener(new Request.MultiPartCleanerListener());
699         }
700     }
701 
702     /* ------------------------------------------------------------ */
703     /**
704      * @see org.eclipse.jetty.server.UserIdentity.Scope#getContextPath()
705      */
706     @Override
707     public String getContextPath()
708     {
709         return _config.getServletContext().getContextPath();
710     }
711 
712     /* ------------------------------------------------------------ */
713     /**
714      * @see org.eclipse.jetty.server.UserIdentity.Scope#getRoleRefMap()
715      */
716     @Override
717     public Map<String, String> getRoleRefMap()
718     {
719         return _roleMap;
720     }
721 
722     /* ------------------------------------------------------------ */
723     @ManagedAttribute(value="role to run servlet as", readonly=true)
724     public String getRunAsRole()
725     {
726         return _runAsRole;
727     }
728 
729     /* ------------------------------------------------------------ */
730     public void setRunAsRole(String role)
731     {
732         _runAsRole = role;
733     }
734     
735     /* ------------------------------------------------------------ */
736     /**
737      * Prepare to service a request.
738      * 
739      * @param baseRequest
740      * @param request
741      * @param response
742      * @throws ServletException
743      * @throws UnavailableException
744      */
745     protected void prepare (Request baseRequest, ServletRequest request, ServletResponse response)
746     throws ServletException, UnavailableException
747     {
748         MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
749         if (mpce != null)
750             baseRequest.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
751     }
752 
753     /* ------------------------------------------------------------ */
754     /** Service a request with this servlet.
755      * @param baseRequest
756      * @param request
757      * @param response
758      * @throws ServletException
759      * @throws UnavailableException
760      * @throws IOException
761      */
762     public void handle(Request baseRequest,
763                        ServletRequest request,
764                        ServletResponse response)
765         throws ServletException,
766                UnavailableException,
767                IOException
768     {
769         if (_class==null)
770             throw new UnavailableException("Servlet Not Initialized");
771 
772         Servlet servlet=_servlet;
773         synchronized(this)
774         {
775             if (!isStarted())
776                 throw new UnavailableException("Servlet not initialized", -1);
777             if (_unavailable!=0 || (!_initOnStartup && servlet==null))
778                 servlet=getServlet();
779             if (servlet==null)
780                 throw new UnavailableException("Could not instantiate "+_class);
781         }
782 
783         // Service the request
784         boolean servlet_error=true;
785         Object old_run_as = null;
786         boolean suspendable = baseRequest.isAsyncSupported();
787         try
788         {
789             // Handle aliased path
790             if (_forcedPath!=null)
791                 adaptForcedPathToJspContainer(request);
792 
793             // Handle run as
794             if (_identityService!=null)
795                 old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
796 
797             if (!isAsyncSupported())
798                 baseRequest.setAsyncSupported(false);
799 
800             servlet.service(request,response);
801             servlet_error=false;
802         }
803         catch(UnavailableException e)
804         {
805             makeUnavailable(e);
806             throw _unavailableEx;
807         }
808         finally
809         {
810             baseRequest.setAsyncSupported(suspendable);
811 
812             // pop run-as role
813             if (_identityService!=null)
814                 _identityService.unsetRunAs(old_run_as);
815 
816             // Handle error params.
817             if (servlet_error)
818                 request.setAttribute("javax.servlet.error.servlet_name",getName());
819         }
820     }
821 
822 
823     /* ------------------------------------------------------------ */
824     private boolean isJspServlet ()
825     {
826         if (_servlet == null)
827             return false;
828 
829         Class<?> c = _servlet.getClass();
830 
831         boolean result = false;
832         while (c != null && !result)
833         {
834             result = isJspServlet(c.getName());
835             c = c.getSuperclass();
836         }
837 
838         return result;
839     }
840 
841 
842     /* ------------------------------------------------------------ */
843     private boolean isJspServlet (String classname)
844     {
845         if (classname == null)
846             return false;
847         return ("org.apache.jasper.servlet.JspServlet".equals(classname));
848     }
849 
850     /* ------------------------------------------------------------ */
851     private void adaptForcedPathToJspContainer (ServletRequest request)
852     {
853         if (_forcedPath != null && _jspContainer != null && JspContainer.GLASSFISH.equals(_jspContainer))
854         {
855             //if container is glassfish, set the request attribute org.apache.catalina.jsp_file to
856             //ensure the delegate jsp will be compiled
857 
858             request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
859         }
860     }
861 
862     /* ------------------------------------------------------------ */
863     private void detectJspContainer ()
864     {
865         if (_jspContainer == null)
866         {
867             //check for glassfish
868             try
869             {
870                 //if container is glassfish, set the request attribute org.apache.catalina.jsp_file to
871                 //ensure the delegate jsp will be compiled
872                 Loader.loadClass(Holder.class, GLASSFISH_SENTINEL_CLASS);
873                 if (LOG.isDebugEnabled())LOG.debug("Glassfish jasper detected");
874                 _jspContainer = JspContainer.GLASSFISH;
875             }
876             catch (ClassNotFoundException e)
877             {
878                 try
879                 {
880                     //check for apache
881                     Loader.loadClass(Holder.class, APACHE_SENTINEL_CLASS);
882                     if (LOG.isDebugEnabled())LOG.debug("Apache jasper detected");
883                     _jspContainer = JspContainer.APACHE;
884                 }
885                 catch (ClassNotFoundException x)
886                 {
887                     if (LOG.isDebugEnabled())LOG.debug("Other jasper detected");
888                     _jspContainer = JspContainer.OTHER;
889                 }
890             }
891         }
892     }
893 
894     /* ------------------------------------------------------------ */
895     private String getNameOfJspClass (String jsp)
896     {
897         if (jsp == null)
898             return "";
899         
900         int i = jsp.lastIndexOf('/') + 1;
901         jsp = jsp.substring(i);
902         try
903         {
904             Class<?> jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
905             Method makeJavaIdentifier = jspUtil.getMethod("makeJavaIdentifier", String.class);
906             return (String)makeJavaIdentifier.invoke(null, jsp);
907         }
908         catch (Exception e)
909         {
910             String tmp = jsp.replace('.','_');
911             LOG.warn("Unable to make identifier for jsp "+jsp +" trying "+tmp+" instead");
912             if (LOG.isDebugEnabled())
913                 LOG.warn(e);
914             return tmp;
915         }
916     }
917     
918     
919     /* ------------------------------------------------------------ */
920     private String getPackageOfJspClass (String jsp)
921     {
922         if (jsp == null)
923             return "";
924         
925         int i = jsp.lastIndexOf('/');
926         if (i <= 0)
927             return "";
928         try
929         {
930             Class<?> jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
931             Method makeJavaPackage = jspUtil.getMethod("makeJavaPackage", String.class);
932             return (String)makeJavaPackage.invoke(null, jsp.substring(0,i));
933         } 
934         catch (Exception e)
935         {
936             String tmp = jsp.substring(1).replace('/','.');
937             LOG.warn("Unable to make package for jsp "+jsp +" trying "+tmp+" instead");
938             if (LOG.isDebugEnabled())
939                 LOG.warn(e);
940             return tmp;
941         }
942     }
943     
944     
945     /* ------------------------------------------------------------ */
946     private String getJspPackagePrefix ()
947     {
948         String jspPackageName = (String)getServletHandler().getServletContext().getInitParameter(JSP_GENERATED_PACKAGE_NAME );
949         if (jspPackageName == null)
950             jspPackageName = "org.apache.jsp";
951         
952         return jspPackageName;
953     }
954     
955     
956     /* ------------------------------------------------------------ */
957     private String getClassNameForJsp (String jsp)
958     {
959         if (jsp == null)
960             return null; 
961         
962         return getJspPackagePrefix() + "." +getPackageOfJspClass(jsp) + "." + getNameOfJspClass(jsp);
963     }
964 
965 
966     /* ------------------------------------------------------------ */
967     /* ------------------------------------------------------------ */
968     /* ------------------------------------------------------------ */
969     protected class Config extends HolderConfig implements ServletConfig
970     {
971         /* -------------------------------------------------------- */
972         @Override
973         public String getServletName()
974         {
975             return getName();
976         }
977 
978     }
979 
980     /* -------------------------------------------------------- */
981     /* -------------------------------------------------------- */
982     /* -------------------------------------------------------- */
983     public class Registration extends HolderRegistration implements ServletRegistration.Dynamic
984     {
985         protected MultipartConfigElement _multipartConfig;
986 
987         @Override
988         public Set<String> addMapping(String... urlPatterns)
989         {
990             illegalStateIfContextStarted();
991             Set<String> clash=null;
992             for (String pattern : urlPatterns)
993             {
994                 ServletMapping mapping = _servletHandler.getServletMapping(pattern);
995                 if (mapping!=null)
996                 {
997                     //if the servlet mapping was from a default descriptor, then allow it to be overridden
998                     if (!mapping.isDefault())
999                     {
1000                         if (clash==null)
1001                             clash=new HashSet<String>();
1002                         clash.add(pattern);
1003                     }
1004                 }
1005             }
1006 
1007             //if there were any clashes amongst the urls, return them
1008             if (clash!=null)
1009                 return clash;
1010 
1011             //otherwise apply all of them
1012             ServletMapping mapping = new ServletMapping();
1013             mapping.setServletName(ServletHolder.this.getName());
1014             mapping.setPathSpecs(urlPatterns);
1015             _servletHandler.addServletMapping(mapping);
1016 
1017             return Collections.emptySet();
1018         }
1019 
1020         @Override
1021         public Collection<String> getMappings()
1022         {
1023             ServletMapping[] mappings =_servletHandler.getServletMappings();
1024             List<String> patterns=new ArrayList<String>();
1025             if (mappings!=null)
1026             {
1027                 for (ServletMapping mapping : mappings)
1028                 {
1029                     if (!mapping.getServletName().equals(getName()))
1030                         continue;
1031                     String[] specs=mapping.getPathSpecs();
1032                     if (specs!=null && specs.length>0)
1033                         patterns.addAll(Arrays.asList(specs));
1034                 }
1035             }
1036             return patterns;
1037         }
1038 
1039         @Override
1040         public String getRunAsRole()
1041         {
1042             return _runAsRole;
1043         }
1044 
1045         @Override
1046         public void setLoadOnStartup(int loadOnStartup)
1047         {
1048             illegalStateIfContextStarted();
1049             ServletHolder.this.setInitOrder(loadOnStartup);
1050         }
1051 
1052         public int getInitOrder()
1053         {
1054             return ServletHolder.this.getInitOrder();
1055         }
1056 
1057         @Override
1058         public void setMultipartConfig(MultipartConfigElement element)
1059         {
1060             _multipartConfig = element;
1061         }
1062 
1063         public MultipartConfigElement getMultipartConfig()
1064         {
1065             return _multipartConfig;
1066         }
1067 
1068         @Override
1069         public void setRunAsRole(String role)
1070         {
1071             _runAsRole = role;
1072         }
1073 
1074         @Override
1075         public Set<String> setServletSecurity(ServletSecurityElement securityElement)
1076         {
1077             return _servletHandler.setServletSecurity(this, securityElement);
1078         }
1079     }
1080 
1081     public ServletRegistration.Dynamic getRegistration()
1082     {
1083         if (_registration == null)
1084             _registration = new Registration();
1085         return _registration;
1086     }
1087 
1088     /* -------------------------------------------------------- */
1089     /* -------------------------------------------------------- */
1090     /* -------------------------------------------------------- */
1091     private class SingleThreadedWrapper implements Servlet
1092     {
1093         Stack<Servlet> _stack=new Stack<Servlet>();
1094 
1095         @Override
1096         public void destroy()
1097         {
1098             synchronized(this)
1099             {
1100                 while(_stack.size()>0)
1101                     try { (_stack.pop()).destroy(); } catch (Exception e) { LOG.warn(e); }
1102             }
1103         }
1104 
1105         @Override
1106         public ServletConfig getServletConfig()
1107         {
1108             return _config;
1109         }
1110 
1111         @Override
1112         public String getServletInfo()
1113         {
1114             return null;
1115         }
1116 
1117         @Override
1118         public void init(ServletConfig config) throws ServletException
1119         {
1120             synchronized(this)
1121             {
1122                 if(_stack.size()==0)
1123                 {
1124                     try
1125                     {
1126                         Servlet s = newInstance();
1127                         s.init(config);
1128                         _stack.push(s);
1129                     }
1130                     catch (ServletException e)
1131                     {
1132                         throw e;
1133                     }
1134                     catch (Exception e)
1135                     {
1136                         throw new ServletException(e);
1137                     }
1138                 }
1139             }
1140         }
1141 
1142         @Override
1143         public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
1144         {
1145             Servlet s;
1146             synchronized(this)
1147             {
1148                 if(_stack.size()>0)
1149                     s=(Servlet)_stack.pop();
1150                 else
1151                 {
1152                     try
1153                     {
1154                         s = newInstance();
1155                         s.init(_config);
1156                     }
1157                     catch (ServletException e)
1158                     {
1159                         throw e;
1160                     }
1161                     catch (Exception e)
1162                     {
1163                         throw new ServletException(e);
1164                     }
1165                 }
1166             }
1167 
1168             try
1169             {
1170                 s.service(req,res);
1171             }
1172             finally
1173             {
1174                 synchronized(this)
1175                 {
1176                     _stack.push(s);
1177                 }
1178             }
1179         }
1180     }
1181 
1182     /* ------------------------------------------------------------ */
1183     /**
1184      * @return the newly created Servlet instance
1185      * @throws ServletException
1186      * @throws IllegalAccessException
1187      * @throws InstantiationException
1188      */
1189     protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException
1190     {
1191         try
1192         {
1193             ServletContext ctx = getServletHandler().getServletContext();
1194             if (ctx instanceof ServletContextHandler.Context)
1195                 return ((ServletContextHandler.Context)ctx).createServlet(getHeldClass());
1196             return getHeldClass().newInstance();
1197         }
1198         catch (ServletException se)
1199         {
1200             Throwable cause = se.getRootCause();
1201             if (cause instanceof InstantiationException)
1202                 throw (InstantiationException)cause;
1203             if (cause instanceof IllegalAccessException)
1204                 throw (IllegalAccessException)cause;
1205             throw se;
1206         }
1207     }
1208     
1209 
1210     /* ------------------------------------------------------------ */
1211     @Override
1212     public String toString()
1213     {
1214         return String.format("%s@%x==%s,%d,%b",_name,hashCode(),_className,_initOrder,_servlet!=null);
1215     }
1216 }