views:

169

answers:

2

Hi,

My web application is developed with Struts2 and it was working fine till recently. All of a sudden one of the modules has started malfunctioning.

The malfunctioning module is 'Update Student details' page. This page has a lot of fields like 'schoolName', 'degreeName', etc .

School 1: <input name="schoolName">

School 2: <input name="schoolName">

..... 
School n: <input name="schoolName">

As mentioned earlier, the page was working perfectly fine till recently. Now, one/many of the values of 'schoolName', 'degreeName', etc are being received as "" (EMPTY STRING) on the server-side.

For debugging, I used firebug and remote-debugging in eclipse. I find that the post-parameters are correct on the client-side. For instance, during one of the submissions the post-parameters were as below (i noted them from firebug).

Content-Type: multipart/form-data; boundary=---------------------------2921238217421 
Content-Length: 48893 

<OTHER_PARAMETERS> <!--Truncated for clarity --> 

-----------------------------2921238217421 
Content-Disposition: form-data; name="schoolName" 

ABC Institute 
-----------------------------2921238217421 
Content-Disposition: form-data; name="schoolName" 

Test School 
-----------------------------2921238217421 
Content-Disposition: form-data; name="schoolName" 

XYZ 
-----------------------------2921238217421 
Content-Disposition: form-data; name="schoolName" 

Texas Institute 
-----------------------------2921238217421 
Content-Disposition: form-data; name="schoolName" 

XXXX School 

-----------------------------2921238217421-- 

But on the server-side, the request params were as below:

schoolName=[ABC Institute, Test School, XYZ, , XXXX School], 

"Texas Institute" was received as "" (EMPTY STRING) in this particular case. This is not happening consistently. The parameters that become NULL (or EMPTY STRING) seem random to me - during one instance, parameter schoolName[3] became null as illustrated above, parameter schoolName[2] became null during yet another submission, etc. At times, none of the parameters are nullified.

The following is the list of the interceptors in the action definition.

List of interceptors: 
----------------------
FileUploadInterceptor
org.apache.struts2.interceptor.FileUploadInterceptor
ServletConfigInterceptor
org.apache.struts2.interceptor.ServletConfigInterceptor  StaticParametersInterceptor
com.opensymphony.xwork2.interceptor.StaticParametersInterceptor
ParametersInterceptor
com.opensymphony.xwork2.interceptor.ParametersInterceptor
MyCustomInterceptor
com.xxxx.yyyy.interceptors.GetLoggedOnUserInterceptor

This issue appears rather weird to me and I have not been able to zero-in on the exact cause of the issue. Any help in this regard would be highly appreciated. Thanks in advance.

Thanks, Raghuram

A: 

An update.

My form sends a multipart request (needed since file upload is enabled on the form). I decided to log the post-data that comes to the server. I modified RequestDumperFilter.java to log the multipart post data. After i added this filter, the parameter loss issue seems to have ceased (0 loss out of 150 submissions of form). I removed the filter and was able to reproduce the issue again.

Thanks, Raghuram

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 filters;


import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Timestamp;
import java.util.Enumeration;
import java.util.Locale;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.xml.ws.RequestWrapper;

import com.oreilly.servlet.multipart.BufferedServletInputStream;



/**
 * Example filter that dumps interesting state information about a request
 * to the associated servlet context log file, before allowing the servlet
 * to process the request in the usual way.  This can be installed as needed
 * to assist in debugging problems.
 *
 * @author Craig McClanahan
 * @version $Revision: 500674 $ $Date: 2007-01-28 00:15:00 +0100 (dim., 28 janv. 2007) $
 */

public final class RequestDumperFilter implements Filter {


    // ----------------------------------------------------- Instance Variables


    /**
     * The filter configuration object we are associated with.  If this value
     * is null, this filter instance is not currently configured.
     */
    private FilterConfig filterConfig = null;

    // --------------------------------------------------------- Public Methods


    /**
     * Take this filter out of service.
     */
    public void destroy() {

        this.filterConfig = null;

    }


