-
Notifications
You must be signed in to change notification settings - Fork 230
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#86 Improve exception handling for local saga steps
- Loading branch information
Showing
9 changed files
with
242 additions
and
8 deletions.
There are no files selected for viewing
21 changes: 21 additions & 0 deletions
21
...ation-simple-dsl/src/main/java/io/eventuate/tram/sagas/simpledsl/LocalExceptionSaver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package io.eventuate.tram.sagas.simpledsl; | ||
|
||
import java.util.function.BiConsumer; | ||
|
||
public class LocalExceptionSaver<Data> { | ||
private final Class<?> exceptionType; | ||
private final BiConsumer<Data, RuntimeException> exceptionConsumer; | ||
|
||
public LocalExceptionSaver(Class<?> exceptionType, BiConsumer<Data,RuntimeException> exceptionConsumer) { | ||
this.exceptionType = exceptionType; | ||
this.exceptionConsumer = exceptionConsumer; | ||
} | ||
|
||
public boolean shouldSave(Exception e) { | ||
return exceptionType.isInstance(e); | ||
} | ||
|
||
public void save(Data data, RuntimeException e) { | ||
exceptionConsumer.accept(data, e); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
...rc/test/java/io/eventuate/tram/sagas/simpledsl/localexceptions/InvalidOrderException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package io.eventuate.tram.sagas.simpledsl.localexceptions; | ||
|
||
public class InvalidOrderException extends RuntimeException { | ||
} |
36 changes: 36 additions & 0 deletions
36
...java/io/eventuate/tram/sagas/simpledsl/localexceptions/LocalExceptionCreateOrderSaga.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package io.eventuate.tram.sagas.simpledsl.localexceptions; | ||
|
||
import io.eventuate.tram.sagas.orchestration.SagaDefinition; | ||
import io.eventuate.tram.sagas.simpledsl.SimpleSaga; | ||
|
||
public class LocalExceptionCreateOrderSaga implements SimpleSaga<LocalExceptionCreateOrderSagaData> { | ||
|
||
private final SagaDefinition<LocalExceptionCreateOrderSagaData> sagaDefinition; | ||
|
||
public LocalExceptionCreateOrderSaga(LocalExceptionCreateOrderSagaSteps steps) { | ||
this.sagaDefinition = | ||
step() | ||
.invokeLocal(steps::createOrder) | ||
.withCompensation(steps::rejectOrder) | ||
.step() | ||
.invokeParticipant(LocalExceptionCreateOrderSagaData::reserveCredit) | ||
.withCompensation(LocalExceptionCreateOrderSagaData::releaseCredit) | ||
.step() | ||
.invokeParticipant(LocalExceptionCreateOrderSagaData::reserveInventory) | ||
.withCompensationNotification(LocalExceptionCreateOrderSagaData::releaseInventory) | ||
.step() | ||
.invokeLocal(steps::approveOrder) | ||
.onException(InvalidOrderException.class, LocalExceptionCreateOrderSagaData::saveInvalidOrder) | ||
.onExceptionRollback(InvalidOrderException.class) | ||
.step() | ||
.notifyParticipant(LocalExceptionCreateOrderSagaData::fulfillOrder) | ||
.build(); | ||
} | ||
|
||
|
||
@Override | ||
public SagaDefinition<LocalExceptionCreateOrderSagaData> getSagaDefinition() { | ||
return this.sagaDefinition; | ||
} | ||
|
||
} |
42 changes: 42 additions & 0 deletions
42
.../io/eventuate/tram/sagas/simpledsl/localexceptions/LocalExceptionCreateOrderSagaData.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package io.eventuate.tram.sagas.simpledsl.localexceptions; | ||
|
||
import io.eventuate.tram.commands.consumer.CommandWithDestination; | ||
import io.eventuate.tram.sagas.simpledsl.ReleaseCreditCommand; | ||
import io.eventuate.tram.sagas.simpledsl.ReserveCreditCommand; | ||
import io.eventuate.tram.sagas.simpledsl.notifications.FulfillOrder; | ||
import io.eventuate.tram.sagas.simpledsl.notifications.ReleaseInventory; | ||
import io.eventuate.tram.sagas.simpledsl.notifications.ReserveInventory; | ||
|
||
public class LocalExceptionCreateOrderSagaData { | ||
|
||
|
||
private boolean invalidOrder = false; | ||
|
||
public CommandWithDestination reserveCredit() { | ||
return new CommandWithDestination("customerService", null, new ReserveCreditCommand()); | ||
} | ||
|
||
public CommandWithDestination releaseCredit() { | ||
return new CommandWithDestination("customerService", null, new ReleaseCreditCommand()); | ||
} | ||
|
||
public CommandWithDestination reserveInventory() { | ||
return new CommandWithDestination("inventoryService", null, new ReserveInventory()); | ||
} | ||
|
||
public CommandWithDestination releaseInventory() { | ||
return new CommandWithDestination("inventoryService", null, new ReleaseInventory()); | ||
} | ||
|
||
public CommandWithDestination fulfillOrder() { | ||
return new CommandWithDestination("fulfillmentService", null, new FulfillOrder()); | ||
} | ||
|
||
public void saveInvalidOrder(InvalidOrderException e) { | ||
this.invalidOrder = true; | ||
} | ||
|
||
public boolean isInvalidOrder() { | ||
return invalidOrder; | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
...io/eventuate/tram/sagas/simpledsl/localexceptions/LocalExceptionCreateOrderSagaSteps.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package io.eventuate.tram.sagas.simpledsl.localexceptions; | ||
|
||
public interface LocalExceptionCreateOrderSagaSteps { | ||
|
||
void createOrder(LocalExceptionCreateOrderSagaData data); | ||
void rejectOrder(LocalExceptionCreateOrderSagaData data); | ||
void approveOrder(LocalExceptionCreateOrderSagaData data); | ||
|
||
} |
93 changes: 93 additions & 0 deletions
93
.../io/eventuate/tram/sagas/simpledsl/localexceptions/LocalExceptionCreateOrderSagaTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package io.eventuate.tram.sagas.simpledsl.localexceptions; | ||
|
||
import io.eventuate.tram.sagas.simpledsl.ReleaseCreditCommand; | ||
import io.eventuate.tram.sagas.simpledsl.ReserveCreditCommand; | ||
import io.eventuate.tram.sagas.simpledsl.notifications.FulfillOrder; | ||
import io.eventuate.tram.sagas.simpledsl.notifications.ReleaseInventory; | ||
import io.eventuate.tram.sagas.simpledsl.notifications.ReserveInventory; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.MockitoJUnitRunner; | ||
|
||
import static io.eventuate.tram.sagas.testing.SagaUnitTestSupport.given; | ||
import static org.junit.Assert.assertTrue; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.Mockito.doThrow; | ||
|
||
@RunWith(MockitoJUnitRunner.class) | ||
public class LocalExceptionCreateOrderSagaTest { | ||
|
||
@Mock | ||
private LocalExceptionCreateOrderSagaSteps steps; | ||
|
||
@Test | ||
public void shouldExecuteAllStepsSuccessfully() { | ||
given(). | ||
saga(new LocalExceptionCreateOrderSaga(steps), new LocalExceptionCreateOrderSagaData()). | ||
expect(). | ||
command(new ReserveCreditCommand()). | ||
to("customerService"). | ||
andGiven(). | ||
successReply(). | ||
expect(). | ||
command(new ReserveInventory()). | ||
to("inventoryService"). | ||
andGiven(). | ||
successReply(). | ||
expect(). | ||
notification(new FulfillOrder()). | ||
to("fulfillmentService"). | ||
expectCompletedSuccessfully() | ||
; | ||
} | ||
|
||
@Test | ||
public void shouldRollbackDueToLocalStepFailure() { | ||
doThrow(new InvalidOrderException()).when(steps).approveOrder(any()); | ||
given(). | ||
saga(new LocalExceptionCreateOrderSaga(steps), new LocalExceptionCreateOrderSagaData()). | ||
expect(). | ||
command(new ReserveCreditCommand()). | ||
to("customerService"). | ||
andGiven(). | ||
successReply(). | ||
expect(). | ||
command(new ReserveInventory()). | ||
to("inventoryService"). | ||
andGiven(). | ||
successReply(). | ||
expect(). | ||
multiple(). | ||
notification(new ReleaseInventory()). | ||
to("inventoryService"). | ||
command(new ReleaseCreditCommand()). | ||
to("customerService"). | ||
verify(). | ||
andGiven(). | ||
successReply(). | ||
expectRolledBack() | ||
.assertSagaData(sagaData -> assertTrue(sagaData.isInvalidOrder())) | ||
; | ||
} | ||
|
||
static class UnexpectedException extends RuntimeException {} | ||
|
||
@Test(expected = UnexpectedException.class) | ||
public void shouldFailWithUnexpectedException() { | ||
doThrow(new UnexpectedException()).when(steps).approveOrder(any()); | ||
given(). | ||
saga(new LocalExceptionCreateOrderSaga(steps), new LocalExceptionCreateOrderSagaData()). | ||
expect(). | ||
command(new ReserveCreditCommand()). | ||
to("customerService"). | ||
andGiven(). | ||
successReply(). | ||
expect(). | ||
command(new ReserveInventory()). | ||
to("inventoryService"). | ||
andGiven(). | ||
successReply(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters