1

I'm trying to assemble a CSV file from some DB data and send it to the client from my Spring MVC controller (and associated services). I'm using SuperCSV to handle the writing and output: http://supercsv.sourceforge.net/

So, here's the controller method:

@RequestMapping(value="/getFullReqData.html", method = RequestMethod.GET)
public void getFullData(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException{
    logger.info("INFO:  ******************************Received request for full Req data dump");

    List<Requirement> allRecords = reqService.getFullDataSet(ProjectService.getProjectID((String)session.getAttribute("currentProject")));
    response.setContentType("text/csv;charset=utf-8"); 
    response.setHeader("Content-Disposition","attachment; filename=nistData.csv");
    OutputStream fout= response.getOutputStream();  
    OutputStream bos = new BufferedOutputStream(fout);   
    OutputStreamWriter outputwriter = new OutputStreamWriter(bos); 

    ICsvBeanWriter writer = new CsvBeanWriter(outputwriter, CsvPreference.EXCEL_PREFERENCE);
    try {
      final String[] header = new String[] { 
              "ReqID", 
              "ColName1",
              "ColName2",
              "ColName3",
              "ColName4",
              "ColName5", 
              "ColName6",
              "ColName7",
              "ColName8",                                 
              "ColName9",
              "ColName10" };

      // the actual writing
      writer.writeHeader(header);

      for(Requirement aCR : allRecords){
          writer.write(aCR, header);
      }

    } finally {
        writer.close();
    }
};

This is actually moving forward and getting the response to the client browser which triggers the download of the csv file.

Two problems:

1) I'm getting a pile of server-side exception spewage (below)

2) The CSV file has only the headers in it and no data, despite the allRecords list showing as fully populated in the debugger.

I'm hoping the failure-to-populate is caused by the exception, but I'm suspicious. Exception trace:

SEVERE: Servlet.service() for servlet jsp threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:633)
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:214)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:105)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:105)
at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:125)
at org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:118)
at org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:182)
at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:123)
at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:80)
at org.apache.jsp.error_jsp._jspService(error_jsp.java:102)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)

The exception looks to get passed along as I'm seeing it 4 times per invocation of this method.

All documentation I've found has people who are embedding their response.getOutputStream() inside scriptlets instead of being in a servlet. The method I've got up above is inside the Spring MVC controller, so the advise of "move it to a servlet!" doesn't seem to apply here. From what I can tell, I'm only invoking the response.getOutputStream() one time, and the method is blowing up on the first call.

I'm stumped. Help?

Raevik
  • 1,945
  • 9
  • 32
  • 53
  • Try not to close the output stream. http://stackoverflow.com/questions/1829784/should-i-close-the-servlet-outputstream – km1 Sep 07 '12 at 21:20
  • Good piece of info there, but not the source of my issue unfortunately. I removed it to prevent any future issues but it doesn't appear to be causing my current IllegalStateException. – Raevik Sep 10 '12 at 14:38

2 Answers2

7

Your flow is probably raising an exception somewhere in the request mapped method - probably in writer.write(aCR, header); which then goes to the finally block, closes the response stream, then the exception bubbles up to the servlet, which tries to handle it by an error page(check your web.xml error-page tags) , which because the response stream is closed cannot render the error jsp cleanly and hence the exception message you are seeing.

It will be good if you can catch the exception within the controller and print the stack trace to see what could be going wrong in the controller while writing to the output stream.

Biju Kunjummen
  • 49,138
  • 14
  • 112
  • 125
  • You nailed it. The exception was actually coming off the CsvBeanWriter because the object I was trying to write had a non-standard method name for a boolean member variable. When the library tried to reflect and invoke it, it couldn't find it and the exception was masked and then mis-reported b/c of my outputStream being closed in the finally call. Brilliant sir. – Raevik Sep 10 '12 at 14:52
0

Can you make it work with printWriter instead:

    response.setContentType("application/vnd.ms-excel;charset=UTF-8");
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + ".xls\"");
    PrintWriter out = response.getWriter();
Solubris
  • 3,603
  • 2
  • 22
  • 37