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