001/*
002 * Copyright 2002-2014 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.orm.hibernate4;
018
019import java.lang.reflect.Method;
020import javax.transaction.Status;
021import javax.transaction.SystemException;
022import javax.transaction.TransactionManager;
023
024import org.apache.commons.logging.LogFactory;
025import org.hibernate.FlushMode;
026import org.hibernate.HibernateException;
027import org.hibernate.Session;
028import org.hibernate.context.spi.CurrentSessionContext;
029import org.hibernate.engine.spi.SessionFactoryImplementor;
030
031import org.springframework.transaction.support.TransactionSynchronizationManager;
032import org.springframework.util.ReflectionUtils;
033
034/**
035 * Implementation of Hibernate 3.1's CurrentSessionContext interface
036 * that delegates to Spring's SessionFactoryUtils for providing a
037 * Spring-managed current Session.
038 *
039 * <p>This CurrentSessionContext implementation can also be specified in custom
040 * SessionFactory setup through the "hibernate.current_session_context_class"
041 * property, with the fully qualified name of this class as value.
042 *
043 * @author Juergen Hoeller
044 * @since 3.1
045 */
046@SuppressWarnings("serial")
047public class SpringSessionContext implements CurrentSessionContext {
048
049        private final SessionFactoryImplementor sessionFactory;
050
051        private TransactionManager transactionManager;
052
053        private CurrentSessionContext jtaSessionContext;
054
055
056        /**
057         * Create a new SpringSessionContext for the given Hibernate SessionFactory.
058         * @param sessionFactory the SessionFactory to provide current Sessions for
059         */
060        public SpringSessionContext(SessionFactoryImplementor sessionFactory) {
061                this.sessionFactory = sessionFactory;
062                try {
063                        Object jtaPlatform = sessionFactory.getServiceRegistry().getService(ConfigurableJtaPlatform.jtaPlatformClass);
064                        Method rtmMethod = ConfigurableJtaPlatform.jtaPlatformClass.getMethod("retrieveTransactionManager");
065                        this.transactionManager = (TransactionManager) ReflectionUtils.invokeMethod(rtmMethod, jtaPlatform);
066                        if (this.transactionManager != null) {
067                                this.jtaSessionContext = new SpringJtaSessionContext(sessionFactory);
068                        }
069                }
070                catch (Exception ex) {
071                        LogFactory.getLog(SpringSessionContext.class).warn(
072                                        "Could not introspect Hibernate JtaPlatform for SpringJtaSessionContext", ex);
073                }
074        }
075
076
077        /**
078         * Retrieve the Spring-managed Session for the current thread, if any.
079         */
080        @Override
081        public Session currentSession() throws HibernateException {
082                Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
083                if (value instanceof Session) {
084                        return (Session) value;
085                }
086                else if (value instanceof SessionHolder) {
087                        SessionHolder sessionHolder = (SessionHolder) value;
088                        Session session = sessionHolder.getSession();
089                        if (!sessionHolder.isSynchronizedWithTransaction() &&
090                                        TransactionSynchronizationManager.isSynchronizationActive()) {
091                                TransactionSynchronizationManager.registerSynchronization(
092                                                new SpringSessionSynchronization(sessionHolder, this.sessionFactory, false));
093                                sessionHolder.setSynchronizedWithTransaction(true);
094                                // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
095                                // with FlushMode.MANUAL, which needs to allow flushing within the transaction.
096                                FlushMode flushMode = session.getFlushMode();
097                                if (flushMode.equals(FlushMode.MANUAL) &&
098                                                !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
099                                        session.setFlushMode(FlushMode.AUTO);
100                                        sessionHolder.setPreviousFlushMode(flushMode);
101                                }
102                        }
103                        return session;
104                }
105
106                if (this.transactionManager != null) {
107                        try {
108                                if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) {
109                                        Session session = this.jtaSessionContext.currentSession();
110                                        if (TransactionSynchronizationManager.isSynchronizationActive()) {
111                                                TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));
112                                        }
113                                        return session;
114                                }
115                        }
116                        catch (SystemException ex) {
117                                throw new HibernateException("JTA TransactionManager found but status check failed", ex);
118                        }
119                }
120
121                if (TransactionSynchronizationManager.isSynchronizationActive()) {
122                        Session session = this.sessionFactory.openSession();
123                        if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
124                                session.setFlushMode(FlushMode.MANUAL);
125                        }
126                        SessionHolder sessionHolder = new SessionHolder(session);
127                        TransactionSynchronizationManager.registerSynchronization(
128                                        new SpringSessionSynchronization(sessionHolder, this.sessionFactory, true));
129                        TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
130                        sessionHolder.setSynchronizedWithTransaction(true);
131                        return session;
132                }
133                else {
134                        throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");
135                }
136        }
137
138}