diff --git a/src/net/socialgamer/cah/CahModule.java b/src/net/socialgamer/cah/CahModule.java index 57a271f..d6fc907 100644 --- a/src/net/socialgamer/cah/CahModule.java +++ b/src/net/socialgamer/cah/CahModule.java @@ -56,6 +56,10 @@ public class CahModule extends AbstractModule { return 30; } + /** + * @return A Hibernate session. Objects which receive a Hibernate session should close the + * session when they are done! + */ @Provides Session provideHibernateSession() { return HibernateUtil.instance.sessionFactory.openSession(); diff --git a/src/net/socialgamer/cah/handlers/Handler.java b/src/net/socialgamer/cah/handlers/Handler.java index 9aefad4..27923a8 100644 --- a/src/net/socialgamer/cah/handlers/Handler.java +++ b/src/net/socialgamer/cah/handlers/Handler.java @@ -23,8 +23,11 @@ package net.socialgamer.cah.handlers; +import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.servlet.http.HttpSession; @@ -33,6 +36,8 @@ import net.socialgamer.cah.Constants.ErrorCode; import net.socialgamer.cah.Constants.ReturnableData; import net.socialgamer.cah.RequestWrapper; +import org.hibernate.Session; + /** * Implementations of this interface MUST also have a public static final String OP. There will be @@ -41,6 +46,8 @@ import net.socialgamer.cah.RequestWrapper; * @author Andy Janata (ajanata@socialgamer.net) */ public abstract class Handler { + private final Logger logger = Logger.getLogger("net.socialgamer.cah.handlers.Handler"); + /** * Handle a request. * @@ -66,4 +73,35 @@ public abstract class Handler { data.put(AjaxResponse.ERROR_CODE, errorCode.toString()); return data; } + + /** + * Clean up after this Handler. Currently, this means using reflection to see if the concrete + * Handler implementation had a field of type Session (Hibernate), and closing it if it does and + * did not already close it. + */ + public final void cleanUp() { + for (final Field field : this.getClass().getDeclaredFields()) { + if (field.getType() == Session.class) { + try { + // This Handler had a Hibernate Session. Try to close it if it wasn't already closed. + // This is extremely dirty but also extremely awesome to not have problems if it is + // forgotten. + field.setAccessible(true); + final Session session = (Session) field.get(this); + if (session.isOpen()) { + session.close(); + logger.log(Level.INFO, "Closing unclosed Hibernate Session in " + + this.getClass().getName()); + } + } catch (final Exception e) { + // Something prevented us from ignoring access control check, so we can't close the + // session. Log about it and continue. + e.printStackTrace(); + logger.log(Level.SEVERE, "Unable to reflect and get Hibernate Session from " + + this.getClass().getName()); + logger.log(Level.SEVERE, e.toString()); + } + } + } + } } diff --git a/src/net/socialgamer/cah/servlets/AjaxServlet.java b/src/net/socialgamer/cah/servlets/AjaxServlet.java index 178fb5c..f70673e 100644 --- a/src/net/socialgamer/cah/servlets/AjaxServlet.java +++ b/src/net/socialgamer/cah/servlets/AjaxServlet.java @@ -91,6 +91,7 @@ public class AjaxServlet extends CahServlet { return; } final Map data = handler.handle(new RequestWrapper(request), hSession); + handler.cleanUp(); data.put(AjaxResponse.SERIAL, serial); returnData(user, out, data); return;