/*
* Databinder: a simple bridge from Wicket to Hibernate
* Copyright (C) 2006 Nathan Hamblen nathan@technically.us
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Note: this class contains code adapted from wicket-contrib-database.
*/
package net.databinder.hib.conv;
import net.databinder.hib.DataRequestCycle;
import net.databinder.hib.Databinder;
import net.databinder.hib.conv.components.IConversationPage;
import org.apache.wicket.Page;
import org.apache.wicket.Response;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WebRequest;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.classic.Session;
import org.hibernate.context.ManagedSessionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Supports extended Hibernate sessions for long conversations. This is useful for a page or
* a series of pages where changes are made to an entity that can not be immediately
* committed. Using a "conversation" session, HibernateObjectModels are used normally, but
* until the session is flushed the changes are not made to persistent storage.
* @author Nathan Hamblen
*/
public class DataConversationRequestCycle extends DataRequestCycle {
private static final Logger log = LoggerFactory.getLogger(DataConversationRequestCycle.class);
public DataConversationRequestCycle(WebApplication application, WebRequest request, Response response) {
super(application, request, response);
}
/**
* Does nothing; The session is open or retreived only when the request target is known.
*/
@Override
protected void onBeginRequest() {
}
/**
* Called by DataStaticService when a session is needed and does not already exist.
* Determines current page and retrieves its associated conversation session if
* appropriate. Does nothing if current page is not yet available.
* @param key factory key object, or null for the default factory
*/
public void dataSessionRequested(Object key) {
Page page = getResponsePage();
if (page == null)
page = getRequest().getPage();
if (page == null) {
Class pageClass = getResponsePageClass();
if (pageClass != null) {
openHibernateSession(key);
// set to manual if we are going to a conv. page
if (IConversationPage.class.isAssignableFrom(pageClass))
Databinder.getHibernateSession(key).setFlushMode(FlushMode.MANUAL);
}
return;
}
// if continuing a conversation page
if (page instanceof IConversationPage) {
// look for existing session
IConversationPage convPage = (IConversationPage) page;
org.hibernate.classic.Session sess = convPage.getConversationSession(key);
// if usable session exists, try to open txn, bind, and return
if (sess != null && sess.isOpen()) {
try {
sess.beginTransaction();
ManagedSessionContext.bind(sess);
keys.add(key);
return;
} catch (HibernateException e) {
log.warn("Existing session exception on beginTransation, opening new", e);
}
}
// else start new one and set in page
sess = openHibernateSession(key);
sess.setFlushMode(FlushMode.MANUAL);
((IConversationPage)page).setConversationSession(key, sess);
return;
}
// start new standard session
openHibernateSession(key);
}
/**
* Inspects responding page to determine if current Hibernate session should be closed
* or left open and stored in the page.
*/
@Override
protected void onEndRequest() {
for (Object key : keys) {
if (!ManagedSessionContext.hasBind(Databinder.getHibernateSessionFactory(key)))
return;
org.hibernate.classic.Session sess = Databinder.getHibernateSession(key);
boolean transactionComitted = false;
if (sess.getTransaction().isActive())
sess.getTransaction().rollback();
else
transactionComitted = true;
Page page = getResponsePage() ;
if (page != null) {
// check for current conversational session
if (page instanceof IConversationPage) {
IConversationPage convPage = (IConversationPage)page;
// close if not dirty contains no changes
if (transactionComitted && !sess.isDirty()) {
sess.close();
sess = null;
}
convPage.setConversationSession(key, sess);
} else
sess.close();
}
ManagedSessionContext.unbind(Databinder.getHibernateSessionFactory(key));
}
}
/**
* Closes and reopens Hibernate session for this Web session. Unrelated models may try to load
* themselves after this point.
*/
@Override
public Page onRuntimeException(Page page, RuntimeException e) {
for (Object key : keys) {
if (Databinder.hasBoundSession(key)) {
Session sess = Databinder.getHibernateSession(key);
try {
if (sess.getTransaction().isActive())
sess.getTransaction().rollback();
} finally {
sess.close();
ManagedSessionContext.unbind(Databinder.getHibernateSessionFactory(key));
}
}
openHibernateSession(key);
}
return null;
}
}