View Javadoc

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