jetty webSocket : java.lang.IllegalStateException: Committed - servlets

I am using Jetty Websockets in my Web Application .
When i am trying to redirect to a logoff jsp , i am getting this error
oejs.ServletHandler:/test
java.lang.IllegalStateException: Committed
at org.eclipse.jetty.server.Response.resetBuffer(Response.java:1069)
at javax.servlet.ServletResponseWrapper.resetBuffer(ServletResponseWrapper.java:232)
at org.eclipse.jetty.http.gzip.GzipResponseWrapper.resetBuffer(GzipResponseWrapper.java:273)
at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:199)
at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:98)
This is the way i am redirecting
RequestDispatcher rd = request.getRequestDispatcher("logoff.jsp");
rd.forward(request, response);
This error is not reproduceble , but could you please tell me when it may occur??

java.lang.IllegalStateException: Committed
I thought I'd provide a more general explanation of what the exception means. First off, Jetty should be ashamed by the exception message. It provides little to no help to the developer unless they already know what it actually means. The exception should be something like:
java.lang.IllegalStateException: Response headers have already been sent. Are you trying to return a result after sending content?
Typically this exception happens when you go and call:
resp.getOutputStream(); // or getWriter()
and then later try to do a redirect or something:
resp.sendRedirect("/someOtherUrl");
// or
return new ModelAndView("redirect:/someOtherUrl");
Once you get the OutputStream or Writer so you can write body bytes to the client, Jetty has to commit the response and send the HTTP 200 and associated headers, so it can start returning the body bytes. Once that happens, you then can't do a redirect nor make any other changes to the status code or headers.
The proper thing to do, once you return body bytes, is to return null from the handler instead of a ModelAndView(...) or just change the handler to return void.

You also get this exception when you call the super method in your own method implementation.
Example:
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
super.doPost(req, resp); // <-- THIS IS THE PROBLEM
resp.sendRedirect("/someOtherUrl");
}

This occurs because your response has already processed a redirect request, you are trying to modify a committed response.
There are two general ways to solve this:
find out where the first redirect is and try to modify the logic to prevent the "two redirect" scenario from happening.
put a "return" after each of your redirect (personally I recommend this solution).

The reason on my side is using jetty with wrong url:
right: http://localhost:8080
wrong: http://localhost:8080/test

Consider you are running javax.servlet.Filter on Jetty server, and you face the same exception. The issue here can be described exactly as Gray's description (Thanks Gray). Typically this exception happens when you go and call:
resp.getOutputStream(); // or getWriter()
then
chain.doFilter(request, response);
If you called resp.getOutputStream();, make sure you are not using chain.doFilter(request, response); on the same request.

In my case I had some repository in my #Service and I declared it as RepositoryFoo repositoryFoo;, in the beginning of my class
I forgot to add #Autowired or even make it private, so it compiled fine and then when running I had this java.lang.IllegalStateException: Committed ... I wasted some time before figuring out the reason !

Related

HttpServletResponse getWriter() write function starts to throw nullpointer exception after a few days?

I have implemented the following servlet post function on a jetty server. In the HttpServletResponse, it will just write some string.
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write("some json string");
response.getWriter().flush();
}
Everything was fine in the beginning. But after some time (a few days; i was not using it and just kept the jetty server running), the servlet starts to throw null pointer exception on the line response.getWriter().write("some json string");
java.lang.NullPointerException
at org.eclipse.jetty.server.ResponseWriter.write(ResponseWriter.java:246)
I don't know what went wrong. But after a restart of the jetty server, the problem was gone. Do you guys know why?
To answer the NullPointerException
java.lang.NullPointerException
at org.eclipse.jetty.server.ResponseWriter.write(ResponseWriter.java:246)
It seems that you gave the Writer a null String.
See: https://github.com/eclipse/jetty.project/blob/jetty-9.4.22.v20191022/jetty-server/src/main/java/org/eclipse/jetty/server/ResponseWriter.java#L246
The part where you say Jetty stops working after a few days is likely because you have a process on your machine that is periodically cleaning out the system temp directory, removing content out from underneath Jetty.
See: Jetty stops responding after some period of time
The NullPointerException is like is likely because the write failed and you didn't check for that fact.
HttpServletResponse.getWriter() returns a java.io.PrintWriter.
Using the various .write() methods will never throw an error or exception.
You need to use .checkError() method to know when the write has failed.
This is an old-school API decision on java.io.PrintWriter that is actually quite awkward.
See: PrintWriter and PrintStream never throw IOExceptions

FIlterChain exception Handling

