3

I was recently surprised to find that the finally block in this play framework controller action code only got called after an Exception, but never when the call actually succeeded.

try {
    InputStream is = getInputStreamMethod();
    renderBinary(is, "out.zip");
catch (Exception e) {
    e.printStackTrace();
} finally {
    cleanUp();
}

Perhaps the thread is terminated or something when renderBinary() is called, but to me, it was non-intuitive. I would suspect the same thing happens for other render() calls, but I didn't verify it.

I solved the problem by moving the renderBinary() to after the try/catch. Further investigation revealed that play provides an @Finally annotation to create a method that gets executed after a controller action executes. The caveat here is that this will get called after the execution of ANY action in the controller, so it may not always be a good choice.

My question is: why does the finally block not get executed after a renderBinary(), and is this documented anywhere? I can't find any reference to it in the play doc.

To clarify the sequence of events that led to this discovery:

  1. The files that were supposed to be deleted as a result of the finally block were not deleted.

  2. Thinking that it couldn't possibly be caused by a non-executing finally block, I changed the methodology to use the Amazon SQS Messaging Queue to send a message in the finally block -- a separate job receives the message and deletes the associated files.

  3. The messages were not getting sent.

  4. I set breakpoints in the code and determined that renderBinary was being called, but the finally block was not getting executed.

  5. Just to be safe, I added log messages to the finally clause, and these also were not present.

  6. I have repeated the debug exercise several times, and each time, the finally clause is not executed.

(Please note that the actual code doesn't really look like the above. This is a very simplified example just to illustrate the case.)

Jeremy Goodell
  • 18,225
  • 5
  • 35
  • 52
  • 1
    the only things I know of that can stop a finally from running is a `System.exit()`, a JVM crash, or if you interrupt the thread. Does `renderBinary()` do any Thread stops that you know of? – CodeChimp Jan 23 '14 at 17:11
  • I don't know. That's what I was hoping someone could answer. It was a big surprise to me when it occurred -- ended up with a bunch of files on our cloud server that were supposed to be deleted! – Jeremy Goodell Jan 23 '14 at 17:24
  • When the files were not deleted, I determined through breakpoints that the finally clause was not getting executed. Appreciate your input though. – Jeremy Goodell Jan 23 '14 at 18:53
  • But did you also verify that your `try` block ever completed? That’s the main reason for a `finally` block being not executed. Besides the possibility of the code hanging forever, as CodeChimp already said, a JVM termination might prevent the execution of a `finally` block too. – Holger Jan 23 '14 at 18:58
  • I've added the steps that led to this discovery in message above. The try happens, the renderBinary happens, the finally doesn't. – Jeremy Goodell Jan 23 '14 at 19:15
  • @Holger Please see https://groups.google.com/forum/#!topic/play-framework/6fs_rMGkNgU for someone else who has reported the same issue. – Jeremy Goodell Jan 23 '14 at 20:00
  • @JeremyGoodell I love those one-phrase, no explanatory answers you get sometimes :). The old "Put this here...cause I said so!" response. – CodeChimp Jan 23 '14 at 21:30
  • Yes, I do know how to solve this problem. I'm just hoping someone familiar with play could explain why play thinks they can hijack the finally functionality. As Holger said: "The playframework cannot change the way finally works." AND YET THEY DID!!! – Jeremy Goodell Jan 23 '14 at 21:43
  • @Jeremy Goodell: It doesn’t change what was said before. If they did, they did in one of the ways already mentioned, i.e. terminating the JVM instead of returning from the method. Unless they transform your code the only way not to execute the finally block is not returning at all. Did you ever check whether a statement *after* the `try…finally` will be executed? – Holger Jan 24 '14 at 09:04
  • @Holger, if the renderBinary() executes, then no statements following the try...finally are executed. I am fairly certain that the renderBinary() is terminating the thread (or somehow otherwise doing something to prevent the finally from executing). This was never in question; I stated that I thought this might be the case in my original post. What I'm looking for is a definitive answer from someone more experienced in play, perhaps someone familiar with the underlying source code. As I said, so far I've been unable to find any documentation that supports this theory. As always, thanks. – Jeremy Goodell Jan 26 '14 at 15:36
  • @Jeremy Goodell: There’s a difference between the Java language and the Java virtual machine. If this framework terminates the JVM at certain points it might have the side effect of certain `finally` blocks not being executed but it’s not surprising that you won’t find something about “play framework inhibiting `finally`” in the documentation. You will have to look for “play framework terminating JVM” or similar then. – Holger Jan 27 '14 at 09:01

1 Answers1

4

It's true. I just found out about this today, since my company uses the play framework and someone ran into it.

As I understand it, this likely only occurs in play versions prior to 2.0, but when you catch all Exceptions following a render call, play apparently rewrites the code to skip the finally block...

I don't understand why or exactly how this is done, but it is apparently the case.

If you catch a specific exception, I don't think this will happen.

But yes, you're not crazy or a bad programmer. This really is just a weird, undocumented play gotcha.

Monty Wild
  • 3,981
  • 1
  • 21
  • 36