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 );
+ }
}