diff --git a/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java b/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java index d0aa61100..7dd81e7c5 100644 --- a/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java +++ b/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java @@ -500,6 +500,8 @@ public abstract class AbstractTransaction implements StrolchTransaction { handleCommit(start, auditTrailDuration, updateObserversDuration); + this.txResult.setState(TransactionState.COMMITTED); + } catch (Exception e) { this.txResult.setState(TransactionState.ROLLING_BACK); try { @@ -520,6 +522,8 @@ public abstract class AbstractTransaction implements StrolchTransaction { handleFailure(start, e); } + this.txResult.setState(TransactionState.FAILED); + String msg = "Strolch Transaction for realm {0} failed due to {1}"; //$NON-NLS-1$ msg = MessageFormat.format(msg, getRealmName(), e.getMessage()); throw new StrolchTransactionException(msg, e); @@ -534,11 +538,14 @@ public abstract class AbstractTransaction implements StrolchTransaction { long start = System.nanoTime(); logger.warn(MessageFormat.format("Rolling back TX for realm {0}...", getRealmName())); //$NON-NLS-1$ try { + this.txResult.setState(TransactionState.ROLLING_BACK); undoCommands(); rollback(this.txResult); handleRollback(start); + this.txResult.setState(TransactionState.ROLLED_BACK); } catch (Exception e) { handleFailure(start, e); + this.txResult.setState(TransactionState.FAILED); } finally { releaseElementLocks(); } @@ -788,7 +795,7 @@ public abstract class AbstractTransaction implements StrolchTransaction { * performing the commands */ private void undoCommands() { - ListIterator iter = this.flushedCommands.listIterator(); + ListIterator iter = this.flushedCommands.listIterator(this.flushedCommands.size()); while (iter.hasPrevious()) { Command command = iter.previous(); command.undo(); diff --git a/li.strolch.persistence.postgresql/src/main/java/li/strolch/persistence/postgresql/PostgresqlDao.java b/li.strolch.persistence.postgresql/src/main/java/li/strolch/persistence/postgresql/PostgresqlDao.java index 292fd34f5..e033ba9a4 100644 --- a/li.strolch.persistence.postgresql/src/main/java/li/strolch/persistence/postgresql/PostgresqlDao.java +++ b/li.strolch.persistence.postgresql/src/main/java/li/strolch/persistence/postgresql/PostgresqlDao.java @@ -385,6 +385,7 @@ public abstract class PostgresqlDao implements Strolch } void commit(TransactionResult txResult) { + // even though we support rollback we can clear the commands here even if we performed them because the DB transaction will be rolled back for (DaoCommand command : this.commands) { command.doComand(txResult); } diff --git a/li.strolch.service/src/test/java/li/strolch/service/FlushTxTest.java b/li.strolch.service/src/test/java/li/strolch/service/FlushTxTest.java index 9ce4a898c..dd81ada02 100644 --- a/li.strolch.service/src/test/java/li/strolch/service/FlushTxTest.java +++ b/li.strolch.service/src/test/java/li/strolch/service/FlushTxTest.java @@ -2,6 +2,7 @@ package li.strolch.service; import li.strolch.command.AddResourceCommand; import li.strolch.command.RemoveResourceCommand; +import li.strolch.command.UpdateResourceCommand; import li.strolch.model.ModelGenerator; import li.strolch.model.Resource; import li.strolch.persistence.api.StrolchTransaction; @@ -17,9 +18,15 @@ import ch.eitchnet.utils.dbc.DBC; public class FlushTxTest extends AbstractRealmServiceTest { @Test - public void shouldFlushSuccessfully() { + public void shouldFlushSuccessfully1() { - runServiceInAllRealmTypes(FlushingCommandsService.class, new ServiceArgument()); + runServiceInAllRealmTypes(FlushingCommandsService1.class, new ServiceArgument()); + } + + @Test + public void shouldFlushSuccessfully2() { + + runServiceInAllRealmTypes(FlushingCommandsService2.class, new ServiceArgument()); } @Test @@ -28,7 +35,7 @@ public class FlushTxTest extends AbstractRealmServiceTest { runServiceInAllRealmTypes(RollbackAfterFlushCommandsService.class, new ServiceArgument()); } - public static class FlushingCommandsService extends AbstractService { + public static class FlushingCommandsService1 extends AbstractService { private static final long serialVersionUID = 1L; @Override @@ -61,6 +68,61 @@ public class FlushTxTest extends AbstractRealmServiceTest { tx.commitOnClose(); } + // now make sure the new resource does not exist + try (StrolchTransaction tx = openTx(arg.realm)) { + + Resource res = tx.getResourceBy(id, id); + if (res != null) { + throw tx.fail("Did not expect resource with id " + id); + } + } + + return ServiceResult.success(); + } + } + + public static class FlushingCommandsService2 extends AbstractService { + private static final long serialVersionUID = 1L; + + @Override + protected ServiceResult getResultInstance() { + return new ServiceResult(); + } + + @Override + protected ServiceResult internalDoService(ServiceArgument arg) throws Exception { + + String id = "flushSuccessfully"; + Resource resource = ModelGenerator.createResource(id, id, id); + + try (StrolchTransaction tx = openTx(arg.realm)) { + + DBC.PRE.assertNull("Did not expect resource with id " + id, tx.getResourceBy(id, id)); + + AddResourceCommand addResCmd = new AddResourceCommand(getContainer(), tx); + addResCmd.setResource(resource); + tx.addCommand(addResCmd); + tx.flush(); + DBC.PRE.assertNotNull("Expected resource with id " + id, tx.getResourceBy(id, id)); + + Resource res = tx.getResourceBy(id, id); + + UpdateResourceCommand updateResCmd = new UpdateResourceCommand(getContainer(), tx); + updateResCmd.setResource(res); + tx.addCommand(updateResCmd); + + tx.commitOnClose(); + } + + // now make sure the new resource does exist + try (StrolchTransaction tx = openTx(arg.realm)) { + + Resource res = tx.getResourceBy(id, id); + if (res == null) { + throw tx.fail("Did not find expected resource with id " + id); + } + } + return ServiceResult.success(); } } @@ -102,6 +164,33 @@ public class FlushTxTest extends AbstractRealmServiceTest { } } + // now do it over, but use throw + try (StrolchTransaction tx = openTx(arg.realm)) { + + DBC.PRE.assertNull("Did not expect resource with id " + id, tx.getResourceBy(id, id)); + + AddResourceCommand addResCmd = new AddResourceCommand(getContainer(), tx); + addResCmd.setResource(resource); + tx.addCommand(addResCmd); + tx.flush(); + DBC.PRE.assertNotNull("Expected resource with id " + id, tx.getResourceBy(id, id)); + + // now force a rollback + throw tx.fail("Oh snap, something went wrong!"); + + } catch (Exception e) { + // expected + } + + // now make sure the new resource does not exist + try (StrolchTransaction tx = openTx(arg.realm)) { + + Resource res = tx.getResourceBy(id, id); + if (res != null) { + throw tx.fail("Did not expect resource with id after rolling back previous TX " + id); + } + } + return ServiceResult.success(); } }