You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2022/11/29 09:04:29 UTC
[fineract] branch develop updated: FINERACT-1760: Raise transaction replayed business event
This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 4c597b61f FINERACT-1760: Raise transaction replayed business event
4c597b61f is described below
commit 4c597b61fb6f460ff0755aa899f278f395c481f6
Author: Adam Saghy <ad...@gmail.com>
AuthorDate: Mon Nov 28 21:24:57 2022 +0100
FINERACT-1760: Raise transaction replayed business event
---
.../fineract/cob/COBBusinessStepServiceImpl.java | 31 +++--
.../service/BusinessEventNotifierService.java | 2 +
.../service/BusinessEventNotifierServiceImpl.java | 6 +
.../domain/LoanAccountDomainServiceJpa.java | 7 ++
...nRescheduleRequestWritePlatformServiceImpl.java | 46 +------
.../LoanChargeWritePlatformServiceImpl.java | 7 ++
.../LoanWritePlatformServiceJpaRepositoryImpl.java | 16 +++
.../ReplayedTransactionBusinessEventService.java | 26 ++++
...eplayedTransactionBusinessEventServiceImpl.java | 59 +++++++++
.../cob/COBBusinessStepServiceStepDefinitions.java | 10 ++
.../InitialisationTaskletStepDefinitions.java | 4 +
...sactionBusinessEventServiceIntegrationTest.java | 140 +++++++++++++++++++++
.../src/test/resources/features/cob/cob.feature | 8 ++
13 files changed, 310 insertions(+), 52 deletions(-)
diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java
index e716c1ac3..2a62c5099 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java
@@ -51,20 +51,27 @@ public class COBBusinessStepServiceImpl implements COBBusinessStepService {
if (executionMap == null || executionMap.isEmpty()) {
throw new BusinessStepException("Execution map is empty! COB Business step execution skipped!");
}
- businessEventNotifierService.startExternalEventRecording();
- for (String businessStep : executionMap.values()) {
- try {
- ThreadLocalContextUtil.setActionContext(ActionContext.COB);
- COBBusinessStep<S> businessStepBean = (COBBusinessStep<S>) applicationContext.getBean(businessStep);
- item = businessStepBean.execute(item);
- } catch (Exception e) {
- throw new BusinessStepException("Error happened during business step execution", e);
- } finally {
- // Fallback to COB action context after each business step
- ThreadLocalContextUtil.setActionContext(ActionContext.COB);
+ // Extra safety net to avoid event leaking
+ try {
+ businessEventNotifierService.startExternalEventRecording();
+
+ for (String businessStep : executionMap.values()) {
+ try {
+ ThreadLocalContextUtil.setActionContext(ActionContext.COB);
+ COBBusinessStep<S> businessStepBean = (COBBusinessStep<S>) applicationContext.getBean(businessStep);
+ item = businessStepBean.execute(item);
+ } catch (Exception e) {
+ throw new BusinessStepException("Error happened during business step execution", e);
+ } finally {
+ // Fallback to COB action context after each business step
+ ThreadLocalContextUtil.setActionContext(ActionContext.COB);
+ }
}
+ businessEventNotifierService.stopExternalEventRecording();
+ } catch (Exception e) {
+ businessEventNotifierService.resetEventRecording();
+ throw e;
}
- businessEventNotifierService.stopExternalEventRecording();
return item;
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierService.java
index d666b0eb6..d0176bc41 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierService.java
@@ -50,4 +50,6 @@ public interface BusinessEventNotifierService {
void startExternalEventRecording();
void stopExternalEventRecording();
+
+ void resetEventRecording();
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierServiceImpl.java
index e1369b22f..dca732ad9 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierServiceImpl.java
@@ -155,4 +155,10 @@ public class BusinessEventNotifierServiceImpl implements BusinessEventNotifierSe
recordedEvents.remove();
}
}
+
+ @Override
+ public void resetEventRecording() {
+ eventRecordingEnabled.set(false);
+ recordedEvents.remove();
+ }
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index 6652bcb1f..0846bbcaa 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -91,6 +91,7 @@ import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualPlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
+import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
import org.apache.fineract.portfolio.note.domain.Note;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
@@ -127,6 +128,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
private final ExternalIdFactory externalIdFactory;
+ private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
@Transactional
@Override
@@ -210,11 +212,14 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
if (changedTransactionDetail != null) {
for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
+
saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
// update loan with references to the newly created transactions
loan.addLoanTransaction(mapEntry.getValue());
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
if (StringUtils.isNotBlank(noteText)) {
@@ -788,6 +793,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
loan.getLoanTransactions().add(mapEntry.getValue());
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
index 1a2244346..6396908d0 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
@@ -30,6 +30,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
@@ -78,10 +79,10 @@ import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanResch
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
+import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.orm.jpa.JpaSystemException;
@@ -89,6 +90,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
+@RequiredArgsConstructor
public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanRescheduleRequestWritePlatformService {
private static final Logger LOG = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
@@ -111,45 +113,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
private final LoanAccountDomainService loanAccountDomainService;
private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository;
-
- /**
- * LoanRescheduleRequestWritePlatformServiceImpl constructor
- *
- *
- **/
- @Autowired
- public LoanRescheduleRequestWritePlatformServiceImpl(final CodeValueRepositoryWrapper codeValueRepositoryWrapper,
- final PlatformSecurityContext platformSecurityContext,
- final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
- final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
- final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
- final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
- final LoanTransactionRepository loanTransactionRepository,
- final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanRepositoryWrapper loanRepositoryWrapper,
- final LoanAssembler loanAssembler, final LoanUtilService loanUtilService,
- final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
- final LoanScheduleGeneratorFactory loanScheduleFactory, final LoanSummaryWrapper loanSummaryWrapper,
- final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
- final LoanAccountDomainService loanAccountDomainService,
- final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository) {
- this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
- this.platformSecurityContext = platformSecurityContext;
- this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator;
- this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
- this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
- this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
- this.loanTransactionRepository = loanTransactionRepository;
- this.journalEntryWritePlatformService = journalEntryWritePlatformService;
- this.loanRepositoryWrapper = loanRepositoryWrapper;
- this.loanAssembler = loanAssembler;
- this.loanUtilService = loanUtilService;
- this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
- this.loanScheduleFactory = loanScheduleFactory;
- this.loanSummaryWrapper = loanSummaryWrapper;
- this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
- this.loanAccountDomainService = loanAccountDomainService;
- this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository;
- }
+ private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
/**
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object and persist
@@ -490,6 +454,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
index c187d86dd..745d76901 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
@@ -155,6 +155,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
private final ExternalIdFactory externalIdFactory;
private final AccountTransferDetailRepository accountTransferDetailRepository;
private final LoanChargeAssembler loanChargeAssembler;
+ private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) {
return e.getFromDate().isBefore(loanCharge.getDueDate()) && !loanCharge.getDueDate().isAfter(e.getDueDate());
@@ -246,6 +247,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
this.loanRepositoryWrapper.save(loan);
}
@@ -740,6 +743,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
}
@@ -1085,6 +1090,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 19d680f0a..7cb647553 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -241,6 +241,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
private final LoanAccountLockService loanAccountLockService;
private final ExternalIdFactory externalIdFactory;
+ private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
@Transactional
@Override
@@ -457,6 +458,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanTransactionRepository.save(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
// auto create standing instruction
@@ -732,6 +735,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanTransactionRepository.save(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
}
@@ -1129,6 +1134,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
final String noteText = command.stringValueOfParameterNamed("note");
@@ -1360,6 +1367,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
final String noteText = command.stringValueOfParameterNamed("note");
@@ -2128,6 +2137,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanTransactionRepository.save(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
saveLoanWithDataIntegrityViolationChecks(loan);
@@ -2268,8 +2279,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (command.entityId() != null && changedTransactionDetail != null) {
for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
+ this.loanTransactionRepository.save(mapEntry.getValue());
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
createLoanScheduleArchive(loan, scheduleGeneratorDTO);
@@ -2326,6 +2340,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
+ // Trigger transaction replayed event
+ replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
loanAccountDomainService.recalculateAccruals(loan);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReplayedTransactionBusinessEventService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReplayedTransactionBusinessEventService.java
new file mode 100644
index 000000000..614c9b719
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReplayedTransactionBusinessEventService.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.service;
+
+import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
+
+public interface ReplayedTransactionBusinessEventService {
+
+ void raiseTransactionReplayedEvents(ChangedTransactionDetail changedTransactionDetail);
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReplayedTransactionBusinessEventServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReplayedTransactionBusinessEventServiceImpl.java
new file mode 100644
index 000000000..1359e00e6
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReplayedTransactionBusinessEventServiceImpl.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.service;
+
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
+import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class ReplayedTransactionBusinessEventServiceImpl implements ReplayedTransactionBusinessEventService {
+
+ private final BusinessEventNotifierService businessEventNotifierService;
+ private final LoanTransactionRepository loanTransactionRepository;
+
+ @Override
+ public void raiseTransactionReplayedEvents(final ChangedTransactionDetail changedTransactionDetail) {
+ if (changedTransactionDetail == null || changedTransactionDetail.getNewTransactionMappings().isEmpty()) {
+ return;
+ }
+ // Extra safety net to avoid event leaking
+ try {
+ businessEventNotifierService.startExternalEventRecording();
+ for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
+ LoanTransaction oldTransaction = loanTransactionRepository.findById(mapEntry.getKey())
+ .orElseThrow(() -> new LoanTransactionNotFoundException(mapEntry.getKey()));
+ LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(oldTransaction);
+ data.setNewTransactionDetail(mapEntry.getValue());
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
+ }
+ businessEventNotifierService.stopExternalEventRecording();
+ } catch (Exception e) {
+ businessEventNotifierService.resetEventRecording();
+ throw e;
+ }
+ }
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/COBBusinessStepServiceStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/cob/COBBusinessStepServiceStepDefinitions.java
index 866e13511..88cf32de9 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/cob/COBBusinessStepServiceStepDefinitions.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/cob/COBBusinessStepServiceStepDefinitions.java
@@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import com.google.common.base.Splitter;
import io.cucumber.java8.En;
@@ -39,6 +40,7 @@ import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.mix.data.MixTaxonomyData;
+import org.mockito.Mockito;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.ApplicationContext;
@@ -147,6 +149,14 @@ public class COBBusinessStepServiceStepDefinitions implements En {
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
});
+ Then("throw exception COBBusinessStepService.run method with verification", () -> {
+ assertThrows(BusinessStepException.class, () -> {
+ resultItem = this.businessStepService.run(this.executionMap, this.item);
+ });
+ verify(businessEventNotifierService, Mockito.times(1)).resetEventRecording();
+ ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
+ });
+
Then("The COBBusinessStepService.getCOBBusinessStepMap result exception", () -> {
assertThrows(BeanCreationException.class, () -> {
this.businessStepService.getCOBBusinessStepMap(this.clazz, this.jobName);
diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/common/InitialisationTaskletStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/cob/common/InitialisationTaskletStepDefinitions.java
index 435266e52..cf4a41fe5 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/cob/common/InitialisationTaskletStepDefinitions.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/cob/common/InitialisationTaskletStepDefinitions.java
@@ -24,6 +24,8 @@ import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import io.cucumber.java8.En;
+import org.apache.fineract.infrastructure.core.domain.ActionContext;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
import org.springframework.batch.repeat.RepeatStatus;
@@ -56,12 +58,14 @@ public class InitialisationTaskletStepDefinitions implements En {
Then("InitialisationTasklet.execute result should match", () -> {
assertEquals(RepeatStatus.FINISHED, resultItem);
assertEquals(appUser, SecurityContextHolder.getContext().getAuthentication().getPrincipal());
+ ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
});
Then("throw exception InitialisationTasklet.execute method", () -> {
assertThrows(RuntimeException.class, () -> {
resultItem = this.initialisationTasklet.execute(null, null);
});
+ ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
});
}
}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/ReplayedTransactionBusinessEventServiceIntegrationTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/ReplayedTransactionBusinessEventServiceIntegrationTest.java
new file mode 100644
index 000000000..824f22c15
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/ReplayedTransactionBusinessEventServiceIntegrationTest.java
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.service;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.verify;
+
+import java.util.Optional;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
+import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class ReplayedTransactionBusinessEventServiceIntegrationTest {
+
+ @Mock
+ private BusinessEventNotifierService businessEventNotifierService;
+
+ @Mock
+ private LoanTransactionRepository loanTransactionRepository;
+
+ private ReplayedTransactionBusinessEventService underTest;
+
+ @BeforeEach
+ public void setUp() {
+ underTest = new ReplayedTransactionBusinessEventServiceImpl(businessEventNotifierService, loanTransactionRepository);
+ }
+
+ @Test
+ public void testWhenParamIsNull() {
+ // given
+ ChangedTransactionDetail changedTransactionDetail = null;
+ // when
+ underTest.raiseTransactionReplayedEvents(changedTransactionDetail);
+ // then
+ verify(businessEventNotifierService, Mockito.times(0)).startExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(0))
+ .notifyPostBusinessEvent(Mockito.any(LoanAdjustTransactionBusinessEvent.class));
+ verify(businessEventNotifierService, Mockito.times(0)).stopExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(0)).resetEventRecording();
+ }
+
+ @Test
+ public void testWhenParamHasNoMapping() {
+ // given
+ ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
+ // when
+ underTest.raiseTransactionReplayedEvents(changedTransactionDetail);
+ // then
+ verify(businessEventNotifierService, Mockito.times(0)).startExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(0))
+ .notifyPostBusinessEvent(Mockito.any(LoanAdjustTransactionBusinessEvent.class));
+ verify(businessEventNotifierService, Mockito.times(0)).stopExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(0)).resetEventRecording();
+ }
+
+ @Test
+ public void testWhenParamHasOneNewTransaction() {
+ // given
+ LoanTransaction oldLoanTransaction = Mockito.mock(LoanTransaction.class);
+ LoanTransaction newLoanTransaction = Mockito.mock(LoanTransaction.class);
+ lenient().when(loanTransactionRepository.findById(1L)).thenReturn(Optional.of(oldLoanTransaction));
+ ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
+ changedTransactionDetail.getNewTransactionMappings().put(1L, newLoanTransaction);
+ // when
+ underTest.raiseTransactionReplayedEvents(changedTransactionDetail);
+ // then
+ verify(businessEventNotifierService, Mockito.times(1)).startExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(1))
+ .notifyPostBusinessEvent(Mockito.any(LoanAdjustTransactionBusinessEvent.class));
+ verify(businessEventNotifierService, Mockito.times(1)).stopExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(0)).resetEventRecording();
+ }
+
+ @Test
+ public void testWhenParamHasTwoNewTransaction() {
+ // given
+ LoanTransaction oldLoanTransaction = Mockito.mock(LoanTransaction.class);
+ LoanTransaction newLoanTransaction = Mockito.mock(LoanTransaction.class);
+ lenient().when(loanTransactionRepository.findById(1L)).thenReturn(Optional.of(oldLoanTransaction));
+ lenient().when(loanTransactionRepository.findById(2L)).thenReturn(Optional.of(oldLoanTransaction));
+ ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
+ changedTransactionDetail.getNewTransactionMappings().put(1L, newLoanTransaction);
+ changedTransactionDetail.getNewTransactionMappings().put(2L, newLoanTransaction);
+ // when
+ underTest.raiseTransactionReplayedEvents(changedTransactionDetail);
+ // then
+ verify(businessEventNotifierService, Mockito.times(1)).startExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(2))
+ .notifyPostBusinessEvent(Mockito.any(LoanAdjustTransactionBusinessEvent.class));
+ verify(businessEventNotifierService, Mockito.times(1)).stopExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(0)).resetEventRecording();
+ }
+
+ @Test
+ public void testWhenParamHasError() {
+ // given
+ doThrow(new RuntimeException()).when(businessEventNotifierService)
+ .notifyPostBusinessEvent(Mockito.any(LoanAdjustTransactionBusinessEvent.class));
+ LoanTransaction oldLoanTransaction = Mockito.mock(LoanTransaction.class);
+ LoanTransaction newLoanTransaction = Mockito.mock(LoanTransaction.class);
+ lenient().when(loanTransactionRepository.findById(1L)).thenReturn(Optional.of(oldLoanTransaction));
+ ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
+ changedTransactionDetail.getNewTransactionMappings().put(1L, newLoanTransaction);
+ // when
+ assertThrows(RuntimeException.class, () -> underTest.raiseTransactionReplayedEvents(changedTransactionDetail));
+ // then
+ verify(businessEventNotifierService, Mockito.times(1)).startExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(1))
+ .notifyPostBusinessEvent(Mockito.any(LoanAdjustTransactionBusinessEvent.class));
+ verify(businessEventNotifierService, Mockito.times(0)).stopExternalEventRecording();
+ verify(businessEventNotifierService, Mockito.times(1)).resetEventRecording();
+ }
+}
diff --git a/fineract-provider/src/test/resources/features/cob/cob.feature b/fineract-provider/src/test/resources/features/cob/cob.feature
index 4818ee6d9..5645e271e 100644
--- a/fineract-provider/src/test/resources/features/cob/cob.feature
+++ b/fineract-provider/src/test/resources/features/cob/cob.feature
@@ -37,6 +37,14 @@ Feature: COB
Examples:
|executionMap|
|null|
+
+ @cob
+ Scenario Outline: COB Business Step Service - run test failure
+ Given The COBBusinessStepService.run method with executeMap <executionMap>
+ Then throw exception COBBusinessStepService.run method with verification
+
+ Examples:
+ |executionMap|
|2,notExist|
|3,|