From 748b775051691fb48125650459d74cb0d59695e5 Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Mon, 17 Apr 2023 15:07:41 +0200 Subject: [PATCH] FINERACT-1724: Use services instead of repositories in COB --- .../listener/AbstractLoanItemListener.java | 12 +-- .../ChunkProcessingLoanItemListener.java | 6 +- .../listener/InlineCOBLoanItemListener.java | 6 +- .../cob/loan/AbstractLoanItemReader.java | 2 - .../cob/loan/AbstractLoanItemWriter.java | 5 +- .../cob/loan/ApplyLoanLockTasklet.java | 22 +---- .../cob/loan/InlineCOBLoanItemWriter.java | 5 +- .../cob/loan/LoanCOBManagerConfiguration.java | 11 +-- .../cob/loan/LoanCOBWorkerConfiguration.java | 12 +-- .../cob/loan/LoanInlineCOBConfig.java | 6 +- .../fineract/cob/loan/LoanItemWriter.java | 5 +- .../cob/loan/LoanLockingConfiguration.java | 44 +++++++++ .../fineract/cob/loan/LoanLockingService.java | 38 ++++++++ .../cob/loan/LoanLockingServiceImpl.java | 97 +++++++++++++++++++ .../fineract/cob/loan/LockLoanTasklet.java | 31 +----- ...RetrieveAllNonClosedLoanIdServiceImpl.java | 6 ++ .../cob/loan/RetrieveLoanIdService.java | 4 + .../cob/loan/StayedLockedLoansTasklet.java | 5 +- .../LoanItemListenerStepDefinitions.java | 31 +++--- .../ApplyLoanLockTaskletStepDefinitions.java | 25 +++-- .../loan/FetchAndLockLoanStepDefinitions.java | 34 ++++--- .../loan/LoanItemWriterStepDefinitions.java | 21 ++-- 22 files changed, 283 insertions(+), 145 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingConfiguration.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingServiceImpl.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/listener/AbstractLoanItemListener.java b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/AbstractLoanItemListener.java index e8246ccd8..b2606e889 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/listener/AbstractLoanItemListener.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/AbstractLoanItemListener.java @@ -21,13 +21,12 @@ package org.apache.fineract.cob.listener; import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW; import java.util.List; -import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.cob.domain.LoanAccountLock; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LockOwner; import org.apache.fineract.cob.exceptions.LoanReadException; +import org.apache.fineract.cob.loan.LoanLockingService; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.serialization.ThrowableSerialization; import org.apache.fineract.portfolio.loanaccount.domain.Loan; @@ -46,7 +45,7 @@ import org.springframework.transaction.support.TransactionTemplate; @RequiredArgsConstructor public abstract class AbstractLoanItemListener { - private final LoanAccountLockRepository accountLockRepository; + private final LoanLockingService loanLockingService; private final TransactionTemplate transactionTemplate; @@ -57,9 +56,10 @@ public abstract class AbstractLoanItemListener { @Override protected void doInTransactionWithoutResult(@NotNull TransactionStatus status) { for (Long loanId : loanIds) { - Optional loanAccountLock = accountLockRepository.findByLoanIdAndLockOwner(loanId, getLockOwner()); - loanAccountLock.ifPresent( - accountLock -> accountLock.setError(String.format(msg, loanId), ThrowableSerialization.serialize(e))); + LoanAccountLock loanAccountLock = loanLockingService.findByLoanIdAndLockOwner(loanId, getLockOwner()); + if (loanAccountLock != null) { + loanAccountLock.setError(String.format(msg, loanId), ThrowableSerialization.serialize(e)); + } } } }); diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/listener/ChunkProcessingLoanItemListener.java b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/ChunkProcessingLoanItemListener.java index 378083684..818dd8fd6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/listener/ChunkProcessingLoanItemListener.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/ChunkProcessingLoanItemListener.java @@ -18,14 +18,14 @@ */ package org.apache.fineract.cob.listener; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LockOwner; +import org.apache.fineract.cob.loan.LoanLockingService; import org.springframework.transaction.support.TransactionTemplate; public class ChunkProcessingLoanItemListener extends AbstractLoanItemListener { - public ChunkProcessingLoanItemListener(LoanAccountLockRepository accountLockRepository, TransactionTemplate transactionTemplate) { - super(accountLockRepository, transactionTemplate); + public ChunkProcessingLoanItemListener(LoanLockingService loanLockingService, TransactionTemplate transactionTemplate) { + super(loanLockingService, transactionTemplate); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/listener/InlineCOBLoanItemListener.java b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/InlineCOBLoanItemListener.java index cedeab776..548c21328 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/listener/InlineCOBLoanItemListener.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/InlineCOBLoanItemListener.java @@ -18,14 +18,14 @@ */ package org.apache.fineract.cob.listener; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LockOwner; +import org.apache.fineract.cob.loan.LoanLockingService; import org.springframework.transaction.support.TransactionTemplate; public class InlineCOBLoanItemListener extends AbstractLoanItemListener { - public InlineCOBLoanItemListener(LoanAccountLockRepository accountLockRepository, TransactionTemplate transactionTemplate) { - super(accountLockRepository, transactionTemplate); + public InlineCOBLoanItemListener(LoanLockingService loanLockingService, TransactionTemplate transactionTemplate) { + super(loanLockingService, transactionTemplate); } @Override 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 c92fdbf8d..40f39a6d3 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 @@ -39,8 +39,6 @@ public abstract class AbstractLoanItemReader implements ItemReader { protected final LoanRepository loanRepository; - @Setter(AccessLevel.PROTECTED) - private List alreadyLockedOrProcessedAccounts; @Setter(AccessLevel.PROTECTED) private List remainingData; private Long loanId; diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemWriter.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemWriter.java index 4fa5e9d0f..061f90762 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemWriter.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemWriter.java @@ -21,7 +21,6 @@ package org.apache.fineract.cob.loan; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LockOwner; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.portfolio.loanaccount.domain.Loan; @@ -32,14 +31,14 @@ import org.springframework.batch.item.data.RepositoryItemWriter; @RequiredArgsConstructor public abstract class AbstractLoanItemWriter extends RepositoryItemWriter { - private final LoanAccountLockRepository accountLockRepository; + private final LoanLockingService loanLockingService; @Override public void write(@NotNull List items) throws Exception { if (!items.isEmpty()) { super.write(items); List loanIds = items.stream().map(AbstractPersistableCustom::getId).toList(); - accountLockRepository.deleteByLoanIdInAndLockOwner(loanIds, getLockOwner()); + loanLockingService.deleteByLoanIdInAndLockOwner(loanIds, getLockOwner()); } } 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 4b550bd22..2f949a27c 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 @@ -30,25 +30,21 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.cob.data.LoanCOBParameter; import org.apache.fineract.cob.domain.LoanAccountLock; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LockOwner; import org.apache.fineract.infrastructure.core.config.FineractProperties; -import org.apache.fineract.infrastructure.core.service.DateUtils; import org.jetbrains.annotations.NotNull; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.jdbc.core.JdbcTemplate; @Slf4j @RequiredArgsConstructor public class ApplyLoanLockTasklet implements Tasklet { - private final LoanAccountLockRepository accountLockRepository; private final FineractProperties fineractProperties; - private final JdbcTemplate jdbcTemplate; + private final LoanLockingService loanLockingService; private final RetrieveLoanIdService retrieveLoanIdService; @Override @@ -64,9 +60,9 @@ public class ApplyLoanLockTasklet implements Tasklet { loanIds = new ArrayList<>( retrieveLoanIdService.retrieveAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(loanCOBParameter)); } - List> loanIdPartitions = Lists.partition(loanIds, fineractProperties.getQuery().getInClauseParameterSizeLimit()); + List> loanIdPartitions = Lists.partition(loanIds, getInClauseParameterSizeLimit()); List accountLocks = new ArrayList<>(); - loanIdPartitions.forEach(loanIdPartition -> accountLocks.addAll(accountLockRepository.findAllByLoanIdIn(loanIdPartition))); + loanIdPartitions.forEach(loanIdPartition -> accountLocks.addAll(loanLockingService.findAllByLoanIdIn(loanIdPartition))); Map alreadySoftLockedAccountsMap = accountLocks.stream() .filter(e -> LockOwner.LOAN_COB_PARTITIONING.equals(e.getLockOwner())) @@ -77,7 +73,7 @@ public class ApplyLoanLockTasklet implements Tasklet { List toBeProcessedLoanIds = new ArrayList<>(alreadySoftLockedAccountsMap.keySet()); - upgradeToHardLock(toBeProcessedLoanIds); + loanLockingService.upgradeLock(toBeProcessedLoanIds, LockOwner.LOAN_COB_CHUNK_PROCESSING); toBeProcessedLoanIds.addAll(alreadyLockedByChunkProcessingAccountIds); List alreadyLockedByInlineCOBOrProcessedLoanIds = new ArrayList<>(loanIds); @@ -88,16 +84,6 @@ public class ApplyLoanLockTasklet implements Tasklet { return RepeatStatus.FINISHED; } - private void upgradeToHardLock(List accountsToLock) { - jdbcTemplate.batchUpdate(""" - UPDATE m_loan_account_locks SET version= version + 1, lock_owner = ?, lock_placed_on = ? WHERE loan_id = ? - """, accountsToLock, getInClauseParameterSizeLimit(), (ps, id) -> { - ps.setString(1, LockOwner.LOAN_COB_CHUNK_PROCESSING.name()); - ps.setObject(2, DateUtils.getOffsetDateTimeOfTenant()); - ps.setLong(3, id); - }); - } - private int getInClauseParameterSizeLimit() { return fineractProperties.getQuery().getInClauseParameterSizeLimit(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemWriter.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemWriter.java index 9092cede0..9a839d326 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemWriter.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemWriter.java @@ -18,13 +18,12 @@ */ package org.apache.fineract.cob.loan; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LockOwner; public class InlineCOBLoanItemWriter extends AbstractLoanItemWriter { - public InlineCOBLoanItemWriter(LoanAccountLockRepository accountLockRepository) { - super(accountLockRepository); + public InlineCOBLoanItemWriter(LoanLockingService loanLockingService) { + super(loanLockingService); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBManagerConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBManagerConfiguration.java index ac086dca8..07c9e962d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBManagerConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBManagerConfiguration.java @@ -25,7 +25,6 @@ import org.apache.fineract.cob.listener.COBExecutionListenerRunner; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.infrastructure.springbatch.PropertyService; -import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; @@ -44,7 +43,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.channel.DirectChannel; -import org.springframework.jdbc.core.JdbcTemplate; @Configuration @EnableBatchIntegration @@ -76,10 +74,7 @@ public class LoanCOBManagerConfiguration { @Autowired private CustomJobParameterResolver customJobParameterResolver; @Autowired - private LoanRepository loanRepository; - - @Autowired - private JdbcTemplate jdbcTemplate; + private LoanLockingService loanLockingService; @Bean @JobScope @@ -123,7 +118,7 @@ public class LoanCOBManagerConfiguration { @Bean @JobScope public LockLoanTasklet lockLoanTasklet() { - return new LockLoanTasklet(jdbcTemplate); + return new LockLoanTasklet(loanLockingService); } @Bean @@ -135,7 +130,7 @@ public class LoanCOBManagerConfiguration { @Bean @JobScope public StayedLockedLoansTasklet stayedLockedTasklet() { - return new StayedLockedLoansTasklet(businessEventNotifierService, loanRepository); + return new StayedLockedLoansTasklet(businessEventNotifierService, retrieveLoanIdService); } @Bean(name = "loanCOBJob") 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 ec6fff199..038353a0c 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 @@ -21,7 +21,6 @@ package org.apache.fineract.cob.loan; import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.common.InitialisationTasklet; import org.apache.fineract.cob.common.ResetContextTasklet; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.listener.ChunkProcessingLoanItemListener; import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.jobs.service.JobName; @@ -41,7 +40,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.channel.QueueChannel; -import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.support.TransactionTemplate; @Configuration @@ -62,8 +60,6 @@ public class LoanCOBWorkerConfiguration { @Autowired private COBBusinessStepService cobBusinessStepService; @Autowired - private LoanAccountLockRepository accountLockRepository; - @Autowired private AppUserRepositoryWrapper userRepository; @Autowired private TransactionTemplate transactionTemplate; @@ -73,7 +69,7 @@ public class LoanCOBWorkerConfiguration { @Autowired private FineractProperties fineractProperties; @Autowired - private JdbcTemplate jdbcTemplate; + private LoanLockingService loanLockingService; @Bean(name = LoanCOBConstant.LOAN_COB_WORKER_STEP) public Step loanCOBWorkerStep() { @@ -120,12 +116,12 @@ public class LoanCOBWorkerConfiguration { @Bean public ChunkProcessingLoanItemListener loanItemListener() { - return new ChunkProcessingLoanItemListener(accountLockRepository, transactionTemplate); + return new ChunkProcessingLoanItemListener(loanLockingService, transactionTemplate); } @Bean public ApplyLoanLockTasklet applyLock() { - return new ApplyLoanLockTasklet(accountLockRepository, fineractProperties, jdbcTemplate, retrieveLoanIdService); + return new ApplyLoanLockTasklet(fineractProperties, loanLockingService, retrieveLoanIdService); } @Bean @@ -148,7 +144,7 @@ public class LoanCOBWorkerConfiguration { @Bean @StepScope public LoanItemWriter cobWorkerItemWriter() { - LoanItemWriter repositoryItemWriter = new LoanItemWriter(accountLockRepository); + LoanItemWriter repositoryItemWriter = new LoanItemWriter(loanLockingService); repositoryItemWriter.setRepository(loanRepository); return repositoryItemWriter; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java index 78a9aa28d..9e92cd61a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java @@ -59,6 +59,8 @@ public class LoanInlineCOBConfig { private TransactionTemplate transactionTemplate; @Autowired private CustomJobParameterRepository loanIdListRepository; + @Autowired + private LoanLockingService loanLockingService; @Bean public InlineLoanCOBBuildExecutionContextTasklet inlineLoanCOBBuildExecutionContextTasklet() { @@ -103,14 +105,14 @@ public class LoanInlineCOBConfig { @Bean public InlineCOBLoanItemWriter inlineCobWorkerItemWriter() { - InlineCOBLoanItemWriter repositoryItemWriter = new InlineCOBLoanItemWriter(accountLockRepository); + InlineCOBLoanItemWriter repositoryItemWriter = new InlineCOBLoanItemWriter(loanLockingService); repositoryItemWriter.setRepository(loanRepository); return repositoryItemWriter; } @Bean public InlineCOBLoanItemListener inlineCobLoanItemListener() { - return new InlineCOBLoanItemListener(accountLockRepository, transactionTemplate); + return new InlineCOBLoanItemListener(loanLockingService, transactionTemplate); } @Bean diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java index 709e2bdd8..2696ee03a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java @@ -18,13 +18,12 @@ */ package org.apache.fineract.cob.loan; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LockOwner; public class LoanItemWriter extends AbstractLoanItemWriter { - public LoanItemWriter(LoanAccountLockRepository accountLockRepository) { - super(accountLockRepository); + public LoanItemWriter(LoanLockingService loanLockingService) { + super(loanLockingService); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingConfiguration.java new file mode 100644 index 000000000..660051907 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingConfiguration.java @@ -0,0 +1,44 @@ +/** + * 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.cob.loan; + +import org.apache.fineract.cob.domain.LoanAccountLockRepository; +import org.apache.fineract.infrastructure.core.config.FineractProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; + +@Configuration +public class LoanLockingConfiguration { + + @Autowired + private JdbcTemplate jdbcTemplate; + @Autowired + private FineractProperties fineractProperties; + @Autowired + private LoanAccountLockRepository loanAccountLockRepository; + + @Bean + @ConditionalOnMissingBean + public LoanLockingService retrieveLoanLockingService() { + return new LoanLockingServiceImpl(jdbcTemplate, fineractProperties, loanAccountLockRepository); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingService.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingService.java new file mode 100644 index 000000000..3a7eadc69 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingService.java @@ -0,0 +1,38 @@ +/** + * 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.cob.loan; + +import java.time.LocalDate; +import java.util.List; +import org.apache.fineract.cob.data.LoanCOBParameter; +import org.apache.fineract.cob.domain.LoanAccountLock; +import org.apache.fineract.cob.domain.LockOwner; + +public interface LoanLockingService { + + void applySoftLock(LocalDate lastClosedBusinessDate, LoanCOBParameter loanCOBParameter); + + void upgradeLock(List accountsToLock, LockOwner lockOwner); + + void deleteByLoanIdInAndLockOwner(List loanIds, LockOwner lockOwner); + + List findAllByLoanIdIn(List loanIds); + + LoanAccountLock findByLoanIdAndLockOwner(Long loanId, LockOwner lockOwner); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingServiceImpl.java new file mode 100644 index 000000000..bf4c76a76 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanLockingServiceImpl.java @@ -0,0 +1,97 @@ +/** + * 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.cob.loan; + +import java.time.LocalDate; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.cob.data.LoanCOBParameter; +import org.apache.fineract.cob.domain.LoanAccountLock; +import org.apache.fineract.cob.domain.LoanAccountLockRepository; +import org.apache.fineract.cob.domain.LockOwner; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.core.config.FineractProperties; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.springframework.jdbc.core.JdbcTemplate; + +@RequiredArgsConstructor +@Slf4j +public class LoanLockingServiceImpl implements LoanLockingService { + + private final JdbcTemplate jdbcTemplate; + private final FineractProperties fineractProperties; + private final LoanAccountLockRepository loanAccountLockRepository; + + @Override + public void applySoftLock(LocalDate lastClosedBusinessDate, LoanCOBParameter loanCOBParameter) { + + LocalDate cobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE); + jdbcTemplate.update(""" + INSERT INTO m_loan_account_locks (loan_id, version, lock_owner, lock_placed_on, lock_placed_on_cob_business_date) + SELECT loan.id, ?, ?, ?, ? FROM m_loan loan + WHERE loan.id NOT IN (SELECT loan_id FROM m_loan_account_locks) + AND loan.id BETWEEN ? AND ? + AND loan.loan_status_id IN (100,200,300,303,304) + AND (? = loan.last_closed_business_date OR loan.last_closed_business_date IS NULL) + """, ps -> { + ps.setLong(1, 1); + ps.setString(2, LockOwner.LOAN_COB_PARTITIONING.name()); + ps.setObject(3, DateUtils.getOffsetDateTimeOfTenant()); + ps.setObject(4, cobBusinessDate); + ps.setObject(5, loanCOBParameter.getMinLoanId()); + ps.setObject(6, loanCOBParameter.getMaxLoanId()); + ps.setObject(7, lastClosedBusinessDate); + }); + } + + @Override + public void upgradeLock(List accountsToLock, LockOwner lockOwner) { + jdbcTemplate.batchUpdate(""" + UPDATE m_loan_account_locks SET version= version + 1, lock_owner = ?, lock_placed_on = ? WHERE loan_id = ? + """, accountsToLock, getInClauseParameterSizeLimit(), (ps, id) -> { + ps.setString(1, lockOwner.name()); + ps.setObject(2, DateUtils.getOffsetDateTimeOfTenant()); + ps.setLong(3, id); + }); + } + + @Override + public List findAllByLoanIdIn(List loanIds) { + return loanAccountLockRepository.findAllByLoanIdIn(loanIds); + } + + @Override + public LoanAccountLock findByLoanIdAndLockOwner(Long loanId, LockOwner lockOwner) { + return loanAccountLockRepository.findByLoanIdAndLockOwner(loanId, lockOwner).orElseGet(() -> { + log.warn("There is no lock for loan account with id: {}", loanId); + return null; + }); + } + + @Override + public void deleteByLoanIdInAndLockOwner(List loanIds, LockOwner lockOwner) { + loanAccountLockRepository.deleteByLoanIdInAndLockOwner(loanIds, lockOwner); + } + + private int getInClauseParameterSizeLimit() { + return fineractProperties.getQuery().getInClauseParameterSizeLimit(); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LockLoanTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LockLoanTasklet.java index b556b4a08..f313998aa 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LockLoanTasklet.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LockLoanTasklet.java @@ -23,28 +23,23 @@ import java.util.Objects; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.cob.data.LoanCOBParameter; -import org.apache.fineract.cob.domain.LockOwner; -import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; -import org.apache.fineract.infrastructure.core.service.DateUtils; -import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.jetbrains.annotations.NotNull; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.jdbc.core.JdbcTemplate; @Slf4j @RequiredArgsConstructor public class LockLoanTasklet implements Tasklet { - private final JdbcTemplate jdbcTemplate; + private final LoanLockingService loanLockingService; @Override public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception { String businessDateParameter = (String) contribution.getStepExecution().getJobExecution().getExecutionContext() .get(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME); - LocalDate businessDate = LocalDate.parse(Objects.requireNonNull(businessDateParameter)) + LocalDate lastClosedBusinessDate = LocalDate.parse(Objects.requireNonNull(businessDateParameter)) .minusDays(LoanCOBConstant.NUMBER_OF_DAYS_BEHIND); LoanCOBParameter loanCOBParameter = (LoanCOBParameter) contribution.getStepExecution().getJobExecution().getExecutionContext() .get(LoanCOBConstant.LOAN_COB_PARAMETER); @@ -52,28 +47,8 @@ public class LockLoanTasklet implements Tasklet { || (Objects.isNull(loanCOBParameter.getMinLoanId()) && Objects.isNull(loanCOBParameter.getMaxLoanId()))) { loanCOBParameter = new LoanCOBParameter(0L, 0L); } - applySoftLock(businessDate, loanCOBParameter); + loanLockingService.applySoftLock(lastClosedBusinessDate, loanCOBParameter); return RepeatStatus.FINISHED; } - - private void applySoftLock(LocalDate businessDate, LoanCOBParameter loanCOBParameter) { - LocalDate cobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE); - jdbcTemplate.update(""" - INSERT INTO m_loan_account_locks (loan_id, version, lock_owner, lock_placed_on, lock_placed_on_cob_business_date) - SELECT loan.id, ?, ?, ?, ? FROM m_loan loan - WHERE loan.id NOT IN (SELECT loan_id FROM m_loan_account_locks) - AND loan.id BETWEEN ? AND ? - AND loan.loan_status_id IN (100,200,300,303,304) - AND (? = loan.last_closed_business_date OR loan.last_closed_business_date IS NULL) - """, ps -> { - ps.setLong(1, 1); - ps.setString(2, LockOwner.LOAN_COB_PARTITIONING.name()); - ps.setObject(3, DateUtils.getOffsetDateTimeOfTenant()); - ps.setObject(4, cobBusinessDate); - ps.setObject(5, loanCOBParameter.getMinLoanId()); - ps.setObject(6, loanCOBParameter.getMaxLoanId()); - ps.setObject(7, businessDate); - }); - } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/RetrieveAllNonClosedLoanIdServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/RetrieveAllNonClosedLoanIdServiceImpl.java index 47aec31e3..258512ca3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/RetrieveAllNonClosedLoanIdServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/RetrieveAllNonClosedLoanIdServiceImpl.java @@ -22,6 +22,7 @@ import java.time.LocalDate; import java.util.List; import lombok.RequiredArgsConstructor; import org.apache.fineract.cob.data.LoanCOBParameter; +import org.apache.fineract.cob.data.LoanIdAndExternalIdAndAccountNo; import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; @@ -54,4 +55,9 @@ public class RetrieveAllNonClosedLoanIdServiceImpl implements RetrieveLoanIdServ ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE).minusDays(LoanCOBConstant.NUMBER_OF_DAYS_BEHIND)); } + @Override + public List findAllStayedLockedByCobBusinessDate(LocalDate cobBusinessDate) { + return loanRepository.findAllStayedLockedByCobBusinessDate(cobBusinessDate); + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/RetrieveLoanIdService.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/RetrieveLoanIdService.java index eec70f899..0063909cc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/RetrieveLoanIdService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/RetrieveLoanIdService.java @@ -21,7 +21,9 @@ package org.apache.fineract.cob.loan; import java.time.LocalDate; import java.util.List; import org.apache.fineract.cob.data.LoanCOBParameter; +import org.apache.fineract.cob.data.LoanIdAndExternalIdAndAccountNo; import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate; +import org.springframework.data.repository.query.Param; public interface RetrieveLoanIdService { @@ -33,4 +35,6 @@ public interface RetrieveLoanIdService { List retrieveAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(LoanCOBParameter loanCOBParameter); + List findAllStayedLockedByCobBusinessDate(@Param("cobBusinessDate") LocalDate cobBusinessDate); + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/StayedLockedLoansTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/StayedLockedLoansTasklet.java index 471098921..0b99ac397 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/StayedLockedLoansTasklet.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/StayedLockedLoansTasklet.java @@ -29,7 +29,6 @@ import org.apache.fineract.cob.data.LoanIdAndExternalIdAndAccountNo; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; -import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; @@ -40,7 +39,7 @@ import org.springframework.batch.repeat.RepeatStatus; public class StayedLockedLoansTasklet implements Tasklet { private final BusinessEventNotifierService businessEventNotifierService; - private final LoanRepository loanRepository; + private final RetrieveLoanIdService retrieveLoanIdService; @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { @@ -53,7 +52,7 @@ public class StayedLockedLoansTasklet implements Tasklet { private LoanAccountsStayedLockedData buildLoanAccountData() { LocalDate cobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE); - List stayedLockedLoanAccounts = loanRepository + List stayedLockedLoanAccounts = retrieveLoanIdService .findAllStayedLockedByCobBusinessDate(cobBusinessDate); List loanAccounts = new ArrayList<>(); stayedLockedLoanAccounts.forEach(loanAccount -> { diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/listener/LoanItemListenerStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/cob/listener/LoanItemListenerStepDefinitions.java index 773b52180..2e7f1b659 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/cob/listener/LoanItemListenerStepDefinitions.java +++ b/fineract-provider/src/test/java/org/apache/fineract/cob/listener/LoanItemListenerStepDefinitions.java @@ -30,11 +30,10 @@ import io.cucumber.java8.En; import java.time.LocalDate; import java.time.ZoneId; import java.util.List; -import java.util.Optional; import org.apache.fineract.cob.domain.LoanAccountLock; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LockOwner; import org.apache.fineract.cob.exceptions.LoanReadException; +import org.apache.fineract.cob.loan.LoanLockingService; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.portfolio.loanaccount.domain.Loan; @@ -45,36 +44,34 @@ import org.springframework.transaction.support.TransactionTemplate; public class LoanItemListenerStepDefinitions implements En { - private LoanAccountLockRepository accountLockRepository = mock(LoanAccountLockRepository.class); + private LoanLockingService loanLockingService = mock(LoanLockingService.class); private TransactionTemplate transactionTemplate = spy(TransactionTemplate.class); - private ChunkProcessingLoanItemListener loanItemListener = new ChunkProcessingLoanItemListener(accountLockRepository, - transactionTemplate); + private ChunkProcessingLoanItemListener loanItemListener = new ChunkProcessingLoanItemListener(loanLockingService, transactionTemplate); private Exception exception; private LoanAccountLock loanAccountLock; - private Loan loan = mock(Loan.class); + private final Loan loan = mock(Loan.class); public LoanItemListenerStepDefinitions() { Given("/^The LoanItemListener.onReadError method (.*)$/", (String action) -> { ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); exception = new LoanReadException(1L, new RuntimeException("fail")); loanAccountLock = new LoanAccountLock(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING, LocalDate.now(ZoneId.systemDefault())); - when(accountLockRepository.findByLoanIdAndLockOwner(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING)) - .thenReturn(Optional.of(loanAccountLock)); + when(loanLockingService.findByLoanIdAndLockOwner(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING)).thenReturn(loanAccountLock); transactionTemplate.setTransactionManager(mock(PlatformTransactionManager.class)); when(loan.getId()).thenReturn(1L); }); When("LoanItemListener.onReadError method executed", () -> { - this.loanItemListener.onReadError(exception); + loanItemListener.onReadError(exception); }); Then("LoanItemListener.onReadError result should match", () -> { verify(transactionTemplate, Mockito.times(1)).setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); verify(transactionTemplate, Mockito.times(1)).execute(any()); - verify(accountLockRepository, Mockito.times(1)).findByLoanIdAndLockOwner(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING); + verify(loanLockingService, Mockito.times(1)).findByLoanIdAndLockOwner(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING); assertEquals("Loan (id: 1) reading is failed", loanAccountLock.getError()); assertNotNull(loanAccountLock.getStacktrace()); }); @@ -83,20 +80,19 @@ public class LoanItemListenerStepDefinitions implements En { ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); exception = new LoanReadException(1L, new RuntimeException("fail")); loanAccountLock = new LoanAccountLock(2L, LockOwner.LOAN_COB_CHUNK_PROCESSING, LocalDate.now(ZoneId.systemDefault())); - when(accountLockRepository.findByLoanIdAndLockOwner(2L, LockOwner.LOAN_COB_CHUNK_PROCESSING)) - .thenReturn(Optional.of(loanAccountLock)); + when(loanLockingService.findByLoanIdAndLockOwner(2L, LockOwner.LOAN_COB_CHUNK_PROCESSING)).thenReturn(loanAccountLock); when(loan.getId()).thenReturn(2L); transactionTemplate.setTransactionManager(mock(PlatformTransactionManager.class)); }); When("LoanItemListener.onProcessError method executed", () -> { - this.loanItemListener.onProcessError(loan, exception); + loanItemListener.onProcessError(loan, exception); }); Then("LoanItemListener.onProcessError result should match", () -> { verify(transactionTemplate, Mockito.times(1)).setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); verify(transactionTemplate, Mockito.times(1)).execute(any()); - verify(accountLockRepository, Mockito.times(1)).findByLoanIdAndLockOwner(2L, LockOwner.LOAN_COB_CHUNK_PROCESSING); + verify(loanLockingService, Mockito.times(1)).findByLoanIdAndLockOwner(2L, LockOwner.LOAN_COB_CHUNK_PROCESSING); assertEquals("Loan (id: 2) processing is failed", loanAccountLock.getError()); assertNotNull(loanAccountLock.getStacktrace()); }); @@ -105,20 +101,19 @@ public class LoanItemListenerStepDefinitions implements En { ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); exception = new LoanReadException(3L, new RuntimeException("fail")); loanAccountLock = new LoanAccountLock(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING, LocalDate.now(ZoneId.systemDefault())); - when(accountLockRepository.findByLoanIdAndLockOwner(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING)) - .thenReturn(Optional.of(loanAccountLock)); + when(loanLockingService.findByLoanIdAndLockOwner(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING)).thenReturn(loanAccountLock); when(loan.getId()).thenReturn(3L); transactionTemplate.setTransactionManager(mock(PlatformTransactionManager.class)); }); When("LoanItemListener.onWriteError method executed", () -> { - this.loanItemListener.onWriteError(exception, List.of(loan)); + loanItemListener.onWriteError(exception, List.of(loan)); }); Then("LoanItemListener.onWriteError result should match", () -> { verify(transactionTemplate, Mockito.times(1)).setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); verify(transactionTemplate, Mockito.times(1)).execute(any()); - verify(accountLockRepository, Mockito.times(1)).findByLoanIdAndLockOwner(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING); + verify(loanLockingService, Mockito.times(1)).findByLoanIdAndLockOwner(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING); assertEquals("Loan (id: 3) writing is failed", loanAccountLock.getError()); assertNotNull(loanAccountLock.getStacktrace()); }); 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 1a3ba4c1e..37851a9da 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 @@ -31,7 +31,6 @@ import java.util.HashMap; import java.util.List; import org.apache.fineract.cob.data.LoanCOBParameter; import org.apache.fineract.cob.domain.LoanAccountLock; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LockOwner; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.core.config.FineractProperties; @@ -43,18 +42,16 @@ import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter; public class ApplyLoanLockTaskletStepDefinitions implements En { ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(List.class); - private LoanAccountLockRepository accountLockRepository = mock(LoanAccountLockRepository.class); + ArgumentCaptor lockOwnerValueCaptor = ArgumentCaptor.forClass(LockOwner.class); + private LoanLockingService loanLockingService = mock(LoanLockingService.class); private FineractProperties fineractProperties = mock(FineractProperties.class); - private JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class); private FineractProperties.FineractQueryProperties fineractQueryProperties = mock(FineractProperties.FineractQueryProperties.class); private RetrieveLoanIdService retrieveLoanIdService = mock(RetrieveLoanIdService.class); - private ApplyLoanLockTasklet applyLoanLockTasklet = new ApplyLoanLockTasklet(accountLockRepository, fineractProperties, jdbcTemplate, + private ApplyLoanLockTasklet applyLoanLockTasklet = new ApplyLoanLockTasklet(fineractProperties, loanLockingService, retrieveLoanIdService); private RepeatStatus resultItem; private StepContribution stepContribution; @@ -69,15 +66,15 @@ public class ApplyLoanLockTaskletStepDefinitions implements En { ExecutionContext executionContext = new ExecutionContext(); LoanCOBParameter loanCOBParameter = new LoanCOBParameter(1L, 4L); executionContext.put(LoanCOBConstant.LOAN_COB_PARAMETER, loanCOBParameter); - lenient().when(this.retrieveLoanIdService.retrieveAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(loanCOBParameter)) + lenient().when(retrieveLoanIdService.retrieveAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(loanCOBParameter)) .thenReturn(List.of(1L, 2L, 3L, 4L)); stepExecution.setExecutionContext(executionContext); - this.stepContribution = new StepContribution(stepExecution); + stepContribution = new StepContribution(stepExecution); if ("error".equals(action)) { lenient().when(fineractProperties.getQuery()).thenReturn(fineractQueryProperties); lenient().when(fineractQueryProperties.getInClauseParameterSizeLimit()).thenReturn(65000); - lenient().when(this.accountLockRepository.findAllByLoanIdIn(Mockito.anyList())).thenThrow(new RuntimeException("fail")); + lenient().when(loanLockingService.findAllByLoanIdIn(Mockito.anyList())).thenThrow(new RuntimeException("fail")); } else { LoanAccountLock lock1 = new LoanAccountLock(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING, LocalDate.now(ZoneId.systemDefault())); LoanAccountLock lock2 = new LoanAccountLock(2L, LockOwner.LOAN_COB_PARTITIONING, LocalDate.now(ZoneId.systemDefault())); @@ -86,26 +83,26 @@ public class ApplyLoanLockTaskletStepDefinitions implements En { List accountLocks = List.of(lock1, lock2, lock3); lenient().when(fineractProperties.getQuery()).thenReturn(fineractQueryProperties); lenient().when(fineractQueryProperties.getInClauseParameterSizeLimit()).thenReturn(65000); - lenient().when(this.accountLockRepository.findAllByLoanIdIn(Mockito.anyList())).thenReturn(accountLocks); + lenient().when(loanLockingService.findAllByLoanIdIn(Mockito.anyList())).thenReturn(accountLocks); } }); When("ApplyLoanLockTasklet.execute method executed", () -> { - resultItem = this.applyLoanLockTasklet.execute(stepContribution, null); + resultItem = applyLoanLockTasklet.execute(stepContribution, null); }); Then("ApplyLoanLockTasklet.execute result should match", () -> { assertEquals(RepeatStatus.FINISHED, resultItem); - verify(this.jdbcTemplate, Mockito.times(1)).batchUpdate(Mockito.anyString(), valueCaptor.capture(), Mockito.anyInt(), - Mockito.any(ParameterizedPreparedStatementSetter.class)); + verify(loanLockingService, Mockito.times(1)).upgradeLock(valueCaptor.capture(), lockOwnerValueCaptor.capture()); List values = valueCaptor.getValue(); assertEquals(2L, values.get(0)); + assertEquals(LockOwner.LOAN_COB_CHUNK_PROCESSING, lockOwnerValueCaptor.getValue()); }); Then("throw exception ApplyLoanLockTasklet.execute method", () -> { assertThrows(RuntimeException.class, () -> { - resultItem = this.applyLoanLockTasklet.execute(stepContribution, null); + resultItem = applyLoanLockTasklet.execute(stepContribution, null); }); }); } diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/FetchAndLockLoanStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/FetchAndLockLoanStepDefinitions.java index 51ef56fa7..bbf77a9be 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/FetchAndLockLoanStepDefinitions.java +++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/FetchAndLockLoanStepDefinitions.java @@ -19,8 +19,6 @@ package org.apache.fineract.cob.loan; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -39,26 +37,26 @@ import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.PreparedStatementSetter; public class FetchAndLockLoanStepDefinitions implements En { private final LoanAccountLockRepository loanAccountLockRepository = mock(LoanAccountLockRepository.class); - private final FineractProperties fineractProperties = mock(FineractProperties.class); private final FineractProperties.FineractQueryProperties fineractQueryProperties = mock( FineractProperties.FineractQueryProperties.class); StepContribution contribution; + ArgumentCaptor dateValueCaptor = ArgumentCaptor.forClass(LocalDate.class); + ArgumentCaptor loanCOBParameterValueCaptor = ArgumentCaptor.forClass(LoanCOBParameter.class); private LockLoanTasklet lockLoanTasklet; private String action; private RepeatStatus result; - private JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class); + private final LoanLockingService loanLockingService = mock(LoanLockingService.class); public FetchAndLockLoanStepDefinitions() { Given("/^The FetchAndLockLoanTasklet.execute method with action (.*)$/", (String action) -> { @@ -106,18 +104,21 @@ public class FetchAndLockLoanStepDefinitions implements En { List.of(new LoanAccountLock(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING, LocalDate.now(ZoneId.systemDefault())))); } - lockLoanTasklet = new LockLoanTasklet(jdbcTemplate); + lockLoanTasklet = new LockLoanTasklet(loanLockingService); }); When("FetchAndLockLoanTasklet.execute method executed", () -> { - result = this.lockLoanTasklet.execute(contribution, null); + result = lockLoanTasklet.execute(contribution, null); }); Then("FetchAndLockLoanTasklet.execute result should match", () -> { if ("empty steps".equals(action)) { assertEquals(RepeatStatus.FINISHED, result); } else if ("good".equals(action)) { - verify(jdbcTemplate).update(anyString(), (PreparedStatementSetter) any()); + verify(loanLockingService).applySoftLock(dateValueCaptor.capture(), loanCOBParameterValueCaptor.capture()); + assertEquals(LocalDate.now(ZoneId.systemDefault()).minusDays(1), dateValueCaptor.getValue()); + assertEquals(1L, loanCOBParameterValueCaptor.getValue().getMinLoanId()); + assertEquals(3L, loanCOBParameterValueCaptor.getValue().getMaxLoanId()); assertEquals(RepeatStatus.FINISHED, result); LoanCOBParameter loanCOBParameter = (LoanCOBParameter) contribution.getStepExecution().getJobExecution() .getExecutionContext().get(LoanCOBConstant.LOAN_COB_PARAMETER); @@ -125,7 +126,10 @@ public class FetchAndLockLoanStepDefinitions implements En { assertEquals(1L, loanCOBParameter.getMinLoanId()); assertEquals(3L, loanCOBParameter.getMaxLoanId()); } else if ("soft lock".equals(action)) { - verify(jdbcTemplate).update(anyString(), (PreparedStatementSetter) any()); + verify(loanLockingService).applySoftLock(dateValueCaptor.capture(), loanCOBParameterValueCaptor.capture()); + assertEquals(LocalDate.now(ZoneId.systemDefault()).minusDays(1), dateValueCaptor.getValue()); + assertEquals(1L, loanCOBParameterValueCaptor.getValue().getMinLoanId()); + assertEquals(3L, loanCOBParameterValueCaptor.getValue().getMaxLoanId()); assertEquals(RepeatStatus.FINISHED, result); LoanCOBParameter loanCOBParameter = (LoanCOBParameter) contribution.getStepExecution().getJobExecution() .getExecutionContext().get(LoanCOBConstant.LOAN_COB_PARAMETER); @@ -133,7 +137,10 @@ public class FetchAndLockLoanStepDefinitions implements En { assertEquals(1L, loanCOBParameter.getMinLoanId()); assertEquals(3L, loanCOBParameter.getMaxLoanId()); } else if ("inline cob".equals(action)) { - verify(jdbcTemplate).update(anyString(), (PreparedStatementSetter) any()); + verify(loanLockingService).applySoftLock(dateValueCaptor.capture(), loanCOBParameterValueCaptor.capture()); + assertEquals(LocalDate.now(ZoneId.systemDefault()).minusDays(1), dateValueCaptor.getValue()); + assertEquals(1L, loanCOBParameterValueCaptor.getValue().getMinLoanId()); + assertEquals(3L, loanCOBParameterValueCaptor.getValue().getMaxLoanId()); assertEquals(RepeatStatus.FINISHED, result); LoanCOBParameter loanCOBParameter = (LoanCOBParameter) contribution.getStepExecution().getJobExecution() .getExecutionContext().get(LoanCOBConstant.LOAN_COB_PARAMETER); @@ -141,7 +148,10 @@ public class FetchAndLockLoanStepDefinitions implements En { assertEquals(1L, loanCOBParameter.getMinLoanId()); assertEquals(3L, loanCOBParameter.getMaxLoanId()); } else if ("chunk processing".equals(action)) { - verify(jdbcTemplate).update(anyString(), (PreparedStatementSetter) any()); + verify(loanLockingService).applySoftLock(dateValueCaptor.capture(), loanCOBParameterValueCaptor.capture()); + assertEquals(LocalDate.now(ZoneId.systemDefault()).minusDays(1), dateValueCaptor.getValue()); + assertEquals(1L, loanCOBParameterValueCaptor.getValue().getMinLoanId()); + assertEquals(3L, loanCOBParameterValueCaptor.getValue().getMaxLoanId()); assertEquals(RepeatStatus.FINISHED, result); LoanCOBParameter loanCOBParameter = (LoanCOBParameter) contribution.getStepExecution().getJobExecution() .getExecutionContext().get(LoanCOBConstant.LOAN_COB_PARAMETER); diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/LoanItemWriterStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/LoanItemWriterStepDefinitions.java index 36ca22972..9df8182e4 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/LoanItemWriterStepDefinitions.java +++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/LoanItemWriterStepDefinitions.java @@ -25,17 +25,16 @@ import static org.mockito.Mockito.verify; import io.cucumber.java8.En; import java.util.Collections; import java.util.List; -import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.mockito.Mockito; public class LoanItemWriterStepDefinitions implements En { - private LoanAccountLockRepository accountLockRepository = mock(LoanAccountLockRepository.class); - private LoanRepository loanRepository = mock(LoanRepository.class); + private final LoanLockingService loanLockingService = mock(LoanLockingService.class); + private final LoanRepository loanRepository = mock(LoanRepository.class); - private LoanItemWriter loanItemWriter = new LoanItemWriter(accountLockRepository); + private final LoanItemWriter loanItemWriter = new LoanItemWriter(loanLockingService); private List items; @@ -45,24 +44,24 @@ public class LoanItemWriterStepDefinitions implements En { Loan loan = mock(Loan.class); lenient().when(loan.getId()).thenReturn(1L); if (action.equals("error")) { - this.items = Collections.emptyList(); + items = Collections.emptyList(); } else { - this.items = Collections.singletonList(loan); - lenient().doNothing().when(this.accountLockRepository).deleteByLoanIdInAndLockOwner(Mockito.anyList(), Mockito.any()); + items = Collections.singletonList(loan); + lenient().doNothing().when(loanLockingService).deleteByLoanIdInAndLockOwner(Mockito.anyList(), Mockito.any()); } - this.loanItemWriter.setRepository(loanRepository); + loanItemWriter.setRepository(loanRepository); }); When("LoanItemWriter.write method executed", () -> { - this.loanItemWriter.write(items); + loanItemWriter.write(items); }); Then("LoanItemWriter.write result should match", () -> { - verify(this.accountLockRepository, Mockito.times(1)).deleteByLoanIdInAndLockOwner(Mockito.any(), Mockito.any()); + verify(loanLockingService, Mockito.times(1)).deleteByLoanIdInAndLockOwner(Mockito.any(), Mockito.any()); }); Then("LoanItemWriter.write should not call repository", () -> { - verify(this.accountLockRepository, Mockito.times(0)).deleteByLoanIdInAndLockOwner(Mockito.any(), Mockito.any()); + verify(loanLockingService, Mockito.times(0)).deleteByLoanIdInAndLockOwner(Mockito.any(), Mockito.any()); }); } }