001    /**
002     * 
003     * $Revision: 13745 $ $Date: 2008-07-14 15:07:17 +0200 (Mo, 14 Jul 2008) $
004     *
005     * This file is part of ** M y C o R e **
006     * Visit our homepage at http://www.mycore.de/ for details.
007     *
008     * This program is free software; you can use it, redistribute it
009     * and / or modify it under the terms of the GNU General Public License
010     * (GPL) as published by the Free Software Foundation; either version 2
011     * of the License or (at your option) any later version.
012     *
013     * This program is distributed in the hope that it will be useful, but
014     * WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program, normally in the file license.txt.
020     * If not, write to the Free Software Foundation Inc.,
021     * 59 Temple Place - Suite 330, Boston, MA  02111-1307 USA
022     *
023     **/
024    package org.mycore.backend.hibernate;
025    
026    import org.apache.log4j.Logger;
027    import org.hibernate.Session;
028    import org.hibernate.context.CurrentSessionContext;
029    import org.hibernate.context.ThreadLocalSessionContext;
030    import org.hibernate.engine.SessionFactoryImplementor;
031    
032    import org.mycore.common.MCRSession;
033    import org.mycore.common.MCRSessionMgr;
034    import org.mycore.common.events.MCRSessionEvent;
035    import org.mycore.common.events.MCRSessionListener;
036    
037    /**
038     * A {@link CurrentSessionContext} implementation which scopes the notion of
039     * current session by the current {@link MCRSession}. As the MCRSession can be
040     * used by more than one {@link Thread} at a time and {@link Session} is not
041     * threadsafe. This implementation allows the first thread of a
042     * {@link MCRSession} to keep the {@link Session} open for a long conversation.
043     * 
044     * @author Thomas Scheffler (yagee)
045     * 
046     * @version $Revision: 13745 $ $Date: 2008-07-14 15:07:17 +0200 (Mo, 14 Jul 2008) $
047     * @since 2.0
048     */
049    public class MCRSessionContext extends ThreadLocalSessionContext implements MCRSessionListener {
050    
051        private static final long serialVersionUID = -801352757721845792L;
052    
053        private static final String SESSION_KEY = "hibernateSession";
054    
055        private static final Logger LOGGER = Logger.getLogger(MCRSessionContext.class);
056    
057        ThreadLocal<Boolean> firstThread = new ThreadLocal<Boolean>() {
058            protected Boolean initialValue() {
059                return Boolean.FALSE;
060            }
061        };
062    
063        public MCRSessionContext(SessionFactoryImplementor factory) {
064            super(factory);
065            MCRSessionMgr.addSessionListener(this);
066        }
067    
068        public void sessionEvent(MCRSessionEvent event) {
069            MCRSession mcrSession = event.getSession();
070            Session currentSession;
071            switch (event.getType()) {
072            case activated:
073                if (event.getConcurrentAccessors() <= 1) {
074                    // mark this Thread as first Thread of MCRSession
075                    LOGGER.debug("First Thread to access " + mcrSession);
076                    firstThread.set(true);
077                }
078                break;
079            case passivated:
080                currentSession = unbind(factory);
081                if (event.getConcurrentAccessors() <= 1) {
082                    // save Session for later use;
083                    LOGGER.debug("Saving hibernate Session for later use in " + mcrSession);
084                    //mcrSession.put(SESSION_KEY, currentSession);
085                    autoCloseSession(currentSession);
086                } else {
087                    autoCloseSession(currentSession);
088                }
089                // reset firstThread marker as this Session passivates now
090                firstThread.remove();
091                break;
092            case destroyed:
093                currentSession = unbind(factory);
094                autoCloseSession(currentSession);
095                Object obj = mcrSession.get(SESSION_KEY);
096                if (obj != null && currentSession != obj) {
097                    autoCloseSession((Session) obj);
098                }
099                firstThread.remove();
100                break;
101            case created:
102                break;
103            default:
104                break;
105            }
106        }
107    
108        /**
109         * Closes Session if Session is still open.
110         */
111        private void autoCloseSession(Session currentSession) {
112            if (currentSession != null && currentSession.isOpen()) {
113                LOGGER.debug("Autoclosing current hibernate Session");
114                currentSession.close();
115            }
116        }
117    
118        @Override
119        protected org.hibernate.classic.Session buildOrObtainSession() {
120            MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
121            if (firstThread.get()) {
122                LOGGER.debug("First Thread to access " + mcrSession);
123                LOGGER.debug("Try to reuse hibernate Session from current " + mcrSession);
124                Object obj = mcrSession.get(SESSION_KEY);
125                if (obj != null && ((Session) obj).isOpen()) {
126                    LOGGER.debug("Reusing old hibernate Session.");
127                    return (org.hibernate.classic.Session) obj;
128                } else if (obj == null) {
129                    LOGGER.debug("No Hibernate Session found.");
130                } else if (!((Session) obj).isOpen()) {
131                    LOGGER.debug("Found only a closed Hibernate Session.");
132                }
133    
134            }
135            // creates a new one
136            LOGGER.debug("Obtaining new hibernate Session.");
137            org.hibernate.classic.Session session = super.buildOrObtainSession();
138            if (mcrSession.get(SESSION_KEY) == null || firstThread.get()) {
139                // must be a Sessions that started before this instance added as
140                // MCRSessionListener or old Session was closed
141                LOGGER.debug("Storing hibernate session in current MCRSession");
142                firstThread.set(true);
143                mcrSession.put(SESSION_KEY, session);
144            }
145            return session;
146        }
147    
148        @Override
149        protected boolean isAutoCloseEnabled() {
150            return false;
151        }
152    
153        @Override
154        protected boolean isAutoFlushEnabled() {
155            return false;
156        }
157    
158        @Override
159        protected CleanupSynch buildCleanupSynch() {
160            return new CleanupSynch(factory) {
161                private static final long serialVersionUID = -7894370437708819993L;
162    
163                public void afterCompletion(int arg0) {
164                }
165    
166                public void beforeCompletion() {
167                }
168            };
169        }
170    
171    }