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/01/13 07:35:00 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/core Context.java Request.java RequestDispatcherImpl.java

costin      00/01/12 22:35:00

  Modified:    src/share/org/apache/tomcat/core Context.java Request.java
                        RequestDispatcherImpl.java
  Log:
  Moved back to the original model of RequestDispatcher.
  
  The most I could do is to document it and make it look
  better (IMHO) - it is still the most difficult part of
  tomcat ( and of the spec ).
  
  Please - in case you want to start from scratch with
  a new architecture, do start with RequestDispatcher and
  an API where RD will fit - all the rest is trivial.
  
  Revision  Changes    Path
  1.29      +19 -11    jakarta-tomcat/src/share/org/apache/tomcat/core/Context.java
  
  Index: Context.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Context.java,v
  retrieving revision 1.28
  retrieving revision 1.29
  diff -u -r1.28 -r1.29
  --- Context.java	2000/01/12 19:54:01	1.28
  +++ Context.java	2000/01/13 06:34:59	1.29
  @@ -255,32 +255,40 @@
       }
   
       public RequestDispatcher getRequestDispatcher(String path) {
  -	if ( path == null   || 
  -	     ! path.startsWith("/")) {
  +	if ( path == null  || ! path.startsWith("/")) {
   	    return null; // spec say "return null if we can't return a dispather
   	}
   
  -	Request subReq=contextM.createRequest( this, path );
  -	contextM.processRequest(subReq);
  +	RequestDispatcherImpl rD=new RequestDispatcherImpl( this );
  +	rD.setPath( path );
  +
  +	return rD;
  +// 	Request subReq=contextM.createRequest( this, path );
  +// 	contextM.processRequest(subReq);
   	
  -	return new RequestDispatcherImpl(subReq);
  +// 	return new RequestDispatcherImpl(subReq);
       }
   
       public RequestDispatcher getNamedDispatcher(String name) {
           if (name == null)
   	    return null;
   
  +	// We need to do the checks 
   	ServletWrapper wrapper = getServletByName( name );
   	if (wrapper == null)
   	    return null;
  +
  +	RequestDispatcherImpl rD=new RequestDispatcherImpl( this );
  +	rD.setName( name );
   
  -	// creates a new subrequest, and set the wrapper.
  -	Request subR = new Request();
  -	subR.setWrapper( wrapper );
  -	subR.setPathInfo("");
  -	subR.setContext( this );
  +	return rD;
  +// 	// creates a new subrequest, and set the wrapper.
  +// 	Request subR = new Request();
  +// 	subR.setWrapper( wrapper );
  +// 	subR.setPathInfo("");
  +// 	subR.setContext( this );
   	
  -        return  new RequestDispatcherImpl(subR);
  +//         return  new RequestDispatcherImpl(subR);
       }
   
       /** Implements getResource() - use a sub-request to let interceptors do the job.
  
  
  
  1.20      +10 -5     jakarta-tomcat/src/share/org/apache/tomcat/core/Request.java
  
  Index: Request.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Request.java,v
  retrieving revision 1.19
  retrieving revision 1.20
  diff -u -r1.19 -r1.20
  --- Request.java	2000/01/12 19:54:02	1.19
  +++ Request.java	2000/01/13 06:35:00	1.20
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Request.java,v 1.19 2000/01/12 19:54:02 costin Exp $
  - * $Revision: 1.19 $
  - * $Date: 2000/01/12 19:54:02 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Request.java,v 1.20 2000/01/13 06:35:00 costin Exp $
  + * $Revision: 1.20 $
  + * $Date: 2000/01/13 06:35:00 $
    *
    * ====================================================================
    *
  @@ -236,7 +236,7 @@
        * original parameters before adding parameters from the
        * query string, if any.
        */
  -    public Hashtable getParametersCopy() {
  +    Hashtable getParametersCopy() {
   	handleParameters();
   	return (Hashtable) parameters.clone();
       }
  @@ -572,6 +572,10 @@
   	// XXX Should we override query parameters ??
       }
   
  +    public Hashtable getParameters() {
  +	return parameters;
  +    }
  +
       public void setContentLength( int  len ) {
   	this.contentLength=len;
       }
  @@ -639,7 +643,8 @@
       }
   
       public void setAttribute(String name, Object value) {
  -        attributes.put(name, value);
  +	if(name!=null && value!=null)
  +	    attributes.put(name, value);
       }
   
       public void removeAttribute(String name) {
  
  
  
  1.11      +205 -42   jakarta-tomcat/src/share/org/apache/tomcat/core/RequestDispatcherImpl.java
  
  Index: RequestDispatcherImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestDispatcherImpl.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- RequestDispatcherImpl.java	2000/01/12 19:54:02	1.10
  +++ RequestDispatcherImpl.java	2000/01/13 06:35:00	1.11
  @@ -66,6 +66,16 @@
   import javax.servlet.*;
   import javax.servlet.http.*;
   
  +/*
  +  We do a new sub-request for each include() or forward().
  +  Even if today we take all decisions based only on path, that may
  +  change ( i.e. a request can take different paths based on authentication,
  +  headers, etc - other Interceptors may affect it).
  +
  +  I think this is the correct action - instead of doing a lookup when
  +  we construct the dispatcher. ( costin )
  + */
  +
   /**
    *
    *
  @@ -73,73 +83,214 @@
    * @author Jason Hunter [jch@eng.sun.com]
    * @author James Todd [gonzo@eng.sun.com]
    * @author Alex Cruikshank [alex@epitonic.com]
  + * @author costin@dnt.ro
    */
   public class RequestDispatcherImpl implements RequestDispatcher {
       private StringManager sm = StringManager.getManager("org.apache.tomcat.core");
       
  -    private Context context;
  -    
  -    private Request subRequest = null;
  +    Context context;
  +    // path dispatchers
  +    String path;
  +    String queryString;
  +
  +    // name dispatchers
  +    String name;
   
  +    /** Used for Context.getRD( path )
  +     */
       RequestDispatcherImpl(Context context) {
           this.context = context;
       }
   
  -    RequestDispatcherImpl(Request  subReq) {
  -        this.subRequest = subReq;
  +    void setPath( String urlPath ) {
  +	// separate the query string
  +	int i = urlPath.indexOf("?");
  +	if( i<0 )
  +	    this.path=urlPath;
  +	else {
  +	    this.path=urlPath.substring( 0,i );
  +	    int len=urlPath.length();
  +	    if( i< len )
  +		this.queryString =urlPath.substring(i + 1);
  +        }
       }
   
  +    void setName( String name ) {
  +	this.name=name;
  +    }
  +    
       public void forward(ServletRequest request, ServletResponse response)
   	throws ServletException, IOException
       {
  -        Request realRequest = ((HttpServletRequestFacade)request).getRealRequest();
  +	Request realRequest = ((HttpServletRequestFacade)request).getRealRequest();
           Response realResponse = ((HttpServletResponseFacade)response).getRealResponse();
  -
  -	String urlPath=realRequest.getLookupPath();
  -	String queryString=realRequest.getQueryString();
  -	
   	// according to specs
  -	if (realResponse.isStarted()) {
  -            String msg = sm.getString("rdi.forward.ise");
  -	    throw new IllegalStateException(msg);
  -        }
  -
  -	// Pre-pend the context name to give appearance of real request
  -	urlPath = subRequest.getContext().getPath() + urlPath;
  +	if (realResponse.isStarted()) 
  +	    throw new IllegalStateException(sm.getString("rdi.forward.ise"));
   
  -	realRequest.setRequestURI( urlPath );
  +	// the strange case in a separate method.
  +	if( name!=null) forwardNamed( request, response );
  +	
  +	// from strange spec reasons, forward and include are very different in
  +	// the way they process the request - if you don't understand the code
  +	// try to understand the spec.
  +	
  +	// in forward case, the Path parametrs of the request are what you would
  +	// expect, so we just do a new processRequest on the modified request
   
  -        addQueryString(realRequest, queryString);
  -        realRequest.setServletPath(this.subRequest.getServletPath());
  -	realRequest.setPathInfo(this.subRequest.getPathInfo());
  +	// set the context - no need to fire context parsing again
  +	realRequest.setContext( context );
   
  -	this.subRequest.getWrapper().handleRequest((HttpServletRequestFacade)request,
  +	// Note that Mapper interceptor uses lookup path. 
  +	realRequest.setLookupPath( path );
  +	realRequest.setRequestURI( context.getPath() + path );
  +
  +	// merge query string as specified in specs - before, it may affect
  +	// the way the request is handled by special interceptors
  +	if( queryString != null )
  +	    addQueryString( realRequest, queryString );
  +	
  +	// run the new request through the context manager
  +	// not that this is a very particular case of forwarding
  +	context.getContextManager().processRequest(realRequest);
  +
  +	// CM should have set the wrapper - call it
  +	// LOG	System.out.println("Forward " + realRequest.getServletPath());
  +	realRequest.getWrapper().handleRequest((HttpServletRequestFacade)request,
   						   (HttpServletResponseFacade)response);
       }
   
       public void include(ServletRequest request, ServletResponse response)
   	throws ServletException, IOException
       {
  -	HttpServletRequest req = (HttpServletRequest)request;
           Request realRequest = ((HttpServletRequestFacade)request).getRealRequest();
   	Response realResponse = ((HttpServletResponseFacade)response).getRealResponse();
  -
  -        // add new query string parameters to request
  -        // if names are duplicates, new values will be prepended to arrays
  -        //XXX TODO addQueryString( reqFacade.getRealRequest(), this.queryString );
   
  +	// the strange case in a separate method
  +	if( name!=null) includeNamed( request, response );
  +	
  +	// Implement the spec that "no changes in response, only write"
  +	// can also be done by setting the response to 0.9 mode ( as Apache does!)
   	IncludedResponse iResponse = new IncludedResponse(realResponse);
  -	// XXX Make sure we "clone" all usefull informations 
  +
  +	// Here the spec is very special, pay attention
  +
  +	// We need to pass the original request, with all the paths - and the new paths
  +	// in special attributes.
  +
  +	// We still need to find out where do we want to go ( today )
  +	// That means we create a subRequest with the new paths ( since
  +	// the mapping and aliasing is done on Requests), and run it
  +	// through prepare.
  +
  +	// That also means that some special cases ( like the invoker !! )
  +	// will have to pay attention to the attributes, or we'll get a loop
  +
  +	Request subRequest=context.getContextManager().createRequest( context, path );
  +
  +	// I hope no interceptor (or code) in processRequest use any
  +	// of the original request info ( like Auth headers )
  +	//
  +	// XXX We need to clone the request, so that processRequest can
  +	// make an informed mapping ( Auth, Authorization, etc)
  +	//
  +	// This will never work corectly unless we do a full clone - but
  +	// for simple cases ( no auth, etc) it does
  +
  +	// note that we also need a dummy response - SessionInterceptors may
  +	// change something !
   	subRequest.setResponse( realResponse );
  -	subRequest.setServerName( realRequest.getServerName() );
   	
  -	try {
  -	    subRequest.getWrapper().handleRequest(subRequest.getFacade() , iResponse);
  -	} catch( Exception ex) {
  -	    ex.printStackTrace();
  -	}
  +	context.getContextManager().processRequest(subRequest);
  +
  +	// Now subRequest containse the processed and aliased paths, plus
  +	// the wrapper that will handle the request.
  +
  +	// We will use the stack a bit - save all path attributes, set the
  +	// new values, and after return from wrapper revert to the original
  +
  +	Object old_request_uri=realRequest.getAttribute("javax.servlet.include.request_uri");
  +	realRequest.setAttribute("javax.servlet.include.request_uri",
  +				 context.getPath() + path );
  +
  +	Object old_context_path=realRequest.getAttribute("javax.servlet.include.context_path");
  +	realRequest.setAttribute("javax.servlet.include.context_path",
  +				 context.getPath()); // never change anyway - RD can't get out
  +
  +	Object old_servlet_path=realRequest.getAttribute("javax.servlet.include.servlet_path");
  +	realRequest.setAttribute("javax.servlet.include.servlet_path",
  +				 subRequest.getServletPath());
  +	
  +	Object old_path_info=realRequest.getAttribute("javax.servlet.include.path_info");
  +	realRequest.setAttribute("javax.servlet.include.path_info",
  +				 subRequest.getPathInfo());
  +
  +	Object old_query_string=realRequest.getAttribute("javax.servlet.include.query_string");
  +	realRequest.setAttribute("javax.servlet.include.request_uri", queryString);
  +
  +	// Not explicitely stated, but we need to save the old parameters before
  +	// adding the new ones
  +	Hashtable old_parameters=realRequest.getParametersCopy();
  +	// NOTE: it has a side effect of _reading_ the form data - which
  +	// is against the specs ( you can't read the post until asked for
  +	// parameters). I see no way of dealing with that -
  +	// if we don't do it and the included request need a parameter,
  +	// the form will be read and we'll have no way to know that.
  +
  +	// IMHO the spec should do something about that - or smarter
  +	// people should implement the spec. ( costin )
  +
  +	addQueryString( realRequest, queryString );
  +
  +	// now it's really strange: we call the wrapper on the subrequest
  +	// for the realRequest ( since the real request will still have the
  +	// original handler/wrapper )
  +	subRequest.getWrapper().handleRequest(realRequest.getFacade() , iResponse);
  +
  +	// After request, we want to restore the include attributes - for
  +	// chained includes.
  +	realRequest.setParameters( old_parameters);
  +
  +	replaceAttribute( realRequest, "javax.servlet.include.request_uri",
  +				 old_request_uri);
  +	replaceAttribute( realRequest, "javax.servlet.include.context_path",
  +				 old_context_path); 
  +	replaceAttribute( realRequest, "javax.servlet.include.servlet_path",
  +				 old_servlet_path);
  +	replaceAttribute( realRequest, "javax.servlet.include.path_info",
  +				 old_path_info);
  +	replaceAttribute( realRequest, "javax.servlet.include.request_uri",
  +				 old_query_string);
       }
   
  +	
  +
  +    /** Named dispatcher include
  +     *  Separate from normal include - which is still too messy
  +     */
  +    public void includeNamed(ServletRequest request, ServletResponse response)
  +	throws ServletException, IOException
  +    {
  +	// Use the original request - as in specification !
  +
  +	// We got here if name!=null, so assert it
  +	ServletWrapper wrapper = context.getServletByName( name );
  +
  +	wrapper.handleRequest( (HttpServletRequestFacade)request,
  +			       (HttpServletResponseFacade)response);
  +
  +    }
  +
  +    /** Named forward
  +     */
  +    public void forwardNamed(ServletRequest request, ServletResponse response)
  +	throws ServletException, IOException
  +    {
  +	ServletWrapper wrapper = context.getServletByName( name );
  +	wrapper.handleRequest( (HttpServletRequestFacade)request,
  +			       (HttpServletResponseFacade)response);
  +    }    
  +
       /**
        * Adds a query string to the existing set of parameters.
        * The additional parameters represented by the query string will be
  @@ -154,16 +305,28 @@
           if ((inQueryString == null) || (inQueryString.trim().length() <= 0))
               return;
   
  -	String queryString=req.getQueryString();
  -        // add query string to existing string
  -        if ((queryString == null) || (queryString.trim().length() <= 0))
  -            queryString = inQueryString;
  -        else
  -            queryString = inQueryString + "&" + queryString;
  +	Hashtable newParams = HttpUtils.parseQueryString(queryString);
  +	Hashtable parameters= req.getParameters();
   
  -	req.setQueryString( queryString );
  -	req.processQueryString();
  +	// add new to old ( it alters the original hashtable in request)
  +	Enumeration e=newParams.keys();
  +	while(e.hasMoreElements() ) {
  +	    String key=(String)e.nextElement();
  +	    parameters.put( key, newParams.get(key));
  +	}
       }
   
  +    /** Restore attribute - if value is null, remove the attribute.
  +     *  X Maybe it should be the befavior of setAttribute() - it is not
  +     *  specified what to do with null.
  +     *  ( or it is - null means no value in getAttribute, so setting to
  +     *    null should mean setting to no value. ?)
  +     */
  +    private void replaceAttribute( Request realRequest, String name, Object value) {
  +	if( value == null )
  +	    realRequest.removeAttribute( name );
  +	else
  +	    realRequest.setAttribute( name, value );
  +    }
   
   }