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