Search code examples
javaservletsdownloadstruts2stream

java.lang.IllegalStateException servlet exception when download file from web server


I have some code for download file from web server. Everything works alright, but in console I have this exception:

        июн 26, 2015 2:08:42 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [default] in context with path [/TestTask] threw exception
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
    at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:462)
    at org.apache.struts2.dispatcher.DefaultDispatcherErrorHandler.handleErrorInDevMode(DefaultDispatcherErrorHandler.java:109)
    at org.apache.struts2.dispatcher.DefaultDispatcherErrorHandler.handleError(DefaultDispatcherErrorHandler.java:57)
    at org.apache.struts2.dispatcher.Dispatcher.sendError(Dispatcher.java:909)
    at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:576)
    at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)
    at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:136)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:526)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1078)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:655)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1566)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1523)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)

Why and what it can be? And how i can fix this problem? I try find answer in the google, but I failed.

The code:

package actions;

import java.io.IOException;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.Action;

import org.apache.log4j.Logger;
import org.apache.struts.chain.contexts.ServletActionContext;

import service.CsvCreator;

import com.opensymphony.xwork2.ActionSupport;

public class DownloadCsvAction extends ActionSupport {

    private static final long serialVersionUID = -4714537109287679996L;
    private CsvCreator CSVcreator;
    private static final Logger logger = Logger.getLogger(DownloadCsvAction.class);

    public CsvCreator getCSVcreator() {
        return CSVcreator;
    }

    public void setCSVcreator(CsvCreator cSVcreator) {
        CSVcreator = cSVcreator;
    }

    @Override
    public String execute() {

        HttpServletResponse response = org.apache.struts2.ServletActionContext.getResponse();
        response.setHeader("Content-Disposition", "attachment; filename=\"phone_records.csv\"");
        response.setContentType("text/csv");

        ServletOutputStream out;
        try {
            out = response.getOutputStream();

            String tableHeader = "Caller, Event, Reciever, Timestamp\n";

            out.write(tableHeader.getBytes("UTF-8"));
            out.write(CSVcreator.getAllRecordsInString().getBytes("UTF-8"));
            
            out.flush();
            out.close();
        } catch (IOException e) {
            logger.error(e.getMessage());
        }

        return SUCCESS;
    }

}

And my struts.xml:

<action name="DownloadCsvAction" class="DownloadCsvAction">
        <result name="success" type="dispatcher"/>
</action>

Solution

  • Why and what it can be?

    Because response is already committed. You have closed response before it's used by the Struts2.

    And how I can fix this problem?

    When your action execution ends, return Action.NONE result code. This code tells the invoker to not execute any result because response might be already committed.

    You can also rewrite the action implementation to use stream result type. In this way you have not to do with the response and let Struts2 do the rest. Example of using stream result is here.