View Javadoc

1   // ========================================================================
2   // Copyright (c) 1999-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.servlet;
15  
16  import java.io.IOException;
17  import java.util.ArrayList;
18  import java.util.Arrays;
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  import java.util.Stack;
27  
28  import javax.servlet.Servlet;
29  import javax.servlet.ServletConfig;
30  import javax.servlet.ServletContext;
31  import javax.servlet.ServletException;
32  import javax.servlet.ServletRequest;
33  import javax.servlet.ServletResponse;
34  import javax.servlet.SingleThreadModel;
35  import javax.servlet.UnavailableException;
36  
37  import org.eclipse.jetty.security.IdentityService;
38  import org.eclipse.jetty.security.RunAsToken;
39  import org.eclipse.jetty.server.Request;
40  import org.eclipse.jetty.server.UserIdentity;
41  import org.eclipse.jetty.server.handler.ContextHandler;
42  import org.eclipse.jetty.servlet.api.ServletRegistration;
43  import org.eclipse.jetty.util.Loader;
44  import org.eclipse.jetty.util.log.Log;
45  import org.eclipse.jetty.util.log.Logger;
46  
47  
48  
49  
50  /* --------------------------------------------------------------------- */
51  /** Servlet Instance and Context Holder.
52   * Holds the name, params and some state of a javax.servlet.Servlet
53   * instance. It implements the ServletConfig interface.
54   * This class will organise the loading of the servlet when needed or
55   * requested.
56   *
57   * 
58   */
59  public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope, Comparable
60  {
61      private static final Logger LOG = Log.getLogger(ServletHolder.class);
62  
63      /* ---------------------------------------------------------------- */
64      private int _initOrder;
65      private boolean _initOnStartup=false;
66      private Map<String, String> _roleMap;
67      private String _forcedPath;
68      private String _runAsRole;
69      private RunAsToken _runAsToken;
70      private IdentityService _identityService;
71      private ServletRegistration.Dynamic _registration;
72      
73      
74      private transient Servlet _servlet;
75      private transient Config _config;
76      private transient long _unavailable;
77      private transient UnavailableException _unavailableEx;
78      public static final Map<String,String> NO_MAPPED_ROLES = Collections.emptyMap();
79  
80      /* ---------------------------------------------------------------- */
81      /** Constructor .
82       */
83      public ServletHolder()
84      {
85      }
86  
87      
88      /* ---------------------------------------------------------------- */
89      /** Constructor for existing servlet.
90       */
91      public ServletHolder(String name,Servlet servlet)
92      {
93          setName(name);
94          setServlet(servlet);
95      }
96  
97      
98      /* ---------------------------------------------------------------- */
99      /** Constructor for existing servlet.
100      */
101     public ServletHolder(Servlet servlet)
102     {
103         setServlet(servlet);
104     }
105 
106     /* ---------------------------------------------------------------- */
107     /** Constructor for servlet class.
108      */
109     public ServletHolder(String name,Class<? extends Servlet> servlet)
110     {
111         setName(name);
112         setHeldClass(servlet);
113     }
114     
115     /* ---------------------------------------------------------------- */
116     /** Constructor for servlet class.
117      */
118     public ServletHolder(Class<? extends Servlet> servlet)
119     {
120         setHeldClass(servlet);
121     }
122 
123     /* ---------------------------------------------------------------- */
124     /**
125      * @return The unavailable exception or null if not unavailable
126      */
127     public UnavailableException getUnavailableException()
128     {
129         return _unavailableEx;
130     }
131     
132     /* ------------------------------------------------------------ */
133     public synchronized void setServlet(Servlet servlet)
134     {
135         if (servlet==null || servlet instanceof SingleThreadModel)
136             throw new IllegalArgumentException();
137 
138         _extInstance=true;
139         _servlet=servlet;
140         setHeldClass(servlet.getClass());
141         if (getName()==null)
142             setName(servlet.getClass().getName()+"-"+super.hashCode());
143     }
144     
145     /* ------------------------------------------------------------ */
146     public int getInitOrder()
147     {
148         return _initOrder;
149     }
150 
151     /* ------------------------------------------------------------ */
152     /** Set the initialize order.
153      * Holders with order<0, are initialized on use. Those with
154      * order>=0 are initialized in increasing order when the handler
155      * is started.
156      */
157     public void setInitOrder(int order)
158     {
159         _initOnStartup=true;
160         _initOrder = order;
161     }
162     
163     public boolean isSetInitOrder()
164     {
165         return _initOnStartup;
166     }
167 
168     /* ------------------------------------------------------------ */
169     /** Comparitor by init order.
170      */
171     public int compareTo(Object o)
172     {
173         if (o instanceof ServletHolder)
174         {
175             ServletHolder sh= (ServletHolder)o;
176             if (sh==this)
177                 return 0;
178             if (sh._initOrder<_initOrder)
179                 return 1;
180             if (sh._initOrder>_initOrder)
181                 return -1;
182             
183             int c=(_className!=null && sh._className!=null)?_className.compareTo(sh._className):0;
184             if (c==0)
185                 c=_name.compareTo(sh._name);
186             if (c==0)
187                 c=this.hashCode()>o.hashCode()?1:-1;
188             return c;
189         }
190         return 1;
191     }
192 
193     /* ------------------------------------------------------------ */
194     public boolean equals(Object o)
195     {
196         return compareTo(o)==0;
197     }
198 
199     /* ------------------------------------------------------------ */
200     public int hashCode()
201     {
202         return _name==null?System.identityHashCode(this):_name.hashCode();
203     }
204 
205     /* ------------------------------------------------------------ */
206     /** Link a user role.
207      * Translate the role name used by a servlet, to the link name
208      * used by the container.
209      * @param name The role name as used by the servlet
210      * @param link The role name as used by the container.
211      */
212     public synchronized void setUserRoleLink(String name,String link)
213     {
214         if (_roleMap==null)
215             _roleMap=new HashMap<String, String>();
216         _roleMap.put(name,link);
217     }
218     
219     /* ------------------------------------------------------------ */
220     /** get a user role link.
221      * @param name The name of the role
222      * @return The name as translated by the link. If no link exists,
223      * the name is returned.
224      */
225     public String getUserRoleLink(String name)
226     {
227         if (_roleMap==null)
228             return name;
229         String link= _roleMap.get(name);
230         return (link==null)?name:link;
231     }
232 
233     /* ------------------------------------------------------------ */
234     public Map<String, String> getRoleMap()
235     {
236         return _roleMap == null? NO_MAPPED_ROLES : _roleMap;
237     }
238     
239     /* ------------------------------------------------------------ */
240     /**
241      * @return Returns the forcedPath.
242      */
243     public String getForcedPath()
244     {
245         return _forcedPath;
246     }
247     
248     /* ------------------------------------------------------------ */
249     /**
250      * @param forcedPath The forcedPath to set.
251      */
252     public void setForcedPath(String forcedPath)
253     {
254         _forcedPath = forcedPath;
255     }
256     
257     /* ------------------------------------------------------------ */
258     public void doStart()
259         throws Exception
260     {
261         _unavailable=0;
262         try
263         {
264             super.doStart();
265             checkServletType();
266         }
267         catch (UnavailableException ue)
268         {
269             makeUnavailable(ue);
270         }
271 
272         _identityService = _servletHandler.getIdentityService();
273         if (_identityService!=null && _runAsRole!=null)
274             _runAsToken=_identityService.newRunAsToken(_runAsRole);
275         
276         _config=new Config();
277 
278         if (_class!=null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class))
279             _servlet = new SingleThreadedWrapper();
280 
281         if (_extInstance || _initOnStartup)
282         {
283             try
284             {
285                 initServlet();
286             }
287             catch(Exception e)
288             {
289                 if (_servletHandler.isStartWithUnavailable())
290                     LOG.ignore(e);
291                 else
292                     throw e;
293             }
294         }  
295     }
296 
297     /* ------------------------------------------------------------ */
298     public void doStop()
299         throws Exception
300     {
301         Object old_run_as = null;
302         if (_servlet!=null)
303         {       
304             try
305             {
306                 if (_identityService!=null)
307                     old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
308 
309                 destroyInstance(_servlet);
310             }
311             catch (Exception e)
312             {
313                 LOG.warn(e);
314             }
315             finally
316             {
317                 if (_identityService!=null)
318                     _identityService.unsetRunAs(old_run_as);
319             }
320         }
321 
322         if (!_extInstance)
323             _servlet=null;
324 
325         _config=null;
326     }
327 
328     /* ------------------------------------------------------------ */
329     public void destroyInstance (Object o)
330     throws Exception
331     {
332         if (o==null)
333             return;
334         Servlet servlet =  ((Servlet)o);
335         servlet.destroy();
336         getServletHandler().destroyServlet(servlet);
337     }
338 
339     /* ------------------------------------------------------------ */
340     /** Get the servlet.
341      * @return The servlet
342      */
343     public synchronized Servlet getServlet()
344         throws ServletException
345     {
346         // Handle previous unavailability
347         if (_unavailable!=0)
348         {
349             if (_unavailable<0 || _unavailable>0 && System.currentTimeMillis()<_unavailable)
350                 throw _unavailableEx;
351             _unavailable=0;
352             _unavailableEx=null;
353         }
354 
355         if (_servlet==null)
356             initServlet();
357         return _servlet;
358     }
359 
360     /* ------------------------------------------------------------ */
361     /** Get the servlet instance (no initialization done).
362      * @return The servlet or null
363      */
364     public Servlet getServletInstance()
365     {
366         return _servlet;
367     }
368         
369     /* ------------------------------------------------------------ */
370     /**
371      * Check to ensure class of servlet is acceptable.
372      * @throws UnavailableException
373      */
374     public void checkServletType ()
375         throws UnavailableException
376     {
377         if (_class==null || !javax.servlet.Servlet.class.isAssignableFrom(_class))
378         {
379             throw new UnavailableException("Servlet "+_class+" is not a javax.servlet.Servlet");
380         }
381     }
382 
383     /* ------------------------------------------------------------ */
384     /** 
385      * @return true if the holder is started and is not unavailable
386      */
387     public boolean isAvailable()
388     {
389         if (isStarted()&& _unavailable==0)
390             return true;
391         try 
392         {
393             getServlet();
394         }
395         catch(Exception e)
396         {
397             LOG.ignore(e);
398         }
399 
400         return isStarted()&& _unavailable==0;
401     }
402     
403     /* ------------------------------------------------------------ */
404     private void makeUnavailable(UnavailableException e)
405     {
406         if (_unavailableEx==e && _unavailable!=0)
407             return;
408 
409         _servletHandler.getServletContext().log("unavailable",e);
410 
411         _unavailableEx=e;
412         _unavailable=-1;
413         if (e.isPermanent())   
414             _unavailable=-1;
415         else
416         {
417             if (_unavailableEx.getUnavailableSeconds()>0)
418                 _unavailable=System.currentTimeMillis()+1000*_unavailableEx.getUnavailableSeconds();
419             else
420                 _unavailable=System.currentTimeMillis()+5000; // TODO configure
421         }
422     }
423 
424     /* ------------------------------------------------------------ */
425 
426     private void makeUnavailable(final Throwable e)
427     {
428         if (e instanceof UnavailableException)
429             makeUnavailable((UnavailableException)e);
430         else
431         {
432             ServletContext ctx = _servletHandler.getServletContext();
433             if (ctx==null)
434                 LOG.info("unavailable",e);
435             else
436                 ctx.log("unavailable",e);
437             _unavailableEx=new UnavailableException(String.valueOf(e),-1)
438             {
439                 {
440                     initCause(e);
441                 }
442             };
443             _unavailable=-1;
444         }
445     }
446 
447     /* ------------------------------------------------------------ */
448     private void initServlet()
449     	throws ServletException
450     {
451         Object old_run_as = null;
452         try
453         {
454             if (_servlet==null)
455                 _servlet=newInstance();
456             if (_config==null)
457                 _config=new Config();
458             
459             // Handle run as
460             if (_identityService!=null)
461             {
462                 old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
463             }
464             
465             // Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet
466             if (isJspServlet())
467             {
468                 initJspServlet();
469             }
470 
471             _servlet.init(_config);
472         }
473         catch (UnavailableException e)
474         {
475             makeUnavailable(e);
476             _servlet=null;
477             _config=null;
478             throw e;
479         }
480         catch (ServletException e)
481         {
482             makeUnavailable(e.getCause()==null?e:e.getCause());
483             _servlet=null;
484             _config=null;
485             throw e;
486         }
487         catch (Exception e)
488         {
489             makeUnavailable(e);
490             _servlet=null;
491             _config=null;
492             throw new ServletException(this.toString(),e);
493         }
494         finally
495         {
496             // pop run-as role
497             if (_identityService!=null)
498                 _identityService.unsetRunAs(old_run_as);
499         }
500     }
501     
502     
503     /* ------------------------------------------------------------ */
504     /**
505      * @throws Exception
506      */
507     protected void initJspServlet () throws Exception
508     {
509         ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler();
510         
511         /* Set the webapp's classpath for Jasper */
512         ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
513 
514         /* Set the system classpath for Jasper */
515         setInitParameter("com.sun.appserv.jsp.classpath", Loader.getClassPath(ch.getClassLoader().getParent())); 
516         
517         /* Set up other classpath attribute */
518         if ("?".equals(getInitParameter("classpath")))
519         {
520             String classpath = ch.getClassPath();
521             LOG.debug("classpath=" + classpath);
522             if (classpath != null) 
523                 setInitParameter("classpath", classpath);
524         }
525     }
526     
527     
528     /* ------------------------------------------------------------ */
529     /**
530      * @see org.eclipse.jetty.server.UserIdentity.Scope#getContextPath()
531      */
532     public String getContextPath()
533     {
534         return _config.getServletContext().getContextPath();
535     }
536 
537     /* ------------------------------------------------------------ */
538     /**
539      * @see org.eclipse.jetty.server.UserIdentity.Scope#getRoleRefMap()
540      */
541     public Map<String, String> getRoleRefMap()
542     {
543         return _roleMap;
544     }
545 
546     /* ------------------------------------------------------------ */
547     public String getRunAsRole() 
548     {
549         return _runAsRole;
550     }
551     
552     /* ------------------------------------------------------------ */
553     public void setRunAsRole(String role) 
554     {
555         _runAsRole = role;
556     }
557     
558     /* ------------------------------------------------------------ */
559     /** Service a request with this servlet.
560      */
561     public void handle(Request baseRequest,
562                        ServletRequest request,
563                        ServletResponse response)
564         throws ServletException,
565                UnavailableException,
566                IOException
567     {
568         if (_class==null)
569             throw new UnavailableException("Servlet Not Initialized");
570         
571         Servlet servlet=_servlet;
572         synchronized(this)
573         {
574             if (_unavailable!=0 || !_initOnStartup)
575                 servlet=getServlet();
576             if (servlet==null)
577                 throw new UnavailableException("Could not instantiate "+_class);
578         }
579         
580         // Service the request
581         boolean servlet_error=true;
582         Object old_run_as = null;
583         boolean suspendable = baseRequest.isAsyncSupported();
584         try
585         {
586             // Handle aliased path
587             if (_forcedPath!=null)
588                 // TODO complain about poor naming to the Jasper folks
589                 request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
590 
591             // Handle run as
592             if (_identityService!=null)
593                 old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
594             
595             if (!isAsyncSupported())
596                 baseRequest.setAsyncSupported(false);
597             
598             servlet.service(request,response);
599             servlet_error=false;
600         }
601         catch(UnavailableException e)
602         {
603             makeUnavailable(e);
604             throw _unavailableEx;
605         }
606         finally
607         {
608             baseRequest.setAsyncSupported(suspendable);
609             
610             // pop run-as role
611             if (_identityService!=null)
612                 _identityService.unsetRunAs(old_run_as);
613 
614             // Handle error params.
615             if (servlet_error)
616                 request.setAttribute("javax.servlet.error.servlet_name",getName());
617         }
618     }
619     
620     
621     /* ------------------------------------------------------------ */
622     private boolean isJspServlet ()
623     {
624         if (_servlet == null)
625             return false;
626         
627         Class c = _servlet.getClass();
628         
629         boolean result = false;
630         while (c != null && !result)
631         {
632             result = isJspServlet(c.getName());
633             c = c.getSuperclass();
634         }
635         
636         return result;
637     }
638     
639     
640     /* ------------------------------------------------------------ */
641     private boolean isJspServlet (String classname)
642     {
643         if (classname == null)
644             return false;
645         return ("org.apache.jasper.servlet.JspServlet".equals(classname));
646     }
647 
648  
649     /* ------------------------------------------------------------ */
650     /* ------------------------------------------------------------ */
651     /* ------------------------------------------------------------ */
652     protected class Config extends HolderConfig implements ServletConfig
653     {   
654         /* -------------------------------------------------------- */
655         public String getServletName()
656         {
657             return getName();
658         }
659         
660     }
661 
662     /* -------------------------------------------------------- */
663     /* -------------------------------------------------------- */
664     /* -------------------------------------------------------- */
665     public class Registration extends HolderRegistration implements ServletRegistration.Dynamic
666     {         
667         public Set<String> addMapping(String... urlPatterns)
668         {
669             illegalStateIfContextStarted();
670             Set<String> clash=null;
671             for (String pattern : urlPatterns)
672             {
673                 if (_servletHandler.getServletMapping(pattern)!=null)
674                 {
675                     if (clash==null)
676                         clash=new HashSet<String>();
677                     clash.add(pattern);
678                 }
679             }
680             
681             if (clash!=null)
682                 return clash;
683             
684             ServletMapping mapping = new ServletMapping();
685             mapping.setServletName(ServletHolder.this.getName());
686             mapping.setPathSpecs(urlPatterns);
687             _servletHandler.addServletMapping(mapping);
688             
689             return Collections.emptySet();
690         }
691 
692         public Collection<String> getMappings()
693         {
694             ServletMapping[] mappings =_servletHandler.getServletMappings();
695             List<String> patterns=new ArrayList<String>();
696             for (ServletMapping mapping : mappings)
697             {
698                 if (!mapping.getServletName().equals(getName()))
699                     continue;
700                 String[] specs=mapping.getPathSpecs();
701                 if (specs!=null && specs.length>0)
702                     patterns.addAll(Arrays.asList(specs));
703             }
704             return patterns;
705         }
706 
707         public String getRunAsRole() 
708         {
709             return _runAsRole;
710         }
711 
712         public void setLoadOnStartup(int loadOnStartup)
713         {
714             illegalStateIfContextStarted();
715             ServletHolder.this.setInitOrder(loadOnStartup);
716         }
717         
718         public int getInitOrder()
719         {
720             return ServletHolder.this.getInitOrder();
721         }
722  
723         public void setRunAsRole(String role) 
724         {
725             _runAsRole = role;
726         }
727     }
728     
729     public ServletRegistration.Dynamic getRegistration()
730     {
731         if (_registration == null)
732             _registration =  new Registration();
733         return _registration;
734     }
735     
736     /* -------------------------------------------------------- */
737     /* -------------------------------------------------------- */
738     /* -------------------------------------------------------- */
739     private class SingleThreadedWrapper implements Servlet
740     {
741         Stack<Servlet> _stack=new Stack<Servlet>();
742         
743         public void destroy()
744         {
745             synchronized(this)
746             {
747                 while(_stack.size()>0)
748                     try { (_stack.pop()).destroy(); } catch (Exception e) { LOG.warn(e); }
749             }
750         }
751 
752         public ServletConfig getServletConfig()
753         {
754             return _config;
755         }
756 
757         public String getServletInfo()
758         {
759             return null;
760         }
761 
762         public void init(ServletConfig config) throws ServletException
763         {
764             synchronized(this)
765             {
766                 if(_stack.size()==0)
767                 {
768                     try
769                     {
770                         Servlet s = newInstance();
771                         s.init(config);
772                         _stack.push(s);
773                     }
774                     catch (ServletException e)
775                     {
776                         throw e;
777                     }
778                     catch (Exception e)
779                     {
780                         throw new ServletException(e);
781                     }
782                 }
783             }
784         }
785 
786         public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
787         {
788             Servlet s;
789             synchronized(this)
790             {
791                 if(_stack.size()>0)
792                     s=(Servlet)_stack.pop();
793                 else
794                 {
795                     try
796                     {
797                         s = newInstance();
798                         s.init(_config);
799                     }
800                     catch (ServletException e)
801                     {
802                         throw e;
803                     }
804                     catch (Exception e)
805                     {
806                         throw new ServletException(e);
807                     }
808                 }
809             }
810             
811             try
812             {
813                 s.service(req,res);
814             }
815             finally
816             {
817                 synchronized(this)
818                 {
819                     _stack.push(s);
820                 }
821             }
822         }
823     }
824     
825     /* ------------------------------------------------------------ */
826     /**
827      * @return the newly created Servlet instance
828      * @throws ServletException
829      * @throws IllegalAccessException
830      * @throws InstantiationException
831      */
832     protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException
833     {
834         try
835         {
836             ServletContext ctx = getServletHandler().getServletContext();
837             if (ctx==null)
838                 return getHeldClass().newInstance();
839             return ((ServletContextHandler.Context)ctx).createServlet(getHeldClass());
840         }
841         catch (ServletException se)
842         {
843             Throwable cause = se.getRootCause();
844             if (cause instanceof InstantiationException)
845                 throw (InstantiationException)cause;
846             if (cause instanceof IllegalAccessException)
847                 throw (IllegalAccessException)cause;
848             throw se;
849         }
850     }
851 }
852 
853 
854 
855 
856