javax.servlet.FilterChain
used to manage
Index: code/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpHandlerChain.java
===================================================================
--- code/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpHandlerChain.java (revision 6232)
+++ code/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpHandlerChain.java (revision )
@@ -42,6 +42,7 @@
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.http.server.jmx.JmxEventListener;
import org.glassfish.grizzly.http.server.jmx.Monitorable;
+import org.glassfish.grizzly.http.server.util.NamedMapper;
import org.glassfish.grizzly.http.util.DataChunk;
import org.glassfish.grizzly.http.util.HttpStatus;
import org.glassfish.grizzly.http.util.RequestURIRef;
@@ -81,7 +82,7 @@
/**
* Internal {@link Mapper} used to Map request to their associated {@link HttpHandler}
*/
- private Mapper mapper;
+ private NamedMapper mapper;
/**
* The default host.
*/
@@ -101,7 +102,7 @@
// ------------------------------------------------------------ Constructors
public HttpHandlerChain(final HttpServer httpServer) {
this.httpServer = httpServer;
- mapper = new Mapper();
+ mapper = new NamedMapper();
mapper.setDefaultHostName(LOCAL_HOST);
// We will decode it
setDecodeUrl(false);
@@ -243,7 +244,7 @@
new String[]{"index.html", "index.htm"}, null);
}
}
- mapper.addWrapper(LOCAL_HOST, ctx, wrapper, httpHandler);
+ mapper.addWrapper(LOCAL_HOST, ctx, wrapper, httpHandler, false, httpHandler.getName(), false);
// String ctx = getContextPath(mapping);
@@ -252,7 +253,7 @@
// mapper.addWrapper(LOCAL_HOST, ctx, mapping.substring(ctx.length()), httpHandler);
}
}
-
+ httpHandler.setMapperResolver( mapper );
}
private void registerJmxForHandler(final HttpHandler httpHandler) {
Index: code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/DispatcherConstants.java
===================================================================
--- code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/DispatcherConstants.java (revision )
+++ code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/DispatcherConstants.java (revision )
@@ -0,0 +1,119 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2008-2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ *
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 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.glassfish.grizzly.servlet;
+
+/**
+ * Constants based on Servlet3.0 spec.
+ * This class will be able to be replaced by Servlet3.0 API
+ * such as javax.servlet.RequestDispatcher
and javax.servlet.DispatcherType
+ *
+ * @author Bongjae Chang
+ */
+public class DispatcherConstants {
+
+ static enum DispatcherType {
+ FORWARD,
+ INCLUDE,
+ REQUEST,
+ ASYNC,
+ ERROR
+ }
+
+ static final String FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
+
+ static final String FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
+
+ static final String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
+
+ static final String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
+
+ static final String FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
+
+ static final String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
+
+ static final String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
+
+ static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
+
+ static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
+
+ static final String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
+
+ static final String ERROR_EXCEPTION = "javax.servlet.error.exception";
+
+ static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
+
+ static final String ERROR_MESSAGE = "javax.servlet.error.message";
+
+ static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
+
+ static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
+
+ static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code";
+
+ // async
+ static final String ASYNC_REQUEST_URI = "javax.servlet.async.request_uri";
+
+ static final String ASYNC_CONTEXT_PATH = "javax.servlet.async.context_path";
+
+ static final String ASYNC_PATH_INFO = "javax.servlet.async.path_info";
+
+ static final String ASYNC_SERVLET_PATH = "javax.servlet.async.servlet_path";
+
+ static final String ASYNC_QUERY_STRING = "javax.servlet.async.query_string";
+}
Index: code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/ServletContextImpl.java
===================================================================
--- code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/ServletContextImpl.java (revision 6018)
+++ code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/ServletContextImpl.java (revision )
@@ -81,8 +81,12 @@
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.glassfish.grizzly.Grizzly;
+import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.util.Enumerator;
+import org.glassfish.grizzly.http.server.util.Mapper;
+import org.glassfish.grizzly.http.server.util.MappingData;
import org.glassfish.grizzly.http.server.util.MimeType;
+import org.glassfish.grizzly.http.util.DataChunk;
import org.glassfish.grizzly.localization.LogMessages;
/**
@@ -115,7 +119,14 @@
private String contextName = "";
private volatile String serverInfo = "Grizzly " + Grizzly.getDotedVersion();
-
+
+ /**
+ * Thread local data used during request dispatch.
+ */
+ private ThreadLocalServletContext
object that corresponds to a
- * specified URI on the server. This method allows servlets to gain
- * access to the context for various parts of the server, and as needed
- * obtain RequestDispatcher
objects or resources from the
- * context. The given path must be absolute (beginning with a "/"),
- * and is interpreted based on our virtual host's document root.
- *
- * @param uri Absolute URI of a resource on the server
+ * {@inheritDoc}
*/
@Override
- public ServletContext getContext(String uri) {
+ public ServletContext getContext( String uri ) {
- return this;
+ // Validate the format of the specified argument
+ if( uri == null || !uri.startsWith( "/" ) ) {
+ return null;
- }
+ }
+ if( mapper == null ) {
+ return null;
+ }
+ // Use the thread local URI and mapping data
+ DispatchData dd = dispatchData.get();
+ if( dd == null ) {
+ dd = new DispatchData();
+ dispatchData.set( dd );
+ } else {
+ dd.recycle();
+ }
+ DataChunk uriDC = dd.uriDC;
+ // Retrieve the thread local mapping data
+ MappingData mappingData = dd.mappingData;
+
+ try {
+ uriDC.setString( uri );
+ mapper.map( DataChunk.newInstance(), uriDC, mappingData );
+ if( mappingData.context == null ) {
+ return null;
+ }
+ } catch( Exception e ) {
+ // Should never happen
+ if( logger.isLoggable( Level.WARNING ) ) {
+ logger.log( Level.WARNING, "Error during mapping", e );
+ }
+ return null;
+ }
+
+ if( !( mappingData.context instanceof ServletHandler ) ) {
+ return null;
+ }
+ ServletHandler context = (ServletHandler)mappingData.context;
+ return context.getServletCtx();
+ }
+
/**
* Return the major version of the Java Servlet API that we implement.
*/
@@ -359,21 +401,116 @@
/**
* {@inheritDoc}
*/
- //TODO.
@Override
- public RequestDispatcher getRequestDispatcher(String arg0) {
- throw new UnsupportedOperationException("Not supported yet.");
+ public RequestDispatcher getRequestDispatcher( String path ) {
+ // Validate the path argument
+ if( path == null ) {
+ return null;
- }
+ }
+ if( mapper == null ) {
+ return null;
+ }
+ if( !path.startsWith( "/" ) && !path.isEmpty() ) {
+ throw new IllegalArgumentException( "Path " + path + " does not start with ''/'' and is not empty" );
+ }
+ // Get query string
+ String queryString = null;
+ int pos = path.indexOf( '?' );
+ if( pos >= 0 ) {
+ queryString = path.substring( pos + 1 );
+ path = path.substring( 0, pos );
+ }
+ path = normalize( path );
+ if( path == null )
+ return null;
+ pos = path.length();
+
+ // Use the thread local URI and mapping data
+ DispatchData dd = dispatchData.get();
+ if( dd == null ) {
+ dd = new DispatchData();
+ dispatchData.set( dd );
+ } else {
+ dd.recycle();
+ }
+ DataChunk uriDC = dd.uriDC;
+ // Retrieve the thread local mapping data
+ MappingData mappingData = dd.mappingData;
+
+ try {
+ uriDC.setString( contextPath + path );
+ mapper.map( DataChunk.newInstance(), uriDC, mappingData );
+ if( mappingData.wrapper == null ) {
+ return null;
+ }
+ } catch( Exception e ) {
+ // Should never happen
+ if( logger.isLoggable( Level.WARNING ) ) {
+ logger.log( Level.WARNING, "Error during mapping", e );
+ }
+ return null;
+ }
+
+ if( !( mappingData.wrapper instanceof ServletHandler ) ) {
+ return null;
+ }
+ ServletHandler wrapper = (ServletHandler)mappingData.wrapper;
+ String wrapperPath = mappingData.wrapperPath.toString();
+ String pathInfo = mappingData.pathInfo.toString();
+ // Construct a RequestDispatcher to process this request
+ return new RequestDispatcherImpl( wrapper, uriDC.toString(), wrapperPath, pathInfo, queryString, null );
+ }
+
/**
* {@inheritDoc}
*/
- //TODO.
@Override
- public RequestDispatcher getNamedDispatcher(String arg0) {
- throw new UnsupportedOperationException("Not supported yet.");
+ public RequestDispatcher getNamedDispatcher( String name ) {
+ // Validate the name argument
+ if( name == null )
+ return null;
+ if( mapper == null ) {
+ return null;
- }
+ }
+ // Use the thread local URI and mapping data
+ DispatchData dd = dispatchData.get();
+ if( dd == null ) {
+ dd = new DispatchData();
+ dispatchData.set( dd );
+ } else {
+ dd.recycle();
+ }
+ DataChunk uriDC = dd.uriDC;
+ DataChunk servletNameDC = dd.servletNameDC;
+ // Retrieve the thread local mapping data
+ MappingData mappingData = dd.mappingData;
+ // Map the name
+ uriDC.setString( contextPath );
+ servletNameDC.setString( name );
+
+ try {
+ mapper.map( DataChunk.newInstance(), uriDC, servletNameDC, mappingData );
+ if( mappingData.context == null ) {
+ return null;
+ }
+ } catch( Exception e ) {
+ // Should never happen
+ if( logger.isLoggable( Level.WARNING ) ) {
+ logger.log( Level.WARNING, "Error during mapping", e );
+ }
+ return null;
+ }
+
+ if( !( mappingData.wrapper instanceof ServletHandler ) ) {
+ return null;
+ }
+ ServletHandler wrapper = (ServletHandler)mappingData.wrapper;
+ // Construct a RequestDispatcher to process this request
+ return new RequestDispatcherImpl( wrapper, null, null, null, null, name );
+ }
+
/**
* @deprecated As of Java Servlet API 2.1, with no direct replacement.
*/
@@ -668,4 +805,33 @@
protected Listjavax.servlet.http.HttpServletResponse
+ * that transforms an application response object (which might be the original
+ * one passed to a servlet.
+ *
+ * @author Bongjae Chang
+ */
+public class DispatchedHttpServletResponse extends HttpServletResponseWrapper {
+
+ /**
+ * Is this wrapped response the subject of an include()
+ * call?
+ */
+ private boolean included = false;
+
+ public DispatchedHttpServletResponse( HttpServletResponse response, boolean included ) {
+ super( response );
+ this.included = included;
+ setResponse( response );
+ }
+
+ /**
+ * Set the response that we are wrapping.
+ *
+ * @param response The new wrapped response
+ */
+ private void setResponse( HttpServletResponse response ) {
+ super.setResponse( response );
+ }
+
+ @Override
+ public void setContentLength( int len ) {
+ if( included )
+ return;
+ super.setContentLength( len );
+ }
+
+ @Override
+ public void setContentType( String type ) {
+ if( included )
+ return;
+ super.setContentType( type );
+ }
+
+ @Override
+ public void setBufferSize( int size ) {
+ if( included )
+ return;
+ super.setBufferSize( size );
+ }
+
+ @Override
+ public void reset() {
+ if( included )
+ return;
+ super.reset();
+ }
+
+ @Override
+ public void setLocale( Locale loc ) {
+ if( included )
+ return;
+ super.setLocale( loc );
+ }
+
+ @Override
+ public void addCookie( Cookie cookie ) {
+ if( included )
+ return;
+ super.addCookie( cookie );
+ }
+
+ @Override
+ public void sendError( int sc, String msg )
+ throws IOException {
+ if( included )
+ return;
+ super.sendError( sc, msg );
+ }
+
+ @Override
+ public void sendError( int sc )
+ throws IOException {
+ if( included )
+ return;
+ super.sendError( sc );
+ }
+
+ public void sendRedirect( String location )
+ throws IOException {
+ if( included )
+ return;
+ super.sendRedirect( location );
+ }
+
+ @Override
+ public void setDateHeader( String name, long date ) {
+ if( included )
+ return;
+ super.setDateHeader( name, date );
+ }
+
+ @Override
+ public void addDateHeader( String name, long date ) {
+ if( included )
+ return;
+ super.addDateHeader( name, date );
+ }
+
+ @Override
+ public void setHeader( String name, String value ) {
+ if( included )
+ return;
+ super.setHeader( name, value );
+ }
+
+ @Override
+ public void addHeader( String name, String value ) {
+ if( included )
+ return;
+ super.addHeader( name, value );
+ }
+
+ @Override
+ public void setIntHeader( String name, int value ) {
+ if( included )
+ return;
+ super.setIntHeader( name, value );
+ }
+
+ @Override
+ public void addIntHeader( String name, int value ) {
+ if( included )
+ return;
+ super.addIntHeader( name, value );
+ }
+
+ @Override
+ public void setStatus( int sc ) {
+ if( included )
+ return;
+ super.setStatus( sc );
+ }
+
+ @Override
+ public void setStatus( int sc, String sm ) {
+ if( included )
+ return;
+ super.setStatus( sc, sm );
+ }
+
+ @Override
+ public void setCharacterEncoding( String charEnc ) {
+ if( included )
+ return;
+ super.setCharacterEncoding( charEnc );
+ }
+}
Index: code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/DispatchedHttpServletRequest.java
===================================================================
--- code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/DispatchedHttpServletRequest.java (revision )
+++ code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/DispatchedHttpServletRequest.java (revision )
@@ -0,0 +1,529 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2008-2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ *
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 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.glassfish.grizzly.servlet;
+
+import org.glassfish.grizzly.http.server.util.Enumerator;
+import org.glassfish.grizzly.http.server.util.Globals;
+import org.glassfish.grizzly.http.server.util.ParameterMap;
+import org.glassfish.grizzly.http.util.Charsets;
+import org.glassfish.grizzly.http.util.DataChunk;
+import org.glassfish.grizzly.http.util.Parameters;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.nio.charset.Charset;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * Wrapper around a javax.servlet.http.HttpServletRequest
+ * that transforms an application request object (which might be the original
+ * one passed to a servlet.
+ *
+ * @author Bongjae Chang
+ */
+public class DispatchedHttpServletRequest extends HttpServletRequestWrapper {
+
+ private String contextPath;
+ private String requestURI;
+ private String servletPath;
+ private String pathInfo;
+ private String queryString;
+ private HashMap specialAttributes;
+ private DispatcherConstants.DispatcherType dispatcherType;
+ private Object requestDispatcherPath;
+
+ /**
+ * The set of attribute names that are special for request dispatchers
+ */
+ private static final HashSetRequestDispatcher
that allows a
+ * request to be forwarded to a different resource to create the ultimate
+ * response, or to include the output of another resource in the response
+ * from this resource. This implementation allows application level servlets
+ * to wrap the request and/or response objects that are passed on to the
+ * called resource, as long as the wrapping classes extend
+ * javax.servlet.ServletRequestWrapper
and
+ * javax.servlet.ServletResponseWrapper
.
+ *
+ * @author Craig R. McClanahan
+ * @author Bongjae Chang
+ */
+public final class RequestDispatcherImpl implements RequestDispatcher {
+
+ private final static Logger logger = Grizzly.logger( RequestDispatcherImpl.class );
+
+ /**
+ * is this dispatch cross context
+ */
+ private Boolean crossContextFlag = null;
+
+ /**
+ * The servlet name for a named dispatcher.
+ */
+ private String name = null;
+
+ /**
+ * The request URI for this RequestDispatcher.
+ */
+ private String requestURI = null;
+
+ /**
+ * The servlet path for this RequestDispatcher.
+ */
+ private String servletPath = null;
+
+ /**
+ * The extra path information for this RequestDispatcher.
+ */
+ private String pathInfo = null;
+
+ /**
+ * The query string parameters for this RequestDispatcher.
+ */
+ private String queryString = null;
+
+ /**
+ * The Wrapper associated with the resource that will be forwarded to
+ * or included.
+ */
+ private ServletHandler wrapper = null;
+
+ /**
+ * Construct a new instance of this class, configured according to the
+ * specified parameters. If both servletPath and pathInfo are
+ * null
, it will be assumed that this RequestDispatcher
+ * was acquired by name, rather than by path.
+ *
+ * @param wrapper The Wrapper associated with the resource that will
+ * be forwarded to or included (required)
+ * @param requestURI The request URI to this resource (if any)
+ * @param servletPath The revised servlet path to this resource (if any)
+ * @param pathInfo The revised extra path information to this resource
+ * (if any)
+ * @param queryString Query string parameters included with this request
+ * (if any)
+ * @param name Servlet name (if a named dispatcher was created)
+ * else null
+ */
+ public RequestDispatcherImpl( ServletHandler wrapper,
+ String requestURI,
+ String servletPath,
+ String pathInfo,
+ String queryString,
+ String name ) {
+ // Save all of our configuration parameters
+ this.wrapper = wrapper;
+ this.requestURI = requestURI;
+ this.servletPath = servletPath;
+ this.pathInfo = pathInfo;
+ this.queryString = queryString;
+ this.name = name;
+
+ if( logger.isLoggable( Level.FINE ) )
+ logger.fine( "servletPath=" + this.servletPath + ", pathInfo=" +
+ this.pathInfo + ", queryString=" + queryString +
+ ", name=" + this.name );
+ }
+
+ /**
+ * Forwards the given request and response to the resource
+ * for which this dispatcher was acquired.
+ *
+ * Any runtime exceptions, IOException, or ServletException thrown + * by the target will be propogated to the caller. + * + * @param request The request to be forwarded + * @param response The response to be forwarded + * + * @throws IOException if an input/output error occurs + * @throws ServletException if a servlet exception occurs + */ + public void forward( ServletRequest request, ServletResponse response ) + throws ServletException, IOException { + dispatch( request, response, DispatcherConstants.DispatcherType.FORWARD ); + } + + /** + * Dispatches the given request and response to the resource + * for which this dispatcher was acquired. + *
+ *Any runtime exceptions, IOException, or ServletException thrown + * by the target will be propogated to the caller. + * + * @param request The request to be forwarded + * @param response The response to be forwarded + * @param dispatcherType The type of dispatch to be performed + * + * @throws IOException if an input/output error occurs + * @throws ServletException if a servlet exception occurs + * @throws IllegalArgumentException if the dispatcher type is different + * from FORWARD, ERROR, and ASYNC + */ + @SuppressWarnings( "unchecked" ) + public void dispatch( ServletRequest request, ServletResponse response, DispatcherConstants.DispatcherType dispatcherType ) + throws ServletException, IOException { + if( !DispatcherConstants.DispatcherType.FORWARD.equals( dispatcherType ) && + !DispatcherConstants.DispatcherType.ERROR.equals( dispatcherType ) && + !DispatcherConstants.DispatcherType.ASYNC.equals( dispatcherType ) ) { + throw new IllegalArgumentException( "Illegal dispatcher type" ); + } + + boolean isCommit = ( DispatcherConstants.DispatcherType.FORWARD.equals( dispatcherType ) || + DispatcherConstants.DispatcherType.ERROR.equals( dispatcherType ) ); + + if( System.getSecurityManager() != null ) { + try { + PrivilegedDispatch dp = new PrivilegedDispatch( + request, response, dispatcherType ); + AccessController.doPrivileged( dp ); + // START SJSAS 6374990 + if( isCommit ) { + closeResponse( response ); + } + // END SJSAS 6374990 + } catch( PrivilegedActionException pe ) { + Exception e = pe.getException(); + if( e instanceof ServletException ) + throw (ServletException)e; + throw (IOException)e; + } + } else { + doDispatch( request, response, dispatcherType ); + // START SJSAS 6374990 + if( isCommit ) { + closeResponse( response ); + } + // END SJSAS 6374990 + } + } + + private void doDispatch( ServletRequest request, ServletResponse response, DispatcherConstants.DispatcherType dispatcherType ) + throws ServletException, IOException { + if( !DispatcherConstants.DispatcherType.ASYNC.equals( dispatcherType ) ) { + // Reset any output that has been buffered, but keep + // headers/cookies + if( response.isCommitted() ) { + if( logger.isLoggable( Level.FINE ) ) + logger.fine( " Forward on committed response --> ISE" ); + throw new IllegalStateException( "Cannot forward after response has been committed" ); + } + + try { + response.resetBuffer(); + } catch( IllegalStateException e ) { + if( logger.isLoggable( Level.FINE ) ) + logger.fine( " Forward resetBuffer() returned ISE: " + e ); + throw e; + } + } + + // Set up to handle the specified request and response + State state = new State( request, response, dispatcherType ); + + // Identify the HTTP-specific request and response objects (if any) + HttpServletRequest hrequest = null; + if( request instanceof HttpServletRequest ) { + hrequest = (HttpServletRequest)request; + } + HttpServletResponse hresponse = null; + if( response instanceof HttpServletResponse ) { + hresponse = (HttpServletResponse)response; + } + + if( ( hrequest == null ) || ( hresponse == null ) ) { + // Handle a non-HTTP forward + DispatchedHttpServletRequest wrequest = wrapRequest( state ); + processRequest( request, response, state, + wrequest.getRequestFacade() ); + unwrapRequest( state ); + wrequest.recycle(); + } else if( ( servletPath == null ) && ( pathInfo == null ) ) { + // Handle an HTTP named dispatcher forward + DispatchedHttpServletRequest wrequest = wrapRequest( state ); + wrequest.setContextPath( hrequest.getContextPath() ); + wrequest.setRequestURI( hrequest.getRequestURI() ); + wrequest.setServletPath( hrequest.getServletPath() ); + wrequest.setPathInfo( hrequest.getPathInfo() ); + wrequest.setQueryString( hrequest.getQueryString() ); + processRequest( request, response, state, wrequest.getRequestFacade() ); + unwrapRequest( state ); + wrequest.recycle(); + } else { + // Handle an HTTP path-based forward + DispatchedHttpServletRequest wrequest = wrapRequest( state ); + + // If the request is being FORWARD- or ASYNC-dispatched for + // the first time, initialize it with the required request + // attributes + if( ( DispatcherConstants.DispatcherType.FORWARD.equals( dispatcherType ) && + hrequest.getAttribute( DispatcherConstants.FORWARD_REQUEST_URI ) == null ) || + ( DispatcherConstants.DispatcherType.ASYNC.equals( dispatcherType ) && + hrequest.getAttribute( DispatcherConstants.ASYNC_REQUEST_URI ) == null ) ) { + wrequest.initSpecialAttributes( hrequest.getRequestURI(), + hrequest.getContextPath(), + hrequest.getServletPath(), + hrequest.getPathInfo(), + hrequest.getQueryString() ); + } + + String targetContextPath = wrapper.getContextPath(); + // START IT 10395 + HttpServletRequestImpl requestFacade = wrequest.getRequestFacade(); + String originContextPath = requestFacade.getContextPath(); + if( originContextPath != null && + originContextPath.equals( targetContextPath ) ) { + targetContextPath = hrequest.getContextPath(); + } + // END IT 10395 + wrequest.setContextPath( targetContextPath ); + wrequest.setRequestURI( requestURI ); + wrequest.setServletPath( servletPath ); + wrequest.setPathInfo( pathInfo ); + if( queryString != null ) { + wrequest.setQueryString( queryString ); + } + + processRequest( request, response, state, wrequest.getRequestFacade() ); + + unwrapRequest( state ); + wrequest.recycle(); + } + } + + + /** + * Prepare the request based on the filter configuration. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * + * @throws IOException if an input/output error occurs + * @throws ServletException if a servlet error occurs + */ + private void processRequest( ServletRequest request, + ServletResponse response, + State state, + HttpServletRequestImpl requestFacade ) + throws IOException, ServletException { + if( request != null ) { + if( state.dispatcherType != DispatcherConstants.DispatcherType.ERROR ) { + state.outerRequest.setAttribute( Globals.DISPATCHER_REQUEST_PATH_ATTR, getCombinedPath() ); + invoke( state.outerRequest, response, requestFacade ); + } else { + invoke( state.outerRequest, response, requestFacade ); + } + } + } + + /** + * Combines the servletPath and the pathInfo. + *
+ * If pathInfo isnull
, it is ignored. If servletPath
+ * is null
, then null
is returned.
+ *
+ * @return The combined path with pathInfo appended to servletInfo
+ */
+ private String getCombinedPath() {
+ if( servletPath == null ) {
+ return null;
+ }
+ if( pathInfo == null ) {
+ return servletPath;
+ }
+ return servletPath + pathInfo;
+ }
+
+ /**
+ * Include the response from another resource in the current response.
+ * Any runtime exception, IOException, or ServletException thrown by the
+ * called servlet will be propogated to the caller.
+ *
+ * @param request The servlet request that is including this one
+ * @param response The servlet response to be appended to
+ *
+ * @throws IOException if an input/output error occurs
+ * @throws ServletException if a servlet exception occurs
+ */
+ @SuppressWarnings( "unchecked" )
+ public void include( ServletRequest request, ServletResponse response )
+ throws ServletException, IOException {
+ if( System.getSecurityManager() != null ) {
+ try {
+ PrivilegedInclude dp = new PrivilegedInclude( request, response );
+ AccessController.doPrivileged( dp );
+ } catch( PrivilegedActionException pe ) {
+ Exception e = pe.getException();
+ if( e instanceof ServletException )
+ throw (ServletException)e;
+ throw (IOException)e;
+ }
+ } else {
+ doInclude( request, response );
+ }
+ }
+
+ private void doInclude( ServletRequest request, ServletResponse response )
+ throws ServletException, IOException {
+
+ // Set up to handle the specified request and response
+ State state = new State( request, response, DispatcherConstants.DispatcherType.INCLUDE );
+
+ // Create a wrapped response to use for this request
+ wrapResponse( state );
+
+ // START GlassFish 6386229
+ // Handle an HTTP named dispatcher include
+ if( name != null ) {
+ // END GlassFish 6386229
+ DispatchedHttpServletRequest wrequest = wrapRequest( state );
+ wrequest.setAttribute( Globals.NAMED_DISPATCHER_ATTR, name );
+ wrequest.setAttribute( Globals.DISPATCHER_REQUEST_PATH_ATTR, getCombinedPath() );
+ try {
+ invoke( state.outerRequest, state.outerResponse, wrequest.getRequestFacade() );
+ } finally {
+ unwrapRequest( state );
+ unwrapResponse( state );
+ }
+ }
+ // Handle an HTTP path based include
+ else {
+ DispatchedHttpServletRequest wrequest = wrapRequest( state );
+ wrequest.initSpecialAttributes( requestURI,
+ wrapper.getContextPath(),
+ servletPath,
+ pathInfo,
+ queryString );
+ if( queryString != null ) {
+ wrequest.setQueryString( queryString );
+ }
+ wrequest.setAttribute( Globals.DISPATCHER_REQUEST_PATH_ATTR,getCombinedPath() );
+ try {
+ invoke( state.outerRequest, state.outerResponse, wrequest.getRequestFacade() );
+ } finally {
+ unwrapRequest( state );
+ unwrapResponse( state );
+ }
+ }
+ }
+
+ /**
+ * Ask the resource represented by this RequestDispatcher to process
+ * the associated request, and create (or append to) the associated
+ * response.
+ *
+ * IMPLEMENTATION NOTE: This implementation assumes
+ * that no filters are applied to a forwarded or included resource,
+ * because they were already done for the original request.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ *
+ * @throws IOException if an input/output error occurs
+ * @throws ServletException if a servlet error occurs
+ */
+ private void invoke( ServletRequest request, ServletResponse response, HttpServletRequestImpl requestFacade )
+ throws IOException, ServletException {
+ boolean crossContext = false;
+ if( crossContextFlag != null && crossContextFlag.booleanValue() ) {
+ crossContext = true;
+ }
+ try {
+ doInvoke( request, response, crossContext, requestFacade );
+ } finally {
+ crossContextFlag = null;
+ }
+ }
+
+ /**
+ * Ask the resource represented by this RequestDispatcher to process
+ * the associated request, and create (or append to) the associated
+ * response.
+ *
+ * IMPLEMENTATION NOTE: This implementation assumes
+ * that no filters are applied to a forwarded or included resource,
+ * because they were already done for the original request.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ *
+ * @throws IOException if an input/output error occurs
+ * @throws ServletException if a servlet error occurs
+ */
+ private void doInvoke( ServletRequest request, ServletResponse response, boolean crossContext, HttpServletRequestImpl requestFacade )
+ throws IOException, ServletException {
+ ClassLoader oldCCL = null;
+ try {
+ // Checking to see if the context classloader is the current context
+ // classloader. If it's not, we're saving it, and setting the context
+ // classloader to the Context classloader
+ if( crossContext ) {
+ ClassLoader contextClassLoader = wrapper.getClassLoader();
+ if( contextClassLoader != null ) {
+ oldCCL = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader( contextClassLoader );
+ }
+ }
+ wrapper.doServletService( request, response );
+ } finally {
+ // Reset the old context class loader
+ if( oldCCL != null )
+ Thread.currentThread().setContextClassLoader( oldCCL );
+ }
+ }
+
+ /**
+ * Unwrap the request if we have wrapped it.
+ */
+ private void unwrapRequest( State state ) {
+ if( state.wrapRequest == null )
+ return;
+
+ ServletRequest previous = null;
+ ServletRequest current = state.outerRequest;
+
+ while( current != null ) {
+
+ // If we run into the container request we are done
+ if( current instanceof HttpServletRequestImpl )
+ break;
+
+ // Remove the current request if it is our wrapper
+ if( current == state.wrapRequest ) {
+ ServletRequest next =
+ ( (ServletRequestWrapper)current ).getRequest();
+ if( previous == null )
+ state.outerRequest = next;
+ else
+ ( (ServletRequestWrapper)previous ).setRequest( next );
+ break;
+ }
+
+ // Advance to the next request in the chain
+ previous = current;
+ current = ( (ServletRequestWrapper)current ).getRequest();
+ }
+ }
+
+ /**
+ * Unwrap the response if we have wrapped it.
+ */
+ private void unwrapResponse( State state ) {
+
+ if( state.wrapResponse == null )
+ return;
+
+ ServletResponse previous = null;
+ ServletResponse current = state.outerResponse;
+
+ while( current != null ) {
+
+ // If we run into the container response we are done
+ if( current instanceof HttpServletResponseImpl )
+ break;
+
+ // Remove the current response if it is our wrapper
+ if( current == state.wrapResponse ) {
+ ServletResponse next =
+ ( (ServletResponseWrapper)current ).getResponse();
+ if( previous == null )
+ state.outerResponse = next;
+ else
+ ( (ServletResponseWrapper)previous ).setResponse( next );
+ break;
+ }
+
+ // Advance to the next response in the chain
+ previous = current;
+ current = ( (ServletResponseWrapper)current ).getResponse();
+ }
+ }
+
+ /**
+ * Create and return a request wrapper that has been inserted in the
+ * appropriate spot in the request chain.
+ */
+ private DispatchedHttpServletRequest wrapRequest( State state ) {
+
+ // Locate the request we should insert in front of
+ ServletRequest previous = null;
+ ServletRequest current = state.outerRequest;
+
+ while( current != null ) {
+ if( !( current instanceof ServletRequestWrapper ) ) {
+ break;
+ }
+ // If we find container-generated wrapper, break out
+ if( current instanceof DispatchedHttpServletRequest ) {
+ break;
+ }
+ if( current instanceof HttpServletRequestImpl ) {
+ break;
+ }
+
+ previous = current;
+ current = ( (ServletRequestWrapper)current ).getRequest();
+ }
+ if( current == null ) {
+ return null;
+ }
+
+ // Compute a crossContext flag
+ HttpServletRequest hcurrent = (HttpServletRequest)current;
+ boolean crossContext = !( wrapper.getContextPath().equals( hcurrent.getContextPath() ) );
+ //START OF 6364900
+ crossContextFlag = Boolean.valueOf( crossContext );
+
+ // Instantiate a new wrapper and insert it in the chain
+ DispatchedHttpServletRequest wrapper = new DispatchedHttpServletRequest( hcurrent, state.dispatcherType );
+ if( previous == null ) {
+ state.outerRequest = wrapper;
+ } else {
+ ( (ServletRequestWrapper)previous ).setRequest( wrapper );
+ }
+
+ state.wrapRequest = wrapper;
+
+ return wrapper;
+ }
+
+ /**
+ * Create and return a response wrapper that has been inserted in the
+ * appropriate spot in the response chain.
+ */
+ private ServletResponse wrapResponse( State state ) {
+
+ // Locate the response we should insert in front of
+ ServletResponse previous = null;
+ ServletResponse current = state.outerResponse;
+
+ while( current != null ) {
+ if( !( current instanceof ServletResponseWrapper ) )
+ break;
+ if( current instanceof DispatchedHttpServletResponse )
+ break;
+ if( current instanceof HttpServletResponseImpl )
+ break;
+
+ previous = current;
+ current = ( (ServletResponseWrapper)current ).getResponse();
+ }
+
+ if( current == null ) {
+ return null;
+ }
+
+ HttpServletResponse hcurrent = (HttpServletResponse)current;
+ // Instantiate a new wrapper and insert it in the chain
+ DispatchedHttpServletResponse wrapper = new DispatchedHttpServletResponse( hcurrent,
+ DispatcherConstants.DispatcherType.INCLUDE.equals( state.dispatcherType ) );
+ if( previous == null )
+ state.outerResponse = wrapper;
+ else
+ ( (ServletResponseWrapper)previous ).setResponse( wrapper );
+ state.wrapResponse = wrapper;
+
+ return wrapper;
+ }
+
+ private static void closeResponse( ServletResponse response ) {
+ try {
+ PrintWriter writer = response.getWriter();
+ writer.flush();
+ writer.close();
+ } catch( IllegalStateException e ) {
+ try {
+ ServletOutputStream stream = response.getOutputStream();
+ stream.flush();
+ stream.close();
+ } catch( IllegalStateException f ) {
+ ;
+ } catch( IOException f ) {
+ ;
+ }
+ } catch( IOException e ) {
+ ;
+ }
+ }
+
+ private class PrivilegedDispatch implements PrivilegedExceptionAction {
+
+ private ServletRequest request;
+ private ServletResponse response;
+ private DispatcherConstants.DispatcherType dispatcherType;
+
+ PrivilegedDispatch( ServletRequest request, ServletResponse response,
+ DispatcherConstants.DispatcherType dispatcherType ) {
+ this.request = request;
+ this.response = response;
+ this.dispatcherType = dispatcherType;
+ }
+
+ public Object run() throws java.lang.Exception {
+ doDispatch( request, response, dispatcherType );
+ return null;
+ }
+ }
+
+ private class PrivilegedInclude implements PrivilegedExceptionAction {
+
+ private ServletRequest request;
+ private ServletResponse response;
+
+ PrivilegedInclude( ServletRequest request, ServletResponse response ) {
+ this.request = request;
+ this.response = response;
+ }
+
+ public Object run() throws ServletException, IOException {
+ doInclude( request, response );
+ return null;
+ }
+ }
+
+ /**
+ * Used to pass state when the request dispatcher is used. Using instance
+ * variables causes threading issues and state is too complex to pass and
+ * return single ServletRequest or ServletResponse objects.
+ */
+ private static class State {
+
+ // Outermost request that will be passed on to the invoked servlet
+ ServletRequest outerRequest = null;
+
+ // Outermost response that will be passed on to the invoked servlet.
+ ServletResponse outerResponse = null;
+
+ // Request wrapper we have created and installed (if any).
+ ServletRequest wrapRequest = null;
+
+ // Response wrapper we have created and installed (if any).
+ ServletResponse wrapResponse = null;
+
+ // The type of dispatch we are performing
+ DispatcherConstants.DispatcherType dispatcherType;
+
+ State( ServletRequest request, ServletResponse response, DispatcherConstants.DispatcherType dispatcherType ) {
+ this.outerRequest = request;
+ this.outerResponse = response;
+ this.dispatcherType = dispatcherType;
+ }
+ }
+}
Index: code/modules/http/src/main/java/org/glassfish/grizzly/http/util/Parameters.java
===================================================================
--- code/modules/http/src/main/java/org/glassfish/grizzly/http/util/Parameters.java (revision 6158)
+++ code/modules/http/src/main/java/org/glassfish/grizzly/http/util/Parameters.java (revision )
@@ -236,7 +236,7 @@
return currentChild.paramHashStringArray.keys();
*/
// START PWC 6057385
- currentChild.paramHashStringArray.keySet();
+ return currentChild.paramHashStringArray.keySet();
// END PWC 6057385
}
// merge in child
Index: code/modules/http-servlet/src/test/java/org/glassfish/grizzly/servlet/RequestDispatcherTest.java
===================================================================
--- code/modules/http-servlet/src/test/java/org/glassfish/grizzly/servlet/RequestDispatcherTest.java (revision )
+++ code/modules/http-servlet/src/test/java/org/glassfish/grizzly/servlet/RequestDispatcherTest.java (revision )
@@ -0,0 +1,410 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2009-2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+package org.glassfish.grizzly.servlet;
+
+import org.glassfish.grizzly.utils.Utils;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+
+/**
+ * Request/NamedDispatcher Test
+ *
+ * @author Bongjae Chang
+ */
+public class RequestDispatcherTest extends HttpServerAbstractTest {
+
+ private static final int PORT = 18890 + 12;
+
+ public void testForward() throws IOException {
+ Utils.dumpOut( "testForward" );
+ try {
+ newHttpServer( PORT );
+
+ String contextPath = "/webapp";
+
+ ServletHandler servletHandler1 = new ServletHandler();
+ servletHandler1.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet1" );
+
+ // relative path test
+ RequestDispatcher dispatcher = request.getRequestDispatcher( "servlet2" );
+ assertNotNull( dispatcher );
+ dispatcher.forward( request, response );
+ out.close();
+ }
+ } );
+ servletHandler1.setContextPath( contextPath );
+ servletHandler1.setServletPath( "/servlet1" );
+ addHttpHandler( contextPath + "/servlet1", servletHandler1 );
+
+ ServletHandler servletHandler2 = new ServletHandler();
+ servletHandler2.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet2" );
+ out.close();
+ }
+ } );
+ servletHandler2.setContextPath( contextPath );
+ servletHandler2.setServletPath( "/servlet2" );
+ addHttpHandler( contextPath + "/servlet2", servletHandler2 );
+
+ httpServer.start();
+ HttpURLConnection conn = getConnection( "/webapp/servlet1", PORT );
+ assertEquals( HttpServletResponse.SC_OK, getResponseCodeFromAlias( conn ) );
+ assertEquals( "Hello, world! I am a servlet2", readMultilineResponse( conn ).toString().trim() );
+ } finally {
+ stopHttpServer();
+ }
+ }
+
+ public void testInclude() throws IOException {
+ Utils.dumpOut( "testInclude" );
+ try {
+ newHttpServer( PORT );
+
+ String contextPath = "/webapp";
+
+ ServletHandler servletHandler1 = new ServletHandler();
+ servletHandler1.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet1" );
+
+ // relative path test
+ RequestDispatcher dispatcher = request.getRequestDispatcher( "servlet2" );
+ assertNotNull( dispatcher );
+ dispatcher.include( request, response );
+ out.close();
+ }
+ } );
+ servletHandler1.setContextPath( contextPath );
+ servletHandler1.setServletPath( "/servlet1" );
+ addHttpHandler( contextPath + "/servlet1", servletHandler1 );
+
+ ServletHandler servletHandler2 = new ServletHandler();
+ servletHandler2.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet2" );
+ out.close();
+ }
+ } );
+ servletHandler2.setContextPath( contextPath );
+ servletHandler2.setServletPath( "/servlet2" );
+ addHttpHandler( contextPath + "/servlet2", servletHandler2 );
+
+ httpServer.start();
+ HttpURLConnection conn = getConnection( "/webapp/servlet1", PORT );
+ assertEquals( HttpServletResponse.SC_OK, getResponseCodeFromAlias( conn ) );
+ assertEquals( "Hello, world! I am a servlet1\nHello, world! I am a servlet2", readMultilineResponse( conn ).toString().trim() );
+ } finally {
+ stopHttpServer();
+ }
+ }
+
+ public void testNamedDispatcherForward() throws IOException {
+ Utils.dumpOut( "testNamedDispatcherForward" );
+ try {
+ newHttpServer( PORT );
+
+ String contextPath = "/webapp";
+
+ ServletHandler servletHandler1 = new ServletHandler();
+ servletHandler1.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet1" );
+
+ ServletContext servletCtx = getServletContext();
+ assertNotNull( servletCtx );
+ RequestDispatcher dispatcher = servletCtx.getNamedDispatcher( "servlet2" );
+ assertNotNull( dispatcher );
+ dispatcher.forward( request, response );
+ out.close();
+ }
+ } );
+ servletHandler1.setContextPath( contextPath );
+ servletHandler1.setServletPath( "/servlet1" );
+ addHttpHandler( contextPath + "/servlet1", servletHandler1 );
+
+ ServletHandler servletHandler2 = new ServletHandler();
+ servletHandler2.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet2" );
+ out.close();
+ }
+ } );
+ servletHandler2.setContextPath( contextPath );
+ servletHandler2.setServletPath( "/servlet2" );
+ servletHandler2.setName( "servlet2" );
+ addHttpHandler( contextPath + "/servlet2", servletHandler2 );
+
+ httpServer.start();
+ HttpURLConnection conn = getConnection( "/webapp/servlet1", PORT );
+ assertEquals( HttpServletResponse.SC_OK, getResponseCodeFromAlias( conn ) );
+ assertEquals( "Hello, world! I am a servlet2", readMultilineResponse( conn ).toString().trim() );
+ } finally {
+ stopHttpServer();
+ }
+ }
+
+ public void testNamedDispatcherInclude() throws IOException {
+ Utils.dumpOut( "testNamedDispatcherInclude" );
+ try {
+ newHttpServer( PORT );
+
+ String contextPath = "/webapp";
+
+ ServletHandler servletHandler1 = new ServletHandler();
+ servletHandler1.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet1" );
+
+ ServletContext servletCtx = getServletContext();
+ assertNotNull( servletCtx );
+ RequestDispatcher dispatcher = servletCtx.getNamedDispatcher( "servlet2" );
+ assertNotNull( dispatcher );
+ dispatcher.include( request, response );
+ out.close();
+ }
+ } );
+ servletHandler1.setContextPath( contextPath );
+ servletHandler1.setServletPath( "/servlet1" );
+ addHttpHandler( contextPath + "/servlet1", servletHandler1 );
+
+ ServletHandler servletHandler2 = new ServletHandler();
+ servletHandler2.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet2" );
+ out.close();
+ }
+ } );
+ servletHandler2.setContextPath( contextPath );
+ servletHandler2.setServletPath( "/servlet2" );
+ servletHandler2.setName( "servlet2" );
+ addHttpHandler( contextPath + "/servlet2", servletHandler2 );
+
+ httpServer.start();
+ HttpURLConnection conn = getConnection( "/webapp/servlet1", PORT );
+ assertEquals( HttpServletResponse.SC_OK, getResponseCodeFromAlias( conn ) );
+ assertEquals( "Hello, world! I am a servlet1\nHello, world! I am a servlet2", readMultilineResponse( conn ).toString().trim() );
+ } finally {
+ stopHttpServer();
+ }
+ }
+
+ public void testCrossContextForward() throws IOException {
+ Utils.dumpOut( "testCrossContextForward" );
+ try {
+ newHttpServer( PORT );
+
+ ServletHandler servletHandler1 = new ServletHandler();
+ servletHandler1.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet1" );
+
+ ServletContext servletCtx1 = getServletContext();
+ assertNotNull( servletCtx1 );
+ RequestDispatcher dispatcher = request.getRequestDispatcher( "servlet2" );
+ assertNull( dispatcher );
+
+ // cross context
+ ServletContext servletCtx2 = servletCtx1.getContext( "/webapp2" );
+ assertNotNull( servletCtx2 );
+ // The pathname must begin with a "/"
+ dispatcher = servletCtx2.getRequestDispatcher( "/servlet2" );
+ assertNotNull( dispatcher );
+ dispatcher.forward( request, response );
+ out.close();
+ }
+ } );
+ servletHandler1.setContextPath( "/webapp1" );
+ servletHandler1.setServletPath( "/servlet1" );
+ addHttpHandler( "/webapp1/servlet1", servletHandler1 );
+
+ ServletHandler servletHandler2 = new ServletHandler();
+ servletHandler2.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet2" );
+ out.close();
+ }
+ } );
+ servletHandler2.setContextPath( "/webapp2" );
+ servletHandler2.setServletPath( "/servlet2" );
+ addHttpHandler( "/webapp2/servlet2", servletHandler2 );
+
+ httpServer.start();
+ HttpURLConnection conn = getConnection( "/webapp1/servlet1", PORT );
+ assertEquals( HttpServletResponse.SC_OK, getResponseCodeFromAlias( conn ) );
+ assertEquals( "Hello, world! I am a servlet2", readMultilineResponse( conn ).toString().trim() );
+ } finally {
+ stopHttpServer();
+ }
+ }
+
+ public void testComplexDispatch() throws IOException {
+ // servlet1 --> dispatcher forward by ServletRequest's API(servlet2) ->
+ // named dispatcher include(servlet3) -> cross context, dispatcher include by ServletContext's API(servlet4)
+ Utils.dumpOut( "testComplexDispatch" );
+ try {
+ newHttpServer( PORT );
+
+ // webapp1(servlet1, servlet2, servlet3)
+ ServletHandler servletHandler1 = new ServletHandler();
+ servletHandler1.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet1" );
+
+ RequestDispatcher dispatcher = request.getRequestDispatcher( "servlet2" );
+ assertNotNull( dispatcher );
+ dispatcher.forward( request, response );
+ out.close();
+ }
+ } );
+ servletHandler1.setContextPath( "/webapp1" );
+ servletHandler1.setServletPath( "/servlet1" );
+ addHttpHandler( "/webapp1/servlet1", servletHandler1 );
+
+ ServletHandler servletHandler2 = new ServletHandler();
+ servletHandler2.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet2" );
+
+ ServletContext servletCtx = getServletContext();
+ assertNotNull( servletCtx );
+ RequestDispatcher dispatcher = servletCtx.getNamedDispatcher( "servlet3" );
+ assertNotNull( dispatcher );
+ dispatcher.include( request, response );
+ out.close();
+ }
+ } );
+ servletHandler2.setContextPath( "/webapp1" );
+ servletHandler2.setServletPath( "/servlet2" );
+ addHttpHandler( "/webapp1/servlet2", servletHandler2 );
+
+ ServletHandler servletHandler3 = new ServletHandler();
+ servletHandler3.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet3" );
+
+ ServletContext servletCtx1 = getServletContext();
+ assertNotNull( servletCtx1 );
+ ServletContext servletCtx2 = servletCtx1.getContext( "/webapp2" );
+ assertNotNull( servletCtx2 );
+ RequestDispatcher dispatcher = servletCtx2.getRequestDispatcher( "/servlet4" );
+ dispatcher.include( request, response );
+ out.close();
+ }
+ } );
+ servletHandler3.setContextPath( "/webapp1" );
+ servletHandler3.setServletPath( "/servlet3" );
+ servletHandler3.setName( "servlet3" );
+ addHttpHandler( "/webapp1/servlet3", servletHandler3 );
+
+ // webapp2(servlet4)
+ ServletHandler servletHandler4 = new ServletHandler();
+ servletHandler4.setServletInstance( new HttpServlet() {
+ @Override
+ public void doGet( HttpServletRequest request, HttpServletResponse response )
+ throws ServletException, IOException {
+ PrintWriter out = response.getWriter();
+ out.println( "Hello, world! I am a servlet4" );
+ out.close();
+ }
+ } );
+ servletHandler4.setContextPath( "/webapp2" );
+ servletHandler4.setServletPath( "/servlet4" );
+ addHttpHandler( "/webapp2/servlet4", servletHandler4 );
+
+ httpServer.start();
+ HttpURLConnection conn = getConnection( "/webapp1/servlet1", PORT );
+ assertEquals( HttpServletResponse.SC_OK, getResponseCodeFromAlias( conn ) );
+ assertEquals( "Hello, world! I am a servlet2\nHello, world! I am a servlet3\nHello, world! I am a servlet4",
+ readMultilineResponse( conn ).toString().trim() );
+ } finally {
+ stopHttpServer();
+ }
+ }
+}
Index: code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/HttpServletRequestImpl.java
===================================================================
--- code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/HttpServletRequestImpl.java (revision 6018)
+++ code/modules/http-servlet/src/main/java/org/glassfish/grizzly/servlet/HttpServletRequestImpl.java (revision )
@@ -412,7 +412,7 @@
}
-
+
/**
* {@inheritDoc}
*/
@@ -676,18 +676,66 @@
}
-
/**
* {@inheritDoc}
*/
@Override
- @SuppressWarnings("unchecked")
+ @SuppressWarnings( "unchecked" )
- public RequestDispatcher getRequestDispatcher(String path) {
+ public RequestDispatcher getRequestDispatcher( String path ) {
- throw new UnsupportedOperationException("Not supported yet.");
+ if( request == null ) {
+ throw new IllegalStateException( sm.getString( "requestFacade.nullRequest" ) );
- }
+ }
+ if( System.getSecurityManager() != null ) {
+ return (RequestDispatcher)AccessController.doPrivileged(
+ new GetRequestDispatcherPrivilegedAction( path ) );
+ } else {
+ return getRequestDispatcherInternal( path );
+ }
+ }
-
+
+ private RequestDispatcher getRequestDispatcherInternal( String path ) {
+ if( contextImpl == null ) {
+ return null;
+ }
+
+ // If the path is already context-relative, just pass it through
+ if( path == null ) {
+ return ( null );
+ } else if( path.startsWith( "/" ) ) {
+ return ( contextImpl.getRequestDispatcher( path ) );
+ }
+
+ // Convert a request-relative path to a context-relative one
+ String servletPath = (String)getAttribute( DispatcherConstants.INCLUDE_SERVLET_PATH );
+ if( servletPath == null ) {
+ servletPath = getServletPath();
+ }
+
+ // Add the path info, if there is any
+ String pathInfo = getPathInfo();
+ String requestPath = null;
+
+ if( pathInfo == null ) {
+ requestPath = servletPath;
+ } else {
+ requestPath = servletPath + pathInfo;
+ }
+
+ int pos = requestPath.lastIndexOf( '/' );
+ String relative = null;
+ if( pos >= 0 ) {
+ relative = requestPath.substring( 0, pos + 1 ) + path;
+ } else {
+ relative = requestPath + path;
+ }
+
+ return contextImpl.getRequestDispatcher( relative );
+ }
+
+
+
/**
* {@inheritDoc}
*/
@@ -1241,7 +1289,7 @@
}
- private static final class GetRequestDispatcherPrivilegedAction
+ private final class GetRequestDispatcherPrivilegedAction
implements PrivilegedAction {
private final String path;
@@ -1252,7 +1300,7 @@
@Override
public Object run() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return getRequestDispatcherInternal(path);
}
}