package com.eucalyptus.entities;
import java.lang.ref.WeakReference;
import java.util.Calendar;
import java.util.UUID;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import org.apache.commons.lang.time.StopWatch;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.ejb.EntityManagerFactoryImpl;
import com.eucalyptus.system.LogLevels;
import com.eucalyptus.util.LogUtil;
public class TxHandle implements Comparable<TxHandle>, EntityTransaction {
private static Logger LOG = Logger.getLogger( TxHandle.class );
private static ConcurrentNavigableMap<String, TxHandle> outstanding = new ConcurrentSkipListMap<String,TxHandle>();
private EntityManager em;
private WeakReference<Session> session;
private EntityTransaction delegate;
private StackTraceElement owner;
private Calendar startTime;
private String txUuid;
private StopWatch stopWatch;
private volatile long splitTime = 0l;
public TxHandle( String ctx ) {
this.txUuid = String.format("%s:%s:%s",ctx, LogLevels.TRACE ? EntityWrapper.getMyStackTraceElement( ) : "n.a", UUID.randomUUID( ).toString( ) );
this.startTime = Calendar.getInstance( );
this.stopWatch = new StopWatch( );
this.stopWatch.start( );
EntityManagerFactory anemf = ( EntityManagerFactoryImpl ) PersistenceContexts.getEntityManagerFactory( ctx );
try {
this.em = anemf.createEntityManager( );
this.delegate = em.getTransaction( );
this.delegate.begin( );
this.session = new WeakReference<Session>(( Session ) em.getDelegate( ));
outstanding.put( txUuid, this );
} catch ( Throwable e ) {
this.rollback( );
LOG.error( e, e );
throw new RuntimeException( e );
}
}
public boolean isExpired() {
long splitTime = split( );
return (splitTime-30000)>this.startTime.getTimeInMillis( );
}
public long splitOperation( ) {
long oldSplit = splitTime;
this.stopWatch.split( );
splitTime = this.stopWatch.getSplitTime( );
this.stopWatch.unsplit( );
return splitTime - oldSplit;
}
public long split( ) {
this.stopWatch.split( );
splitTime = this.stopWatch.getSplitTime( );
this.stopWatch.unsplit( );
return splitTime;
}
public void rollback( ) {
if( this.session != null ) {
this.session.clear( );
}
try {
if ( this.delegate != null && this.delegate.isActive( ) ) {
this.delegate.rollback( );
}
} catch( Throwable e ) {
LOG.error( e, e );
} finally {
this.delegate = null;
if( this.txUuid != null ) {
outstanding.remove( this.txUuid );
}
if( this.em != null ) {
this.em.close( );
}
this.em = null;
}
}
private void verifyOpen( ) {
if( this.delegate == null || this.em == null ) {
throw new RuntimeException( "Calling a closed tx handle: " + this.txUuid );
}
}
public void commit( ) {
if( this.session != null ) {
this.session.clear( );
}
this.verifyOpen( );
try {
this.delegate.commit( );
} catch( RuntimeException e ) {
if( this.delegate != null && this.delegate.isActive( ) ) {
this.delegate.rollback( );
LOG.debug( e, e );
throw e;
}
} finally {
this.delegate = null;
outstanding.remove( this.txUuid );
if( this.em != null ) {
this.em.close( );
}
this.em = null;
}
}
public static void printTxStatus( ) {
for ( String uuid : outstanding.keySet( ) ) {
TxHandle tx = outstanding.get( uuid );
if( tx.isExpired() ) {
LOG.error( LogUtil.subheader( "Long outstanding transaction handle for: " + uuid + " " + tx.txUuid ) );
outstanding.remove( uuid );
}
}
}
public String getTxUuid( ) {
this.split();
return this.txUuid;
}
public boolean getRollbackOnly( ) {
return delegate.getRollbackOnly( );
}
public boolean isActive( ) {
return delegate.isActive( );
}
public void setRollbackOnly( ) {
delegate.setRollbackOnly( );
}
public Session getSession( ) {
if( session.get( ) == null ) {
RuntimeException e = new RuntimeException( "Someone is calling a closed tx handle: " + this.txUuid );
LOG.error( e, e );
throw e;
}
return session.get( );
}
public Calendar getStartTime( ) {
return startTime;
}
public EntityManager getEntityManager( ) {
return this.em;
}
public void begin( ) {
delegate.begin( );
}
@Override
public int hashCode( ) {
final int prime = 31;
int result = 1;
result = prime * result + ( ( owner == null ) ? 0 : owner.hashCode( ) );
result = prime * result + ( ( startTime == null ) ? 0 : startTime.hashCode( ) );
return result;
}
@Override
public boolean equals( Object obj ) {
if ( this == obj ) return true;
if ( obj == null ) return false;
if ( getClass( ) != obj.getClass( ) ) return false;
TxHandle other = ( TxHandle ) obj;
if ( owner == null ) {
if ( other.owner != null ) return false;
} else if ( !owner.equals( other.owner ) ) return false;
if ( startTime == null ) {
if ( other.startTime != null ) return false;
} else if ( !startTime.equals( other.startTime ) ) return false;
return true;
}
@Override
public int compareTo( TxHandle o ) {
return this.startTime.compareTo( o.getStartTime( ) );
}
}