[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>