001    /*
002     * Databinder: a simple bridge from Wicket to Hibernate
003     * Copyright (C) 2006  Nathan Hamblen nathan@technically.us
004     *
005     * This library is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU Lesser General Public
007     * License as published by the Free Software Foundation; either
008     * version 2.1 of the License, or (at your option) any later version.
009     * 
010     * This library is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     * Lesser General Public License for more details.
014     * 
015     * You should have received a copy of the GNU Lesser General Public
016     * License along with this library; if not, write to the Free Software
017     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
018     */
019    
020    /*
021     * Note: this class contains code adapted from wicket-contrib-database. 
022     */
023    
024    package net.databinder.hib;
025    
026    import java.util.HashSet;
027    
028    import net.databinder.CookieRequestCycle;
029    
030    import org.apache.wicket.Page;
031    import org.apache.wicket.Response;
032    import org.apache.wicket.protocol.http.WebApplication;
033    import org.apache.wicket.protocol.http.WebRequest;
034    import org.hibernate.Session;
035    import org.hibernate.SessionFactory;
036    import org.hibernate.context.ManagedSessionContext;
037    
038    /**
039     * <p>Opens Hibernate sessions and transactions as required and closes them at a request's
040     * end. Uncomitted transactions are rolled back. Uses keyed Hibernate session factories from
041     * Databinder service.</p>
042     * @see Databinder
043     * @author Nathan Hamblen
044     */
045    public class DataRequestCycle extends CookieRequestCycle implements HibernateRequestCycle {
046            
047            /** Keys for session factories that have been opened for this request */ 
048            protected HashSet<Object> keys = new HashSet<Object>();
049    
050            public DataRequestCycle(WebApplication application, WebRequest request, Response response) {
051                    super(application, request, response);
052            }
053    
054            /** Roll back active transactions and close session. */
055            protected void closeSession(Object key) {
056                    Session sess = Databinder.getHibernateSession(key);
057                    
058                    if (sess.isOpen())
059                            try {
060                                    if (sess.getTransaction().isActive())
061                                            sess.getTransaction().rollback();
062                            } finally {
063                                    sess.close();
064                            }
065            }
066    
067            /**
068             * Called by DataStaticService when a session is needed and does not already exist. 
069             * Opens a new thread-bound Hibernate session.
070             */
071            public void dataSessionRequested(Object key) {
072                    openHibernateSession(key);
073            }
074            
075            /**
076             * Open a session and begin a transaction for the keyed session factory.
077             * @param key object, or null for the default factory
078             * @return newly opened session
079             */
080            protected org.hibernate.classic.Session openHibernateSession(Object key) {
081                    org.hibernate.classic.Session sess = Databinder.getHibernateSessionFactory(key).openSession();
082                    sess.beginTransaction();
083                    ManagedSessionContext.bind(sess);
084                    keys.add(key);
085                    return sess;
086            }
087    
088            /**
089             * Closes all Hibernate sessions opened for this request. If a transaction has
090             * not been committed, it will be rolled back before closing the session.
091             * @see net.databinder.components.hib.DataForm#onSubmit()
092             */
093            @Override
094            protected void onEndRequest() {
095                    for (Object key : keys) {
096                            SessionFactory sf = Databinder.getHibernateSessionFactory(key);
097                            if (ManagedSessionContext.hasBind(sf)) {
098                                    closeSession(key);
099                                    ManagedSessionContext.unbind(sf);
100                            }
101                    }
102            }
103    
104            /** 
105             * Closes and reopens sessions for this request cycle. Unrelated models may try to load 
106             * themselves after this point. 
107             */
108            @Override
109            public Page onRuntimeException(Page page, RuntimeException e) {
110                    onEndRequest();
111                    onBeginRequest();
112                    return null;
113            }
114    
115    }