Return-Path: Mailing-List: contact tomcat-dev-help@jakarta.apache.org; run by ezmlm Delivered-To: mailing list tomcat-dev@jakarta.apache.org Received: (qmail 60531 invoked by uid 500); 1 May 2000 23:15:17 -0000 Delivered-To: apmail-jakarta-tomcat-cvs@apache.org Received: (qmail 60527 invoked by uid 1052); 1 May 2000 23:15:16 -0000 Date: 1 May 2000 23:15:16 -0000 Message-ID: <20000501231516.60526.qmail@locus.apache.org> From: costin@locus.apache.org To: jakarta-tomcat-cvs@apache.org Subject: 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; i0 ) 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 * . * * [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[]; }