I am trying to understand Spring security which involves method security and URL based security . URL based security is completely based on Servlet Filters. There are some scenarios when Spring security deals with exception thrown by filters chain to do its work.
I know how filters are executed by Servlet Container but I am particularly interested in how filterChain handle exceptions thrown by doFilter method in filter chain.
I am particularly interested in how filterChain handle exceptions thrown by doFilter method in filter chain.
It does nothing with them. In other words, it just lets them go and bubble up. They'll eventually end up in servletcontainer's builtin exception handler which already knows how to deal with them based on <error-page> configuration in web.xml.
You can however control it yourself by placing FilterChain#doFilter() call in a try-catch block like so:
try {
chain.doFilter(request, response) {
} catch (ServletException e) {
Throwable cause = e.getRootCause();
// ... (handle it)
}
If anything else down the chain (filter, servlet, jsp, etc) throws an uncaught exception, it'll end up there.
See also:
How does server prioritize which type of web.xml error page to use?
What is the good approach to forward the exception from servlets to a jsp page?
I don't think you are able to return anything meaningful in an errorMessage in the HttpServletResponse stream when trying/catching the doFilter method like that, unless you specifically intercept the HttpServletResponse stream and call something like sendError. And that has to be done even before the call to doFilter.
I am particularly interested in how filterChain handle exceptions
thrown by doFilter method in filter chain.
But I am interested in knowing how to send a meaningful response back into the stream when the exception happens within the filterChain. Because even if there is no Exception, how would you go about handling the success response if you have already called a method like sendError before the doFilter (?)

flush() in rd.include vs rd.forward()

I have the following code snippet in my web app:
if (request.getParameter("user").equals("luke")||session == null && !(uri.endsWith("html") || uri.endsWith("LoginServlet"))) {
System.out.println("<<<----------denied------------->>>>");
pw.println("zzzzzzzzzz");
this.context.log("Unauthorized access request");
pw.flush();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//res.sendRedirect("login.html");
req.getRequestDispatcher("login.html").include(request,response);
}
When I am using flush before Forward(), it's throwing IllegalStateException (since the response is sent the moment we call flush and later we are trying to resend the response using forward) but the code works fine even after I provided flush before the rd.include(). Why doesn't it throw the exception here?
TIA
This behavior is explained in the Servlet Specification for the forward method in chapter 9.4 and for the include method in chapter 9.3.
The javadoc also explains this in a little detail.
The javadoc entry of HttpServletResponse#getWriter() states
Calling flush() on the PrintWriter commits the response.
Committing the response means writing the status line and headers and flushing whatever part of the response body is in the buffer.
The javadoc of forward states
Forwards a request from a servlet to another resource (servlet, JSP
file, or HTML file) on the server. This method allows one servlet to
do preliminary processing of a request and another resource to
generate the response.
So the forward to resource must take care of generating and committing the response. It can't do that if the response has already been committed.
The javadoc of include states
The included servlet cannot change the response status code or set
headers; any attempt to make a change is ignored.
This method just takes the content of another resource and writes it to the response. It can't do anything to headers or the status code. As such, committing before or after makes no difference.
The forward and the include are different methods, the include method allows you to add content to the response, you have a include tag in jsp, the include method is the similar but in the server.
The forward method transfer the request processing to another process (servlet, jsp) and that process must change the response, that is why you get that exception, you can't change the response before forward it.
You could check the documentation for those methods here.

IllegalStateException:STREAM when calling response.getWriter

This is a method that's used for handle ajax request. So the output is written to the response
public ModelAndView myAction(HttpServletRequest request, HttpServletResponse response, BindException errors) throws Exception
{
//call other methods and send the response as arg
//call getWriter on the response
}
According to this doc, I would definitely have an IllegalStateException if I call getWriter having already called getOutputStream(), so I suspect the methods I passed the response to did this although I don't really see them doing so...
The only thing for sure is that at some point, one of the methods may do response.sendError().
Does this some how call getOutputStream()?
HttpServletResponse#sendError() commits the response and send an error status code. The javadoc states
If the response has already been committed, this method throws an
IllegalStateException. After using this method, the response should be
considered to be committed and should not be written to.
In other words, after you call that method, the HTTP response has basically been sent. It makes no sense to call any of the getOutputStream() or getWriter() methods. Your Servlet container further makes it foolproof by throwing an Exception if you attempt to.
I had similar issues but I had not called sendError(), just setContentType() before that. As per this source, it can trigger the same behaviour:
I am guessing its because you have already opened the stream by
calling the resp.setContentType("text/plain"); method, and are then
trying to get a Writer object. You can either use Stream based
classes, or Writer based classes - not both.
Either remove the setContentType(), or send the response using
response.getOutputStream() method. That should solve the problem.
And indeed, it resolved the similar error for me.

response sendRedirect giving 404 error

I am trying to redirect to an error page upon some condition in my servlet code. But until now nothing is working out.
So I am using weblogic 10.x as my app server. am deploying apps directly into managed servers using the console.
So basically i jar them up as .war files and deploy them as webapps.
public void doGet (HttpServletRequest request, HttpServletResponse response)
throws IOException , ServletException
{
try
{
throw new Exception("503_Exception") ;
}
catch(Exception e)
{
response.sendRedirect(response.encodeRedirectURL(HandleError.handle(e, request)));
}
}
public class HandleError{
public static String handle(Throwable t, javax.servlet.http.HttpServletRequest request)
{
String sErrorMsg = t.getMessage();
if (sErrorMsg.equals("503_Exception")) {
request.setAttribute("msg", "INVALID SESSION");
return "/jsp/error/custom.html";
}
return "/default_error.html";
}
}
war file structure
->jsp->error->custom.html
->web-inf
->web-inf->classes->project2->Class1.class
http://machineNAME:3030/Application3-Project2-context-root ->redirects to ->http://machineNAME:3030/jsp/error/custom.html -->> where the actual context root is missing ..
Error 404--Not Found From RFC 2068
Hypertext Transfer Protocol --
HTTP/1.1:
10.4.5 404 Not Found
The server has not found anything
matching the Request-URI. No
indication is given of whether the
condition is temporary or permanent.
If the server does not wish to make
this information available to the
client, the status code 403
(Forbidden) can be used instead. The
410 (Gone) status code SHOULD be used
if the server knows, through some
internally configurable mechanism,
that an old resource is permanently
unavailable and has no forwarding
address.
But if i give -
response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + HandleError.handle(e, request)));
i get Error 310 (net::ERR_TOO_MANY_REDIRECTS): in chrome and in FF error says too many re-directions .
Could someone help me out ?
thanks in advance. :)
Appending request.getServletContext().getContextPath() in the beginning is a fine way to do it. But you are obviously entering an endless redirection loop. Do not forget to log your exceptions. Thus you will be able to see what the problem is.

Resources