java.lang.IllegalStateException: Cannot (forward | sendRedirect | create session) after response has been committed
Asked Answered
S

9

110

This method throws

java.lang.IllegalStateException: Cannot forward after response has been committed

and I am unable to spot the problem. Any help?

    int noOfRows = Integer.parseInt(request.getParameter("noOfRows"));
    String chkboxVal = "";
    // String FormatId=null;
    Vector vRow = new Vector();
    Vector vRow1 = new Vector();
    String GroupId = "";
    String GroupDesc = "";
    for (int i = 0; i < noOfRows; i++) {
        if ((request.getParameter("chk_select" + i)) == null) {
            chkboxVal = "notticked";
        } else {
            chkboxVal = request.getParameter("chk_select" + i);
            if (chkboxVal.equals("ticked")) {
                fwdurl = "true";
                Statement st1 = con.createStatement();
                GroupId = request.getParameter("GroupId" + i);
                GroupDesc = request.getParameter("GroupDesc" + i);
                ResultSet rs1 = st1
                        .executeQuery("select FileId,Description from cs2k_Files "
                                + " where FileId like 'M%' and co_code = "
                                + ccode);
                ResultSetMetaData rsm = rs1.getMetaData();
                int cCount = rsm.getColumnCount();

                while (rs1.next()) {
                    Vector vCol1 = new Vector();
                    for (int j = 1; j <= cCount; j++) {
                        vCol1.addElement(rs1.getObject(j));
                    }
                    vRow.addElement(vCol1);
                }
                rs1 = st1
                        .executeQuery("select FileId,NotAllowed from cs2kGroupSub "
                                + " where FileId like 'M%' and GroupId = '"
                                + GroupId + "'" + " and co_code = " + ccode);
                rsm = rs1.getMetaData();
                cCount = rsm.getColumnCount();

                while (rs1.next()) {
                    Vector vCol2 = new Vector();
                    for (int j = 1; j <= cCount; j++) {
                        vCol2.addElement(rs1.getObject(j));
                    }
                    vRow1.addElement(vCol2);
                }

                // throw new Exception("test");

                break;
            }
        }
    }
    if (fwdurl.equals("true")) {
        // throw new Exception("test");
        // response.sendRedirect("cs2k_GroupCopiedUpdt.jsp") ;
        request.setAttribute("GroupId", GroupId);
        request.setAttribute("GroupDesc", GroupDesc);
        request.setAttribute("vRow", vRow);
        request.setAttribute("vRow1", vRow1);
        getServletConfig().getServletContext().getRequestDispatcher(
                "/GroupCopiedUpdt.jsp").forward(request, response);
    }
Silma answered 23/1, 2010 at 14:47 Comment(1)
Its difficult to see like this, but it seems you've already send some output before your forward. Could you please print the full code and check if you don't have any filter in place ?Hardiness
H
273

forward/sendRedirect/sendError do NOT exit the method!

A common misunderstanding among starters is that they think that the call of a forward(), sendRedirect(), or sendError() method would magically exit and "jump" out of the method block, hereby ignoring the remnant of the code. For example in a servlet:

protected void doXxx(...) {
    if (someCondition) {
        response.sendRedirect(...);
    }

    dispatcher.forward(...); // This is STILL invoked when someCondition is true!
}

Or in a filter:

public void doFilter(...) {
    if (someCondition) {
        response.sendRedirect(...);
    }

    chain.doFilter(...); // This is STILL invoked when someCondition is true!
}

