@Override public SagaInstance create(Data sagaData, Optional<String> resource) { SagaInstance sagaInstance = new SagaInstance(getSagaType(), null, "????", null, SagaDataSerde.serializeSagaData(sagaData), new HashSet<>()); sagaInstanceRepository.save(sagaInstance); String sagaId = sagaInstance.getId(); resource.ifPresent( r -> Assert.isTrue(sagaLockManager.claimLock(getSagaType(), sagaId, r), "Cannot claim lock for resource")); SagaActions<Data> actions = getStateDefinition().start(sagaData); processActions(sagaId, sagaInstance, sagaData, actions); return sagaInstance; }
private void handleAggregateInstanceEvent(String sagaType, String sagaId, Message message, String aggregateType, String aggregateId, String eventType) { System.out.println("Got handleAggregateInstanceEvent: " + message + ", type=" + sagaType + ", instance=" + sagaId); SagaInstanceData<Data> sagaInstanceAndData = sagaInstanceRepository.findWithData(sagaType, sagaId); SagaInstance sagaInstance = sagaInstanceAndData.getSagaInstance(); Data sagaData = sagaInstanceAndData.getSagaData(); String currentState = sagaInstance.getStateName(); logger.info("Current state={}", currentState); Optional<SagaEventHandler<Data>> eventHandler = getStateDefinition().findEventHandler(saga, currentState, sagaData, aggregateType, Long.parseLong(aggregateId), eventType); if (!eventHandler.isPresent()) { logger.error("No event handler for: {}", message); return; } logger.info("Invoking event handler for {}", message); SagaActions<Data> actions = eventHandler.get().getAction().apply(sagaData, new DomainEventEnvelopeImpl<>(null, null, null, null, null)); // TOOD // TODO - doesn't this do something??? Commands sagaInstance.setSerializedSagaData(SagaDataSerde.serializeSagaData(sagaData)); sagaInstanceRepository.update(sagaInstance); }
private void updateEventInstanceSubscriptions(Data sagaData, String sagaId, String stateName) { List<EventClassAndAggregateId> instanceEvents = getStateDefinition().findEventHandlers(saga, stateName, sagaData); aggregateInstanceSubscriptionsDAO.update(getSagaType(), sagaId, instanceEvents); }
private void handleReply(Message message) { if (!isReplyForThisSagaType(message)) return; logger.debug("Handle reply: {}", message); String sagaId = message.getRequiredHeader(SagaReplyHeaders.REPLY_SAGA_ID); String sagaType = message.getRequiredHeader(SagaReplyHeaders.REPLY_SAGA_TYPE); SagaInstance sagaInstance = sagaInstanceRepository.find(sagaType, sagaId); Data sagaData = SagaDataSerde.deserializeSagaData(sagaInstance.getSerializedSagaData()); message.getHeader(SagaReplyHeaders.REPLY_LOCKED).ifPresent(lockedTarget -> { String destination = message.getRequiredHeader(CommandMessageHeaders.inReply(CommandMessageHeaders.DESTINATION)); sagaInstance.addDestinationsAndResources(singleton(new DestinationAndResource(destination, lockedTarget))); }); String currentState = sagaInstance.getStateName(); logger.info("Current state={}", currentState); SagaActions<Data> actions = getStateDefinition().handleReply(currentState, sagaData, message); logger.info("Handled reply. Sending commands {}", actions.getCommands()); processActions(sagaId, sagaInstance, sagaData, actions); }
private void maybePerformEndStateActions(String sagaId, SagaInstance sagaInstance, Optional<String> possibleNewState) { possibleNewState.ifPresent(newState -> { if (getStateDefinition().isEndState(newState)) { performEndStateActions(sagaId, sagaInstance); } }); }
@Override public SagaInstance create(Data sagaData, Optional<String> resource) { SagaInstance sagaInstance = new SagaInstance(getSagaType(), null, "????", null, SagaDataSerde.serializeSagaData(sagaData), new HashSet<>()); sagaInstanceRepository.save(sagaInstance); String sagaId = sagaInstance.getId(); resource.ifPresent( r -> Assert.isTrue(sagaLockManager.claimLock(getSagaType(), sagaId, r), "Cannot claim lock for resource")); SagaActions<Data> actions = getStateDefinition().getStartingHandler().get().apply(sagaData); List<CommandWithDestination> commands = actions.getCommands(); sagaData = actions.getUpdatedSagaData().orElse(sagaData); sagaInstance.setLastRequestId(sendCommands(sagaId, commands)); sagaInstance.setSerializedSagaData(SagaDataSerde.serializeSagaData(sagaData)); publishEvents(sagaId, actions.getEventsToPublish(), actions.getUpdatedState()); Optional<String> possibleNewState = actions.getUpdatedState(); maybeUpdateState(sagaInstance, possibleNewState); maybePerformEndStateActions(sagaId, sagaInstance, possibleNewState); sagaInstanceRepository.update(sagaInstance); updateEnlistedAggregates(sagaId, actions.getEnlistedAggregates()); updateEventInstanceSubscriptions(sagaData, sagaId, sagaInstance.getStateName()); return sagaInstance; }
.findReplyHandler(saga, sagaInstance, currentState, sagaData, requestId, message);
private void handleReply(boolean compensating) { SagaInstance expectedSagaInstanceAfterSecondStep = makeExpectedSagaInstanceAfterSecondStep(); when(sagaInstanceRepository.find(sagaType, sagaId)) .thenReturn(sagaInstance); when(sagaDefinition.handleReply(anyString(), any(TestSagaData.class), any(Message.class))) .thenReturn(makeSecondSagaActions(compensating)); when(sagaCommandProducer.sendCommands(anyString(), anyString(), anyList(), anyString())).thenReturn (requestId2.asString()); sagaMessageHandler.accept(replyFromParticipant1); verify(sagaInstanceRepository).find(sagaType, sagaId); verify(sagaCommandProducer).sendCommands(sagaType, sagaId, Collections.singletonList(commandForParticipant2), sagaReplyChannel); verify(sagaInstanceRepository).update(argThat(sagaInstance -> sagaInstanceEquals(expectedSagaInstanceAfterSecondStep, sagaInstance))); assertSagaInstanceEquals(expectedSagaInstanceAfterSecondStep, sagaInstance); verifyNoMoreInteractions(sagaInstanceRepository, sagaCommandProducer); }
private void publishEvents(String sagaId, Set<EventToPublish> eventsToPublish, Optional<String> updatedState) { Set<EnlistedAggregate> elas = emptySet(); //enlistedAggregatesDao.findEnlistedAggregates(sagaId); boolean isEndState = updatedState.filter(s -> getStateDefinition().isEndState(s)).isPresent(); // TODO - an alternative model is to 'onResume()' based on a SagaCompletedEvent being published // Domain object doesn't have to publish event // What about automatically suspending if there is already a saga-in-progress? and delay the state check until much later for (EventToPublish event : eventsToPublish) { Map<String, String> headers = new HashMap<>(); if (isEndState) { Set<String> sagaIds = enlistedAggregatesDao.findSagas(event.getAggregateType(), event.getAggregateId()); sagaIds.remove(sagaId); headers.put("participating-saga-ids", sagaIds.stream().collect(joining(","))); } domainEventPublisher.publish(event.getAggregateType().getName(), event.getAggregateId(), headers, event.getDomainEvents()); } }
private void startSaga() { when(sagaDefinition.start(initialSagaData)).thenReturn(makeFirstSagaActions()); when(sagaCommandProducer.sendCommands(anyString(), anyString(), anyList(), anyString())) .thenReturn(requestId1.asString()); doAnswer(this::assignSagaIdWhenSaved).when(sagaInstanceRepository).save(any(SagaInstance.class)); sagaInstance = sm.create(initialSagaData); SagaInstance expectedSagaInstanceAfterFirstStep = makeExpectedSagaInstanceAfterFirstStep(); assertSagaInstanceEquals(expectedSagaInstanceAfterFirstStep, sagaInstance); verify(sagaCommandProducer).sendCommands(sagaType, sagaId, Collections.singletonList(commandForParticipant1), sagaReplyChannel); verify(sagaInstanceRepository).save(any(SagaInstance.class)); verify(sagaInstanceRepository).update(argThat(sagaInstance -> sagaInstanceEquals(expectedSagaInstanceAfterFirstStep, sagaInstance))); assertSagaInstanceEquals(expectedSagaInstanceAfterFirstStep, sagaInstance); verifyNoMoreInteractions(sagaInstanceRepository, sagaCommandProducer); }