Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.context;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import javax.transaction.Synchronization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.hibernate.engine.SessionFactoryImplementor;
/**
* A {@link CurrentSessionContext} impl which scopes the notion of current
* session by the current thread of execution. Unlike the JTA counterpart,
* threads do not give us a nice hook to perform any type of cleanup making
* it questionable for this impl to actually generate Session instances. In
* the interest of usability, it was decided to have this default impl
* actually generate a session upon first request and then clean it up
* after the {@link org.hibernate.Transaction} associated with that session
* is committed/rolled-back. In order for ensuring that happens, the sessions
* generated here are unusable until after {@link Session#beginTransaction()}
* has been called. If close() is called on a session managed by
* this class, it will be automatically unbound.
*
* Additionally, the static {@link #bind} and {@link #unbind} methods are
* provided to allow application code to explicitly control opening and
* closing of these sessions. This, with some from of interception,
* is the preferred approach. It also allows easy framework integration
* and one possible approach for implementing long-sessions.
*
* The {@link #buildOrObtainSession}, {@link #isAutoCloseEnabled},
* {@link #isAutoFlushEnabled}, {@link #getConnectionReleaseMode}, and
* {@link #buildCleanupSynch} methods are all provided to allow easy
* subclassing (for long-running session scenarios, for example).
*
* @author Steve Ebersole
*/
public class ThreadLocalSessionContext implements CurrentSessionContext {
private static final Logger log = LoggerFactory.getLogger( ThreadLocalSessionContext.class );
private static final Class[] SESSION_PROXY_INTERFACES = new Class[] {
org.hibernate.classic.Session.class,
org.hibernate.engine.SessionImplementor.class,
org.hibernate.jdbc.JDBCContext.Context.class,
org.hibernate.event.EventSource.class,
org.hibernate.engine.jdbc.LobCreationContext.class
};
/**
* A ThreadLocal maintaining current sessions for the given execution thread.
* The actual ThreadLocal variable is a java.util.Map to account for
* the possibility for multiple SessionFactorys being used during execution
* of the given thread.
*/
private static final ThreadLocal context = new ThreadLocal();
protected final SessionFactoryImplementor factory;
public ThreadLocalSessionContext(SessionFactoryImplementor factory) {
this.factory = factory;
}
/**
* {@inheritDoc}
*/
public final Session currentSession() throws HibernateException {
Session current = existingSession( factory );
if (current == null) {
current = buildOrObtainSession();
// register a cleanup synch
current.getTransaction().registerSynchronization( buildCleanupSynch() );
// wrap the session in the transaction-protection proxy
if ( needsWrapping( current ) ) {
current = wrap( current );
}
// then bind it
doBind( current, factory );
}
return current;
}
private boolean needsWrapping(Session session) {
// try to make sure we don't wrap and already wrapped session
return session != null
&& ! Proxy.isProxyClass( session.getClass() )
|| ( Proxy.getInvocationHandler( session ) != null
&& ! ( Proxy.getInvocationHandler( session ) instanceof TransactionProtectionWrapper ) );
}
/**
* Getter for property 'factory'.
*
* @return Value for property 'factory'.
*/
protected SessionFactoryImplementor getFactory() {
return factory;
}
/**
* Strictly provided for subclassing purposes; specifically to allow long-session
* support.
*
* This implementation always just opens a new session.
*
* @return the built or (re)obtained session.
*/
protected Session buildOrObtainSession() {
return factory.openSession(
null,
isAutoFlushEnabled(),
isAutoCloseEnabled(),
getConnectionReleaseMode()
);
}
protected CleanupSynch buildCleanupSynch() {
return new CleanupSynch( factory );
}
/**
* Mainly for subclass usage. This impl always returns true.
*
* @return Whether or not the the session should be closed by transaction completion.
*/
protected boolean isAutoCloseEnabled() {
return true;
}
/**
* Mainly for subclass usage. This impl always returns true.
*
* @return Whether or not the the session should be flushed prior transaction completion.
*/
protected boolean isAutoFlushEnabled() {
return true;
}
/**
* Mainly for subclass usage. This impl always returns after_transaction.
*
* @return The connection release mode for any built sessions.
*/
protected ConnectionReleaseMode getConnectionReleaseMode() {
return factory.getSettings().getConnectionReleaseMode();
}
protected Session wrap(Session session) {
TransactionProtectionWrapper wrapper = new TransactionProtectionWrapper( session );
Session wrapped = ( Session ) Proxy.newProxyInstance(
Session.class.getClassLoader(),
SESSION_PROXY_INTERFACES,
wrapper
);
// yick! need this for proper serialization/deserialization handling...
wrapper.setWrapped( wrapped );
return wrapped;
}
/**
* Associates the given session with the current thread of execution.
*
* @param session The session to bind.
*/
public static void bind(org.hibernate.Session session) {
SessionFactory factory = session.getSessionFactory();
cleanupAnyOrphanedSession( factory );
doBind( session, factory );
}
private static void cleanupAnyOrphanedSession(SessionFactory factory) {
Session orphan = doUnbind( factory, false );
if ( orphan != null ) {
log.warn( "Already session bound on call to bind(); make sure you clean up your sessions!" );
try {
if ( orphan.getTransaction() != null && orphan.getTransaction().isActive() ) {
try {
orphan.getTransaction().rollback();
}
catch( Throwable t ) {
log.debug( "Unable to rollback transaction for orphaned session", t );
}
}
orphan.close();
}
catch( Throwable t ) {
log.debug( "Unable to close orphaned session", t );
}
}
}
/**
* Unassociate a previously bound session from the current thread of execution.
*
* @return The session which was unbound.
*/
public static Session unbind(SessionFactory factory) {
return doUnbind( factory, true );
}
private static Session existingSession(SessionFactory factory) {
Map sessionMap = sessionMap();
if ( sessionMap == null ) {
return null;
}
else {
return ( Session ) sessionMap.get( factory );
}
}
protected static Map sessionMap() {
return ( Map ) context.get();
}
private static void doBind(org.hibernate.Session session, SessionFactory factory) {
Map sessionMap = sessionMap();
if ( sessionMap == null ) {
sessionMap = new HashMap();
context.set( sessionMap );
}
sessionMap.put( factory, session );
}
private static Session doUnbind(SessionFactory factory, boolean releaseMapIfEmpty) {
Map sessionMap = sessionMap();
Session session = null;
if ( sessionMap != null ) {
session = ( Session ) sessionMap.remove( factory );
if ( releaseMapIfEmpty && sessionMap.isEmpty() ) {
context.set( null );
}
}
return session;
}
/**
* JTA transaction synch used for cleanup of the internal session map.
*/
protected static class CleanupSynch implements Synchronization, Serializable {
protected final SessionFactory factory;
public CleanupSynch(SessionFactory factory) {
this.factory = factory;
}
/**
* {@inheritDoc}
*/
public void beforeCompletion() {
}
/**
* {@inheritDoc}
*/
public void afterCompletion(int i) {
unbind( factory );
}
}
private class TransactionProtectionWrapper implements InvocationHandler, Serializable {
private final Session realSession;
private Session wrappedSession;
public TransactionProtectionWrapper(Session realSession) {
this.realSession = realSession;
}
/**
* {@inheritDoc}
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// If close() is called, guarantee unbind()
if ( "close".equals( method.getName()) ) {
unbind( realSession.getSessionFactory() );
}
else if ( "toString".equals( method.getName() )
|| "equals".equals( method.getName() )
|| "hashCode".equals( method.getName() )
|| "getStatistics".equals( method.getName() )
|| "isOpen".equals( method.getName() )
|| "getListeners".equals( method.getName() ) //useful for HSearch in particular
) {
// allow these to go through the the real session no matter what
}
else if ( !realSession.isOpen() ) {
// essentially, if the real session is closed allow any
// method call to pass through since the real session
// will complain by throwing an appropriate exception;
// NOTE that allowing close() above has the same basic effect,
// but we capture that there simply to doAfterTransactionCompletion the unbind...
}
else if ( !realSession.getTransaction().isActive() ) {
// limit the methods available if no transaction is active
if ( "beginTransaction".equals( method.getName() )
|| "getTransaction".equals( method.getName() )
|| "isTransactionInProgress".equals( method.getName() )
|| "setFlushMode".equals( method.getName() )
|| "getSessionFactory".equals( method.getName() ) ) {
log.trace( "allowing method [" + method.getName() + "] in non-transacted context" );
}
else if ( "reconnect".equals( method.getName() )
|| "disconnect".equals( method.getName() ) ) {
// allow these (deprecated) methods to pass through
}
else {
throw new HibernateException( method.getName() + " is not valid without active transaction" );
}
}
log.trace( "allowing proxied method [" + method.getName() + "] to proceed to real session" );
return method.invoke( realSession, args );
}
catch ( InvocationTargetException e ) {
if ( e.getTargetException() instanceof RuntimeException ) {
throw ( RuntimeException ) e.getTargetException();
}
else {
throw e;
}
}
}
/**
* Setter for property 'wrapped'.
*
* @param wrapped Value to set for property 'wrapped'.
*/
public void setWrapped(Session wrapped) {
this.wrappedSession = wrapped;
}
// serialization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private void writeObject(ObjectOutputStream oos) throws IOException {
// if a ThreadLocalSessionContext-bound session happens to get
// serialized, to be completely correct, we need to make sure
// that unbinding of that session occurs.
oos.defaultWriteObject();
if ( existingSession( factory ) == wrappedSession ) {
unbind( factory );
}
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// on the inverse, it makes sense that if a ThreadLocalSessionContext-
// bound session then gets deserialized to go ahead and re-bind it to
// the ThreadLocalSessionContext session map.
ois.defaultReadObject();
realSession.getTransaction().registerSynchronization( buildCleanupSynch() );
doBind( wrappedSession, factory );
}
}
}