[Resending - attachments not accepted.]

Hello, 

I have a nagging problem with a wrapper-filter for file upload requests. The 
core of the problem is that 
request.getRequestDispatcher(String aPath)
is not behaving as expected. I am passing *query params* in 'aPath'. When I use 
a file upload wrapper on the request, these query params are not visible to the 
target JSP.

Attached is a minimal harness to demonstrate the problem. It uses a Controller 
that traps all '*.test' requests, and does a hard-coded forward to 
'test.jsp?x=1', using RequestDispatcher.

The test.jsp displays all request params that it can see. It also does double 
duty by POSTing data to the Controller.

When (and only when) the FileUploadWrapper is used, the hard-coded x=1 param is 
no longer visible in test.jsp - that is the issue. Am I missing something? 

For reference :
The docs for ServletRequestWrapper:
http://java.sun.com/javaee/5/docs/api/javax/servlet/ServletRequestWrapper.html#getRequestDispatcher(java.lang.String)

The wrapper I am using (FileUploadWrapper):
 - extends HttpsServletRequestWrapper
 - does not override getRequestDispatcher(String)

Hence :
- the default impl in ServletRequestWrapper is used for 
getRequestDispatcher(String)
- that impl does a call-forward to the underlying request (right?)

Given that, why does it fail? Is there something different about file upload 
requests that makes this fail? Why? It's not that the forward fails entirely - 
only that the *params* are not passed along.Using Tomcat 5.5.23 on Win 
XP.Thanks in advance,John 
O'Hanley-----------------------------------------------(Sorry if the formatting 
is crappy...)CONTROLLER :package hirondelle.fish.exercise.fileupload;import 
java.io.IOException;import javax.servlet.*;import javax.servlet.http.*;public 
final class TestController extends HttpServlet { @Override protected void 
doGet(HttpServletRequest aRequest, HttpServletResponse aResponse) throws 
ServletException, IOException {  RequestDispatcher dispatcher = 
aRequest.getRequestDispatcher("test.jsp?x=1");  dispatcher.forward(aRequest, 
aResponse); } @Override protected void doPost(HttpServletRequest aRequest, 
HttpServletResponse aResponse) throws ServletException, IOException {  
RequestDispatcher dispatcher = aRequest.getRequestDispatcher("test.jsp?x=1");  
dispatcher.forward(aRequest, aResponse); 
}-----------------------------------------------------------------------------------TEST
 FILTER :package hirondelle.fish.exercise.fileupload;import 
java.io.IOException;import javax.servlet.*;import 
javax.servlet.http.HttpServletRequest;public final class TestFilter implements 
Filter {public void init(FilterConfig aConfig) throws ServletException { 
}public void destroy() { }public void doFilter(ServletRequest aRequest, 
ServletResponse aResponse, FilterChain aChain) throws IOException, 
ServletException { //doNothing(aRequest, aResponse, aChain); 
useDoNothingWrapper(aRequest, aResponse, aChain); 
//useFileUploadWrapper(aRequest, aResponse, aChain);}/** Behaves as expected : 
params OK */private void doNothing(ServletRequest aRequest, ServletResponse 
aResponse, FilterChain aChain) throws IOException, ServletException { 
aChain.doFilter(aRequest, aResponse);}/** * Behaves as expected : params OK.* * 
Params seen in destination page :* - form controls : yes* - x=1 : yes*/private 
void useDoNothingWrapper(ServletRequest aRequest, ServletResponse aResponse, 
FilterChain aChain) throws IOException, ServletException { HttpServletRequest 
request = (HttpServletRequest) aRequest; TestWrapper wrapper = new 
TestWrapper(request); aChain.doFilter(wrapper, aResponse);}/*** Does not behave 
as expected. The 'x=1' param seen in other styles is not seen here.* * 
<P>Params seen in destination page :* - form controls : yes (the form contains 
a file upload control)* - x=1 : NO*/private void 
useFileUploadWrapper(ServletRequest aRequest, ServletResponse aResponse, 
FilterChain aChain) throws IOException, ServletException { HttpServletRequest 
request = (HttpServletRequest) aRequest; FileUploadWrapper wrapper = new 
FileUploadWrapper(request); aChain.doFilter(wrapper, 
aResponse);}}----------------------------------------------------------------------------------------------TEST
 WRAPPER :package hirondelle.fish.exercise.fileupload;import 
javax.servlet.http.HttpServletRequestWrapper;import 
javax.servlet.http.HttpServletRequest;public final class TestWrapper extends 
HttpServletRequestWrapper { public TestWrapper(HttpServletRequest aRequest){  
super(aRequest); 
}}---------------------------------------------------------------------------------------FILE
 UPLOAD WRAPPER:package hirondelle.fish.exercise.fileupload;import 
java.util.*;import java.io.*;import java.util.logging.*;import 
javax.servlet.http.HttpServletRequestWrapper;import 
javax.servlet.http.HttpServletRequest;import 
org.apache.commons.fileupload.FileUploadException;import 
org.apache.commons.fileupload.servlet.ServletFileUpload;import 
org.apache.commons.fileupload.disk.DiskFileItemFactory;import 
org.apache.commons.fileupload.FileItem;import static 
hirondelle.web4j.util.Consts.EMPTY_STRING;import 
hirondelle.web4j.util.Util;/*** Wrapper for a file upload request.* * <P>File 
upload requests are not handled graciously by the Servlet API. * This class is 
used as a wrapper around the underlying file upload request, to * allow it to 
behave much as a regular request.* * <P>This class uses the Apache Commons <a 
href='http://commons.apache.org/fileupload/'>File Upload tool</a>.* The 
generous Apache License will very likely allow you to use it in your 
applications as well. */public class FileUploadWrapper extends 
HttpServletRequestWrapper {/** Constructor. */public 
FileUploadWrapper(HttpServletRequest aRequest) throws IOException { 
super(aRequest); ServletFileUpload upload = new ServletFileUpload( new 
DiskFileItemFactory()); try { List<FileItem> fileItems = 
upload.parseRequest(aRequest); convertToMaps(fileItems); //logParams(); } 
catch(FileUploadException ex){ throw new IOException("Cannot parse underlying 
request: " + ex.toString()); }}/*** Return all request parameter names, for 
both regular controls and file upload controls.* * <P>Returning the name of 
file upload controls allows checking of the returned param names versus * a 
'white list' of expected names. This increases security, which is especially 
important for file upload forms.* See [EMAIL PROTECTED] 
hirondelle.web4j.security.ApplicationFirewallImpl} as well. */@Override public 
Enumeration getParameterNames() { Set<String> allNames = new 
LinkedHashSet<String>(); allNames.addAll(fRegularParams.keySet()); 
allNames.addAll(fFileParams.keySet()); return 
Collections.enumeration(allNames);}/*** Return the parameter value. Applies 
only to regular parameters, not to file upload parameters.* * <P>If the 
parameter is not present in the underlying request, then <tt>null</tt> is 
returned.* <P>If the parameter is present, but has no associated value, then an 
empty string is returned.* <P>If the parameter is multivalued, return the first 
value that appears in the request. */@Override public String 
getParameter(String aName) { String result = null; List<String> values = 
fRegularParams.get(aName); if( values == null ){ //param name not known - 
return null } else if ( values.isEmpty() ) { //param name known, but no values 
present result = EMPTY_STRING; } else { //return first value in list result = 
values.get(FIRST_VALUE); } return result;}/*** Return the parameter values. 
Applies only to regular parameters, not to file upload parameters.*/@Override 
public String[] getParameterValues(String aName) { String[] result = null; 
List<String> values = fRegularParams.get(aName); if( values != null ) { result 
= values.toArray(new String[values.size()]); }  return result;}/*** Return a 
[EMAIL PROTECTED] Map<String, String>} for all regular parameters.* Does not 
return any file upload paramters at all. */@Override public Map 
getParameterMap() { return Collections.unmodifiableMap(fRegularParams);}/*** 
Return a [EMAIL PROTECTED] List<FileItem>}, in the same order as they appear in 
the underlying request.*/public List<FileItem> getFileItems(){ return new 
ArrayList<FileItem>(fFileParams.values());}/*** Return the [EMAIL PROTECTED] 
FileItem} of the given name.* <P>If the name is unknown, then return 
<tt>null</tt>.*/public FileItem getFileItem(String aFieldName){ return 
fFileParams.get(aFieldName);}// PRIVATE ///** Store regular params only. May be 
multivalued (hence the List). */private final Map<String, List<String>> 
fRegularParams = new LinkedHashMap<String, List<String>>();/** Store file 
params only. */private final Map<String, FileItem> fFileParams = new 
LinkedHashMap<String, FileItem>();private static final int FIRST_VALUE = 
0;private static final Logger fLogger = 
Util.getLogger(FileUploadWrapper.class);private void 
convertToMaps(List<FileItem> aFileItems){ for(FileItem item: aFileItems) {  if 
( isFileUploadField(item) ) {  fFileParams.put(item.getFieldName(), item);  }  
else {   if( alreadyHasValue(item) ){    addMultivaluedItem(item);   }   else { 
   addSingleValueItem(item);   }  } }}private boolean 
isFileUploadField(FileItem aFileItem){ return ! 
aFileItem.isFormField();}private boolean alreadyHasValue(FileItem aItem){ 
return fRegularParams.get(aItem.getFieldName()) != null;}private void 
addSingleValueItem(FileItem aItem){ List<String> list = new 
ArrayList<String>(); list.add(aItem.getString()); 
fRegularParams.put(aItem.getFieldName(), list);}private void 
addMultivaluedItem(FileItem aItem){ List<String> values = 
fRegularParams.get(aItem.getFieldName()); 
values.add(aItem.getString());}private void logParams(){ 
fLogger.fine(Util.logOnePerLine(fRegularParams));  
fLogger.fine(Util.logOnePerLine(fFileParams)); 
}}--------------------------------------------------------------------------------------------WEB.XML
 :<web-app   xmlns="http://java.sun.com/xml/ns/j2ee";   
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";  
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd";  version="2.4">  
<display-name>Test</display-name>  <description>Test</description>    <filter>  
 <filter-name>TestFilter</filter-name>   
<display-name>TestFilter</display-name>   
<filter-class>hirondelle.fish.exercise.fileupload.TestFilter</filter-class> 
</filter>   <filter-mapping>   <filter-name>TestFilter</filter-name>   
<servlet-name>TestController</servlet-name>  </filter-mapping>    <!-- Required 
by file upload tool. -->  <listener>    
<listener-class>org.apache.commons.fileupload.servlet.FileCleanerCleanup</listener-class>
  </listener>   <servlet>    <servlet-name>TestController</servlet-name>    
<servlet-class>hirondelle.fish.exercise.fileupload.TestController</servlet-class>
  </servlet>    <servlet-mapping>    
<servlet-name>TestController</servlet-name>    
<url-pattern>*.test</url-pattern>  </servlet-mapping>    <session-config>    
<session-timeout>15</session-timeout>  </session-config>     <taglib>   
<taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>   
<taglib-location>/WEB-INF/tlds/c.tld</taglib-location>  </taglib>    <taglib>   
<taglib-uri>http://java.sun.com/jsp/jstl/fmt</taglib-uri>   
<taglib-location>/WEB-INF/tlds/fmt.tld</taglib-location>  </taglib>  <taglib>   
<taglib-uri>http://java.sun.com/jsp/jstl/functions</taglib-uri>   
<taglib-location>/WEB-INF/tlds/fn.tld</taglib-location>  </taglib>    
</web-app>-------------------------------------------------------------------------test.jsp<%@
 taglib uri="http://java.sun.com/jsp/jstl/core"; prefix="c" %> <!DOCTYPE HTML 
PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";> 
<html><head><title>Test</title></head><body><%--Show all param names and 
values. --%> <c:forEach var="aParam" items="${paramValues}">param: 
${aParam.key}values: <c:forEach var="aValue" 
items="${aParam.value}">${aValue}</c:forEach><br></c:forEach><P>Form for 
uploading a file:<P><form action='Blah.test' enctype="multipart/form-data" 
method="POST"><table align="center"><tr><td><label>Description</label> 
*</td><td><input name="Description" type="text"></td></tr><tr><td><label>Image 
File</label> *</td><td><input name="ImageFile" type="file"></td></tr><tr><td 
align="center" colspan=2><input type="submit" value="Submit 
File"></td></tr></table></form><P>Regular form, no file upload control:<P><form 
action='Blah.test' method="POST"><table align="center"><tr><td><label>Some 
Entry</label> *</td><td><input name="Entry" type="text"></td></tr><tr><td 
align="center" colspan=2><input type="submit" value="Submit 
File"></td></tr></table></form></body></html>

Reply via email to