views:

245

answers:

4

I have a custom tag that does some processing and then sets a cookie. However, the cookie wasn't being set, and I couldn't figure out why. Another developer pointed out that because we're using a template system, the point at which the tag evaluates, the response header has already been flushed as a part of an include. Since the header has been sent, it doesn't look possible to add the cookie (however, no state exceptions are thrown when I try to do it). Is there a way around this? Does this sound like that might be the problem?

A: 

Well, a HTTP cookie is part of the HTTP header, so that definitely would be your problem. And for that reason you simply can't write a cookie after having flushed the HTTP headers.

Any specific reason you are flushing and not using response buffering? Alternatively, try and make sure you modify the header and add the cookie before it gets flushed.

Wim Hollebrandse
A: 

I guess a possible workaround would be to emit JavaScript to set the cookie. There are of course obvious problems with this method. A similar trick would be to include a 1x1 pixel webbug that sets a cookie when loaded.

I suggest keeping JSP (and similar) concerned only with creating the page view. And also keeping very little session state.

Tom Hawtin - tackline
A: 

HTTP also knows footers added after body to be handled like headers (same format). Unfortunately the HttpServletResponse has no addFooter method (Resin API has, for example). Without an api that would be hard. No chance to simply add it to the body - that will be parsed as body, not footer. Sorry.

If you write html for a browser, maybe you can include a piece of javascript at the end of the page (before </html>) to set the cookie.

Arne Burmeister
+1  A: 

Like other folks have said, you really want to avoid having to do this in the first place. You'd like maybe a nice MVC kind of model where you have some servlets that do your heavy work like database and cookies and all that, and then pass control off to a JSP that actually renders the response. Since the JSP isn't called to generate HTML until after your servlets are done, you shouldn't run into grief like this.

It is kind of lame that setCookie doesn't tell you when it fails, but it also makes sense that probably you don't want it to break your entire page. ServletResponse.isCommitted() will tell you if the headers are already written, and consequently if your setCookie() call will fail.

If you are in dire straits and absolutely need to be able to do this (while you look for a better solution), you could create a servlet filter to buffer the response in memory until after your cookie is set. The pattern would be something like this:

doFilter(request, response, chain)
{
  BufferedResponse bufferedResponse = new BufferedResponse(response);
  try
  {
     // pass control to the next filter or to the JSP/servlet servicing the request
     chain.doFilter(request, bufferedResponse);
  }
  finally
  {
     bufferedResponse.flush();
  }
}

BufferedResponse would need to implement HttpServletResponse and basically keep everything in memory until you explicitly flush it. At that point it would write out the headers, cookies, and the buffered response body.

This will totally work, but it will cause your web server to use a bunch of extra memory since it has to buffer the entire response body in memory for every request. Also, your pages will load slower since the server can't start sending anything to the client browser until after your page is completely done. Bad juju my friend.

Jemiah
I ended up using a servlet filter, but I used it actually perform the work of setting the cookie instead of managing the response like your example above. Thanks for the insight!
hal10001