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