|
What this is
Other links
The source code/* * Copyright 1999-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.tomcat.facade; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.tomcat.core.Context; import org.apache.tomcat.core.Handler; import org.apache.tomcat.core.Request; import org.apache.tomcat.core.Response; import org.apache.tomcat.util.compat.Action; import org.apache.tomcat.util.compat.Jdk11Compat; import org.apache.tomcat.util.http.Parameters; import org.apache.tomcat.util.res.StringManager; /* This code needs a re-write, it's very ugly. The hardest problem is the requirement to pass the "same" request, but with small modifications. One solution is to use a facade ( was used in tomcat origianlly ). The current solution is to save the modified attributes and restore after the method returns. This saves one object creation - since the subRequest object still has to be created. The details are facade-specific, shouldn't affect the core. */ /* 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), that means we need to call CM. I think this is the correct action - instead of doing a lookup when we construct the dispatcher. ( costin ) */ /** * * * @author James Duncan Davidson [duncan@eng.sun.com] * @author Jason Hunter [jch@eng.sun.com] * @author James Todd [gonzo@eng.sun.com] * @author Alex Cruikshank [alex@epitonic.com] * @author costin@dnt.ro */ final class RequestDispatcherImpl implements RequestDispatcher { static final boolean debug=false; // Use the strings from core private static StringManager sm = StringManager. getManager("org.apache.tomcat.resources"); // Attributes that will be replaced during include private static final String A_REQUEST_URI= "javax.servlet.include.request_uri"; private static final String A_CONTEXT_PATH= "javax.servlet.include.context_path"; private static final String A_SERVLET_PATH= "javax.servlet.include.servlet_path"; private static final String A_PATH_INFO= "javax.servlet.include.path_info"; private static final String A_QUERY_STRING= "javax.servlet.include.query_string"; Context context; // path dispatchers String path; String queryString; // name dispatchers String name; private Object accessControlContext=null; /** Used for Context.getRD( path ) */ public RequestDispatcherImpl(Context context, Object acc) { this.context = context; accessControlContext=acc; } public 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); } } public void setName( String name ) { this.name=name; } // -------------------- Public methods -------------------- // Wrappers for jdk1.2 priviledged actions Jdk11Compat jdk11Compat=Jdk11Compat.getJdkCompat(); RDIAction forwardAction=new RDIAction( this,false); RDIAction includeAction=new RDIAction( this,true); public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException { if( System.getSecurityManager() != null ) { try { forwardAction.prepare( request, response ); jdk11Compat.doPrivileged( forwardAction, accessControlContext ); } catch( Exception e) { wrapException( e, null ); } } else { doForward(request,response); } } public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException { if( System.getSecurityManager() != null ) { try { includeAction.prepare( request, response ); jdk11Compat.doPrivileged( includeAction, accessControlContext ); } catch( Exception e) { wrapException( e, null ); } } else { doInclude(request,response); } } // -------------------- Actual forward/include impl -------------------- private void doForward(ServletRequest request, ServletResponse response) throws ServletException, IOException { /** We need to find the request/response. The servlet API * guarantees that we will receive the original request as parameter. */ Request realRequest = ((HttpServletRequestFacade)request). getRealRequest(); Response realResponse = realRequest.getResponse(); // according to specs (as of 2.2: started is OK, just not committed) if (realResponse.isBufferCommitted()) throw new IllegalStateException(sm.getString("rdi.forward.ise")); // reset the output buffer but not the headers and cookies realResponse.resetBuffer(); // the strange case in a separate method. if( name!=null) { forwardNamed( request, response ); return; } // 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 // expect, so we just do a new processRequest on the modified request // set the context - no need to fire context parsing again realRequest.setContext( context ); realRequest.requestURI().setString( context.getPath() + path ); // # 3162 realRequest.unparsedURI().recycle(); //realRequest.query().recycle(); realRequest.servletPath().recycle(); realRequest.pathInfo().recycle(); realRequest.setChild(null); // merge query string as specified in specs - before, it may affect // the way the request is handled by special interceptors if( queryString != null ) { // Process existing parameters, if not already done so // ( otherwise we'll process some twice ) realRequest.parameters().handleQueryParameters(); // Set the query string - the sum of the new one and old one. String oldQS=realRequest.queryString().toString(); String newQS=(oldQS==null ) ? queryString : queryString + "&" + oldQS; realRequest.queryString().setString(newQS); // Process the additional parsm. We don't know if the old // params were processed ( so we need to make sure they are, // i.e. a known state ). realRequest.parameters().push(); Parameters child=realRequest.parameters().getCurrentSet(); child.processParameters( queryString ); //realRequest.parameters().processParameters( queryString ); } // run the new request through the context manager // not that this is a very particular case of forwarding context.getContextManager().processRequest(realRequest); // unset "included" attribute if any - we may be in a servlet // included from another servlet, // in which case the attribute will create problems realRequest.removeAttribute( A_REQUEST_URI); realRequest.removeAttribute( A_SERVLET_PATH); // CM should have set the wrapper - call it Handler wr=realRequest.getHandler(); if( wr!=null ) { try { wr.service(realRequest, realResponse); } catch( Exception ex ) { realResponse.setErrorException(ex); } } // Clean up the request and response as needed // No action required if ( realResponse.isExceptionPresent() ) { // if error URI not set, set our URI if ( null == realResponse.getErrorURI() ) realResponse.setErrorURI( context.getPath() + path ); Exception ex = realResponse.getErrorException(); wrapException( ex, sm.getString("dispatcher.forwardException")); } // close the response - output after this point will be discarded. // XXX XXX Maybe this is Henri's bug !!! realResponse.finish(); } // -------------------- Include -------------------- private void doInclude(ServletRequest request, ServletResponse response) throws ServletException, IOException { Request realRequest = ((HttpServletRequestFacade)request). getRealRequest(); Response realResponse = realRequest.getResponse(); if( debug ) { System.out.println("RDI: doInclude: " + path + " " + name + " " + queryString ); } // the strange case in a separate method if( name!=null) { includeNamed( request, response ); return; } // Implement the spec that "no changes in response, only write" // can also be done by setting the response to 0.9 mode // IncludedResponse iResponse = new IncludedResponse(realResponse); boolean old_included=realResponse.isIncluded(); if( ! old_included ) { realResponse.setIncluded( true ); } // 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 ); subRequest.setParent( realRequest ); subRequest.getTop(); // control inclusion depth // 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 ); 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=replaceAttribute(realRequest, A_REQUEST_URI, context.getPath() + path ); Object old_context_path=replaceAttribute(realRequest, A_CONTEXT_PATH, context.getPath()); Object old_servlet_path=replaceAttribute(realRequest, A_SERVLET_PATH, subRequest.servletPath().toString()); Object old_path_info=replaceAttribute(realRequest, A_PATH_INFO, subRequest.pathInfo().toString()); Object old_query_string=replaceAttribute(realRequest, A_QUERY_STRING, queryString); if( debug ) { System.out.println("RDI: old " + old_request_uri + " " + old_context_path + " " + old_servlet_path + " " + old_path_info + " " + old_query_string); System.out.println("RDI: new "+context.getPath() + " " + path + " " + subRequest.servletPath().toString() + " " + subRequest.pathInfo().toString() + " " + queryString); } if( queryString != null ) { // the original parameters will be preserved, and a new // child Parameters will be used for the included request. realRequest.parameters().push(); Parameters child=realRequest.parameters().getCurrentSet(); child.processParameters( queryString ); } Request old_child = realRequest.getChild(); subRequest.setParent( old_child ); realRequest.setChild( subRequest ); // 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 ) Handler wr=subRequest.getHandler(); if( wr!=null ) { try { wr.service(realRequest, realResponse); } catch( Exception ex ) { realResponse.setErrorException(ex); } } // After request, we want to restore the include attributes - for // chained includes. realRequest.setChild( old_child ); if( queryString != null ) { // restore the parameters realRequest.parameters().pop(); } //realRequest.setParameters( old_parameters); replaceAttribute( realRequest, A_REQUEST_URI, old_request_uri); replaceAttribute( realRequest, A_CONTEXT_PATH,old_context_path); replaceAttribute( realRequest, A_SERVLET_PATH, old_servlet_path); replaceAttribute( realRequest, A_PATH_INFO, old_path_info); replaceAttribute( realRequest, A_QUERY_STRING, old_query_string); // revert to the response behavior if( ! old_included ) { realResponse.setIncluded( false ); } // Rethrow original error if present if ( realResponse.isExceptionPresent() ) { // if error URI not set, set our URI if ( null == realResponse.getErrorURI() ) realResponse.setErrorURI( context.getPath() + path ); Exception ex = realResponse.getErrorException(); wrapException( ex, sm.getString("dispatcher.includeException")); } } // -------------------- Special case of "named" dispatcher ------------- /** Named dispatcher include * Separate from normal include - which is still too messy */ private void includeNamed(ServletRequest request, ServletResponse response) throws ServletException, IOException { // We got here if name!=null, so assert it Handler wr = context.getServletByName( name ); // Use the original request - as in specification ! Request realRequest=((HttpServletRequestFacade)request).getRealRequest(); Response realResponse = realRequest.getResponse(); // Set the "included" flag so that things like header setting in the // included servlet will be correctly ignored boolean old_included=realResponse.isIncluded(); if( ! old_included ) realResponse.setIncluded( true ); if( wr!=null) { try { wr.service(realRequest, realResponse); } catch( Exception ex ) { realResponse.setErrorException( ex ); } } // Clean up the request and response as needed if( ! old_included ) { realResponse.setIncluded( false ); } // Rethrow original error if present if ( realResponse.isExceptionPresent() ) { // if error URI not set, set our URI if ( null == realResponse.getErrorURI() ) realResponse.setErrorURI( "named servlet: " + name ); wrapException( realResponse.getErrorException(), sm.getString("dispatcher.includeException")); } } /** Named forward */ private void forwardNamed(ServletRequest request, ServletResponse response) throws ServletException, IOException { // We got here if name!=null, so assert it Handler wr = context.getServletByName( name ); // Use the original request - as in specification ! Request realRequest=((HttpServletRequestFacade)request). getRealRequest(); Response realResponse = realRequest.getResponse(); // Don't set included #3726 // boolean old_included=realResponse.isIncluded(); // if( ! old_included ) realResponse.setIncluded( true ); if( wr!=null) { try { wr.service(realRequest, realResponse); } catch( Exception ex ) { wrapException( ex, null ); } } // Clean up the request and response as needed // No action required // Rethrow original error if present if ( realResponse.isExceptionPresent() ) { // if error URI not set, set our URI if ( null == realResponse.getErrorURI() ) realResponse.setErrorURI( "named servlet: " + name ); wrapException( realResponse.getErrorException(), sm.getString("dispatcher.forwardException")); } } // -------------------- Special methods -------------------- /** Restore attribute - if value is null, remove the attribute. * ( or it is - null means no value in getAttribute, so setting to * null should mean setting to no value. ?) */ private Object replaceAttribute( Request realRequest, String name, Object value) { Object oldAttribute=realRequest.getAttribute(name); if( value == null ) realRequest.removeAttribute( name ); else realRequest.setAttribute( name, value ); return oldAttribute; } // Rethrow original error if present private void wrapException(Exception ex, String msg) throws IOException, ServletException, RuntimeException { if ( ex instanceof IOException ) throw (IOException) ex; if ( ex instanceof RuntimeException ) throw (RuntimeException) ex; else if ( ex instanceof ServletException ) throw (ServletException) ex; else if( msg==null ) throw new ServletException(ex ); else throw new ServletException(msg, ex ); } // -------------------- Used for doPriviledged in JDK1.2 ---------- static class RDIAction extends Action { ServletRequest req; ServletResponse res; RequestDispatcherImpl rdi; boolean include; RDIAction(RequestDispatcherImpl rdi, boolean incl) { this.rdi=rdi; include=incl; } public void prepare( ServletRequest req, ServletResponse res ) { this.req=req; this.res=res; } public Object run() throws Exception { if( include ) rdi.doInclude( req, res ); else rdi.doForward( req, res ); return null; } } } |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.