View Javadoc

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