Spring MVC Controller code: getOutputStream() has already been called for this response
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?
Answers
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.