Skip to content

Commit

Permalink
Merge pull request #648 from jakewins/rollback-causes
Browse files Browse the repository at this point in the history
Set cause on RollbackException
  • Loading branch information
tinwelint committed Jun 28, 2012
2 parents b7fc3e5 + 2d904fe commit 21fbb28
Show file tree
Hide file tree
Showing 9 changed files with 551 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -317,38 +317,24 @@ private class TxCommitHook implements Synchronization
this.tx = tx;
}

public void afterCompletion( int param )
@Override
public void afterCompletion( int param )
{
try
releaseConnections( tx );
if ( param == Status.STATUS_COMMITTED )
{
releaseConnections( tx );
if ( param == Status.STATUS_COMMITTED )
{
lockReleaser.commit();
}
else
{
lockReleaser.rollback();
}
lockReleaser.commit();
}
catch ( Throwable t )
else
{
log.log( Level.SEVERE,
"Unable to release connections for " + tx, t );
lockReleaser.rollback();
}
}

public void beforeCompletion()
@Override
public void beforeCompletion()
{
try
{
delistResourcesForTransaction();
}
catch ( Throwable t )
{
log.log( Level.SEVERE,
"Unable to delist resources for " + tx, t );
}
delistResourcesForTransaction();
}

private void releaseConnections( Transaction tx )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.neo4j.helpers.Exceptions;
import org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaDataSource;
import org.neo4j.kernel.impl.transaction.xaframework.ForceMode;
import org.neo4j.kernel.impl.util.MultipleCauseException;

class TransactionImpl implements Transaction
{
Expand Down Expand Up @@ -109,7 +110,8 @@ public String toString()
return txString.toString();
}

public synchronized void commit() throws RollbackException,
@Override
public synchronized void commit() throws RollbackException,
HeuristicMixedException, HeuristicRollbackException,
IllegalStateException, SystemException
{
Expand All @@ -122,14 +124,16 @@ boolean isGlobalStartRecordWritten()
return globalStartRecordWritten;
}

public synchronized void rollback() throws IllegalStateException,
@Override
public synchronized void rollback() throws IllegalStateException,
SystemException
{
// make sure tx not suspended
txManager.rollback();
}

public synchronized boolean enlistResource( XAResource xaRes )
@Override
public synchronized boolean enlistResource( XAResource xaRes )
throws RollbackException, IllegalStateException, SystemException
{
if ( xaRes == null )
Expand Down Expand Up @@ -251,7 +255,8 @@ private void addResourceToList( Xid xid, XAResource xaRes )
}
}

public synchronized boolean delistResource( XAResource xaRes, int flag )
@Override
public synchronized boolean delistResource( XAResource xaRes, int flag )
throws IllegalStateException
{
if ( xaRes == null )
Expand Down Expand Up @@ -306,7 +311,8 @@ public synchronized boolean delistResource( XAResource xaRes, int flag )
}

// TODO: figure out if this needs syncrhonization or make status volatile
public int getStatus() // throws SystemException
@Override
public int getStatus() // throws SystemException
{
return status;
}
Expand All @@ -320,7 +326,8 @@ void setStatus( int status )
private List<Synchronization> syncHooksAdded =
new ArrayList<Synchronization>();

public synchronized void registerSynchronization( Synchronization s )
@Override
public synchronized void registerSynchronization( Synchronization s )
throws RollbackException, IllegalStateException
{
if ( s == null )
Expand Down Expand Up @@ -367,8 +374,7 @@ synchronized void doBeforeCompletion()
}
catch ( Throwable t )
{
log.log( Level.WARNING, "Caught exception from tx syncronization[" + s
+ "] beforeCompletion()", t );
addRollbackCause(t);
}
}
// execute any hooks added since we entered doBeforeCompletion
Expand All @@ -389,7 +395,7 @@ synchronized void doBeforeCompletion()
}
}

