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.conv;
025    
026    import net.databinder.hib.DataRequestCycle;
027    import net.databinder.hib.Databinder;
028    import net.databinder.hib.conv.components.IConversationPage;
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.FlushMode;
035    import org.hibernate.HibernateException;
036    import org.hibernate.classic.Session;
037    import org.hibernate.context.ManagedSessionContext;
038    import org.slf4j.Logger;
039    import org.slf4j.LoggerFactory;
040    
041    /**
042     * Supports extended Hibernate sessions for long conversations. This is useful for a page or
043     * a series of pages where changes are made to an entity that can not be immediately
044     * committed. Using a "conversation" session, HibernateObjectModels are used normally, but
045     * until the session is flushed the changes are not made to persistent storage.   
046     * @author Nathan Hamblen
047     */
048    public class DataConversationRequestCycle extends DataRequestCycle {    
049            private static final Logger log = LoggerFactory.getLogger(DataConversationRequestCycle.class);
050    
051            public DataConversationRequestCycle(WebApplication application, WebRequest request, Response response) {
052                    super(application, request, response);
053            }
054            
055            /**
056             * Does nothing; The session is open or retreived only when the request target is known.
057             */
058            @Override
059            protected void onBeginRequest() {
060            }
061            
062            /**
063             * Called by DataStaticService when a session is needed and does not already exist. 
064             * Determines current page and retrieves its associated conversation session if 
065             * appropriate. Does nothing if current page is not yet available.
066             * @param key factory key object, or null for the default factory
067             */
068            public void dataSessionRequested(Object key) {
069                    Page page = getResponsePage();
070                    if (page == null)
071                            page = getRequest().getPage();
072                    
073                    if (page == null) {
074                            Class pageClass = getResponsePageClass(); 
075                            if (pageClass != null) {
076                                    openHibernateSession(key);
077                                    // set to manual if we are going to a conv. page
078                                    if (IConversationPage.class.isAssignableFrom(pageClass))
079                                            Databinder.getHibernateSession(key).setFlushMode(FlushMode.MANUAL);
080                            }
081                            return;
082                    }
083    
084                    // if continuing a conversation page
085                    if (page instanceof  IConversationPage) {
086                            // look for existing session
087                            IConversationPage convPage = (IConversationPage) page;
088                            org.hibernate.classic.Session sess = convPage.getConversationSession(key);
089                            
090                            // if usable session exists, try to open txn, bind, and return
091                            if (sess != null && sess.isOpen()) {
092                                    try {
093                                            sess.beginTransaction();
094                                            ManagedSessionContext.bind(sess);
095                                            keys.add(key);
096                                            return;
097                                    } catch (HibernateException e) {
098                                            log.warn("Existing session exception on beginTransation, opening new", e);
099                                    }
100                            }
101                            // else start new one and set in page
102                            sess = openHibernateSession(key);
103                            sess.setFlushMode(FlushMode.MANUAL);
104                            ((IConversationPage)page).setConversationSession(key, sess);
105                            return;
106                    }
107                    // start new standard session
108                    openHibernateSession(key);
109            }
110            
111            /**
112             * Inspects responding page to determine if current Hibernate session should be closed
113             * or left open and stored in the page.
114             */
115            @Override
116            protected void onEndRequest() {
117                    for (Object key : keys) {
118                            if (!ManagedSessionContext.hasBind(Databinder.getHibernateSessionFactory(key)))
119                                    return;
120                            org.hibernate.classic.Session sess = Databinder.getHibernateSession(key);
121                            boolean transactionComitted = false;
122                            if (sess.getTransaction().isActive())
123                                    sess.getTransaction().rollback();
124                            else
125                                    transactionComitted = true;
126                            
127                            Page page = getResponsePage() ;
128                            
129                            if (page != null) {
130                                    // check for current conversational session
131                                    if (page instanceof IConversationPage) {
132                                            IConversationPage convPage = (IConversationPage)page;
133                                            // close if not dirty contains no changes
134                                            if (transactionComitted && !sess.isDirty()) {
135                                                    sess.close();
136                                                    sess = null;
137                                            }
138                                            convPage.setConversationSession(key, sess);
139                                    } else
140                                            sess.close();
141                            }               
142                            ManagedSessionContext.unbind(Databinder.getHibernateSessionFactory(key));
143                    }
144            }
145    
146            /** 
147             * Closes and reopens Hibernate session for this Web session. Unrelated models may try to load 
148             * themselves after this point. 
149             */
150            @Override
151            public Page onRuntimeException(Page page, RuntimeException e) {
152                    for (Object key : keys) {
153                            if (Databinder.hasBoundSession(key)) {
154                                    Session sess = Databinder.getHibernateSession(key);
155                                    try {
156                                            if (sess.getTransaction().isActive())
157                                                    sess.getTransaction().rollback();
158                                    } finally {
159                                            sess.close();
160                                            ManagedSessionContext.unbind(Databinder.getHibernateSessionFactory(key));
161                                    }
162                            }
163                            openHibernateSession(key);
164                    }
165                    return null;
166            }
167    
168    }