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     /** Get the servlet instance (no initialization done).
    345      * @return The servlet or null
    346      */
    347     public Servlet getServletInstance()
    348     {
    349         return _servlet;
    350     }
    351
    352     /* ------------------------------------------------------------ */
    353     /**
    354      * Check to ensure class of servlet is acceptable.
    355      * @throws UnavailableException
    356      */
    357     public void checkServletType ()
    358         throws UnavailableException
    359     {
    360         if (!javax.servlet.Servlet.class.isAssignableFrom(_class))
    361         {
    362             throw new UnavailableException("Servlet "+_class+" is not a javax.servlet.Servlet");
    363         }
    364     }
    365
    366     /* ------------------------------------------------------------ */
    367     /**
    368      * @return true if the holder is started and is not unavailable
    369      */
    370     public boolean isAvailable()
    371     {
    372         if (isStarted()&& _unavailable==0)
    373             return true;
    374         try
    375         {
    376             getServlet();
    377         }
    378         catch(Exception e)
    379         {
    380             Log.ignore(e);
    381         }
    382
    383         return isStarted()&& _unavailable==0;
    384     }
    385
    386     /* ------------------------------------------------------------ */
    387     private void makeUnavailable(UnavailableException e)
    388     {
    389         if (_unavailableEx==e && _unavailable!=0)
    390             return;
    391
    392         _servletHandler.getServletContext().log("Unavailable "+e);
    393
    394         _unavailableEx=e;
    395         _unavailable=-1;
    396         if (e.isPermanent())
    397             _unavailable=-1;
    398         else
    399         {
    400             if (_unavailableEx.getUnavailableSeconds()>0)
    401                 _unavailable=System.currentTimeMillis()+1000*_unavailableEx.getUnavailableSeconds();
    402             else
    403                 _unavailable=System.currentTimeMillis()+5000; // TODO configure
    404         }
    405     }
    406
    407     /* ------------------------------------------------------------ */
    408     private void makeUnavailable(Throwable e)
    409     {
    410         if (e instanceof UnavailableException)
    411             makeUnavailable((UnavailableException)e);
    412         else
    413         {
    414             _servletHandler.getServletContext().log("unavailable",e);
    415             _unavailableEx=new UnavailableException(e.toString(),-1);
    416             _unavailable=-1;
    417         }
    418     }
    419
    420     /* ------------------------------------------------------------ */
    421     private void initServlet()
    422     	throws ServletException
    423     {
    424         Principal user=null;
    425         try
    426         {
    427             if (_servlet==null)
    428                 _servlet=(Servlet)newInstance();
    429             if (_config==null)
    430                 _config=new Config();
    431
    432             //handle any cusomizations of the servlet, such as @postConstruct
    433             if (!(_servlet instanceof SingleThreadedWrapper))
    434                 _servlet = getServletHandler().customizeServlet(_servlet);
    435
    436             // Handle run as
    437             if (_runAs!=null && _realm!=null)
    438                 user=_realm.pushRole(null,_runAs);
    439
    440             _servlet.init(_config);
    441         }
    442         catch (UnavailableException e)
    443         {
    444             makeUnavailable(e);
    445             _servlet=null;
    446             _config=null;
    447             throw e;
    448         }
    449         catch (ServletException e)
    450         {
    451             makeUnavailable(e.getCause()==null?e:e.getCause());
    452             _servlet=null;
    453             _config=null;
    454             throw e;
    455         }
    456         catch (Exception e)
    457         {
    458             makeUnavailable(e);
    459             _servlet=null;
    460             _config=null;
    461             throw new ServletException(e);
    462         }
    463         finally
    464         {
    465             // pop run-as role
    466             if (_runAs!=null && _realm!=null && user!=null)
    467                 _realm.popRole(user);
    468         }
    469     }
    470
    471     /* ------------------------------------------------------------ */
    472     /** Service a request with this servlet.
    473      */
    474     public void handle(ServletRequest request,
    475                        ServletResponse response)
    476         throws ServletException,
    477                UnavailableException,
    478                IOException
    479     {
    480         if (_class==null)
    481             throw new UnavailableException("Servlet Not Initialized");
    482
    483         Servlet servlet=_servlet;
    484         synchronized(this)
    485         {
    486             if (_unavailable!=0 || !_initOnStartup)
    487                 servlet=getServlet();
    488             if (servlet==null)
    489                 throw new UnavailableException("Could not instantiate "+_class);
    490         }
    491
    492         // Service the request
    493         boolean servlet_error=true;
    494         Principal user=null;
    495         Request base_request=null;
    496         try
    497         {
    498             // Handle aliased path
    499             if (_forcedPath!=null)
    500                 // TODO complain about poor naming to the Jasper folks
    501                 request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
    502
    503             // Handle run as
    504             if (_runAs!=null && _realm!=null)
    505             {
    506                 base_request=HttpConnection.getCurrentConnection().getRequest();
    507                 user=_realm.pushRole(base_request.getUserPrincipal(),_runAs);
    508                 base_request.setUserPrincipal(user);
    509             }
    510
    511             servlet.service(request,response);
    512             servlet_error=false;
    513         }
    514         catch(UnavailableException e)
    515         {
    516             makeUnavailable(e);
    517             throw _unavailableEx;
    518         }
    519         finally
    520         {
    521             // pop run-as role
    522             if (_runAs!=null && _realm!=null && user!=null && base_request!=null)
    523             {
    524                 user=_realm.popRole(user);
    525                 base_request.setUserPrincipal(user);
    526             }
    527
    528             // Handle error params.
    529             if (servlet_error)
    530                 request.setAttribute("javax.servlet.error.servlet_name",getName());
    531         }
    532     }
    533
    534
    535     /* ------------------------------------------------------------ */
    536     /* ------------------------------------------------------------ */
    537     /* ------------------------------------------------------------ */
    538     class Config implements ServletConfig
    539     {
    540         /* -------------------------------------------------------- */
    541         public String getServletName()
    542         {
    543             return getName();
    544         }
    545
    546         /* -------------------------------------------------------- */
    547         public ServletContext getServletContext()
    548         {
    549             return _servletHandler.getServletContext();
    550         }
    551
    552         /* -------------------------------------------------------- */
    553         public String getInitParameter(String param)
    554         {
    555             return ServletHolder.this.getInitParameter(param);
    556         }
    557
    558         /* -------------------------------------------------------- */
    559         public Enumeration getInitParameterNames()
    560         {
    561             return ServletHolder.this.getInitParameterNames();
    562         }
    563     }
    564
    565     /* -------------------------------------------------------- */
    566     /* -------------------------------------------------------- */
    567     /* -------------------------------------------------------- */
    568     private class SingleThreadedWrapper implements Servlet
    569     {
    570         Stack _stack=new Stack();
    571
    572         public void destroy()
    573         {
    574             synchronized(this)
    575             {
    576                 while(_stack.size()>0)
    577                     try { ((Servlet)_stack.pop()).destroy(); } catch (Exception e) { Log.warn(e); }
    578             }
    579         }
    580
    581         public ServletConfig getServletConfig()
    582         {
    583             return _config;
    584         }
    585
    586         public String getServletInfo()
    587         {
    588             return null;
    589         }
    590
    591         public void init(ServletConfig config) throws ServletException
    592         {
    593             synchronized(this)
    594             {
    595                 if(_stack.size()==0)
    596                 {
    597                     try
    598                     {
    599                         Servlet s = (Servlet) newInstance();
    600                         s = getServletHandler().customizeServlet(s);
    601                         s.init(config);
    602                         _stack.push(s);
    603                     }
    604                     catch (ServletException e)
    605                     {
    606                         throw e;
    607                     }
    608                     catch (Exception e)
    609                     {
    610                         throw new ServletException(e);
    611                     }
    612                 }
    613             }
    614         }
    615
    616         public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
    617         {
    618             Servlet s;
    619             synchronized(this)
    620             {
    621                 if(_stack.size()>0)
    622                     s=(Servlet)_stack.pop();
    623                 else
    624                 {
    625                     try
    626                     {
    627                         s = (Servlet) newInstance();
    628                         s = getServletHandler().customizeServlet(s);
    629                         s.init(_config);
    630                     }
    631                     catch (ServletException e)
    632                     {
    633                         throw e;
    634                     }
    635                     catch (IOException e)
    636                     {
    637                         throw e;
    638                     }
    639                     catch (Exception e)
    640                     {
    641                         throw new ServletException(e);
    642                     }
    643                 }
    644             }
    645
    646             try
    647             {
    648                 s.service(req,res);
    649             }
    650             finally
    651             {
    652                 synchronized(this)
    653                 {
    654                     _stack.push(s);
    655                 }
    656             }
    657         }
    658
    659     }
    660 }
    661
    662
    663
    664
    665