The webapp uses Spring’s OpenSessionInViewInterceptor to open a Hibernate session for each request, closing it at the end of request processing. This works very well. However some requests like file downloads can last a long time, keeping the session open and the db connection tied up. In our case, copying the file data to the response output stream is the last thing the controller does, and doesn’t require the Hibernate session (the file data is read from the server filesystem). These file download requests can quickly consume most of the connections in our db connection pool.
I’ve given the controller a reference to the SessionFactory, and invoke the following method just before copying the file data to the response output stream:
SessionFactoryUtils.closeSession(SessionFactoryUtils.getSession(sessionFactory, false));
OpenSessionInViewInterceptor does the same thing later in the request process, and closing the session appears to be idempotent (i.e. calling this method multiple times has the same effect as calling it once). In other words, local testing of this change went well. But it seems a little dangerous; if OpenSessionInViewInterceptor or SessionFactoryUtils change in later versions of Spring, this approach might break in subtle ways (possibly leaking connections?).
The end goal is to close the database connection before starting to send the file data, and this is one way to achieve that. Another solution would be the controller putting the input stream into the request as attribute, and have a filter (firing after the OpenSessionInViewInterceptor) copy the input stream’s data to the response. This has its own set of challenges (e.g. the relevant code is now split between the controller and the filter, instead of being centralized in the controller).
Is there a better way to handle this?
One option would be to not use OSiV for controllers/actions returning a file. Another would be to create your own version that you had more confidence in (after all, it’s not terribly complex).
I doubt that you’d leak connections if the interceptor/utils changed. I’m not excited about the idea of having something that returned the file after the interceptor has done its thing; that implies having some dangling state picked up by a follow-up interceptor (or filter, although IMO doing it in a chained interceptor is probably cleaner) and it strikes me as clunky and error-prone.