    /**
     * Time the processing that is performed by all subsequent filters in the
     * current filter stack, including the ultimately invoked servlet.
     *
     * @param request The servlet request we are processing
     * @param result The servlet response we are creating
     * @param chain The filter chain we are processing
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet error occurs
     */
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
    throws IOException, ServletException {

        if (filterConfig == null)
        return;

    // Render the generic servlet request properties
    StringWriter sw = new StringWriter();
    PrintWriter writer = new PrintWriter(sw);
    writer.println("Request Received at " +
               (new Timestamp(System.currentTimeMillis())));
    writer.println(" characterEncoding=" + request.getCharacterEncoding());
    writer.println("     contentLength=" + request.getContentLength());
    writer.println("       contentType=" + request.getContentType());
    writer.println("            locale=" + request.getLocale());
    writer.print("           locales=");
    Enumeration locales = request.getLocales();
    boolean first = true;
    while (locales.hasMoreElements()) {
        Locale locale = (Locale) locales.nextElement();
        if (first)
            first = false;
        else
            writer.print(", ");
        writer.print(locale.toString());
    }
    writer.println();
    Enumeration names = request.getParameterNames();
    while (names.hasMoreElements()) {
        String name = (String) names.nextElement();
        writer.print("         parameter=" + name + "=");
        String values[] = request.getParameterValues(name);
        for (int i = 0; i < values.length; i++) {
            if (i > 0)
            writer.print(", ");
        writer.print(values[i]);
        }
        writer.println();
    }
    writer.println("          protocol=" + request.getProtocol());
    writer.println("        remoteAddr=" + request.getRemoteAddr());
    writer.println("        remoteHost=" + request.getRemoteHost());
    writer.println("            scheme=" + request.getScheme());
    writer.println("        serverName=" + request.getServerName());
    writer.println("        serverPort=" + request.getServerPort());
    writer.println("          isSecure=" + request.isSecure());

    // Render the HTTP servlet request properties
    if (request instanceof HttpServletRequest) {
        writer.println("---------------------------------------------");
        HttpServletRequest hrequest = (HttpServletRequest) request;
        writer.println("       contextPath=" + hrequest.getContextPath());
        Cookie cookies[] = hrequest.getCookies();
            if (cookies == null)
                cookies = new Cookie[0];
        for (int i = 0; i < cookies.length; i++) {
            writer.println("            cookie=" + cookies[i].getName() +
                   "=" + cookies[i].getValue());
        }
        names = hrequest.getHeaderNames();
        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();
        String value = hrequest.getHeader(name);
            writer.println("            header=" + name + "=" + value);
        }
        writer.println("            method=" + hrequest.getMethod());
        writer.println("          pathInfo=" + hrequest.getPathInfo());
        writer.println("       queryString=" + hrequest.getQueryString());
        writer.println("        remoteUser=" + hrequest.getRemoteUser());
        writer.println("requestedSessionId=" +
               hrequest.getRequestedSessionId());
        writer.println("        requestURI=" + hrequest.getRequestURI());
        writer.println("       servletPath=" + hrequest.getServletPath());


        /*
        The following section is added by me to print the post data of a multipart request.
        */
        writer.println("=============================================");
        writer.println("POST-DATA:");
        writer.println("----------");
        String line;
        BufferedRequestWrapper bufferedRequest= new BufferedRequestWrapper(hrequest);
        //Here obtain InputStream to process POST data!
        InputStream is = bufferedRequest.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        while((line = br.readLine()) != null){
              writer.println(line);
        }
        writer.println("=============================================");
        // Log the resulting string
        writer.flush();
        filterConfig.getServletContext().log(sw.getBuffer().toString());
        // Pass control on to the next filter
        chain.doFilter(bufferedRequest, response);

    }
    else
    {
        writer.println("=============================================");

        // Log the resulting string
        writer.flush();
        filterConfig.getServletContext().log(sw.getBuffer().toString());

        // Pass control on to the next filter
            chain.doFilter(request, response);
    }
    }


    /**
     * Place this filter into service.
     *
     * @param filterConfig The filter configuration object
     */
    public void init(FilterConfig filterConfig) throws ServletException {

    this.filterConfig = filterConfig;

    }


    /**
     * Return a String representation of this object.
     */
    public String toString() {

    if (filterConfig == null)
        return ("RequestDumperFilter()");
    StringBuffer sb = new StringBuffer("RequestDumperFilter(");
    sb.append(filterConfig);
    sb.append(")");
    return (sb.toString());

    }

    public class BufferedRequestWrapper extends HttpServletRequestWrapper {

        ByteArrayInputStream bais;
        ByteArrayOutputStream baos;
        BufferedServletInputStream bsis;
        byte [] buffer; 

        public BufferedRequestWrapper(HttpServletRequest req) throws IOException {
        super(req);
        // Read InputStream and store its content in a buffer.
        InputStream is = req.getInputStream();
        baos = new ByteArrayOutputStream();
        byte buf[] = new byte[1024];
        int letti;
        while ((letti=is.read(buf))>0) baos.write(buf,0,letti);
        buffer = baos.toByteArray();
        }

        public ServletInputStream getInputStream() {
        try {
        // Generate a new InputStream by stored buffer
        bais = new ByteArrayInputStream(buffer);
        // Istantiate a subclass of ServletInputStream
        // (Only ServletInputStream or subclasses of it are accepted by the servlet engine!)
        bsis = new BufferedServletInputStream(bais); 
        } 
        catch (Exception ex) {
        ex.printStackTrace();
        } 
        finally {
        return bsis;
        }
        }

        }

    /*
    Subclass of ServletInputStream needed by the servlet engine.
    All inputStream methods are wrapped and are delegated to 
    the ByteArrayInputStream (obtained as constructor parameter)!
    */
    public class BufferedServletInputStream extends ServletInputStream {

    ByteArrayInputStream bais;

    public BufferedServletInputStream(ByteArrayInputStream bais) {
    this.bais = bais;
    }

    public int available() {
    return bais.available();
    }

    public int read() {
    return bais.read();
    }

    public int read(byte[] buf,int off,int len) {
    return bais.read(buf,off,len);
    }

    }


}
Raghuram Duraisamy
A: 

What about this tomcat bug: https://issues.apache.org/bugzilla/show_bug.cgi?id=37794

When an HTTP/1.1 client POST with transfer-encoding: chunked to a servlet, the getParameter() family and getQueryString() methods fail to retrieve the correct result by returning nothing. In contrast, reading until end-of-file on the servlets input stream produces correct result.

The fix is in 6.0.21.

WW