This is thus actually not true. They do certainly not behave differently than any other Java methods (expect of System#exit() of course). When the someCondition in above example is true and you're thus calling forward() or doFilter() after sendRedirect() or sendError() on the same request/response, then the chance is big that you will get the exception:

java.lang.IllegalStateException: Cannot forward after response has been committed

This also applies to the inverse condition. If the if statement calls a forward() and you're afterwards calling sendRedirect() or sendError(), then below exception can be thrown:

java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed

To fix this, you need either to add a return; statement afterwards

protected void doXxx(...) {
    if (someCondition) {
        response.sendRedirect(...);
        return;
    }

    dispatcher.forward(...);
}

... or to introduce an else block.

protected void doXxx(...) {
    if (someCondition) {
        response.sendRedirect(....);
    }
    else {
        dispatcher.forward(...);
    }
}

To naildown the root cause in your code, just search for any line which calls a forward(), sendRedirect() or sendError() without exiting the method block or skipping the remnant of the code. This can be inside the same servlet before the particular code line, but also in any servlet or filter which was been called before the particular servlet.

In case of sendError(), if your sole purpose is to set the response status, use setStatus() instead.

Do not write any string before forward/sendRedirect/sendError

Another probable cause is that the servlet writes to the response while a forward() will be called, or has been called in the very same method.

protected void doXxx() {
    out.write("<p>some html</p>");

    // ...

    dispatcher.forward(); // Fail!
}

The response buffer size defaults in most server to 2KB, so if you write more than 2KB to it, then it will be committed and forward() will fail the same way:

java.lang.IllegalStateException: Cannot forward after response has been committed

Solution is obvious, just don't write to the response in the servlet. That's the responsibility of the JSP. You just set a request attribute like so request.setAttribute("data", "some string") and then print it in JSP like so ${data}. See also our Servlets wiki page to learn how to use Servlets the right way.

Do not write any file before forward/sendRedirect/sendError

Another probable cause is that the servlet writes a file download to the response after which e.g. a forward() is called.

protected void doXxx() {
    out.write(bytes);

    // ...
 
    dispatcher.forward(); // Fail!
}

This is technically not possible. You need to remove the forward() call. The enduser will stay on the currently opened page. If you actually intend to change the page after a file download, then you need to move the file download logic to page load of the target page. Basically: first create a temporary file on disk using the way mentioned in this answer How to save generated file temporarily in servlet based web application, then send a redirect with the file name/identifier as request param, and in the target page conditionally print based on the presence of that request param a <script>window.location='...';</script> which immediately downloads the temporary file via one of the ways mentioned in this answer Simplest way to serve static data from outside the application server in a Java web application.

Do not call forward/sendRedirect/sendError in JSP

Yet another probable cause is that the forward(), sendRedirect() or sendError() methods are invoked via Java code embedded in a JSP file in form of old fashioned way <% scriptlets %>, a practice which was officially discouraged since 2003. For example:

<!DOCTYPE html>
<html lang="en">
    <head>
        ... 
    </head>
    <body>
        ...
        <% response.sendRedirect(...); %>
        ...
    </body>
</html>

The problem here is that JSP internally immediately writes template text (i.e. HTML code) via out.write("<!DOCTYPE html> ... etc ...") as soon as it's encountered. This is thus essentially the same problem as explained in previous section.

Solution is obvious, just don't write Java code in a JSP file. That's the responsibility of a normal Java class such as a Servlet or a Filter. See also our Servlets wiki page to learn how to use Servlets the right way.

See also:


Unrelated to your concrete problem, your JDBC code is leaking resources. Fix that as well. For hints, see also How often should Connection, Statement and ResultSet be closed in JDBC?

Haywood answered 23/1, 2010 at 22:51 Comment(4)
@Haywood Do you have an idea on this related problem? stackoverflow.com/questions/18658021/…Paratrooper
@confile: I don't do Grails, but based on the call stack, it is still performing a forward() call while it shouldn't be doing that. JSF, which I'm familiar with, does that also unless you explicitly call FacesContext#responseComplete(). This related question (which I found using keywords "grails prevent render response") may be helpful: stackoverflow.com/questions/5708654/…Haywood
@Haywood Grails is basically Java, but the problem is related to Servlets. Do you have any other idea what I can do. I put a return after each render, redirect and forward as you suggested.Paratrooper
@confile: I know. I have already answered the cause: Grails is still performing a forward() call while it shouldn't be doing that. The solution is functionally obvious: tell it to not do that. It had namely no idea that you have programmatically taken over the job Grails was supposed to do: handling the response. Technically, I have no idea how tell Grails that. But I know that a lot of other MVC frameworks supports this (being instructed to not handle the response by itself), such as JSF, Spring MVC, Wicket, etc. I'd be surprised if this is impossible in Grails.Haywood
E
21

even adding a return statement brings up this exception, for which only solution is this code:

if(!response.isCommitted())
// Place another redirection
Ermelindaermengarde answered 23/2, 2013 at 19:44 Comment(0)
S
6

Typically you see this error after you have already done a redirect and then try to output some more data to the output stream. In the cases where I have seen this in the past, it is often one of the filters that is trying to redirect the page, and then still forwards through to the servlet. I cannot see anything immediately wrong with the servlet, so you might want to try having a look at any filters that you have in place as well.

Edit: Some more help in diagnosing the problem…

The first step to diagnosing this problem is to ascertain exactly where the exception is being thrown. We are assuming that it is being thrown by the line

getServletConfig().getServletContext()
                  .getRequestDispatcher("/GroupCopiedUpdt.jsp")
                  .forward(request, response);

But you might find that it is being thrown later in the code, where you are trying to output to the output stream after you have tried to do the forward. If it is coming from the above line, then it means that somewhere before this line you have either:

  1. output data to the output stream, or
  2. done another redirect beforehand.

Good luck!

Seibold answered 23/1, 2010 at 14:50 Comment(0)
I
3

Bump...

I just had the same error. I noticed that I was invoking super.doPost(request, response); when overriding the doPost() method as well as explicitly invoking the superclass constructor

    public ScheduleServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

As soon as I commented out the super.doPost(request, response); from within doPost() statement it worked perfectly...

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //super.doPost(request, response);
        // More code here...

}

Needless to say, I need to re-read on super() best practices :p

Ignitron answered 12/8, 2016 at 15:58 Comment(0)
E
2

You should add return statement while you are forwarding or redirecting the flow.

Example:

if forwardind,

    request.getRequestDispatcher("/abs.jsp").forward(request, response);
    return;

if redirecting,

    response.sendRedirect(roundTripURI);
    return;
Engedi answered 19/8, 2012 at 7:16 Comment(0)
R
2

This is because your servlet is trying to access a request object which is no more exist.. A servlet's forward or include statement does not stop execution of method block. It continues to the end of method block or first return statement just like any other java method.

The best way to resolve this problem just set the page (where you suppose to forward the request) dynamically according your logic. That is:

protected void doPost(request , response){
String returnPage="default.jsp";
if(condition1){
 returnPage="page1.jsp";
}
if(condition2){
   returnPage="page2.jsp";
}
request.getRequestDispatcher(returnPage).forward(request,response); //at last line
}

and do the forward only once at last line...

you can also fix this problem using return statement after each forward() or put each forward() in if...else block

Rycca answered 20/11, 2014 at 17:59 Comment(0)
C
2

I removed

        super.service(req, res);

Then it worked fine for me

Cycloid answered 14/8, 2015 at 7:21 Comment(0)
K
0

After return forward method you can simply do this:

return null;

It will break the current scope.

Kitten answered 26/5, 2018 at 9:35 Comment(0)
L
0

If you see this on a Spring based web application, make sure you have your method annotated with @ResponseBody or the controller annotated with @RestController instead of @Controller. It will also throw this exception if a method returns JSON, but has not been configured to have that as the response, Spring will instead look for a jsp page to render and throw this exception.

Lorrianelorrie answered 22/3, 2022 at 16:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.