[FINERACT-1678] Loan COB - skip non-owned locked loan accounts
This commit is contained in:
committed by
Arnold Gálovics
parent
1cf37341d5
commit
c9a4aab466
+3
-3
@@ -18,9 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.cob.exceptions;
|
package org.apache.fineract.cob.exceptions;
|
||||||
|
|
||||||
public class LoanAccountWasAlreadyLocked extends Exception {
|
public class LoanAccountWasAlreadyLockedOrProcessed extends Exception {
|
||||||
|
|
||||||
public LoanAccountWasAlreadyLocked(Long loanId) {
|
public LoanAccountWasAlreadyLockedOrProcessed(Long loanId) {
|
||||||
super(String.format("Loan is in already locked state! loanId: %d", loanId));
|
super(String.format("Loan is in already locked state, or has been already processed! loanId: %d", loanId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-4
@@ -23,7 +23,7 @@ import lombok.AccessLevel;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.cob.exceptions.LoanReadException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||||
@@ -41,7 +41,7 @@ public abstract class AbstractLoanItemReader implements ItemReader<Loan> {
|
|||||||
private final LoanRepository loanRepository;
|
private final LoanRepository loanRepository;
|
||||||
|
|
||||||
@Setter(AccessLevel.PROTECTED)
|
@Setter(AccessLevel.PROTECTED)
|
||||||
private List<Long> alreadyLockedAccounts;
|
private List<Long> alreadyLockedOrProcessedAccounts;
|
||||||
@Setter(AccessLevel.PROTECTED)
|
@Setter(AccessLevel.PROTECTED)
|
||||||
private List<Long> remainingData;
|
private List<Long> remainingData;
|
||||||
private Long loanId;
|
private Long loanId;
|
||||||
@@ -51,8 +51,8 @@ public abstract class AbstractLoanItemReader implements ItemReader<Loan> {
|
|||||||
try {
|
try {
|
||||||
if (remainingData.size() > 0) {
|
if (remainingData.size() > 0) {
|
||||||
loanId = remainingData.remove(0);
|
loanId = remainingData.remove(0);
|
||||||
if (alreadyLockedAccounts != null && alreadyLockedAccounts.remove(loanId)) {
|
if (alreadyLockedOrProcessedAccounts != null && alreadyLockedOrProcessedAccounts.remove(loanId)) {
|
||||||
throw new LoanAccountWasAlreadyLocked(loanId);
|
throw new LoanAccountWasAlreadyLockedOrProcessed(loanId);
|
||||||
}
|
}
|
||||||
return loanRepository.findById(loanId).orElseThrow(() -> new LoanNotFoundException(loanId));
|
return loanRepository.findById(loanId).orElseThrow(() -> new LoanNotFoundException(loanId));
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-24
@@ -45,41 +45,35 @@ public class ApplyLoanLockTasklet implements Tasklet {
|
|||||||
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
|
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
|
||||||
ExecutionContext executionContext = contribution.getStepExecution().getExecutionContext();
|
ExecutionContext executionContext = contribution.getStepExecution().getExecutionContext();
|
||||||
List<Long> loanIds = (List<Long>) executionContext.get(LoanCOBConstant.LOAN_IDS);
|
List<Long> loanIds = (List<Long>) executionContext.get(LoanCOBConstant.LOAN_IDS);
|
||||||
List<Long> remainingLoanIds = new ArrayList<>(loanIds);
|
|
||||||
|
|
||||||
List<LoanAccountLock> accountLocks = accountLockRepository.findAllByLoanIdIn(remainingLoanIds);
|
List<LoanAccountLock> accountLocks = accountLockRepository.findAllByLoanIdIn(loanIds);
|
||||||
|
|
||||||
List<Long> alreadyHardLockedAccountIds = accountLocks.stream()
|
|
||||||
.filter(e -> LockOwner.LOAN_COB_CHUNK_PROCESSING.equals(e.getLockOwner())).map(LoanAccountLock::getLoanId).toList();
|
|
||||||
|
|
||||||
List<Long> alreadyUnderProcessingAccountIds = accountLocks.stream()
|
|
||||||
.filter(e -> LockOwner.LOAN_INLINE_COB_PROCESSING.equals(e.getLockOwner())).map(LoanAccountLock::getLoanId).toList();
|
|
||||||
|
|
||||||
Map<Long, LoanAccountLock> alreadySoftLockedAccountsMap = accountLocks.stream()
|
Map<Long, LoanAccountLock> alreadySoftLockedAccountsMap = accountLocks.stream()
|
||||||
.filter(e -> LockOwner.LOAN_COB_PARTITIONING.equals(e.getLockOwner()))
|
.filter(e -> LockOwner.LOAN_COB_PARTITIONING.equals(e.getLockOwner()))
|
||||||
.collect(Collectors.toMap(LoanAccountLock::getLoanId, Function.identity()));
|
.collect(Collectors.toMap(LoanAccountLock::getLoanId, Function.identity()));
|
||||||
|
|
||||||
remainingLoanIds.removeAll(alreadyHardLockedAccountIds);
|
List<Long> alreadyLockedByChunkProcessingAccountIds = accountLocks.stream()
|
||||||
remainingLoanIds.removeAll(alreadyUnderProcessingAccountIds);
|
.filter(e -> LockOwner.LOAN_COB_CHUNK_PROCESSING.equals(e.getLockOwner())).map(LoanAccountLock::getLoanId).toList();
|
||||||
|
|
||||||
for (Long loanId : remainingLoanIds) {
|
List<Long> toBeProcessedLoanIds = new ArrayList<>(alreadySoftLockedAccountsMap.keySet());
|
||||||
LoanAccountLock loanAccountLock = addLock(loanId, alreadySoftLockedAccountsMap);
|
|
||||||
accountLockRepository.save(loanAccountLock);
|
for (Long loanId : toBeProcessedLoanIds) {
|
||||||
|
upgradeToHardLock(loanId, alreadySoftLockedAccountsMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
executionContext.put(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS, new ArrayList<>(alreadyUnderProcessingAccountIds));
|
toBeProcessedLoanIds.addAll(alreadyLockedByChunkProcessingAccountIds);
|
||||||
|
List<Long> 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;
|
return RepeatStatus.FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanAccountLock addLock(Long loanId, Map<Long, LoanAccountLock> alreadySoftLockedAccountsMap) {
|
private void upgradeToHardLock(Long loanId, Map<Long, LoanAccountLock> alreadySoftLockedAccountsMap) {
|
||||||
LoanAccountLock loanAccountLock;
|
LoanAccountLock loanAccountLock = alreadySoftLockedAccountsMap.get(loanId);
|
||||||
if (alreadySoftLockedAccountsMap.containsKey(loanId)) {
|
// Upgrade lock
|
||||||
// Upgrade lock
|
loanAccountLock.setNewLockOwner(LockOwner.LOAN_COB_CHUNK_PROCESSING);
|
||||||
loanAccountLock = alreadySoftLockedAccountsMap.get(loanId);
|
accountLockRepository.save(loanAccountLock);
|
||||||
loanAccountLock.setNewLockOwner(LockOwner.LOAN_COB_CHUNK_PROCESSING);
|
|
||||||
} else {
|
|
||||||
loanAccountLock = new LoanAccountLock(loanId, LockOwner.LOAN_COB_CHUNK_PROCESSING);
|
|
||||||
}
|
|
||||||
return loanAccountLock;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public final class LoanCOBConstant {
|
|||||||
public static final String BUSINESS_STEP_MAP = "businessStepMap";
|
public static final String BUSINESS_STEP_MAP = "businessStepMap";
|
||||||
public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
|
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";
|
public static final String INLINE_LOAN_COB_JOB_NAME = "INLINE_LOAN_COB";
|
||||||
|
|
||||||
private LoanCOBConstant() {
|
private LoanCOBConstant() {
|
||||||
|
|||||||
+1
-1
@@ -149,7 +149,7 @@ public class LoanCOBWorkerConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
public ExecutionContextPromotionListener promotionListener() {
|
public ExecutionContextPromotionListener promotionListener() {
|
||||||
ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
|
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;
|
return listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ public class LoanItemReader extends AbstractLoanItemReader {
|
|||||||
ExecutionContext executionContext = stepExecution.getExecutionContext();
|
ExecutionContext executionContext = stepExecution.getExecutionContext();
|
||||||
ExecutionContext jobExecutionContext = stepExecution.getJobExecution().getExecutionContext();
|
ExecutionContext jobExecutionContext = stepExecution.getJobExecution().getExecutionContext();
|
||||||
List<Long> loanIds = (List<Long>) executionContext.get(LoanCOBConstant.LOAN_IDS);
|
List<Long> loanIds = (List<Long>) executionContext.get(LoanCOBConstant.LOAN_IDS);
|
||||||
setAlreadyLockedAccounts((List<Long>) jobExecutionContext.get(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS));
|
setAlreadyLockedOrProcessedAccounts(
|
||||||
|
(List<Long>) jobExecutionContext.get(LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS));
|
||||||
setRemainingData(new ArrayList<>(loanIds));
|
setRemainingData(new ArrayList<>(loanIds));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -73,14 +73,14 @@ public class ApplyLoanLockTaskletStepDefinitions implements En {
|
|||||||
|
|
||||||
Then("ApplyLoanLockTasklet.execute result should match", () -> {
|
Then("ApplyLoanLockTasklet.execute result should match", () -> {
|
||||||
assertEquals(RepeatStatus.FINISHED, resultItem);
|
assertEquals(RepeatStatus.FINISHED, resultItem);
|
||||||
assertEquals(3L,
|
assertEquals(3L, ((List) stepContribution.getStepExecution().getExecutionContext()
|
||||||
((List) stepContribution.getStepExecution().getExecutionContext().get(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS)).get(0));
|
.get(LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS)).get(0));
|
||||||
verify(this.accountLockRepository, Mockito.times(2)).save(valueCaptor.capture());
|
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<LoanAccountLock> values = valueCaptor.getAllValues();
|
List<LoanAccountLock> values = valueCaptor.getAllValues();
|
||||||
assertEquals(2L, values.get(0).getLoanId());
|
assertEquals(2L, values.get(0).getLoanId());
|
||||||
assertEquals(LockOwner.LOAN_COB_CHUNK_PROCESSING, values.get(0).getLockOwner());
|
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", () -> {
|
Then("throw exception ApplyLoanLockTasklet.execute method", () -> {
|
||||||
|
|||||||
+2
-1
@@ -57,7 +57,8 @@ public class LoanItemReaderStepDefinitions implements En {
|
|||||||
List<String> splitLockedAccountsStr = Splitter.on(',').splitToList(lockedAccounts);
|
List<String> splitLockedAccountsStr = Splitter.on(',').splitToList(lockedAccounts);
|
||||||
splitLockedAccounts = splitLockedAccountsStr.stream().map(Long::parseLong).toList();
|
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);
|
jobExecution.setExecutionContext(jobExecutionContext);
|
||||||
StepExecution stepExecution = new StepExecution("test", jobExecution);
|
StepExecution stepExecution = new StepExecution("test", jobExecution);
|
||||||
ExecutionContext stepExecutionContext = new ExecutionContext();
|
ExecutionContext stepExecutionContext = new ExecutionContext();
|
||||||
|
|||||||
Reference in New Issue
Block a user