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