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