You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/05/02 01:15:16 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/request AccessInterceptor.java SessionInterceptor.java SimpleMapper1.java

costin      00/05/01 16:15:16

  Modified:    src/share/org/apache/tomcat/request SessionInterceptor.java
                        SimpleMapper1.java
  Added:       src/share/org/apache/tomcat/request AccessInterceptor.java
  Log:
  - moved all session-related code from Mapper to SessionInterceptor (
  no longer need to remove the session ID from the request in mapper).
  Need to change server.xml ( SessionInterceptor have to be called before mapper,
  and any other interceptor that will change the URL before context map ).
  
  - SimpleMapper1 - a lot of cleanup, documentation. Authorization is no longer
  part of this - it will have it's own interceptor. It involves the
  same steps ( pattern matching) and it's more efficient to do that in one
  step instead of duplicating the code, but I think it's safer to
  separate them. Probably after security code is stable we can do
   all pattern matching in one step.
  
  Revision  Changes    Path
  1.16      +50 -10    jakarta-tomcat/src/share/org/apache/tomcat/request/SessionInterceptor.java
  
  Index: SessionInterceptor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/request/SessionInterceptor.java,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- SessionInterceptor.java	2000/03/21 01:27:08	1.15
  +++ SessionInterceptor.java	2000/05/01 23:15:16	1.16
  @@ -95,11 +95,51 @@
   	this.cm=cm;
       }
   
  +    /** Extract the session id from the request.
  +     * SessionInterceptor will have to be called _before_ mapper,
  +     * to avoid coding session stuff inside the mapper.
  +     *
  +     * When we fix the interceptors we'll have to specify something
  +     * similar with the priority in apache hooks, right now it's just
  +     * a config issue.
  +     */
  +    public int contextMap(Request request ) {
  +	if( request.getRequestedSessionId() != null ) {
  +	    // probably Apache already did that for us
  +	    return 0;
  +	}
  +
  +	// fix URL rewriting
  +	String sig=";jsessionid=";
  +	int foundAt=-1;
  +	String uri=request.getRequestURI();
  +	String sessionId;
  +	
  +	if ((foundAt=uri.indexOf(sig))!=-1){
  +	    sessionId=uri.substring(foundAt+sig.length()); // I hope the optimizer does it's job:-)
  +
  +	    // rewrite URL, do I need to do anything more?
  +	    request.setRequestURI(uri.substring(0, foundAt));
  +
  +	    // No validate now - we just note that this is what the user
  +	    // requested. 
  +	    request.setRequestedSessionIdFromURL(true);
  +	    request.setRequestedSessionId( sessionId );
  +	}
  +	return 0;
  +    }
  +
  +    /** This happens after context map, so we know the context.
  +     *  We can probably do it later too.
  +     */
       public int requestMap(Request request ) {
   	String sessionId = null;
   
   	Cookie cookies[]=request.getCookies(); // assert !=null
  +	boolean fromCookie=false;
   	
  +	// Give priority to cookies. I don't know if that's part
  +	// of the spec - XXX
   	for( int i=0; i<cookies.length; i++ ) {
   	    Cookie cookie = cookies[i];
   	    
  @@ -108,20 +148,21 @@
   		sessionId=validateSessionId(request, sessionId);
   		if (sessionId!=null){
   		    request.setRequestedSessionIdFromCookie(true);
  +		    fromCookie=true;
   		}
   	    }
   	}
  -	
  -	String sig=";jsessionid=";
  -	int foundAt=-1;
  -	if( debug>0 ) cm.log(" XXX RURI=" + request.getRequestURI());
  -	if ((foundAt=request.getRequestURI().indexOf(sig))!=-1){
  -	    sessionId=request.getRequestURI().substring(foundAt+sig.length());
  -	    // rewrite URL, do I need to do anything more?
  -	    request.setRequestURI(request.getRequestURI().substring(0, foundAt));
  +
  +	if( ! fromCookie ) {
  +	    // we don't have the session id from cookie, maybe URL rewriting
  +	    // was used ?
  +	    sessionId=request.getRequestedSessionId();
   	    sessionId=validateSessionId(request, sessionId);
   	    if (sessionId!=null){
  -		request.setRequestedSessionIdFromURL(true);
  +		// it's already done in contextMap
  +		// request.setRequestedSessionIdFromURL(true);
  +		// set it with load balancing removed
  +		request.setRequestedSessionId( sessionId );
   	    }
   	}
   	return 0;
  @@ -163,7 +204,6 @@
   	return null;
       }
     
  -
   
       public int beforeBody( Request rrequest, Response response ) {
       	String reqSessionId = response.getSessionId();
  
  
  
  1.4       +87 -312   jakarta-tomcat/src/share/org/apache/tomcat/request/SimpleMapper1.java
  
  Index: SimpleMapper1.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/request/SimpleMapper1.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- SimpleMapper1.java	2000/04/26 18:57:36	1.3
  +++ SimpleMapper1.java	2000/05/01 23:15:16	1.4
  @@ -66,72 +66,42 @@
   import javax.servlet.http.*;
   import java.util.*;
   
  -/** Parse request URI and find ContextPath, ServletPath, PathInfo and QueryString
  - *  This interceptor is an adapter between tomcat and a parsing/matching engine
  - *  ( the mapper ). Use normal OO programming to reuse and extend - you can
  - *  either start a completely new mapping interceptor or use parts of this
  - *  one, and you can use the simple mapper or a better one.
  +/**
  + *  This class will set up the data structures used by a simple patern matching
  + *  alghoritm and use it to extract the path components from the request URI.
    *
  - *  This class contains mostly support functions and implements a "bridge"
  - *  pattern. A different bridge can be used - but try to keep the same
  - *  semantics ( i.e. allow run-time changes )
  - *
  + *  The interceptor will be called in standalone case, for "integrated" mode
  + *  we should have all the data from the web server - that means the performance
  + *  of this code is not relevant for production mode if a web server is used.
  + * 
    *  This particular implementation does the following:
  - *  - avoid mapping if someone already did that ( web server )
    *  - extract the information that is relevant to matching from the Request object.
    *    The current implementation deals with the Host header and the request URI.
  - *    If you want more complex rules - you'll need to either extend this interceptor
  - *    and use more of info from request ( IP, etc) OR create a more specialized mapper.
  - *  - Represent the information in the format understood by mapper and call the
  - *    right mapping methods in order to respect the servlet specs.
  - *
  - *  The mapper we use is a generic matching alghoritm - we feed it with CharChunks (
  - *   in order to avoid String garbage ). This is a generic problem - probably
  - *   xalan have similar needs, so optimizations will be generally usefull.
  - *
  - *  This also prepare us to deal with Adapters that are more recylcing-friendly, and
  - *  work with char[] instead of Strings.
  + *  - Use an external mapper to find the best match.
  + *  - Adjust the request paths
  + * 
  + *  The execution time is proportional with the number of hosts, number of context, number of
  + *  mappings and with the length of the request.
    *
  - *  A very interesting experiment would be to use a RE package instead of our simple
  - *  mapper - of course req-exp mapping is not supported by servlets, but using a real
  - *  RE engine even for the simple mapping rules defined in the API may provide benefits.
  - *  Again - it's the interceptor responsability to feed the right information and regexps
  - *  to  RE engine, in order to implement the spec as is, without extensions.
  + *  Security mappings are more complex ( method, transport are also part of the
  + *  matching ). We can share the same mapping alghoritm or even the mapper - but
  + *  until security code will be stable it's better to keep it separated.
    *  
  - *  Another interesting experiment is re-using the matching code in XPath/XSL - again
  - *  it's a much wider set of rules, but that also mean they are forced to be very
  - *  aggressive in optimizations and we can reuse more code.
    */
   public class SimpleMapper1 extends  BaseInterceptor  {
       int debug=0;
       ContextManager cm;
  -    Mappings map;
  -    Hashtable vhostMaps=new Hashtable();
  +
  +    PrefixMapper map;
  +
  +    // We store the extension maps as per/context notes.
       int ctExtMapNote=-1;
  +
  +    // Property for the PrefixMapper - cache the mapping results
       boolean mapCacheEnabled=false;
       
  -    // Cache the most recent mappings
  -    // Disabled by default ( since we haven't implemented
  -    // capacity and remove ). 
  -    SimpleHashtable mapCache=new SimpleHashtable();
  -    // By using TreeMap instead of SimpleMap you go from 143 to 161 RPS
  -    // ( at least on my machine )
  -    // Interesting - even if SimpleHashtable is faster than Hashtable
  -    // most of the time, the average is very close for both - it seems
  -    // that while the synchronization in Hashtable is locking, GC have
  -    // a chance to work, while in SimpleHashtable case GC creates big
  -    // peeks. That will go away with more reuse, so we should use SH.
  -
  -    // An alternative to explore after everything works is to use specialized
  -    // mappers ( extending this one for example ) using 1.2 collections
  -    // TreeMap mapCache;
  -    int capacity;
  -    int currentL;
  -
       public SimpleMapper1() {
  -	map=new Mappings();
  -	mapCache=new SimpleHashtable();
  -	//	mapCache=new TreeMap();
  +	map=new PrefixMapper();
       }
   
       /* -------------------- Support functions -------------------- */
  @@ -147,77 +117,36 @@
   	    cm.log( msg );
       }
   
  +    /** Allow the mapper to cache mapping results - resulting in a
  +     *  faster match for frequent requests. ( treat this as experimental)
  +     */
       public void setMapCache( boolean v ) {
   	mapCacheEnabled = v;
  +	map.setMapCache( v );
       }
   
       /* -------------------- Initialization -------------------- */
       
  -    /** In normal operation - it will do nothing but set cm.
  -	If you add the interceptor at run-time ( is it usefull ??)
  -	this will also set it up with the current config from other components.
  -	( the feature was never tested ).
  -    */
  +    /** Set the context manager. To keep it simple we don't support
  +     *  dynamic add/remove for this interceptor. 
  +     */
       public void setContextManager( ContextManager cm ) {
   	this.cm=cm;
  -	// set-up a per/container note - will be used to keep private
  -	// data for this object.
  +	
  +	// set-up a per/container note for maps
   	try {
  -	    ctExtMapNote = cm.getNoteId( ContextManager.CONTAINER_NOTE, "Extension maps");
  +	    ctExtMapNote = cm.getNoteId( ContextManager.CONTAINER_NOTE, "map.extension");
   	} catch( TomcatException ex ) {
   	    ex.printStackTrace();
   	    throw new RuntimeException( "Invalid state ");
   	}
  -	// now "simulate" all the callbacks that we missed by
  -	// beeing late
  -	
  -	// Add all context that are set in CM
  -	Enumeration enum=cm.getContexts();
  -	while( enum.hasMoreElements() ) {
  -	    
  -	    try {
  -		Context ctx=(Context)enum.nextElement();
  -		addContext( cm, ctx );
  -	    } catch (TomcatException ex ) {
  -		ex.printStackTrace();
  -	    }
  -	}
       }
   
  -    /** Called when a context is added - it may have all the mappings already
  -	in, so we need to add them too. Most of the time ( i.e. in normal
  -	operation ) this is a no-op, but we want to support run-time
  -	changes in interceptors and contexts.
  +    /** Called when a context is added.
        */
       public void addContext( ContextManager cm, Context ctx ) throws TomcatException
       {
  -	// find all the mappings that are declared in context,
  -	// and register them.
  -	
  -	// this is called when the interceptor is added and
  -	// we have pre-set mappings - not very common 
  -	// or tested. Normal operation is to set up tomcat
  -	// and the interceptors and then add the contexts.
  -	String vhost=ctx.getHost();
  -	if( vhost == null ) {
  -	    map.prefixMappedServlets.put( ctx.getPath(), ctx.getContainer());
  -        }  else {
  -	    Mappings vmap=(Mappings)vhostMaps.get( vhost );
  -	    if( vmap == null ) {
  -		vmap=new Mappings();
  -		vhostMaps.put( vhost, vmap );
  -	    }
  -	    vmap.prefixMappedServlets.put( ctx.getPath(), ctx.getContainer());
  -	}
  -
  -	if(debug>0) log( "SM: default map " + vhost +":" + ctx.getPath() + " -> " + ctx.getContainer() );
  -	
  -	Enumeration ctE=ctx.getContainers();
  -	while( ctE.hasMoreElements() ) {
  -	    // the internal method to add this (existing)
  -	    // mapping.
  -	    addContainer( (Container)ctE.nextElement() );
  -	}
  +	map.addMapping( ctx.getHost(), ctx.getPath(), ctx.getContainer());
       }
   
       /** Called when a context is removed from a CM - we must ask the mapper to
  @@ -226,14 +155,10 @@
       public void removeContext( ContextManager cm, Context ctx ) throws TomcatException
       {
   	if(debug>0) log( "Removed from maps ");
  -
  -	// Remove all mappings associated with this context ( including the default
  -	// servlet associated with the context container
  -	
  -	// XXX specific to internal representation !!!
  -	String ctxP=ctx.getPath();
  +	map.removeAllMappings( ctx.getHost(), ctx.getPath());
  +	// extension mappings are local to ctx, no need to do something about that
       }
  -
  +    
   
       /**
        * Associate URL pattern  to a set of propreties.
  @@ -254,20 +179,16 @@
   	String path=ct.getPath();
   	String ctxP=ctx.getPath();
   
  -	Mappings myMap;
  -	if( vhost==null )
  -	    myMap=map; // global contexs
  -	else
  -	    myMap=(Mappings)vhostMaps.get( vhost );
  -	// assert myMap!= null ( we just added the context, so the map is there
  -
  -	String vhostS=( vhost==null )? "": vhost;
  +	if(ct.getHandler() == null ) {
  +	    // it was only a security map, no handler defined
  +	    return;
  +	}
   	
   	switch( ct.getMapType() ) {
   	case Container.PREFIX_MAP:
  -	    // cut /* !
  -	    myMap.prefixMappedServlets.put( ctxP + path.substring( 0, path.length()-2 ), ct);
  -	    if( debug>0 ) log("SM: prefix map " + vhostS + ":" +  ctxP + path + " -> " + ct + " " );
  +	    // cut /* ( no need to do a string concat for every match )
  +	    map.addMapping( vhost, ctxP + path.substring( 0, path.length()-2 ), ct);
  +	    if( debug>0 ) log("SM: prefix map " + vhost + ":" +  ctxP + path + " -> " + ct + " " );
   	    break;
   	case Container.EXTENSION_MAP:
   	    // Add it per/defaultContainer - as spec require ( it may also be
  @@ -286,12 +207,14 @@
   	    if(debug>0) log( "SM: extension map " + ctxP + "/" + path + " " + ct + " " );
   	    break;
   	case Container.PATH_MAP:
  -	    myMap.prefixMappedServlets.put( ctxP + path, ct);
  -	    if( debug>0 ) log("SM: exact map " + vhostS + ":" + ctxP + path + " -> " + ct + " " );
  +	    map.addExactMapping( vhost, ctxP + path, ct);
  +	    if( debug>0 ) log("SM: exact map " + vhost + ":" + ctxP + path + " -> " + ct + " " );
   	    break;
   	}
       }
   
  +    // XXX not implemented - will deal with that after everything else works.
  +    // Remove context will still work
       public void removeContainer( Container ct )
   	throws TomcatException
       {
  @@ -307,79 +230,47 @@
   
   
       /** First step of request porcessing is finding the Context.
  -     *  Advanced mappers will do only one parsing.
        */
       public int contextMap( Request req ) {
   	String path = req.getRequestURI();
   	if( path==null) throw new RuntimeException("ASSERT: null path in request URI");
   	if( path.indexOf("?") >=0 ) throw new RuntimeException("ASSERT: ? in requestURI");
   	
  -	// strip session URL rewrite part which interferes processing
  -	// XXX works only if ;jsessionid= is path param for the last component
  -	// of the path! 
  -	String sig=";jsessionid=";
  -	int foundAt=-1;
  -	if ((foundAt=path.indexOf(sig))!=-1){
  -	    path=path.substring(0, foundAt);  
  -	}
  -
  -	String host=req.getServerName();
  -	cm.log("Host = " + host);
  -
  -	// try to find a vhost with this name
  -	Mappings myMap=(Mappings)vhostMaps.get( host );
  -	if( myMap==null ) myMap = map; // default server
  -	
   	try {
  +	    String host=req.getServerName();
  +	    if(debug>0) cm.log("Host = " + host);
   
  -	Context ctx = null;
  -	Mappings mapC=null;
  -	Container container = null;
  -	boolean cached=false;
  -	
  -	if( mapCacheEnabled ) {
  -	    container=(Container)getCachedResult( host + ":" + path );//XXX remove strings!
  -	    if( container != null ) {
  -		cached=true;
  -		if(debug>0) log( "CM: cache hit " + path);
  -	    }
  -	}
  -	
  -	if( ! cached )
  -	    container=(Container)myMap.getLongestPrefixMatch(  path );
  -	
  -	if( container == null ) throw new RuntimeException( "Assertion failed - container==null");
  -	if( container.getHandler() == null ) throw new RuntimeException( "Assertion failed - container.handler==null");
  -	
  -	if(debug>0) cm.log("SM: Prefix match " + path + " -> " + container.getPath() + " " +
  -			   container.getHandler()  + " " + container.getRoles());
  -
  -	// Once - adjust for prefix and context path
  -	// If cached - we don't need to do it again ( since it is the final Container,
  -	// either prefix or extension )
  -	fixRequestPaths( path, req, container );
  -	
  +	    Context ctx = null;
  +	    Container container =(Container)map.getLongestPrefixMatch(  host, path );
  +	    
  +	    if( container == null ) throw new RuntimeException( "Assertion failed - container==null");
  +	    if( container.getHandler() == null ) throw new RuntimeException( "Assertion failed - container.handler==null");
  +	    
  +	    if(debug>0) cm.log("SM: Prefix match " + path + " -> " + container.getPath() + " " +
  +			       container.getHandler()  + " " + container.getRoles());
   
  -	// if it's default container - try extension match
  -	if ( ! cached && container.getMapType() == Container.DEFAULT_MAP ) {
  -	    Container extC = matchExtension( req );
  -	
  -	    if( extC != null ) {
  -		// change the handler
  -		if( extC.getHandler() != null ) {
  -		    fixRequestPaths( path, req, extC );
  -		    container=extC;
  +	    // Once - adjust for prefix and context path
  +	    // If cached - we don't need to do it again ( since it is the final Container,
  +	    // either prefix or extension )
  +	    fixRequestPaths( path, req, container );
  +	
  +
  +	    // if it's default container - try extension match
  +	    if (  container.getMapType() == Container.DEFAULT_MAP ) {
  +		Container extC = matchExtension( req );
  +	
  +		if( extC != null ) {
  +		    // change the handler
  +		    if( extC.getHandler() != null ) {
  +			fixRequestPaths( path, req, extC );
  +			container=extC;
  +		    }
  +		    if( debug > 0 ) log("SM: Found extension mapping " + extC.getHandler());
  +		    // change security roles
   		}
  -		if( debug > 0 ) log("SM: Found extension mapping " + extC.getHandler());
  -		// change security roles
   	    }
  -	}
  -	
  -	if( mapCacheEnabled && ! cached ) {
  -	    addCachedResult( host + ":" + path, container );//XXX remove strings!
  -	}
   	    
  -	if(debug>0) log("SM: After mapping " + req + " " + req.getWrapper());
  +	    if(debug>0) log("SM: After mapping " + req + " " + req.getWrapper());
   	
   	} catch(Exception ex ) {
   	    ex.printStackTrace();
  @@ -388,7 +279,8 @@
   	return OK;
       }
       
  -    /** 
  +    /** No need to do that - we finished everything in the first step.
  +     *  
        */
       public int requestMap(Request req) {
   	// No op. All mapping is done in the first step - it's better because the
  @@ -402,43 +294,26 @@
   	
   	return OK;
       }
  -
  -    void mergeSecurityInfo( ) {
  -    	// Merge the security info into the container
  -	//
  -	// XXX merging is a very usefull optimization, but we should do it
  -	// at init time, it's very expensive because we need to be sure it has the same
  -	// pattern !!
  -	// 	if(debug>0) log("SM: Merging security constraint " + scontainer + " into " + container );
  -	// 	container.setRoles( scontainer.getRoles());
  -	// 	container.setTransport( scontainer.getTransport());
  -
  -	// until merging is implemented, we'll just create a new container with the combined
  -	// properties. This code needs optimizations ( i.e. alghoritm + data, not OptimizeIt!)
  -// 	Container ct=container.getClone();
  -//  	ct.setRoles( scontainer.getRoles());
  -// 	ct.setTransport( scontainer.getTransport());
  -// 	req.setContainer( ct );
  -// 	if(debug>0) log("SM: Set security constraings " + req + " " + container );
  -    }
   
  +    // -------------------- Implementation methods --------------------
       
       /** Will match an extension - note that Servlet API use special rules
        *  for mapping extension, different from what is used in existing web servers.
        *  That makes this code very easy ( only need to deal with the last component
        *  of the name ), but it's hard to integrate and you have no way to use pathInfo.
        */
  -    public Container matchExtension( Request req ) {
  +    Container matchExtension( Request req ) {
   	Context ctx=req.getContext();
   	String ctxP=ctx.getPath();
  +
   	String path = req.getPathInfo(); // we haven't matched any prefix,
  -	// we check path Info
   	if( path == null ) return null;
  -	String extension=StringUtil.getExtension( path );
   
  -	if(debug>0) cm.log("SM: Extension match " + ctxP +  " " + path + " " + extension );
  +	String extension=URLUtil.getExtension( path );
   	if( extension == null ) return null;
   
  +	if(debug>0) cm.log("SM: Extension match " + ctxP +  " " + path + " " + extension );
  +
   	// Find extension maps for the context
   	SimpleHashtable extM=(SimpleHashtable)ctx.getContainer().getNote( ctExtMapNote );
   	if( extM==null ) return null;
  @@ -457,6 +332,8 @@
   	return container; 
       }
   
  +    /** Adjust the paths in request after matching a container
  +     */
       void fixRequestPaths( String path, Request req, Container container ) {
   	// Set servlet path and path info
   	// Found a match !
  @@ -496,107 +373,5 @@
   	req.setContainer( container );
       }
       
  -    /** Cache for request results - exploit the fact that few
  -     *  request are more "popular" than other.
  -     *  Disable it if you want to benchmark the mapper !!!
  -     */
  -    Object getCachedResult(String path) {
  -	// XXX make sure we don't keep too many requests in memory
  -	return mapCache.get( path );
  -    }
  -
  -    void addCachedResult(String path, Object o) {
  -	// XXX make sure we don't keep too many requests in memory
  -	mapCache.put( path, o );
  -    }
  -    
   }
   
  -/** Mapping alghoritm.
  -    XXX finish factoring out the creation of the map ( right now direct field access is
  -    used, since the code was just cut out from SimpleMapper).
  -    XXX make sure the code is useable as a general path mapper - or at least a bridge
  -    can be created between SimpleMapper and a patern matcher like the one in XPath
  - */
  -class Mappings {
  -    SimpleHashtable prefixMappedServlets;
  -    
  -    Mappings() {
  -	prefixMappedServlets=new SimpleHashtable();
  -    }
  -    
  -    // -------------------- Implementation --------------------
  -
  -    /** Match a prefix rule - /foo/bar/index.html/abc
  -     */
  -    public Object getLongestPrefixMatch( String path ) {
  -	Container container = null;
  -        String s = path;
  -	boolean exact=true;
  -	
  -	// ??/baz/== /baz ==/baz/* 
  -	//if( s.endsWith( "/" ))
  -	//  s=removeLast(s);
  -	
  -	while (s.length() >= 0) {
  -	    //if(debug>8) context.log( "Prefix: " + s  );
  -	    container = (Container)prefixMappedServlets.get(s);
  -	    
  -	    if (container == null) {
  -		s=StringUtil.removeLast( s );
  -		exact=false;
  -	    }  else {
  -		if( container.getMapType() == Container.PATH_MAP &&
  -		    ! exact ) {
  -		    // we matched a path_map, but we have path_info,
  -		    // so this is not a good map
  -		} else {
  -		    break;
  -		}
  -		    
  -	    }
  -	}
  -	return container;
  -    }
  -
  -}
  -
  -/* -------------------- Caching results -------------------- */
  -class StringUtil {
  -
  -    public static String removeLast( String s) {
  -	int i = s.lastIndexOf("/");
  -	
  -	if (i > 0) {
  -	    s = s.substring(0, i);
  -	} else if (i == 0 && ! s.equals("/")) {
  -	    s = "/";
  -	} else {
  -	    s = "";
  -	}
  -	return s;
  -    }
  -
  -    public static String getFirst( String path ) {
  -	if (path.startsWith("/")) 
  -	    path = path.substring(1);
  -	
  -	int i = path.indexOf("/");
  -	if (i > -1) {
  -	    path = path.substring(0, i);
  -	}
  -
  -	return  "/" + path;
  -    }
  -    
  -    public static String getExtension( String path ) {
  -        int i = path.lastIndexOf(".");
  -	int j = path.lastIndexOf("/");
  -
  -	if ((i > 0) && (i > j))
  -	    return path.substring(i);
  -	else
  -	    return null;
  -    }
  -
  -}
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/request/AccessInterceptor.java
  
  Index: AccessInterceptor.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  
  package org.apache.tomcat.request;
  
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.core.Constants;
  import org.apache.tomcat.util.*;
  import javax.servlet.http.*;
  import java.util.*;
  
  // XXX maybe it's a good idea to use a different model for adding secuirty
  // constraints - we use Container now because we want to generalize all
  // per/URL properties. 
  
  /**
   *  Access control - find if a request matches any web-resource-collection
   *  and set the "required" attributes.
   *
   *  The spec requires additive checking ( i.e. there is no "best match"
   *  defined, but "all requests that contain a request path that mathces the
   *  URL pattern in the resource collection are subject to the constraing" ).
   *
   *  In "integrated" mode this interceptor will be no-op, we'll use the
   *  web server ( assuming we can map the security to web-server equivalent
   *  concepts - I think we can do that, but need to experiment with that)
   */
  public class AccessInterceptor extends  BaseInterceptor  {
      int debug=0;
      ContextManager cm;
  
      // Security mapping note
      int secMapNote;
      
      public AccessInterceptor() {
      }
  
      /* -------------------- Support functions -------------------- */
      public void setDebug( int level ) {
  	if(level!=0) log("SM: AccessInterceptor - set debug " + level);
  	debug=level;
      }
  
      void log( String msg ) {
  	if( cm==null) 
  	    System.out.println("AccessInterceptor: " + msg );
  	else
  	    cm.log( msg );
      }
  
  
      /* -------------------- Initialization -------------------- */
      
      /** Set the context manager. To keep it simple we don't support
       *  dynamic add/remove for this interceptor. 
       */
      public void setContextManager( ContextManager cm ) {
  	this.cm=cm;
  	
  	// set-up a per/container note for maps
  	try {
  	    secMapNote = cm.getNoteId( ContextManager.CONTAINER_NOTE, "map.security");
  	} catch( TomcatException ex ) {
  	    ex.printStackTrace();
  	    throw new RuntimeException( "Invalid state ");
  	}
      }
  
      /** Called when a context is added.
       */
      public void addContext( ContextManager cm, Context ctx ) throws TomcatException
      {
  	Hashtable sec=new Hashtable();
  	Container ct=ctx.getContainer();
  	ct.setNote( secMapNote, sec );
      }
  
      /** Called when a context is removed from a CM - we must ask the mapper to
  	remove all the maps related with this context
       */
      public void removeContext( ContextManager cm, Context ctx ) throws TomcatException
      {
  	// nothing - will go away with the ctx
      }
      
  
      /**
       */
      public void addContainer( Container ct )
  	throws TomcatException
      {
  	Context ctx=ct.getContext();
  	String path=ct.getPath();
  	String ctxP=ctx.getPath();
  
  	if( ct.getRoles() != null ) {
  	    return; // XXX - right now we add either security or handler,
  	    // later we can use a more general/efficient aproach
  	}
  	
  	if(ct.getHandler() == null ) {
  	    // it was only a security map
  	    return;
  	}
      }
  
      // XXX not implemented - will deal with that after everything else works.
      public void removeContainer( Container ct )
  	throws TomcatException
      {
      }
  
  
      /* -------------------- Request mapping -------------------- */
  
      /** Check if this request requires auth, and if so check the roles.
       *  This interceptor needs to be "up-chain" from security check interceptor.
       *  It is also possible to move this check at requestMap stage.
       */
      public int authorize( Request req, Response response )
      {
  	Context ctx=req.getContext();
  
  	// first we check if this request _requires_ access control
  
  	// this could be optimized and part of the contextMap, to
  	// avoid double parsing and lookup - but in production mode
  	// both methods will be no-ops anyway ( the server has already done
  	// it ) - and in standalone mode it's not the biggest problem and
  	// we can optimize it later if needed.
  	
  	String roles[]=req.getContainer().getRoles();
  	if( roles==null ) {
  	    return 0;
  	}
  
   	return 0;
      }
  
      // -------------------- Implementation methods --------------------
      
      
  }
  
  class ResourceCollectionPattern {
      String methods[];
      String prefixPatterns[];
      String extPatterns[];
      String exactPatterns[];
  
      
  }