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 362e42f16..85df9fedd 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 @@ -448,12 +448,25 @@ public abstract class AbstractTransaction implements StrolchTransaction { return getResourceMap().getBy(this, refP, assertExists); } + @Override + public void flush() { + try { + validateCommands(); + doCommands(); + writeChanges(this.txResult); + this.commands.clear(); + } catch (Exception e) { + this.closeStrategy = TransactionCloseStrategy.ROLLBACK; + + String msg = "Strolch Transaction for realm {0} failed due to {1}"; //$NON-NLS-1$ + msg = MessageFormat.format(msg, getRealmName(), e.getMessage()); + throw new StrolchPersistenceException(msg, e); + } + } + @Override public void autoCloseableCommit() { long start = System.nanoTime(); - if (logger.isDebugEnabled()) { - logger.info(MessageFormat.format("Committing TX for realm {0}...", getRealmName())); //$NON-NLS-1$ - } try { this.txResult.setState(TransactionState.COMMITTING); diff --git a/li.strolch.agent/src/main/java/li/strolch/persistence/api/StrolchTransaction.java b/li.strolch.agent/src/main/java/li/strolch/persistence/api/StrolchTransaction.java index 2b7daaa10..ff37e569f 100644 --- a/li.strolch.agent/src/main/java/li/strolch/persistence/api/StrolchTransaction.java +++ b/li.strolch.agent/src/main/java/li/strolch/persistence/api/StrolchTransaction.java @@ -17,7 +17,6 @@ package li.strolch.persistence.api; import java.util.List; -import ch.eitchnet.privilege.model.Certificate; import li.strolch.agent.api.AuditTrail; import li.strolch.agent.api.OrderMap; import li.strolch.agent.api.ResourceMap; @@ -46,6 +45,7 @@ import li.strolch.model.query.OrderQuery; import li.strolch.model.query.ResourceQuery; import li.strolch.runtime.StrolchConstants; import li.strolch.service.api.Command; +import ch.eitchnet.privilege.model.Certificate; /** *

@@ -174,6 +174,17 @@ public interface StrolchTransaction extends AutoCloseable { @Override public void close() throws StrolchPersistenceException; + /** + *

+ * Performs all registered commands + *

+ * + *

+ * This method does not release any locks, nor does it notify any observers + *

+ */ + public void flush(); + /** * @return true if the transaction is still open, i.e. not being closed or rolling back, committing, etc. */ 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 885847d4f..292fd34f5 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 @@ -388,6 +388,7 @@ public abstract class PostgresqlDao implements Strolch for (DaoCommand command : this.commands) { command.doComand(txResult); } + this.commands.clear(); } void rollback() { 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 new file mode 100644 index 000000000..0f278a320 --- /dev/null +++ b/li.strolch.service/src/test/java/li/strolch/service/FlushTxTest.java @@ -0,0 +1,104 @@ +package li.strolch.service; + +import li.strolch.command.AddResourceCommand; +import li.strolch.command.RemoveResourceCommand; +import li.strolch.model.ModelGenerator; +import li.strolch.model.Resource; +import li.strolch.persistence.api.StrolchTransaction; +import li.strolch.persistence.api.TransactionCloseStrategy; +import li.strolch.service.api.AbstractService; +import li.strolch.service.api.ServiceArgument; +import li.strolch.service.api.ServiceResult; +import li.strolch.service.test.AbstractRealmServiceTest; + +import org.junit.Test; + +import ch.eitchnet.utils.dbc.DBC; + +public class FlushTxTest extends AbstractRealmServiceTest { + + @Test + public void shouldFlushSuccessfully() { + + runServiceInAllRealmTypes(FlushingCommandsService.class, new ServiceArgument()); + } + + @Test + public void shouldRollbackSuccessfully() { + + runServiceInAllRealmTypes(RollbackAfterFlushCommandsService.class, new ServiceArgument()); + } + + public static class FlushingCommandsService 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)); + + RemoveResourceCommand rmResCmd = new RemoveResourceCommand(getContainer(), tx); + rmResCmd.setResource(resource); + tx.addCommand(rmResCmd); + tx.flush(); + DBC.PRE.assertNull("Expect to remove resource with id " + id, tx.getResourceBy(id, id)); + } + + return ServiceResult.success(); + } + } + + public static class RollbackAfterFlushCommandsService 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 = "flushSuccessfully2"; + 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)); + + // now force a rollback + tx.setCloseStrategy(TransactionCloseStrategy.ROLLBACK); + } + + // now make sure the new resource does not exist + try (StrolchTransaction tx = openTx(arg.realm)) { + DBC.PRE.assertNull("Did not expect resource with id after rolling back previous TX " + id, + tx.getResourceBy(id, id)); + } + + return ServiceResult.success(); + } + } +}