org.lareferencia.backend.util.StatelessSessionFactoryBean.java Source code

Java tutorial

Introduction

Here is the source code for org.lareferencia.backend.util.StatelessSessionFactoryBean.java

Source

/*******************************************************************************
 * Copyright (c) 2013 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Lautaro Matas (lmatas@gmail.com) - Desarrollo e implementacin
 *     Emiliano Marmonti(emarmonti@gmail.com) - Coordinacin del componente III
 * 
 * Este software fue desarrollado en el marco de la consultora "Desarrollo e implementacin de las soluciones - Prueba piloto del Componente III -Desarrollador para las herramientas de back-end" del proyecto Estrategia Regional y Marco de Interoperabilidad y Gestin para una Red Federada Latinoamericana de Repositorios Institucionales de Documentacin Cientfica? financiado por Banco Interamericano de Desarrollo (BID) y ejecutado por la Cooperacin Latino Americana de Redes Avanzadas, CLARA.
 ******************************************************************************/
package org.lareferencia.backend.util;

import static org.springframework.orm.jpa.EntityManagerFactoryUtils.ENTITY_MANAGER_SYNCHRONIZATION_ORDER;
import static org.springframework.util.ReflectionUtils.invokeMethod;

import java.sql.Connection;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.transaction.spi.TransactionContext;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.LocalEntityManagerFactoryBean;
import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * Hibernate's {@link StatelessSession} factory which will be bound to the
 * current transaction. This factory returns a Proxy which delegates method
 * calls to the underlying {@link StatelessSession} bound to transaction. At the
 * end of the transaction the session is automatically closed. This class
 * borrows idea's from {@link DataSourceUtils},
 * {@link EntityManagerFactoryUtils}, {@link ResourceHolderSynchronization} and
 * {@link LocalEntityManagerFactoryBean}.
 */
public class StatelessSessionFactoryBean implements FactoryBean<StatelessSession> {

    private final HibernateEntityManagerFactory entityManagerFactory;
    private SessionFactory sessionFactory;

    @Autowired
    public StatelessSessionFactoryBean(HibernateEntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
        this.sessionFactory = entityManagerFactory.getSessionFactory();
    }

    /**
     * Use this to override the {@link SessionFactory} obtained from the
     * {@link EntityManagerFactory}. Please note that the connection will still
     * be used from the {@link EntityManager}.
     */
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    public StatelessSession getObject() throws Exception {
        StatelessSessionInterceptor statelessSessionInterceptor = new StatelessSessionInterceptor(
                entityManagerFactory, sessionFactory);
        return ProxyFactory.getProxy(StatelessSession.class, statelessSessionInterceptor);
    }

    @Override
    public Class<?> getObjectType() {
        return StatelessSession.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    private static class StatelessSessionInterceptor implements MethodInterceptor {

        private final EntityManagerFactory entityManagerFactory;
        private final SessionFactory sessionFactory;

        public StatelessSessionInterceptor(EntityManagerFactory entityManagerFactory,
                SessionFactory sessionFactory) {
            this.entityManagerFactory = entityManagerFactory;
            this.sessionFactory = sessionFactory;
        }

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            StatelessSession statelessSession = getCurrentSession();
            return invokeMethod(invocation.getMethod(), statelessSession, invocation.getArguments());
        }

        private StatelessSession getCurrentSession() {
            if (!TransactionSynchronizationManager.isActualTransactionActive()) {
                throw new IllegalStateException("There should be an active transaction for the current thread.");
            }
            StatelessSession statelessSession = (StatelessSession) TransactionSynchronizationManager
                    .getResource(sessionFactory);
            if (statelessSession == null) {
                statelessSession = openNewStatelessSession();
                bindWithTransaction(statelessSession);
            }
            return statelessSession;
        }

        private StatelessSession openNewStatelessSession() {
            Connection connection = obtainPhysicalConnection();
            return sessionFactory.openStatelessSession(connection);
        }

        /**
         * It is important we obtain the physical (real) connection otherwise it
         * will be double proxied and there will be problems releasing the
         * connection.
         */
        private Connection obtainPhysicalConnection() {
            EntityManager entityManager = EntityManagerFactoryUtils
                    .getTransactionalEntityManager(entityManagerFactory);
            SessionImplementor sessionImplementor = (SessionImplementor) entityManager.getDelegate();
            return sessionImplementor.getTransactionCoordinator().getJdbcCoordinator().getLogicalConnection()
                    .getConnection();
        }

        private void bindWithTransaction(StatelessSession statelessSession) {
            TransactionSynchronizationManager
                    .registerSynchronization(new StatelessSessionSynchronization(sessionFactory, statelessSession));
            TransactionSynchronizationManager.bindResource(sessionFactory, statelessSession);
        }
    }

    private static class StatelessSessionSynchronization extends TransactionSynchronizationAdapter {

        private final SessionFactory sessionFactory;
        private final StatelessSession statelessSession;

        public StatelessSessionSynchronization(SessionFactory sessionFactory, StatelessSession statelessSession) {
            this.sessionFactory = sessionFactory;
            this.statelessSession = statelessSession;
        }

        @Override
        public int getOrder() {
            return ENTITY_MANAGER_SYNCHRONIZATION_ORDER - 100;
        }

        @Override
        public void beforeCommit(boolean readOnly) {
            if (!readOnly) {
                ((TransactionContext) statelessSession).managedFlush();
            }
        }

        @Override
        public void beforeCompletion() {
            TransactionSynchronizationManager.unbindResource(sessionFactory);
            statelessSession.close();
        }

    }

}