synchronized void doAfterCompletion()
synchronized void doAfterCompletion()
{
for ( Synchronization s : syncHooks )
{
Expand All @@ -406,7 +412,8 @@ synchronized void doAfterCompletion()
syncHooks = null; // help gc
}

public void setRollbackOnly() throws IllegalStateException
@Override
public void setRollbackOnly() throws IllegalStateException
{
if ( status == Status.STATUS_ACTIVE ||
status == Status.STATUS_PREPARING ||
Expand Down Expand Up @@ -436,6 +443,8 @@ public boolean equals( Object o )

private volatile int hashCode = 0;

private Throwable rollbackCause;

@Override
public int hashCode()
{
Expand Down Expand Up @@ -650,4 +659,27 @@ public ForceMode getForceMode()
{
return forceMode;
}

public Throwable getRollbackCause()
{
return rollbackCause;
}

private void addRollbackCause( Throwable cause )
{
if ( rollbackCause == null )
{
rollbackCause = cause;
}
else
{
if ( !(rollbackCause instanceof MultipleCauseException) )
{
rollbackCause = new MultipleCauseException(
"Multiple exceptions occurred, stack traces of all of them available below, or via #getCauses().",
rollbackCause );
}
((MultipleCauseException) rollbackCause).addCause( cause );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
Expand All @@ -40,6 +41,7 @@
import javax.transaction.Transaction;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;

import org.neo4j.graphdb.TransactionFailureException;
import org.neo4j.graphdb.event.ErrorState;
import org.neo4j.helpers.Exceptions;
Expand All @@ -48,6 +50,7 @@
import org.neo4j.kernel.impl.nioneo.store.FileSystemAbstraction;
import org.neo4j.kernel.impl.transaction.xaframework.ForceMode;
import org.neo4j.kernel.impl.transaction.xaframework.XaResource;
import org.neo4j.kernel.impl.util.ExceptionCauseSetter;
import org.neo4j.kernel.impl.util.StringLogger;
import org.neo4j.kernel.lifecycle.Lifecycle;

Expand Down Expand Up @@ -318,7 +321,8 @@ public void attemptWaitForTxCompletionAndBlockFutureTransactions( long maxWaitTi
while ( txThreadMap.size() > 0 && System.currentTimeMillis() < endTime ) Thread.yield();
}

public void begin() throws NotSupportedException, SystemException
@Override
public void begin() throws NotSupportedException, SystemException
{
begin( ForceMode.forced );
}
Expand Down Expand Up @@ -376,7 +380,8 @@ void writeStartRecord( byte globalId[] ) throws SystemException
}
}

public void commit() throws RollbackException, HeuristicMixedException,
@Override
public void commit() throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, IllegalStateException, SystemException
{
assertTmOk( "tx commit" );
Expand Down Expand Up @@ -579,11 +584,14 @@ private void rollbackCommit( Thread thread, TransactionImpl tx )
+ " error writing transaction log" ), e ));
}
tx.setStatus( Status.STATUS_NO_TRANSACTION );
throw new RollbackException(
RollbackException rollbackException = new RollbackException(
"Failed to commit, transaction rolledback" );
ExceptionCauseSetter.setCause(rollbackException, tx.getRollbackCause());
throw rollbackException;
}

public void rollback() throws IllegalStateException, SystemException
@Override
public void rollback() throws IllegalStateException, SystemException
{
assertTmOk( "tx rollback" );
Thread thread = Thread.currentThread();
Expand Down Expand Up @@ -654,7 +662,8 @@ public void rollback() throws IllegalStateException, SystemException
}
}

public int getStatus()
@Override
public int getStatus()
{
Thread thread = Thread.currentThread();
TransactionImpl tx = txThreadMap.get( thread );
Expand All @@ -665,12 +674,14 @@ public int getStatus()
return Status.STATUS_NO_TRANSACTION;
}

public Transaction getTransaction()
@Override
public Transaction getTransaction()
{
return txThreadMap.get( Thread.currentThread() );
}

public void resume( Transaction tx ) throws IllegalStateException,
@Override
public void resume( Transaction tx ) throws IllegalStateException,
SystemException
{
assertTmOk( "tx resume" );
Expand All @@ -695,7 +706,8 @@ public void resume( Transaction tx ) throws IllegalStateException,
}
}

public Transaction suspend() throws SystemException
@Override
public Transaction suspend() throws SystemException
{
assertTmOk( "tx suspend" );
// check for ACTIVE/MARKED_ROLLBACK?
Expand All @@ -708,7 +720,8 @@ public Transaction suspend() throws SystemException
return tx;
}

public void setRollbackOnly() throws IllegalStateException, SystemException
@Override
public void setRollbackOnly() throws IllegalStateException, SystemException
{
assertTmOk( "tx set rollback only" );
Thread thread = Thread.currentThread();
Expand All @@ -720,7 +733,8 @@ public void setRollbackOnly() throws IllegalStateException, SystemException
tx.setRollbackOnly();
}

public void setTransactionTimeout( int seconds ) throws SystemException
@Override
public void setTransactionTimeout( int seconds ) throws SystemException
{
assertTmOk( "tx set timeout" );
// ...
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2002-2012 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.util;

import java.lang.reflect.Field;

/**
* Because the JTA spec was built before java exceptions
* supported "cause", we can't directly set the cause on
* those exceptions when we want to throw them.
*
* However, since they extend normal java exceptions which
* today do support "cause", we can set the cause of old exception
* classes using introspection, which is what this class does.
*/
public class ExceptionCauseSetter {

public static void setCause(Throwable original, Throwable cause)
{
try {
Field field = Throwable.class.getDeclaredField("cause");
field.setAccessible(true);
field.set(original, cause);
} catch (Exception e) {
// Failed to set cause. Don't throw an exception from this
// as we are most likely already recovering from an exception,
// and being unable to set the cause is most likely a JVM issue
// that the user can't help anyway.
// Print an exception from this though, including the cause exception
// to help debugging.

// TODO: Use proper logging.
Exception error = new Exception("Unable to set cause of exception (see nested), will print stacktrace of the exception causing all this below.",e);
error.printStackTrace();
cause.printStackTrace();
}
}

}
Loading

0 comments on commit 21fbb28

Please sign in to comment.