diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/exceptions/LoanAccountWasAlreadyLocked.java b/fineract-provider/src/main/java/org/apache/fineract/cob/exceptions/LoanAccountWasAlreadyLockedOrProcessed.java similarity index 77% rename from fineract-provider/src/main/java/org/apache/fineract/cob/exceptions/LoanAccountWasAlreadyLocked.java rename to fineract-provider/src/main/java/org/apache/fineract/cob/exceptions/LoanAccountWasAlreadyLockedOrProcessed.java index c33f4cd0f..8580567e8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/exceptions/LoanAccountWasAlreadyLocked.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/exceptions/LoanAccountWasAlreadyLockedOrProcessed.java @@ -18,9 +18,9 @@ */ package org.apache.fineract.cob.exceptions; -public class LoanAccountWasAlreadyLocked extends Exception { +public class LoanAccountWasAlreadyLockedOrProcessed extends Exception { - public LoanAccountWasAlreadyLocked(Long loanId) { - super(String.format("Loan is in already locked state! loanId: %d", loanId)); + public LoanAccountWasAlreadyLockedOrProcessed(Long loanId) { + super(String.format("Loan is in already locked state, or has been already processed! loanId: %d", loanId)); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemReader.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemReader.java index 3a4499185..1c795065c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemReader.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemReader.java @@ -23,7 +23,7 @@ import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.apache.fineract.cob.exceptions.LoanAccountWasAlreadyLocked; +import org.apache.fineract.cob.exceptions.LoanAccountWasAlreadyLockedOrProcessed; import org.apache.fineract.cob.exceptions.LoanReadException; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; @@ -41,7 +41,7 @@ public abstract class AbstractLoanItemReader implements ItemReader { private final LoanRepository loanRepository; @Setter(AccessLevel.PROTECTED) - private List alreadyLockedAccounts; + private List alreadyLockedOrProcessedAccounts; @Setter(AccessLevel.PROTECTED) private List remainingData; private Long loanId; @@ -51,8 +51,8 @@ public abstract class AbstractLoanItemReader implements ItemReader { try { if (remainingData.size() > 0) { loanId = remainingData.remove(0); - if (alreadyLockedAccounts != null && alreadyLockedAccounts.remove(loanId)) { - throw new LoanAccountWasAlreadyLocked(loanId); + if (alreadyLockedOrProcessedAccounts != null && alreadyLockedOrProcessedAccounts.remove(loanId)) { + throw new LoanAccountWasAlreadyLockedOrProcessed(loanId); } return loanRepository.findById(loanId).orElseThrow(() -> new LoanNotFoundException(loanId)); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/ApplyLoanLockTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/ApplyLoanLockTasklet.java index 41f3b6110..fbfb39a47 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/ApplyLoanLockTasklet.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/ApplyLoanLockTasklet.java @@ -45,41 +45,35 @@ public class ApplyLoanLockTasklet implements Tasklet { public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception { ExecutionContext executionContext = contribution.getStepExecution().getExecutionContext(); List loanIds = (List) executionContext.get(LoanCOBConstant.LOAN_IDS); - List remainingLoanIds = new ArrayList<>(loanIds); - List accountLocks = accountLockRepository.findAllByLoanIdIn(remainingLoanIds); - - List alreadyHardLockedAccountIds = accountLocks.stream() - .filter(e -> LockOwner.LOAN_COB_CHUNK_PROCESSING.equals(e.getLockOwner())).map(LoanAccountLock::getLoanId).toList(); - - List alreadyUnderProcessingAccountIds = accountLocks.stream() - .filter(e -> LockOwner.LOAN_INLINE_COB_PROCESSING.equals(e.getLockOwner())).map(LoanAccountLock::getLoanId).toList(); + List accountLocks = accountLockRepository.findAllByLoanIdIn(loanIds); Map alreadySoftLockedAccountsMap = accountLocks.stream() .filter(e -> LockOwner.LOAN_COB_PARTITIONING.equals(e.getLockOwner())) .collect(Collectors.toMap(LoanAccountLock::getLoanId, Function.identity())); - remainingLoanIds.removeAll(alreadyHardLockedAccountIds); - remainingLoanIds.removeAll(alreadyUnderProcessingAccountIds); + List alreadyLockedByChunkProcessingAccountIds = accountLocks.stream() + .filter(e -> LockOwner.LOAN_COB_CHUNK_PROCESSING.equals(e.getLockOwner())).map(LoanAccountLock::getLoanId).toList(); - for (Long loanId : remainingLoanIds) { - LoanAccountLock loanAccountLock = addLock(loanId, alreadySoftLockedAccountsMap); - accountLockRepository.save(loanAccountLock); + List toBeProcessedLoanIds = new ArrayList<>(alreadySoftLockedAccountsMap.keySet()); + + for (Long loanId : toBeProcessedLoanIds) { + upgradeToHardLock(loanId, alreadySoftLockedAccountsMap); } - executionContext.put(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS, new ArrayList<>(alreadyUnderProcessingAccountIds)); + toBeProcessedLoanIds.addAll(alreadyLockedByChunkProcessingAccountIds); + List alreadyLockedByInlineCOBOrProcessedLoanIds = new ArrayList<>(loanIds); + alreadyLockedByInlineCOBOrProcessedLoanIds.removeAll(toBeProcessedLoanIds); + + executionContext.put(LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS, + new ArrayList<>(alreadyLockedByInlineCOBOrProcessedLoanIds)); return RepeatStatus.FINISHED; } - private LoanAccountLock addLock(Long loanId, Map alreadySoftLockedAccountsMap) { - LoanAccountLock loanAccountLock; - if (alreadySoftLockedAccountsMap.containsKey(loanId)) { - // Upgrade lock - loanAccountLock = alreadySoftLockedAccountsMap.get(loanId); - loanAccountLock.setNewLockOwner(LockOwner.LOAN_COB_CHUNK_PROCESSING); - } else { - loanAccountLock = new LoanAccountLock(loanId, LockOwner.LOAN_COB_CHUNK_PROCESSING); - } - return loanAccountLock; + private void upgradeToHardLock(Long loanId, Map alreadySoftLockedAccountsMap) { + LoanAccountLock loanAccountLock = alreadySoftLockedAccountsMap.get(loanId); + // Upgrade lock + loanAccountLock.setNewLockOwner(LockOwner.LOAN_COB_CHUNK_PROCESSING); + accountLockRepository.save(loanAccountLock); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java index ccb889b12..bce9222af 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java @@ -26,7 +26,7 @@ public final class LoanCOBConstant { public static final String BUSINESS_STEP_MAP = "businessStepMap"; public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep"; - public static final String ALREADY_LOCKED_LOAN_IDS = "alreadyLockedLoanIds"; + public static final String ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS = "alreadyLockedOrProcessedLoanIds"; public static final String INLINE_LOAN_COB_JOB_NAME = "INLINE_LOAN_COB"; private LoanCOBConstant() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBWorkerConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBWorkerConfiguration.java index 823675a31..0f6bccbb7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBWorkerConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBWorkerConfiguration.java @@ -149,7 +149,7 @@ public class LoanCOBWorkerConfiguration { @Bean public ExecutionContextPromotionListener promotionListener() { ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener(); - listener.setKeys(new String[] { LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS }); + listener.setKeys(new String[] { LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS }); return listener; } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java index 8237ba928..2b7993e88 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java @@ -39,7 +39,8 @@ public class LoanItemReader extends AbstractLoanItemReader { ExecutionContext executionContext = stepExecution.getExecutionContext(); ExecutionContext jobExecutionContext = stepExecution.getJobExecution().getExecutionContext(); List loanIds = (List) executionContext.get(LoanCOBConstant.LOAN_IDS); - setAlreadyLockedAccounts((List) jobExecutionContext.get(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS)); + setAlreadyLockedOrProcessedAccounts( + (List) jobExecutionContext.get(LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS)); setRemainingData(new ArrayList<>(loanIds)); } } diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/ApplyLoanLockTaskletStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/ApplyLoanLockTaskletStepDefinitions.java index aa0b8dce4..fe8030eb9 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/ApplyLoanLockTaskletStepDefinitions.java +++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/ApplyLoanLockTaskletStepDefinitions.java @@ -73,14 +73,14 @@ public class ApplyLoanLockTaskletStepDefinitions implements En { Then("ApplyLoanLockTasklet.execute result should match", () -> { assertEquals(RepeatStatus.FINISHED, resultItem); - assertEquals(3L, - ((List) stepContribution.getStepExecution().getExecutionContext().get(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS)).get(0)); - verify(this.accountLockRepository, Mockito.times(2)).save(valueCaptor.capture()); + assertEquals(3L, ((List) stepContribution.getStepExecution().getExecutionContext() + .get(LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS)).get(0)); + assertEquals(4L, ((List) stepContribution.getStepExecution().getExecutionContext() + .get(LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS)).get(1)); + verify(this.accountLockRepository, Mockito.times(1)).save(valueCaptor.capture()); List values = valueCaptor.getAllValues(); assertEquals(2L, values.get(0).getLoanId()); assertEquals(LockOwner.LOAN_COB_CHUNK_PROCESSING, values.get(0).getLockOwner()); - assertEquals(4L, values.get(1).getLoanId()); - assertEquals(LockOwner.LOAN_COB_CHUNK_PROCESSING, values.get(1).getLockOwner()); }); Then("throw exception ApplyLoanLockTasklet.execute method", () -> { diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/LoanItemReaderStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/LoanItemReaderStepDefinitions.java index 1d30d048d..f8f1758ad 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/LoanItemReaderStepDefinitions.java +++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/LoanItemReaderStepDefinitions.java @@ -57,7 +57,8 @@ public class LoanItemReaderStepDefinitions implements En { List splitLockedAccountsStr = Splitter.on(',').splitToList(lockedAccounts); splitLockedAccounts = splitLockedAccountsStr.stream().map(Long::parseLong).toList(); } - jobExecutionContext.put(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS, new ArrayList<>(splitLockedAccounts)); + jobExecutionContext.put(LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS, + new ArrayList<>(splitLockedAccounts)); jobExecution.setExecutionContext(jobExecutionContext); StepExecution stepExecution = new StepExecution("test", jobExecution); ExecutionContext stepExecutionContext = new ExecutionContext();