12 Replies Latest reply on Jul 23, 2005 9:11 AM by 843841

    Tricky: Servlet cannot forward after response has been committed

    843841
      This might be a little tricky.

      I have an abstract servlet class I use as a front controller. It kind of looks like this:
           public void doGet(HttpServletRequest request, HttpServletResponse response)
                     throws ServletException, IOException {
                
                this.doPreprocess(request, response);
           }
           
           public final void doPreprocess(HttpServletRequest request, HttpServletResponse response) 
                     throws ServletException, IOException {          
                
                HttpSession session = request.getSession();
                                    
                String labelId = request.getParameter("labelId");
                Integer lblId = null;
                
                if (labelId != null) lblId = new Integer(labelId);
                LabelData ld = new LabelData(lblId);
                request.setAttribute("allLabels", ld.getAllLabels());
                
                if (ld.getFilteredLabels() != null) {
                     request.setAttribute("filteredLabels", ld.getFilteredLabels());
                }
                
                this.processRequest(request, response);
           }
           
           // Abstract method to be defined by extending class.
           public abstract void processRequest(HttpServletRequest request, HttpServletResponse response) 
                     throws ServletException, IOException;
      I then extend it with this:
      public class Home extends BaseController {
           
           public static final long serialVersionUID = 10; 
      
           public void processRequest(HttpServletRequest request, HttpServletResponse response) 
                     throws ServletException, IOException {
                
                RequestDispatcher d = request.getRequestDispatcher("/home.jsp");
                d.forward(request, response);          
           }
      }
      The problem is, when I run it I get:
      java.lang.IllegalStateException: Cannot forward after response has been committed
      Does anyone know why? I am not even forwarding control to another servlet. I am simply extending the class.

      Thanks.
        • 1. Re: Tricky: Servlet cannot forward after response has been committed
          843841
          Hi,

          Check J2EE api
          java.lang.IllegalStateException - if the response was already committed

          Had you done some operators in response object? Just like : writeHead, flushBuffer, act. If only run code that input in the form, can't view the root case.

          Could you share more codes detail for it?
          eg. web.xml, which web server, From one servlet to here? repsonse is enclosed by yourslef or original?
          • 2. Re: Tricky: Servlet cannot forward after response has been committed
            843841
            The documentation of RequestDispatcher.forward() talks about IllegalStateException.

            As an experiment, comment out the request.getSession() call. I'm guessing it does a setCookie() when creating a new session, and maybe forward() doesn't like the response header (Set-Cookie: ...) having been set.

            If that isn't it, keep commenting out code until you find which operation sends something as a response prior to the forward() call.
            • 3. Re: Tricky: Servlet cannot forward after response has been committed
              843841
              Thanks for your replies.

              The Session calls don't appear to be the problem because when I comment out the request.setAttribute() pieces in the BaseController class, everything works fine. What I can't understand is why the request.setAttribute() calls would generate a response.

              The stack trace points to d.forward(request, response); as the point of failure.
              • 4. Re: Tricky: Servlet cannot forward after response has been committed
                843841
                raintung,

                Here is the information you requested.

                Here is the full base controller class:
                package com.blah.km.servlets;
                
                import java.io.IOException;
                
                import javax.servlet.ServletException;
                import javax.servlet.http.HttpServlet;
                import javax.servlet.http.HttpServletRequest;
                import javax.servlet.http.HttpServletResponse;
                import javax.servlet.http.HttpSession;
                import javax.servlet.RequestDispatcher;
                import javax.servlet.ServletContext;
                
                import com.blah.km.config.BConfig;
                import com.blah.km.data.LabelData;
                
                public abstract class BBaseController extends HttpServlet {
                     
                     public static final long serialVersionUID = 10; 
                
                     /**
                      * Constructor of the object.
                      */
                     public BBaseController() {
                          super();
                     }
                
                     /**
                      * Destruction of the servlet. <br>
                      */
                     public void destroy() {
                          super.destroy(); // Just puts "destroy" string in log
                          // Put your code here
                     }
                
                     /**
                      * The doGet method of the servlet. <br>
                      *
                      * This method is called when a form has its tag value method equals to get.
                      * 
                      * @param request the request send by the client to the server
                      * @param response the response send by the server to the client
                      * @throws ServletException if an error occurred
                      * @throws IOException if an error occurred
                      */
                     public void doGet(HttpServletRequest request, HttpServletResponse response)
                               throws ServletException, IOException {
                
                          this.doPreprocess(request, response);
                     }
                
                     /**
                      * The doPost method of the servlet. <br>
                      *
                      * This method is called when a form has its tag value method equals to post.
                      * 
                      * @param request the request send by the client to the server
                      * @param response the response send by the server to the client
                      * @throws ServletException if an error occurred
                      * @throws IOException if an error occurred
                      */
                     public void doPost(HttpServletRequest request, HttpServletResponse response)
                               throws ServletException, IOException {
                          
                          this.doPreprocess(request, response);
                     }
                     
                     public final void doPreprocess(HttpServletRequest request, HttpServletResponse response) 
                               throws ServletException, IOException {          
                          
                          HttpSession session = request.getSession();
                                    
                          String reqUri = request.getRequestURI().toString();
                          String ctxPath = request.getContextPath();
                          
                          // 
                          // Security Check
                          //
                          if (reqUri.indexOf("/Login") == -1) {               
                               // Perform security check.
                               if (session.getAttribute("user") == null) {
                                    // Set session redirect var, so user will be redirected to the page
                                    // they originally requested before being asked to login.
                                    reqUri = reqUri.substring(reqUri.indexOf(ctxPath) + ctxPath.length(), reqUri.length());
                                    session.setAttribute("redirectUrl", reqUri);
                                    // Forward to login page.
                                    RequestDispatcher d = request.getRequestDispatcher("/Login");
                                    d.forward(request, response);
                               }
                          }
                          
                          //
                          // Application Initialization
                          // 
                          ServletContext ctx = getServletContext();
                          Object o = ctx.getAttribute("BConfig");
                          
                          // If the application scope config object hasn't been set,
                          // set it now.
                          if (o == null) {
                               
                               BConfig BConf = new BConfig();     
                               
                               BConf.setArticleLockTimer(new Integer(
                                         ctx.getInitParameter("articleLockTimer")).intValue());
                               BConf.setArticlePreviewLength(new Integer(
                                         ctx.getInitParameter("articlePreviewLength")).intValue());
                               BConf.setArticlesPerPage(new Integer(
                                         ctx.getInitParameter("articlesPerPage")).intValue());
                               BConf.setLabelColumns(new Integer(
                                         ctx.getInitParameter("labelColumns")).intValue());
                               BConf.setMaxChildLabelsWidth(new Integer(
                                         ctx.getInitParameter("maxChildLabelsWidth")).intValue());
                               BConf.setNumLatestArticles(new Integer(
                                         ctx.getInitParameter("numLatestArticles")).intValue());
                               BConf.setPageBoundary(new Integer(
                                         ctx.getInitParameter("pageBoundary")).intValue());
                               
                               ctx.setAttribute("BConfig", BConf);
                          }
                          
                          //
                          // Initialize Header Data
                          //          
                          String labelId = request.getParameter("labelId");
                          Integer lblId = null;
                          
                          if (labelId != null) lblId = new Integer(labelId);
                          LabelData ld = new LabelData(lblId);
                          request.setAttribute("allLabels", ld.getAllLabels());
                          
                          if (ld.getFilteredLabels() != null) {
                               request.setAttribute("filteredLabels", ld.getFilteredLabels());
                          }
                          
                          this.processRequest(request, response);
                     }
                     
                     // Abstract method to be defined by extending class.
                     public abstract void processRequest(HttpServletRequest request, HttpServletResponse response) 
                               throws ServletException, IOException;
                
                     /**
                      * Initialization of the servlet. <br>
                      *
                      * @throws ServletException if an error occure
                      */
                     public void init() throws ServletException {
                          // Put your code here
                     }
                }
                
                And here is the extending class:
                package com.blah.km.servlets;

                import java.io.IOException;

                import javax.servlet.RequestDispatcher;
                import javax.servlet.ServletException;
                import javax.servlet.http.HttpServlet;
                import javax.servlet.http.HttpServletRequest;
                import javax.servlet.http.HttpServletResponse;
                import javax.servlet.http.HttpSession;

                import java.sql.*;
                import javax.sql.*;
                import javax.naming.*;

                import com.blah.km.entities.User;

                public class Home extends BBaseController {
                     
                     public static final long serialVersionUID = 10;

                     public void processRequest(HttpServletRequest request, HttpServletResponse response)
                               throws ServletException, IOException {
                          
                          RequestDispatcher d = request.getRequestDispatcher("/home.jsp");
                          d.forward(request, response);          
                     }
                }


                I'm running Tomcat5.5 on Java 1.5, JSP2.0, JSTL1.1, J2EE1.4

                I should note that the entire Security section is being skipped because the user object is already in the session.

                Thanks!
                • 5. Re: Tricky: Servlet cannot forward after response has been committed
                  843841
                  Hi,

                  I think you mistake for request.setAttribute(), it don't affect response abject.
                  But
                  HttpSession session = request.getSession();[
                  It is dangerous, because this will write cook for response.
                  For weblogic, it willn't setHead,
                  but tomcate will do it when create a new session.
                  Tomcate:
                  addHeader("Set-Cookie", sb.toString());[[/b]code]

                  If you want to jump url in servlet(RequestDispatcher.forward) for tomcate, at best don't invoke request.getSession() when [b]session is new or invalid
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
                  • 6. Re: Tricky: Servlet cannot forward after response has been committed
                    843841
                    HttpSession session = request.getSession();
                    When session is new, will write the Header in tomcate.
                    • 7. Re: Tricky: Servlet cannot forward after response has been committed
                      843841
                      Then how to get a session when When session is new?
                      I got a same problem with unsavory .

                      and i execute the following statement after forward to another URL

                      ...
                      session = req.getSession(true);
                      ...
                      this.getServletContext().getRequestDispatcher("/main.jsp").forward(req, res);

                      there is no any other statement after forward statement.

                      who can tell me how to resolve this problem?

                      BTW, usually JBoss would not throw that exception. but it will throw exception if there is only 5 users to login the web concurrent under LoadRunner.
                      • 8. Re: Tricky: Servlet cannot forward after response has been committed
                        843841
                        I'm not sure that this will solve your problem. But keep in mind, that calling forward on the RequestDispatcher will not stop the servlet from executing the code after the call to forward. In the case above, if the user does not exist, he gets forwarded to the login screen, but the servlet keeps on exexuting until the second forward is called, which definitely leads to an IllegalStateException.

                        if(somethingistrue){
                        dispatcher.forward(request, response);
                        return; // Don't forget this return statement.
                        }

                        anotherdispatcher.forward(request, response)
                        • 9. Re: Tricky: Servlet cannot forward after response has been committed
                          843841
                          it can not resolve my problem though i add return after the following statement:
                          this.getServletContext().getRequestDispatcher("/main.jsp").forward(req, res);

                          after this statement there is no other statement.
                          the Exception is :
                          2005-07-22 15:00:27,656 ERROR [org.jboss.web.localhost.Engine] StandardWrapperValve[loginservlet]: Servlet.service() for servlet loginservlet threw exception
                          java.lang.IllegalStateException: Cannot forward after response has been committed
                          at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:324)
                          at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312)
                          at com.saintbo.servlet.sys.LoginServlet.handle(LoginServlet.java:82)
                          at com.saintbo.servlet.MyExtendedServlet.service(MyExtendedServlet.java:75)
                          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
                          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                          at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:75)
                          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                          at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:214)
                          at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
                          at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
                          at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:198)
                          at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:152)
                          at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
                          at org.jboss.web.tomcat.security.CustomPrincipalValve.invoke(CustomPrincipalValve.java:66)
                          at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
                          at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:162)
                          at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
                          at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
                          at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
                          at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
                          at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:118)
                          at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
                          at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
                          at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                          at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
                          at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
                          at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:929)
                          at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:160)
                          at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:799)
                          at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:705)
                          at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:577)
                          at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
                          at java.lang.Thread.run(Thread.java:536)
                          • 10. Re: Tricky: Servlet cannot forward after response has been committed
                            843841
                            Are you doing any forward/include/sendError/sendRedirect before the line

                            this.getServletContext().getRequestDispatcher("/main.jsp").forward(req, res);
                            ?
                            • 11. Re: Tricky: Servlet cannot forward after response has been committed
                              843841
                              no, there is no any forward/include/sendError/sendRedirect before the statement of :
                              this.getServletContext().getRequestDispatcher("/main.jsp").forward(req, res);

                              I am sure. Actually I have commented all println statements.
                              And that statement is the last statement besides return.

                              Maybe it is coursed by buffer according from the following page
                              http://java.oreilly.com/news/jsptips_1100.html
                              • 12. Re: Tricky: Servlet cannot forward after response has been committed
                                843841
                                By the way, the system would not throw any exception and error if I just access the login JSP which will redirect to the login Servlet containg the statement:
                                this.getServletContext().getRequestDispatcher("/main.jsp").forward(req, res);
                                And there is no any information in LOG file server.log of JBOSS. But that statement will throw exception under LoadRunner with 2 concurrent users and each user will write each EJB QL to server.log.
                                So file of server.log became very large(19M) in couple of minutes.