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