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
    18  import java.io.IOException;
    19  import java.util.ArrayList;
    20  import java.util.Arrays;
    21  import java.util.HashMap;
    22  import java.util.List;
    23  import java.util.Map;
    24
    25  import javax.servlet.Filter;
    26  import javax.servlet.FilterChain;
    27  import javax.servlet.RequestDispatcher;
    28  import javax.servlet.Servlet;
    29  import javax.servlet.ServletContext;
    30  import javax.servlet.ServletException;
    31  import javax.servlet.ServletRequest;
    32  import javax.servlet.ServletResponse;
    33  import javax.servlet.UnavailableException;
    34  import javax.servlet.http.HttpServletRequest;
    35  import javax.servlet.http.HttpServletResponse;
    36
    37  import org.mortbay.jetty.EofException;
    38  import org.mortbay.jetty.HttpConnection;
    39  import org.mortbay.jetty.HttpException;
    40  import org.mortbay.jetty.Request;
    41  import org.mortbay.jetty.RetryRequest;
    42  import org.mortbay.jetty.Server;
    43  import org.mortbay.jetty.handler.AbstractHandler;
    44  import org.mortbay.jetty.handler.ContextHandler;
    45  import org.mortbay.log.Log;
    46  import org.mortbay.util.LazyList;
    47  import org.mortbay.util.MultiException;
    48  import org.mortbay.util.MultiMap;
    49  import org.mortbay.util.URIUtil;
    50
    51
    52  /* --------------------------------------------------------------------- */
    53  /** Servlet HttpHandler.
    54   * This handler maps requests to servlets that implement the
    55   * javax.servlet.http.HttpServlet API.
    56   * <P>
    57   * This handler does not implement the full J2EE features and is intended to
    58   * be used when a full web application is not required.  Specifically filters
    59   * and request wrapping are not supported.
    60   *
    61   * Unless run as part of a {@link Context} or derivative, the {@link #initialize()}
    62   * method must be called manually after start().
    63   *
    64   * @see org.mortbay.jetty.webapp.WebAppContext
    65   * @author Greg Wilkins
    66   */
    67  public class ServletHandler extends AbstractHandler
    68  {
    69      /* ------------------------------------------------------------ */
    70      public static final String __DEFAULT_SERVLET="default";
    71      public static final String __J_S_CONTEXT_TEMPDIR="javax.servlet.context.tempdir";
    72      public static final String __J_S_ERROR_EXCEPTION="javax.servlet.error.exception";
    73      public static final String __J_S_ERROR_EXCEPTION_TYPE="javax.servlet.error.exception_type";
    74      public static final String __J_S_ERROR_MESSAGE="javax.servlet.error.message";
    75      public static final String __J_S_ERROR_REQUEST_URI="javax.servlet.error.request_uri";
    76      public static final String __J_S_ERROR_SERVLET_NAME="javax.servlet.error.servlet_name";
    77      public static final String __J_S_ERROR_STATUS_CODE="javax.servlet.error.status_code";
    78
    79      /* ------------------------------------------------------------ */
    80      private ContextHandler _contextHandler;
    81      private ContextHandler.SContext _servletContext;
    82      private FilterHolder[] _filters;
    83      private FilterMapping[] _filterMappings;
    84      private boolean _filterChainsCached=true;
    85      private int _maxFilterChainsCacheSize=1000;
    86      private boolean _startWithUnavailable=true;
    87
    88      private ServletHolder[] _servlets;
    89      private ServletMapping[] _servletMappings;
    90
    91      private transient Map _filterNameMap= new HashMap();
    92      private transient List _filterPathMappings;
    93      private transient MultiMap _filterNameMappings;
    94
    95      private transient Map _servletNameMap=new HashMap();
    96      private transient PathMap _servletPathMap;
    97
    98      protected transient HashMap _chainCache[];
    99
    100
    101     /* ------------------------------------------------------------ */
    102     /** Constructor.
    103      */
    104     public ServletHandler()
    105     {
    106     }
    107
    108     /* ------------------------------------------------------------ */
    109     /*
    110      * @see org.mortbay.jetty.handler.AbstractHandler#setServer(org.mortbay.jetty.Server)
    111      */
    112     public void setServer(Server server)
    113     {
    114         if (getServer()!=null && getServer()!=server)
    115         {
    116             getServer().getContainer().update(this, _filters, null, "filter",true);
    117             getServer().getContainer().update(this, _filterMappings, null, "filterMapping",true);
    118             getServer().getContainer().update(this, _servlets, null, "servlet",true);
    119             getServer().getContainer().update(this, _servletMappings, null, "servletMapping",true);
    120         }
    121         if (server!=null && getServer()!=server)
    122         {
    123             server.getContainer().update(this, null, _filters, "filter",true);
    124             server.getContainer().update(this, null, _filterMappings, "filterMapping",true);
    125             server.getContainer().update(this, null, _servlets, "servlet",true);
    126             server.getContainer().update(this, null, _servletMappings, "servletMapping",true);
    127         }
    128         super.setServer(server);
    129
    130     }
    131
    132     /* ----------------------------------------------------------------- */
    133     protected synchronized void doStart()
    134         throws Exception
    135     {
    136         _servletContext=ContextHandler.getCurrentContext();
    137         _contextHandler=_servletContext==null?null:_servletContext.getContextHandler();
    138
    139         updateNameMappings();
    140         updateMappings();
    141
    142         if(_filterChainsCached)
    143             _chainCache=     new HashMap[]{null,new HashMap(),new HashMap(),null,new HashMap(),null,null,null,new HashMap()};
    144
    145         super.doStart();
    146
    147         if (_contextHandler==null || !(_contextHandler instanceof Context))
    148             initialize();
    149     }
    150
    151     /* ----------------------------------------------------------------- */
    152     protected synchronized void doStop()
    153         throws Exception
    154     {
    155         super.doStop();
    156
    157         // Stop filters
    158         if (_filters!=null)
    159         {
    160             for (int i=_filters.length; i-->0;)
    161             {
    162                 try { _filters[i].stop(); }catch(Exception e){Log.warn(Log.EXCEPTION,e);}
    163             }
    164         }
    165
    166         // Stop servlets
    167         if (_servlets!=null)
    168         {
    169             for (int i=_servlets.length; i-->0;)
    170             {
    171                 try { _servlets[i].stop(); }catch(Exception e){Log.warn(Log.EXCEPTION,e);}
    172             }
    173         }
    174
    175         _filterPathMappings=null;
    176         _filterNameMappings=null;
    177
    178         _servletPathMap=null;
    179         _chainCache=null;
    180     }
    181
    182
    183     /* ------------------------------------------------------------ */
    184     /**
    185      * @return Returns the contextLog.
    186      */
    187     public Object getContextLog()
    188     {
    189         return null;
    190     }
    191     /* ------------------------------------------------------------ */
    192     /**
    193      * @return Returns the filterMappings.
    194      */
    195     public FilterMapping[] getFilterMappings()
    196     {
    197         return _filterMappings;
    198     }
    199
    200     /* ------------------------------------------------------------ */
    201     /** Get Filters.
    202      * @return Array of defined servlets
    203      */
    204     public FilterHolder[] getFilters()
    205     {
    206         return _filters;
    207     }
    208
    209     /* ------------------------------------------------------------ */
    210     /** ServletHolder matching path.
    211      * @param pathInContext Path within _context.
    212      * @return PathMap Entries pathspec to ServletHolder
    213      */
    214     public PathMap.Entry getHolderEntry(String pathInContext)
    215     {
    216         if (_servletPathMap==null)
    217             return null;
    218         return _servletPathMap.getMatch(pathInContext);
    219     }
    220
    221     /* ------------------------------------------------------------ */
    222     /**
    223      * @return A {@link RequestDispatcher dispatcher} wrapping the resource at <code>uriInContext</code>,
    224      *  or <code>null</code> if the specified uri cannot be dispatched to.
    225      */
    226     public RequestDispatcher getRequestDispatcher(String uriInContext)
    227     {
    228         if (uriInContext == null)
    229             return null;
    230
    231         if (!uriInContext.startsWith("/"))
    232             return null;
    233
    234         try
    235         {
    236             String query=null;
    237             int q=0;
    238             if ((q=uriInContext.indexOf('?'))>0)
    239             {
    240                 query=uriInContext.substring(q+1);
    241                 uriInContext=uriInContext.substring(0,q);
    242             }
    243             if ((q=uriInContext.indexOf(';'))>0)
    244                 uriInContext=uriInContext.substring(0,q);
    245
    246             String pathInContext=URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
    247             String uri=URIUtil.addPaths(_contextHandler.getContextPath(), uriInContext);
    248             return new Dispatcher(_contextHandler, uri, pathInContext, query);
    249         }
    250         catch(Exception e)
    251         {
    252             Log.ignore(e);
    253         }
    254         return null;
    255     }
    256
    257     /* ------------------------------------------------------------ */
    258     public ServletContext getServletContext()
    259     {
    260         return _servletContext;
    261     }
    262     /* ------------------------------------------------------------ */
    263     /**
    264      * @return Returns the servletMappings.
    265      */
    266     public ServletMapping[] getServletMappings()
    267     {
    268         return _servletMappings;
    269     }
    270
    271     /* ------------------------------------------------------------ */
    272     /** Get Servlets.
    273      * @return Array of defined servlets
    274      */
    275     public ServletHolder[] getServlets()
    276     {
    277         return _servlets;
    278     }
    279
    280     /* ------------------------------------------------------------ */
    281     public ServletHolder getServlet(String name)
    282     {
    283         return (ServletHolder)_servletNameMap.get(name);
    284     }
    285
    286     /* ------------------------------------------------------------ */
    287     /*
    288      * @see org.mortbay.jetty.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
    289      */
    290     public void handle(String target, HttpServletRequest request,HttpServletResponse response, int type)
    291          throws IOException, ServletException
    292     {
    293         if (!isStarted())
    294             return;
    295
    296         // Get the base requests
    297         final Request base_request=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
    298         final String old_servlet_name=base_request.getServletName();
    299         final String old_servlet_path=base_request.getServletPath();
    300         final String old_path_info=base_request.getPathInfo();
    301         final Map old_role_map=base_request.getRoleMap();
    302
    303         try
    304         {
    305             ServletHolder servlet_holder=null;
    306             FilterChain chain=null;
    307
    308             // find the servlet
    309             if (target.startsWith("/"))
    310             {
    311                 // Look for the servlet by path
    312                 PathMap.Entry entry=getHolderEntry(target);
    313                 if (entry!=null)
    314                 {
    315                     servlet_holder=(ServletHolder)entry.getValue();
    316                     base_request.setServletName(servlet_holder.getName());
    317                     base_request.setRoleMap(servlet_holder.getRoleMap());
    318                     if(Log.isDebugEnabled())Log.debug("servlet="+servlet_holder);
    319
    320                     String servlet_path_spec=(String)entry.getKey();
    321                     String servlet_path=entry.getMapped()!=null?entry.getMapped():PathMap.pathMatch(servlet_path_spec,target);
    322                     String path_info=PathMap.pathInfo(servlet_path_spec,target);
    323
    324                     if (type==INCLUDE)
    325                     {
    326                         base_request.setAttribute(Dispatcher.__INCLUDE_SERVLET_PATH,servlet_path);
    327                         base_request.setAttribute(Dispatcher.__INCLUDE_PATH_INFO, path_info);
    328                     }
    329                     else
    330                     {
    331                         base_request.setServletPath(servlet_path);
    332                         base_request.setPathInfo(path_info);
    333                     }
    334
    335                     if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0)
    336                         chain=getFilterChain(type, target, servlet_holder);
    337                 }
    338             }
    339             else
    340             {
    341                 // look for a servlet by name!
    342                 servlet_holder=(ServletHolder)_servletNameMap.get(target);
    343                 if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0)
    344                 {
    345                     base_request.setServletName(servlet_holder.getName());
    346                     chain=getFilterChain(type, null,servlet_holder);
    347                 }
    348             }
    349
    350             if (Log.isDebugEnabled())
    351             {
    352                 Log.debug("chain="+chain);
    353                 Log.debug("servlet holder="+servlet_holder);
    354             }
    355
    356             // Do the filter/handling thang
    357             if (servlet_holder!=null)
    358             {
    359                 base_request.setHandled(true);
    360                 if (chain!=null)
    361                     chain.doFilter(request, response);
    362                 else
    363                     servlet_holder.handle(request,response);
    364             }
    365             else
    366                 notFound(request, response);
    367         }
    368         catch(RetryRequest e)
    369         {
    370             base_request.setHandled(false);
    371             throw e;
    372         }
    373         catch(EofException e)
    374         {
    375             throw e;
    376         }
    377         catch(Exception e)
    378         {
    379             if (type!=REQUEST)
    380             {
    381                 if (e instanceof IOException)
    382                     throw (IOException)e;
    383                 if (e instanceof RuntimeException)
    384                     throw (RuntimeException)e;
    385                 if (e instanceof ServletException)
    386                     throw (ServletException)e;
    387             }
    388
    389
    390             // unwrap cause
    391             Throwable th=e;
    392             if (th instanceof UnavailableException)
    393             {
    394                 Log.debug(th);
    395             }
    396             else if (th instanceof ServletException)
    397             {
    398                 Log.debug(th);
    399                 Throwable cause=((ServletException)th).getRootCause();
    400                 if (cause!=th && cause!=null)
    401                     th=cause;
    402             }
    403
    404             // hnndle or log exception
    405             if (th instanceof RetryRequest)
    406             {
    407                 base_request.setHandled(false);
    408                 throw (RetryRequest)th;
    409             }
    410             else if (th instanceof HttpException)
    411                 throw (HttpException)th;
    412             else if (Log.isDebugEnabled())
    413             {
    414                 Log.warn(request.getRequestURI(), th);
    415                 Log.debug(request.toString());
    416             }
    417             else if (th instanceof IOException || th instanceof UnavailableException)
    418             {
    419                 Log.warn(request.getRequestURI()+": "+th);
    420             }
    421             else
    422             {
    423                 Log.warn(request.getRequestURI(),th);
    424             }
    425
    426             // TODO httpResponse.getHttpConnection().forceClose();
    427             if (!response.isCommitted())
    428             {
    429                 request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE,th.getClass());
    430                 request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION,th);
    431                 if (th instanceof UnavailableException)
    432                 {
    433                     UnavailableException ue = (UnavailableException)th;
    434                     if (ue.isPermanent())
    435                         response.sendError(HttpServletResponse.SC_NOT_FOUND,th.getMessage());
    436                     else
    437                         response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,th.getMessage());
    438                 }
    439                 else
    440                     response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,th.getMessage());
    441             }
    442             else
    443                 if(Log.isDebugEnabled())Log.debug("Response already committed for handling "+th);
    444         }
    445         catch(Error e)
    446         {
    447             if (type!=REQUEST)
    448                 throw e;
    449             Log.warn("Error for "+request.getRequestURI(),e);
    450             if(Log.isDebugEnabled())Log.debug(request.toString());
    451
    452             // TODO httpResponse.getHttpConnection().forceClose();
    453             if (!response.isCommitted())
    454             {
    455                 request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE,e.getClass());
    456                 request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION,e);
    457                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,e.getMessage());
    458             }
    459             else
    460                 if(Log.isDebugEnabled())Log.debug("Response already committed for handling ",e);
    461         }
    462         finally
    463         {
    464             base_request.setServletName(old_servlet_name);
    465             base_request.setRoleMap(old_role_map);
    466             if (type!=INCLUDE)
    467             {
    468                 base_request.setServletPath(old_servlet_path);
    469                 base_request.setPathInfo(old_path_info);
    470             }
    471         }
    472         return;
    473     }
    474
    475     /* ------------------------------------------------------------ */
    476     private FilterChain getFilterChain(int requestType, String pathInContext, ServletHolder servletHolder)
    477     {
    478         String key=pathInContext==null?servletHolder.getName():pathInContext;
    479
    480         if (_filterChainsCached && _chainCache!=null)
    481         {
    482             synchronized(this)
    483             {
    484                 if(_chainCache[requestType].containsKey(key))
    485                     return (FilterChain)_chainCache[requestType].get(key);
    486             }
    487         }
    488
    489
    490         // Build list of filters
    491         Object filters= null;
    492
    493
    494         // Path filters
    495         if (pathInContext!=null && _filterPathMappings!=null)
    496         {
    497             for (int i= 0; i < _filterPathMappings.size(); i++)
    498             {
    499                 FilterMapping mapping = (FilterMapping)_filterPathMappings.get(i);
    500                 if (mapping.appliesTo(pathInContext, requestType))
    501                     filters= LazyList.add(filters, mapping.getFilterHolder());
    502             }
    503         }
    504
    505         // Servlet name filters
    506         if (servletHolder != null && _filterNameMappings!=null && _filterNameMappings.size() > 0)
    507         {
    508             // Servlet name filters
    509             if (_filterNameMappings.size() > 0)
    510             {
    511                 Object o= _filterNameMappings.get(servletHolder.getName());
    512                 for (int i=0; i<LazyList.size(o);i++)
    513                 {
    514                     FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
    515                     if (mapping.appliesTo(requestType))
    516                         filters=LazyList.add(filters,mapping.getFilterHolder());
    517                 }
    518
    519                 o= _filterNameMappings.get("*");
    520                 for (int i=0; i<LazyList.size(o);i++)
    521                 {
    522                     FilterMapping mapping = (FilterMapping)LazyList.get(o,i);
    523                     if (mapping.appliesTo(requestType))
    524                         filters=LazyList.add(filters,mapping.getFilterHolder());
    525                 }
    526             }
    527         }
    528
    529         if (filters==null)
    530             return null;
    531
    532         FilterChain chain = null;
    533         if (_filterChainsCached)
    534         {
    535             if (LazyList.size(filters) > 0)
    536                 chain= new CachedChain(filters, servletHolder);
    537             synchronized(this)
    538             {
    539                 if (_maxFilterChainsCacheSize>0 && _chainCache[requestType].size()>_maxFilterChainsCacheSize)
    540                     _chainCache[requestType].clear();
    541                 _chainCache[requestType].put(key,chain);
    542             }
    543         }
    544         else if (LazyList.size(filters) > 0)
    545             chain = new Chain(filters, servletHolder);
    546
    547         return chain;
    548     }
    549
    550     /* ------------------------------------------------------------ */
    551     /**
    552      * @return Returns the initializeAtStart.
    553      * @deprecated
    554      */
    555     public boolean isInitializeAtStart()
    556     {
    557         return false;
    558     }
    559
    560     /* ------------------------------------------------------------ */
    561     /**
    562      * @param initializeAtStart The initializeAtStart to set.
    563      * @deprecated
    564      */
    565     public void setInitializeAtStart(boolean initializeAtStart)
    566     {
    567     }
    568
    569     /* ------------------------------------------------------------ */
    570     /**
    571      * @return true if the handler is started and there are no unavailable servlets
    572      */
    573     public boolean isAvailable()
    574     {
    575         if (!isStarted())
    576             return false;
    577         ServletHolder[] holders = getServlets();
    578         for (int i=0;i<holders.length;i++)
    579         {
    580             ServletHolder holder = holders[i];
    581             if (holder!=null && !holder.isAvailable())
    582                 return false;
    583         }
    584         return true;
    585     }
    586
    587     /* ------------------------------------------------------------ */
    588     /**
    589      * @param start True if this handler will start with unavailable servlets
    590      */
    591     public void setStartWithUnavailable(boolean start)
    592     {
    593         _startWithUnavailable=start;
    594     }
    595
    596     /* ------------------------------------------------------------ */
    597     /**
    598      * @return True if this handler will start with unavailable servlets
    599      */
    600     public boolean isStartWithUnavailable()
    601     {
    602         return _startWithUnavailable;
    603     }
    604
    605
    606
    607     /* ------------------------------------------------------------ */
    608     /** Initialize filters and load-on-startup servlets.
    609      * Called automatically from start if autoInitializeServlet is true.
    610      */
    611     public void initialize()
    612         throws Exception
    613     {
    614         MultiException mx = new MultiException();
    615
    616         // Start filters
    617         if (_filters!=null)
    618         {
    619             for (int i=0;i<_filters.length; i++)
    620                 _filters[i].start();
    621         }
    622
    623         if (_servlets!=null)
    624         {
    625             // Sort and Initialize servlets
    626             ServletHolder[] servlets = (ServletHolder[])_servlets.clone();
    627             Arrays.sort(servlets);
    628             for (int i=0; i<servlets.length; i++)
    629             {
    630                 try
    631                 {
    632                     if (servlets[i].getClassName()==null && servlets[i].getForcedPath()!=null)
    633                     {
    634                         ServletHolder forced_holder = (ServletHolder)_servletPathMap.match(servlets[i].getForcedPath());
    635                         if (forced_holder==null || forced_holder.getClassName()==null)
    636                         {
    637                             mx.add(new IllegalStateException("No forced path servlet for "+servlets[i].getForcedPath()));
    638                             continue;
    639                         }
    640                         servlets[i].setClassName(forced_holder.getClassName());
    641                     }
    642
    643                     servlets[i].start();
    644                 }
    645                 catch(Throwable e)
    646                 {
    647                     Log.debug(Log.EXCEPTION,e);
    648                     mx.add(e);
    649                 }
    650             }
    651             mx.ifExceptionThrow();
    652         }
    653     }
    654
    655     /* ------------------------------------------------------------ */
    656     /**
    657      * @return Returns the filterChainsCached.
    658      */
    659     public boolean isFilterChainsCached()
    660     {
    661         return _filterChainsCached;
    662     }
    663
    664     /* ------------------------------------------------------------ */
    665     /**
    666      * @see also newServletHolder(Class)
    667      */
    668     public ServletHolder newServletHolder()
    669     {
    670         return new ServletHolder();
    671     }
    672
    673     /* ------------------------------------------------------------ */
    674     public ServletHolder newServletHolder(Class servlet)
    675     {
    676         return new ServletHolder(servlet);
    677     }
    678
    679     /* ------------------------------------------------------------ */
    680     /** conveniance method to add a servlet.
    681      * @return The servlet holder.
    682      */
    683     public ServletHolder addServletWithMapping (String className,String pathSpec)
    684     {
    685         ServletHolder holder = newServletHolder(null);
    686         holder.setName(className+"-"+holder.hashCode());
    687         holder.setClassName(className);
    688
    689         addServletWithMapping(holder,pathSpec);
    690
    691         return holder;
    692     }
    693
    694     /* ------------------------------------------------------------ */
    695     /** conveniance method to add a servlet.
    696      * @return The servlet holder.
    697      */
    698     public ServletHolder addServletWithMapping (Class servlet,String pathSpec)
    699     {
    700         ServletHolder holder = newServletHolder(servlet);
    701         setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
    702
    703         addServletWithMapping(holder,pathSpec);
    704
    705         return holder;
    706     }
    707
    708     /* ------------------------------------------------------------ */
    709     /** conveniance method to add a servlet.
    710      * @param name
    711      * @param className
    712      * @param pathSpec
    713      * @return The servlet holder.
    714      */
    715     public void addServletWithMapping (ServletHolder servlet,String pathSpec)
    716     {
    717         ServletHolder[] holders=getServlets();
    718         if (holders!=null)
    719             holders = (ServletHolder[])holders.clone();
    720
    721         try
    722         {
    723             setServlets((ServletHolder[])LazyList.addToArray(holders, servlet, ServletHolder.class));
    724
    725             ServletMapping mapping = new ServletMapping();
    726             mapping.setServletName(servlet.getName());
    727             mapping.setPathSpec(pathSpec);
    728             setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class));
    729         }
    730         catch (Exception e)
    731         {
    732             setServlets(holders);
    733             if (e instanceof RuntimeException)
    734                 throw (RuntimeException)e;
    735             throw new RuntimeException(e);
    736         }
    737     }
    738
    739     /* ------------------------------------------------------------ */
    740     /** Convenience method to add a servlet with a servlet mapping.
    741      * @param className
    742      * @param pathSpec
    743      * @return
    744      * @deprecated
    745      */
    746     public ServletHolder addServlet (String className, String pathSpec)
    747     {
    748         return addServletWithMapping (className, pathSpec);
    749     }
    750
    751
    752     /* ------------------------------------------------------------ */
    753     /**Convenience method to add a pre-constructed ServletHolder.
    754      * @param holder
    755      */
    756     public void addServlet(ServletHolder holder)
    757     {
    758         setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
    759     }
    760
    761     /* ------------------------------------------------------------ */
    762     /** Convenience method to add a pre-constructed ServletMapping.
    763      * @param mapping
    764      */
    765     public void addServletMapping (ServletMapping mapping)
    766     {
    767         setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class));
    768     }
    769
    770     /* ------------------------------------------------------------ */
    771     public FilterHolder newFilterHolder(Class filter)
    772     {
    773         return new FilterHolder(filter);
    774     }
    775
    776     /* ------------------------------------------------------------ */
    777     /**
    778      * @see {@link #newFilterHolder(Class)}
    779      */
    780     public FilterHolder newFilterHolder()
    781     {
    782         return new FilterHolder();
    783     }
    784
    785     /* ------------------------------------------------------------ */
    786     public FilterHolder getFilter(String name)
    787     {
    788         return (FilterHolder)_filterNameMap.get(name);
    789     }
    790
    791     /* ------------------------------------------------------------ */
    792     /** conveniance method to add a filter.
    793      * @param name
    794      * @param className
    795      * @param pathSpec
    796      * @param dispatches see {@link FilterMapping#setDispatches(int)}
    797      * @return The filter holder.
    798      */
    799     public FilterHolder addFilterWithMapping (Class filter,String pathSpec,int dispatches)
    800     {
    801         FilterHolder holder = newFilterHolder(filter);
    802         addFilterWithMapping(holder,pathSpec,dispatches);
    803
    804         return holder;
    805     }
    806
    807     /* ------------------------------------------------------------ */
    808     /** conveniance method to add a filter.
    809      * @param name
    810      * @param className
    811      * @param pathSpec
    812      * @param dispatches see {@link FilterMapping#setDispatches(int)}
    813      * @return The filter holder.
    814      */
    815     public FilterHolder addFilterWithMapping (String className,String pathSpec,int dispatches)
    816     {
    817         FilterHolder holder = newFilterHolder(null);
    818         holder.setName(className+"-"+holder.hashCode());
    819         holder.setClassName(className);
    820
    821         addFilterWithMapping(holder,pathSpec,dispatches);
    822         return holder;
    823     }
    824
    825     /* ------------------------------------------------------------ */
    826     /** conveniance method to add a filter.
    827      * @param name
    828      * @param className
    829      * @param pathSpec
    830      * @param dispatches see {@link FilterMapping#setDispatches(int)}
    831      * @return The filter holder.
    832      */
    833     public void addFilterWithMapping (FilterHolder holder,String pathSpec,int dispatches)
    834     {
    835         FilterHolder[] holders = getFilters();
    836         if (holders!=null)
    837             holders = (FilterHolder[])holders.clone();
    838
    839         try
    840         {
    841             setFilters((FilterHolder[])LazyList.addToArray(holders, holder, FilterHolder.class));
    842
    843             FilterMapping mapping = new FilterMapping();
    844             mapping.setFilterName(holder.getName());
    845             mapping.setPathSpec(pathSpec);
    846             mapping.setDispatches(dispatches);
    847             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
    848         }
    849         catch (RuntimeException e)
    850         {
    851             setFilters(holders);
    852             throw e;
    853         }
    854         catch (Error e)
    855         {
    856             setFilters(holders);
    857             throw e;
    858         }
    859
    860     }
    861
    862     /* ------------------------------------------------------------ */
    863     /** Convenience method to add a filter with a mapping
    864      * @param className
    865      * @param pathSpec
    866      * @param dispatches
    867      * @return
    868      * @deprecated
    869      */
    870     public FilterHolder addFilter (String className,String pathSpec,int dispatches)
    871     {
    872         return addFilterWithMapping(className, pathSpec, dispatches);
    873     }
    874
    875     /* ------------------------------------------------------------ */
    876     /**
    877      * convenience method to add a filter and mapping
    878      * @param filter
    879      * @param filterMapping
    880      */
    881     public void addFilter (FilterHolder filter, FilterMapping filterMapping)
    882     {
    883         if (filter != null)
    884             setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class));
    885         if (filterMapping != null)
    886             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), filterMapping, FilterMapping.class));
    887     }
    888
    889     /* ------------------------------------------------------------ */
    890     /** Convenience method to add a preconstructed FilterHolder
    891      * @param filter
    892      */
    893     public void addFilter (FilterHolder filter)
    894     {
    895         if (filter != null)
    896             setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class));
    897     }
    898
    899     /* ------------------------------------------------------------ */
    900     /** Convenience method to add a preconstructed FilterMapping
    901      * @param mapping
    902      */
    903     public void addFilterMapping (FilterMapping mapping)
    904     {
    905         if (mapping != null)
    906             setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
    907     }
    908
    909     /* ------------------------------------------------------------ */
    910     protected synchronized void updateNameMappings()
    911     {
    912         // update filter name map
    913         _filterNameMap.clear();
    914         if (_filters!=null)
    915         {
    916             for (int i=0;i<_filters.length;i++)
    917             {
    918                 _filterNameMap.put(_filters[i].getName(),_filters[i]);
    919                 _filters[i].setServletHandler(this);
    920             }
    921         }
    922
    923         // Map servlet names to holders
    924         _servletNameMap.clear();
    925         if (_servlets!=null)
    926         {
    927             // update the maps
    928             for (int i=0;i<_servlets.length;i++)
    929             {
    930                 _servletNameMap.put(_servlets[i].getName(),_servlets[i]);
    931                 _servlets[i].setServletHandler(this);
    932             }
    933         }
    934     }
    935
    936     /* ------------------------------------------------------------ */
    937     protected synchronized void updateMappings()
    938     {
    939         // update filter mappings
    940         if (_filterMappings==null)
    941         {
    942             _filterPathMappings=null;
    943             _filterNameMappings=null;
    944         }
    945         else
    946         {
    947             _filterPathMappings=new ArrayList();
    948             _filterNameMappings=new MultiMap();
    949             for (int i=0;i<_filterMappings.length;i++)
    950             {
    951                 FilterHolder filter_holder = (FilterHolder)_filterNameMap.get(_filterMappings[i].getFilterName());
    952                 if (filter_holder==null)
    953                     throw new IllegalStateException("No filter named "+_filterMappings[i].getFilterName());
    954                 _filterMappings[i].setFilterHolder(filter_holder);
    955                 if (_filterMappings[i].getPathSpecs()!=null)
    956                     _filterPathMappings.add(_filterMappings[i]);
    957
    958                 if (_filterMappings[i].getServletNames()!=null)
    959                 {
    960                     String[] names=_filterMappings[i].getServletNames();
    961                     for (int j=0;j<names.length;j++)
    962                     {
    963                         if (names[j]!=null)
    964                             _filterNameMappings.add(names[j], _filterMappings[i]);
    965                     }
    966                 }
    967             }
    968         }
    969
    970         // Map servlet paths to holders
    971         if (_servletMappings==null || _servletNameMap==null)
    972         {
    973             _servletPathMap=null;
    974         }
    975         else
    976         {
    977             PathMap pm = new PathMap();
    978
    979             // update the maps
    980             for (int i=0;i<_servletMappings.length;i++)
    981             {
    982                 ServletHolder servlet_holder = (ServletHolder)_servletNameMap.get(_servletMappings[i].getServletName());
    983                 if (servlet_holder==null)
    984                     throw new IllegalStateException("No such servlet: "+_servletMappings[i].getServletName());
    985                 else if (_servletMappings[i].getPathSpecs()!=null)
    986                 {
    987                     String[] pathSpecs = _servletMappings[i].getPathSpecs();
    988                     for (int j=0;j<pathSpecs.length;j++)
    989                         if (pathSpecs[j]!=null)
    990                             pm.put(pathSpecs[j],servlet_holder);
    991                 }
    992             }
    993
    994             _servletPathMap=pm;
    995         }
    996
    997
    998
    999         if (Log.isDebugEnabled())
    1000         {
    1001             Log.debug("filterNameMap="+_filterNameMap);
    1002             Log.debug("pathFilters="+_filterPathMappings);
    1003             Log.debug("servletFilterMap="+_filterNameMappings);
    1004             Log.debug("servletPathMap="+_servletPathMap);
    1005             Log.debug("servletNameMap="+_servletNameMap);
    1006         }
    1007
    1008         try
    1009         {
    1010             if (isStarted())
    1011                 initialize();
    1012         }
    1013         catch (Exception e)
    1014         {
    1015             throw new RuntimeException(e);
    1016         }
    1017     }
    1018
    1019
    1020     /* ------------------------------------------------------------ */
    1021     protected void notFound(HttpServletRequest request,
    1022                   HttpServletResponse response)
    1023         throws IOException
    1024     {
    1025         if(Log.isDebugEnabled())Log.debug("Not Found "+request.getRequestURI());
    1026         response.sendError(HttpServletResponse.SC_NOT_FOUND);
    1027     }
    1028
    1029     /* ------------------------------------------------------------ */
    1030     /**
    1031      * @param filterChainsCached The filterChainsCached to set.
    1032      */
    1033     public void setFilterChainsCached(boolean filterChainsCached)
    1034     {
    1035         _filterChainsCached = filterChainsCached;
    1036     }
    1037
    1038     /* ------------------------------------------------------------ */
    1039     /**
    1040      * @param filterMappings The filterMappings to set.
    1041      */
    1042     public void setFilterMappings(FilterMapping[] filterMappings)
    1043     {
    1044         if (getServer()!=null)
    1045             getServer().getContainer().update(this,_filterMappings,filterMappings,"filterMapping",true);
    1046         _filterMappings = filterMappings;
    1047         updateMappings();
    1048     }
    1049
    1050     /* ------------------------------------------------------------ */
    1051     public synchronized void setFilters(FilterHolder[] holders)
    1052     {
    1053         if (getServer()!=null)
    1054             getServer().getContainer().update(this,_filters,holders,"filter",true);
    1055         _filters=holders;
    1056         updateNameMappings();
    1057     }
    1058
    1059     /* ------------------------------------------------------------ */
    1060     /**
    1061      * @param servletMappings The servletMappings to set.
    1062      */
    1063     public void setServletMappings(ServletMapping[] servletMappings)
    1064     {
    1065         if (getServer()!=null)
    1066             getServer().getContainer().update(this,_servletMappings,servletMappings,"servletMapping",true);
    1067         _servletMappings = servletMappings;
    1068         updateMappings();
    1069     }
    1070
    1071     /* ------------------------------------------------------------ */
    1072     /** Set Servlets.
    1073      * @param holders Array of servletsto define
    1074      */
    1075     public synchronized void setServlets(ServletHolder[] holders)
    1076     {
    1077         if (getServer()!=null)
    1078             getServer().getContainer().update(this,_servlets,holders,"servlet",true);
    1079         _servlets=holders;
    1080         updateNameMappings();
    1081     }
    1082
    1083
    1084     /* ------------------------------------------------------------ */
    1085     /* ------------------------------------------------------------ */
    1086     private class CachedChain implements FilterChain
    1087     {
    1088         FilterHolder _filterHolder;
    1089         CachedChain _next;
    1090         ServletHolder _servletHolder;
    1091
    1092         /* ------------------------------------------------------------ */
    1093         CachedChain(Object filters, ServletHolder servletHolder)
    1094         {
    1095             if (LazyList.size(filters)>0)
    1096             {
    1097                 _filterHolder=(FilterHolder)LazyList.get(filters, 0);
    1098                 filters=LazyList.remove(filters,0);
    1099                 _next=new CachedChain(filters,servletHolder);
    1100             }
    1101             else
    1102                 _servletHolder=servletHolder;
    1103         }
    1104
    1105         /* ------------------------------------------------------------ */
    1106         public void doFilter(ServletRequest request, ServletResponse response)
    1107             throws IOException, ServletException
    1108         {
    1109             // pass to next filter
    1110             if (_filterHolder!=null)
    1111             {
    1112                 if (Log.isDebugEnabled())
    1113                     Log.debug("call filter " + _filterHolder);
    1114                 Filter filter= _filterHolder.getFilter();
    1115                 filter.doFilter(request, response, _next);
    1116                 return;
    1117             }
    1118
    1119             // Call servlet
    1120             if (_servletHolder != null)
    1121             {
    1122                 if (Log.isDebugEnabled())
    1123                     Log.debug("call servlet " + _servletHolder);
    1124                 _servletHolder.handle(request, response);
    1125             }
    1126             else // Not found
    1127                 notFound((HttpServletRequest)request, (HttpServletResponse)response);
    1128         }
    1129
    1130         public String toString()
    1131         {
    1132             if (_filterHolder!=null)
    1133                 return _filterHolder+"->"+_next.toString();
    1134             if (_servletHolder!=null)
    1135                 return _servletHolder.toString();
    1136             return "null";
    1137         }
    1138     }
    1139
    1140     /* ------------------------------------------------------------ */
    1141     /* ------------------------------------------------------------ */
    1142     private class Chain implements FilterChain
    1143     {
    1144         int _filter= 0;
    1145         Object _chain;
    1146         ServletHolder _servletHolder;
    1147
    1148         /* ------------------------------------------------------------ */
    1149         Chain(Object filters, ServletHolder servletHolder)
    1150         {
    1151             _chain= filters;
    1152             _servletHolder= servletHolder;
    1153         }
    1154
    1155         /* ------------------------------------------------------------ */
    1156         public void doFilter(ServletRequest request, ServletResponse response)
    1157             throws IOException, ServletException
    1158         {
    1159             if (Log.isDebugEnabled()) Log.debug("doFilter " + _filter);
    1160
    1161             // pass to next filter
    1162             if (_filter < LazyList.size(_chain))
    1163             {
    1164                 FilterHolder holder= (FilterHolder)LazyList.get(_chain, _filter++);
    1165                 if (Log.isDebugEnabled()) Log.debug("call filter " + holder);
    1166                 Filter filter= holder.getFilter();
    1167                 filter.doFilter(request, response, this);
    1168                 return;
    1169             }
    1170
    1171             // Call servlet
    1172             if (_servletHolder != null)
    1173             {
    1174                 if (Log.isDebugEnabled()) Log.debug("call servlet " + _servletHolder);
    1175                 _servletHolder.handle(request, response);
    1176             }
    1177             else // Not found
    1178                 notFound((HttpServletRequest)request, (HttpServletResponse)response);
    1179         }
    1180
    1181         /* ------------------------------------------------------------ */
    1182         public String toString()
    1183         {
    1184             StringBuffer b = new StringBuffer();
    1185             for (int i=0; i<LazyList.size(_chain);i++)
    1186             {
    1187                 b.append(LazyList.get(_chain, i).toString());
    1188                 b.append("->");
    1189             }
    1190             b.append(_servletHolder);
    1191             return b.toString();
    1192         }
    1193     }
    1194
    1195     /* ------------------------------------------------------------ */
    1196     /**
    1197      * @return The maximum entries in a filter chain cache.
    1198      */
    1199     public int getMaxFilterChainsCacheSize()
    1200     {
    1201         return _maxFilterChainsCacheSize;
    1202     }
    1203
    1204     /* ------------------------------------------------------------ */
    1205     /** Set the maximum filter chain cache size.
    1206      * Filter chains are cached if {@link #isFilterChainsCached()} is true. If the max cache size
    1207      * is greater than zero, then the cache is flushed whenever it grows to be this size.
    1208      *
    1209      * @param maxFilterChainsCacheSize  the maximum number of entries in a filter chain cache.
    1210      */
    1211     public void setMaxFilterChainsCacheSize(int maxFilterChainsCacheSize)
    1212     {
    1213         _maxFilterChainsCacheSize = maxFilterChainsCacheSize;
    1214     }
    1215
    1216     /**
    1217      * Customize a servlet.
    1218      *
    1219      * Called before the servlet goes into service.
    1220      * Subclasses of ServletHandler should override
    1221      * this method.
    1222      *
    1223      * @param servlet
    1224      * @return
    1225      * @throws Exception
    1226      */
    1227     public Servlet customizeServlet (Servlet servlet)
    1228     throws Exception
    1229     {
    1230         return servlet;
    1231     }
    1232
    1233
    1234     public Servlet customizeServletDestroy (Servlet servlet)
    1235     throws Exception
    1236     {
    1237         return servlet;
    1238     }
    1239
    1240
    1241     /**
    1242      * Customize a Filter.
    1243      *
    1244      * Called before the Filter goes into service.
    1245      * Subclasses of ServletHandler should override
    1246      * this method.
    1247      *
    1248      * @param filter
    1249      * @return
    1250      * @throws Exception
    1251      */
    1252     public Filter customizeFilter (Filter filter)
    1253     throws Exception
    1254     {
    1255         return filter;
    1256     }
    1257
    1258
    1259     public Filter customizeFilterDestroy (Filter filter)
    1260     throws Exception
    1261     {
    1262         return filter;
    1263     }
    1264 }