- [x] Move from JobParameter to CustomJobParameter

- [x] Improve CustomJobParameterResolver to raise custom exception and work with StepExecution
- [x] Improve tests
This commit is contained in:
Janos Haber
2023-05-10 10:49:04 +02:00
committed by Arnold Gálovics
parent 0458643d8f
commit ac1aeea12d
15 changed files with 127 additions and 86 deletions
@@ -20,7 +20,11 @@ package org.apache.fineract.cob.common;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
import com.google.gson.Gson; import com.google.gson.Gson;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.cob.exceptions.CustomJobParameterNotFoundException; import org.apache.fineract.cob.exceptions.CustomJobParameterNotFoundException;
@@ -29,7 +33,9 @@ import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameter; import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameter;
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository; import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
import org.apache.fineract.infrastructure.springbatch.SpringBatchJobConstants; import org.apache.fineract.infrastructure.springbatch.SpringBatchJobConstants;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -50,7 +56,8 @@ public class CustomJobParameterResolver implements InitializingBean {
public void resolve(StepContribution contribution, ChunkContext chunkContext, String customJobParameterKey, public void resolve(StepContribution contribution, ChunkContext chunkContext, String customJobParameterKey,
String parameterNameInExecutionContext) { String parameterNameInExecutionContext) {
Set<JobParameterDTO> jobParameterDTOList = getCustomJobParameterSet(chunkContext); Set<JobParameterDTO> jobParameterDTOList = getCustomJobParameterSet(chunkContext.getStepContext().getStepExecution())
.orElseThrow(() -> new CustomJobParameterNotFoundException(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY));
JobParameterDTO businessDateParameter = jobParameterDTOList.stream() JobParameterDTO businessDateParameter = jobParameterDTOList.stream()
.filter(jobParameterDTO -> customJobParameterKey.equals(jobParameterDTO.getParameterName())) // .filter(jobParameterDTO -> customJobParameterKey.equals(jobParameterDTO.getParameterName())) //
.findFirst().orElseThrow(() -> new CustomJobParameterNotFoundException(customJobParameterKey)); .findFirst().orElseThrow(() -> new CustomJobParameterNotFoundException(customJobParameterKey));
@@ -58,12 +65,38 @@ public class CustomJobParameterResolver implements InitializingBean {
businessDateParameter.getParameterValue()); businessDateParameter.getParameterValue());
} }
private Set<JobParameterDTO> getCustomJobParameterSet(ChunkContext chunkContext) { /**
Long customJobParameterId = (Long) chunkContext.getStepContext().getJobParameters() * Get parameter set from custom job parameter table
.get(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY); *
CustomJobParameter customJobParameter = customJobParameterRepository.findById(customJobParameterId) * @param stepExecution
.orElseThrow(() -> new CustomJobParameterNotFoundException(customJobParameterId)); * @return
String parameterJson = customJobParameter.getParameterJson(); */
return gson.fromJson(parameterJson, new TypeToken<HashSet<JobParameterDTO>>() {}.getType()); public Optional<Set<JobParameterDTO>> getCustomJobParameterSet(StepExecution stepExecution) {
Long customJobParameterId = (Long) getJobParameters(stepExecution).get(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY);
return customJobParameterRepository.findById(customJobParameterId).map(CustomJobParameter::getParameterJson)
.map(json -> gson.fromJson(json, new TypeToken<HashSet<JobParameterDTO>>() {}.getType()));
} }
public Optional<String> getCustomJobParameterById(StepExecution stepExecution, String key) {
return getCustomJobParameterSet(stepExecution)
.flatMap(paramterList -> paramterList.stream().filter(dto -> dto.getParameterName().equals(key)).findFirst())
.map(JobParameterDTO::getParameterValue);
}
/**
* Resolve job parameters from step execution context,
* like @org.springframework.batch.core.scope.context.StepContext#getJobParameters()
*
* @param stepExecution
* StepExecution context
* @return
*/
private Map<String, Object> getJobParameters(StepExecution stepExecution) {
Map<String, Object> result = new HashMap<>();
for (Map.Entry<String, JobParameter> entry : stepExecution.getJobParameters().getParameters().entrySet()) {
result.put(entry.getKey(), entry.getValue().getValue());
}
return Collections.unmodifiableMap(result);
}
} }
@@ -28,6 +28,7 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.data.LoanCOBParameter; import org.apache.fineract.cob.data.LoanCOBParameter;
import org.apache.fineract.cob.domain.LoanAccountLock; import org.apache.fineract.cob.domain.LoanAccountLock;
import org.apache.fineract.cob.domain.LockOwner; import org.apache.fineract.cob.domain.LockOwner;
@@ -41,11 +42,12 @@ import org.springframework.batch.repeat.RepeatStatus;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class ApplyLoanLockTasklet implements Tasklet, LoanCatchUpSupport { public class ApplyLoanLockTasklet implements Tasklet {
private final FineractProperties fineractProperties; private final FineractProperties fineractProperties;
private final LoanLockingService loanLockingService; private final LoanLockingService loanLockingService;
private final RetrieveLoanIdService retrieveLoanIdService; private final RetrieveLoanIdService retrieveLoanIdService;
private final CustomJobParameterResolver customJobParameterResolver;
@Override @Override
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception { public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
@@ -57,8 +59,11 @@ public class ApplyLoanLockTasklet implements Tasklet, LoanCatchUpSupport {
|| (loanCOBParameter.getMinLoanId().equals(0L) && loanCOBParameter.getMaxLoanId().equals(0L))) { || (loanCOBParameter.getMinLoanId().equals(0L) && loanCOBParameter.getMaxLoanId().equals(0L))) {
loanIds = Collections.emptyList(); loanIds = Collections.emptyList();
} else { } else {
loanIds = new ArrayList<>(retrieveLoanIdService loanIds = new ArrayList<>(
.retrieveAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(loanCOBParameter, isCatchUp(contribution))); retrieveLoanIdService.retrieveAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(loanCOBParameter,
customJobParameterResolver
.getCustomJobParameterById(contribution.getStepExecution(), LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME)
.map(Boolean::parseBoolean).orElse(false)));
} }
List<List<Long>> loanIdPartitions = Lists.partition(loanIds, getInClauseParameterSizeLimit()); List<List<Long>> loanIdPartitions = Lists.partition(loanIds, getInClauseParameterSizeLimit());
List<LoanAccountLock> accountLocks = new ArrayList<>(); List<LoanAccountLock> accountLocks = new ArrayList<>();
@@ -30,6 +30,7 @@ import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.data.BusinessStepNameAndOrder; import org.apache.fineract.cob.data.BusinessStepNameAndOrder;
import org.apache.fineract.cob.exceptions.CustomJobParameterNotFoundException; import org.apache.fineract.cob.exceptions.CustomJobParameterNotFoundException;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
@@ -52,6 +53,7 @@ public class InlineLoanCOBBuildExecutionContextTasklet implements Tasklet {
private final COBBusinessStepService cobBusinessStepService; private final COBBusinessStepService cobBusinessStepService;
private final CustomJobParameterRepository customJobParameterRepository; private final CustomJobParameterRepository customJobParameterRepository;
private final CustomJobParameterResolver customJobParameterResolver;
private final Gson gson = GoogleGsonSerializerHelper.createSimpleGson(); private final Gson gson = GoogleGsonSerializerHelper.createSimpleGson();
@@ -87,12 +89,9 @@ public class InlineLoanCOBBuildExecutionContextTasklet implements Tasklet {
} }
private List<Long> getLoanIdsFromJobParameters(ChunkContext chunkContext) { private List<Long> getLoanIdsFromJobParameters(ChunkContext chunkContext) {
Long customJobParameterId = (Long) chunkContext.getStepContext().getJobParameters() Set<JobParameterDTO> jobParameters = customJobParameterResolver
.get(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY); .getCustomJobParameterSet(chunkContext.getStepContext().getStepExecution())
CustomJobParameter customJobParameter = customJobParameterRepository.findById(customJobParameterId) .orElseThrow(() -> new LoanNotFoundException(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY));
.orElseThrow(() -> new LoanNotFoundException(customJobParameterId));
String parameterJson = customJobParameter.getParameterJson();
Set<JobParameterDTO> jobParameters = gson.fromJson(parameterJson, new TypeToken<HashSet<JobParameterDTO>>() {}.getType());
JobParameterDTO loanIdsParameter = jobParameters.stream() JobParameterDTO loanIdsParameter = jobParameters.stream()
.filter(jobParameterDTO -> jobParameterDTO.getParameterName().equals(LoanCOBConstant.LOAN_IDS_PARAMETER_NAME)).findFirst() .filter(jobParameterDTO -> jobParameterDTO.getParameterName().equals(LoanCOBConstant.LOAN_IDS_PARAMETER_NAME)).findFirst()
.orElseThrow(() -> new CustomJobParameterNotFoundException(LoanCOBConstant.LOAN_IDS_PARAMETER_NAME)); .orElseThrow(() -> new CustomJobParameterNotFoundException(LoanCOBConstant.LOAN_IDS_PARAMETER_NAME));
@@ -112,13 +112,13 @@ public class LoanCOBManagerConfiguration {
@Bean @Bean
@JobScope @JobScope
public LoanIdParameterTasklet loanIdParameterTasklet() { public LoanIdParameterTasklet loanIdParameterTasklet() {
return new LoanIdParameterTasklet(retrieveLoanIdService); return new LoanIdParameterTasklet(retrieveLoanIdService, customJobParameterResolver);
} }
@Bean @Bean
@JobScope @JobScope
public LockLoanTasklet lockLoanTasklet() { public LockLoanTasklet lockLoanTasklet() {
return new LockLoanTasklet(loanLockingService); return new LockLoanTasklet(loanLockingService, customJobParameterResolver);
} }
@Bean @Bean
@@ -19,6 +19,7 @@
package org.apache.fineract.cob.loan; package org.apache.fineract.cob.loan;
import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.common.InitialisationTasklet; import org.apache.fineract.cob.common.InitialisationTasklet;
import org.apache.fineract.cob.common.ResetContextTasklet; import org.apache.fineract.cob.common.ResetContextTasklet;
import org.apache.fineract.cob.listener.ChunkProcessingLoanItemListener; import org.apache.fineract.cob.listener.ChunkProcessingLoanItemListener;
@@ -71,6 +72,9 @@ public class LoanCOBWorkerConfiguration {
@Autowired @Autowired
private LoanLockingService loanLockingService; private LoanLockingService loanLockingService;
@Autowired
private CustomJobParameterResolver customJobParameterResolver;
@Bean(name = LoanCOBConstant.LOAN_COB_WORKER_STEP) @Bean(name = LoanCOBConstant.LOAN_COB_WORKER_STEP)
public Step loanCOBWorkerStep() { public Step loanCOBWorkerStep() {
return stepBuilderFactory.get("Loan COB worker - Step").inputChannel(inboundRequests).flow(flow()).build(); return stepBuilderFactory.get("Loan COB worker - Step").inputChannel(inboundRequests).flow(flow()).build();
@@ -122,7 +126,7 @@ public class LoanCOBWorkerConfiguration {
@Bean @Bean
public ApplyLoanLockTasklet applyLock() { public ApplyLoanLockTasklet applyLock() {
return new ApplyLoanLockTasklet(fineractProperties, loanLockingService, retrieveLoanIdService); return new ApplyLoanLockTasklet(fineractProperties, loanLockingService, retrieveLoanIdService, customJobParameterResolver);
} }
@Bean @Bean
@@ -133,7 +137,7 @@ public class LoanCOBWorkerConfiguration {
@Bean @Bean
@StepScope @StepScope
public LoanItemReader cobWorkerItemReader() { public LoanItemReader cobWorkerItemReader() {
return new LoanItemReader(loanRepository, retrieveLoanIdService); return new LoanItemReader(loanRepository, retrieveLoanIdService, customJobParameterResolver);
} }
@Bean @Bean
@@ -22,6 +22,7 @@ import java.time.LocalDate;
import java.util.Objects; import java.util.Objects;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.data.LoanCOBParameter; import org.apache.fineract.cob.data.LoanCOBParameter;
import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.scope.context.ChunkContext;
@@ -30,9 +31,10 @@ import org.springframework.batch.repeat.RepeatStatus;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class LoanIdParameterTasklet implements Tasklet, LoanCatchUpSupport { public class LoanIdParameterTasklet implements Tasklet {
private final RetrieveLoanIdService retrieveLoanIdService; private final RetrieveLoanIdService retrieveLoanIdService;
private final CustomJobParameterResolver customJobParameterResolver;
@Override @Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
@@ -40,7 +42,8 @@ public class LoanIdParameterTasklet implements Tasklet, LoanCatchUpSupport {
.get(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME); .get(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME);
LocalDate businessDate = LocalDate.parse(Objects.requireNonNull(businessDateParameter)); LocalDate businessDate = LocalDate.parse(Objects.requireNonNull(businessDateParameter));
LoanCOBParameter minAndMaxLoanId = retrieveLoanIdService.retrieveMinAndMaxLoanIdsNDaysBehind(LoanCOBConstant.NUMBER_OF_DAYS_BEHIND, LoanCOBParameter minAndMaxLoanId = retrieveLoanIdService.retrieveMinAndMaxLoanIdsNDaysBehind(LoanCOBConstant.NUMBER_OF_DAYS_BEHIND,
businessDate, isCatchUp(contribution)); businessDate, customJobParameterResolver.getCustomJobParameterById(chunkContext.getStepContext().getStepExecution(),
LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME).map(Boolean::parseBoolean).orElse(false));
if (Objects.isNull(minAndMaxLoanId) if (Objects.isNull(minAndMaxLoanId)
|| (Objects.isNull(minAndMaxLoanId.getMinLoanId()) && Objects.isNull(minAndMaxLoanId.getMaxLoanId()))) { || (Objects.isNull(minAndMaxLoanId.getMinLoanId()) && Objects.isNull(minAndMaxLoanId.getMaxLoanId()))) {
contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.LOAN_COB_PARAMETER, contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.LOAN_COB_PARAMETER,
@@ -19,6 +19,7 @@
package org.apache.fineract.cob.loan; package org.apache.fineract.cob.loan;
import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.common.ResetContextTasklet; import org.apache.fineract.cob.common.ResetContextTasklet;
import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LoanAccountLockRepository;
import org.apache.fineract.cob.listener.InlineCOBLoanItemListener; import org.apache.fineract.cob.listener.InlineCOBLoanItemListener;
@@ -58,13 +59,17 @@ public class LoanInlineCOBConfig {
@Autowired @Autowired
private TransactionTemplate transactionTemplate; private TransactionTemplate transactionTemplate;
@Autowired @Autowired
private CustomJobParameterRepository loanIdListRepository; private CustomJobParameterRepository customJobParameterRepository;
@Autowired
private CustomJobParameterResolver customJobParameterResolver;
@Autowired @Autowired
private LoanLockingService loanLockingService; private LoanLockingService loanLockingService;
@Bean @Bean
public InlineLoanCOBBuildExecutionContextTasklet inlineLoanCOBBuildExecutionContextTasklet() { public InlineLoanCOBBuildExecutionContextTasklet inlineLoanCOBBuildExecutionContextTasklet() {
return new InlineLoanCOBBuildExecutionContextTasklet(cobBusinessStepService, loanIdListRepository); return new InlineLoanCOBBuildExecutionContextTasklet(cobBusinessStepService, customJobParameterRepository,
customJobParameterResolver);
} }
@Bean @Bean
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.data.LoanCOBParameter; import org.apache.fineract.cob.data.LoanCOBParameter;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -29,13 +30,16 @@ import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.annotation.BeforeStep; import org.springframework.batch.core.annotation.BeforeStep;
import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ExecutionContext;
public class LoanItemReader extends AbstractLoanItemReader implements LoanCatchUpSupport { public class LoanItemReader extends AbstractLoanItemReader {
private final RetrieveLoanIdService retrieveLoanIdService; private final RetrieveLoanIdService retrieveLoanIdService;
private final CustomJobParameterResolver customJobParameterResolver;
public LoanItemReader(LoanRepository loanRepository, RetrieveLoanIdService retrieveLoanIdService) { public LoanItemReader(LoanRepository loanRepository, RetrieveLoanIdService retrieveLoanIdService,
CustomJobParameterResolver customJobParameterResolver) {
super(loanRepository); super(loanRepository);
this.retrieveLoanIdService = retrieveLoanIdService; this.retrieveLoanIdService = retrieveLoanIdService;
this.customJobParameterResolver = customJobParameterResolver;
} }
@BeforeStep @BeforeStep
@@ -50,7 +54,8 @@ public class LoanItemReader extends AbstractLoanItemReader implements LoanCatchU
loanIds = Collections.emptyList(); loanIds = Collections.emptyList();
} else { } else {
loanIds = retrieveLoanIdService.retrieveAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(loanCOBParameter, loanIds = retrieveLoanIdService.retrieveAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(loanCOBParameter,
isCatchUp(stepExecution)); customJobParameterResolver.getCustomJobParameterById(stepExecution, LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME)
.map(Boolean::parseBoolean).orElse(false));
} }
setRemainingData(new ArrayList<>(loanIds)); setRemainingData(new ArrayList<>(loanIds));
} }
@@ -22,6 +22,7 @@ import java.time.LocalDate;
import java.util.Objects; import java.util.Objects;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.data.LoanCOBParameter; import org.apache.fineract.cob.data.LoanCOBParameter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepContribution;
@@ -31,9 +32,10 @@ import org.springframework.batch.repeat.RepeatStatus;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class LockLoanTasklet implements Tasklet, LoanCatchUpSupport { public class LockLoanTasklet implements Tasklet {
private final LoanLockingService loanLockingService; private final LoanLockingService loanLockingService;
private final CustomJobParameterResolver customJobParameterResolver;
@Override @Override
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception { public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
@@ -47,7 +49,10 @@ public class LockLoanTasklet implements Tasklet, LoanCatchUpSupport {
|| (Objects.isNull(loanCOBParameter.getMinLoanId()) && Objects.isNull(loanCOBParameter.getMaxLoanId()))) { || (Objects.isNull(loanCOBParameter.getMinLoanId()) && Objects.isNull(loanCOBParameter.getMaxLoanId()))) {
loanCOBParameter = new LoanCOBParameter(0L, 0L); loanCOBParameter = new LoanCOBParameter(0L, 0L);
} }
loanLockingService.applySoftLock(lastClosedBusinessDate, loanCOBParameter, isCatchUp(contribution)); loanLockingService.applySoftLock(lastClosedBusinessDate, loanCOBParameter,
customJobParameterResolver
.getCustomJobParameterById(contribution.getStepExecution(), LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME)
.map(Boolean::parseBoolean).orElse(false));
return RepeatStatus.FINISHED; return RepeatStatus.FINISHED;
} }
@@ -18,11 +18,11 @@
*/ */
package org.apache.fineract.cob.service; package org.apache.fineract.cob.service;
import com.google.gson.Gson;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.fineract.cob.data.IsCatchUpRunningDTO; import org.apache.fineract.cob.data.IsCatchUpRunningDTO;
import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate; import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate;
import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO; import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO;
@@ -30,7 +30,9 @@ import org.apache.fineract.cob.loan.LoanCOBConstant;
import org.apache.fineract.cob.loan.RetrieveLoanIdService; import org.apache.fineract.cob.loan.RetrieveLoanIdService;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.core.domain.FineractContext; import org.apache.fineract.infrastructure.core.domain.FineractContext;
import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
import org.apache.fineract.infrastructure.jobs.domain.JobExecutionRepository; import org.apache.fineract.infrastructure.jobs.domain.JobExecutionRepository;
import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.explore.JobExplorer;
@@ -46,6 +48,8 @@ public class LoanCOBCatchUpServiceImpl implements LoanCOBCatchUpService {
private final RetrieveLoanIdService retrieveLoanIdService; private final RetrieveLoanIdService retrieveLoanIdService;
private final LoanAccountLockService accountLockService; private final LoanAccountLockService accountLockService;
private final CustomJobParameterRepository customJobParameterRepository;
protected Gson gson = GoogleGsonSerializerHelper.createSimpleGson();
@Override @Override
public void unlockHardLockedLoans() { public void unlockHardLockedLoans() {
@@ -75,12 +79,13 @@ public class LoanCOBCatchUpServiceImpl implements LoanCOBCatchUpService {
public IsCatchUpRunningDTO isCatchUpRunning() { public IsCatchUpRunningDTO isCatchUpRunning() {
List<Long> runningCatchUpExecutionIds = jobExecutionRepository.getRunningJobsIdsByExecutionParameter(LoanCOBConstant.JOB_NAME, List<Long> runningCatchUpExecutionIds = jobExecutionRepository.getRunningJobsIdsByExecutionParameter(LoanCOBConstant.JOB_NAME,
LoanCOBConstant.LOAN_COB_CUSTOM_JOB_PARAMETER_KEY, LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME, "true"); LoanCOBConstant.LOAN_COB_CUSTOM_JOB_PARAMETER_KEY, LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME, "true");
if (CollectionUtils.isNotEmpty(runningCatchUpExecutionIds)) { return runningCatchUpExecutionIds //
JobExecution jobExecution = jobExplorer.getJobExecution(runningCatchUpExecutionIds.get(0)); .stream() //
String executionDateString = (String) jobExecution.getExecutionContext().get(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME); .findFirst() //
return new IsCatchUpRunningDTO(true, LocalDate.parse(executionDateString, DateTimeFormatter.ISO_DATE)); .map(jobExplorer::getJobExecution) //
} else { .map(JobExecution::getExecutionContext) //
return new IsCatchUpRunningDTO(false, null); .map(executionContext -> executionContext.getString(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME)) //
} .map(result -> new IsCatchUpRunningDTO(true, LocalDate.parse(result, DateTimeFormatter.ISO_DATE))) //
.orElse(new IsCatchUpRunningDTO(false, null));
} }
} }
@@ -163,37 +163,6 @@ public class JobExecutionRepository implements InitializingBean {
""", Map.of("status", FAILED.name(), "jobExecutionId", stuckJobId)); """, Map.of("status", FAILED.name(), "jobExecutionId", stuckJobId));
} }
public List<Long> getRunningJobsByExecutionParameter(String jobName, String parameterKeyName, String parameterValue) {
return namedParameterJdbcTemplate.queryForList("""
SELECT bje.JOB_EXECUTION_ID
FROM BATCH_JOB_INSTANCE bji
INNER JOIN BATCH_JOB_EXECUTION bje
ON bji.JOB_INSTANCE_ID = bje.JOB_INSTANCE_ID
INNER JOIN BATCH_JOB_EXECUTION_PARAMS bjep
ON bje.JOB_EXECUTION_ID = bjep.JOB_EXECUTION_ID
WHERE
bje.STATUS IN (:statuses)
AND
bji.JOB_NAME = :jobName
AND
bjep.KEY_NAME = :parameterKeyName
AND
bjep.STRING_VAL = :parameterValue
AND
bje.JOB_INSTANCE_ID NOT IN (
SELECT bje.JOB_INSTANCE_ID
FROM BATCH_JOB_INSTANCE bji
INNER JOIN BATCH_JOB_EXECUTION bje
ON bji.JOB_INSTANCE_ID = bje.JOB_INSTANCE_ID
WHERE
bje.STATUS = :completedStatus
AND
bji.JOB_NAME = :jobName
)
""", Map.of("statuses", List.of(STARTED.name(), STARTING.name()), "jobName", jobName, "completedStatus", COMPLETED.name(),
"parameterKeyName", parameterKeyName, "parameterValue", parameterValue), Long.class);
}
public List<Long> getRunningJobsIdsByExecutionParameter(String jobName, String jobCustomParamKeyName, String parameterKeyName, public List<Long> getRunningJobsIdsByExecutionParameter(String jobName, String jobCustomParamKeyName, String parameterKeyName,
String parameterValue) { String parameterValue) {
final StringBuilder sqlStatementBuilder = new StringBuilder(); final StringBuilder sqlStatementBuilder = new StringBuilder();
@@ -218,7 +187,6 @@ public class JobExecutionRepository implements InitializingBean {
} else { } else {
throw new IllegalStateException("Database type is not supported for json query " + databaseTypeResolver.databaseType()); throw new IllegalStateException("Database type is not supported for json query " + databaseTypeResolver.databaseType());
} }
} }
} }
@@ -16,18 +16,13 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.cob.loan; package org.apache.fineract.infrastructure.jobs.domain;
import org.springframework.batch.core.StepContribution; import lombok.Data;
import org.springframework.batch.core.StepExecution;
public interface LoanCatchUpSupport { @Data
public class RunningJobWithCustomParameters {
default boolean isCatchUp(StepContribution contribution) { private Long executionId;
return isCatchUp(contribution.getStepExecution()); private String parameter;
}
default boolean isCatchUp(StepExecution execution) {
return "true".equalsIgnoreCase(execution.getExecutionContext().getString(LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME, "false"));
}
} }
@@ -20,6 +20,7 @@ package org.apache.fineract.cob.loan;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -29,6 +30,8 @@ import java.time.LocalDate;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.data.LoanCOBParameter; import org.apache.fineract.cob.data.LoanCOBParameter;
import org.apache.fineract.cob.domain.LoanAccountLock; import org.apache.fineract.cob.domain.LoanAccountLock;
import org.apache.fineract.cob.domain.LockOwner; import org.apache.fineract.cob.domain.LockOwner;
@@ -51,8 +54,10 @@ public class ApplyLoanLockTaskletStepDefinitions implements En {
private FineractProperties fineractProperties = mock(FineractProperties.class); private FineractProperties fineractProperties = mock(FineractProperties.class);
private FineractProperties.FineractQueryProperties fineractQueryProperties = mock(FineractProperties.FineractQueryProperties.class); private FineractProperties.FineractQueryProperties fineractQueryProperties = mock(FineractProperties.FineractQueryProperties.class);
private RetrieveLoanIdService retrieveLoanIdService = mock(RetrieveLoanIdService.class); private RetrieveLoanIdService retrieveLoanIdService = mock(RetrieveLoanIdService.class);
private CustomJobParameterResolver customJobParameterResolver = mock(CustomJobParameterResolver.class);
private ApplyLoanLockTasklet applyLoanLockTasklet = new ApplyLoanLockTasklet(fineractProperties, loanLockingService, private ApplyLoanLockTasklet applyLoanLockTasklet = new ApplyLoanLockTasklet(fineractProperties, loanLockingService,
retrieveLoanIdService); retrieveLoanIdService, customJobParameterResolver);
private RepeatStatus resultItem; private RepeatStatus resultItem;
private StepContribution stepContribution; private StepContribution stepContribution;
@@ -86,6 +91,7 @@ public class ApplyLoanLockTaskletStepDefinitions implements En {
lenient().when(fineractQueryProperties.getInClauseParameterSizeLimit()).thenReturn(65000); lenient().when(fineractQueryProperties.getInClauseParameterSizeLimit()).thenReturn(65000);
lenient().when(loanLockingService.findAllByLoanIdIn(Mockito.anyList())).thenReturn(accountLocks); lenient().when(loanLockingService.findAllByLoanIdIn(Mockito.anyList())).thenReturn(accountLocks);
} }
lenient().when(customJobParameterResolver.getCustomJobParameterSet(any())).thenReturn(Optional.empty());
}); });
@@ -29,6 +29,8 @@ import java.time.ZoneId;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.data.LoanCOBParameter; import org.apache.fineract.cob.data.LoanCOBParameter;
import org.apache.fineract.cob.domain.LoanAccountLock; import org.apache.fineract.cob.domain.LoanAccountLock;
import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LoanAccountLockRepository;
@@ -60,6 +62,8 @@ public class FetchAndLockLoanStepDefinitions implements En {
private RepeatStatus result; private RepeatStatus result;
private final LoanLockingService loanLockingService = mock(LoanLockingService.class); private final LoanLockingService loanLockingService = mock(LoanLockingService.class);
private final CustomJobParameterResolver customJobParameterResolver = mock(CustomJobParameterResolver.class);
public FetchAndLockLoanStepDefinitions() { public FetchAndLockLoanStepDefinitions() {
Given("/^The FetchAndLockLoanTasklet.execute method with action (.*)$/", (String action) -> { Given("/^The FetchAndLockLoanTasklet.execute method with action (.*)$/", (String action) -> {
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
@@ -73,7 +77,7 @@ public class FetchAndLockLoanStepDefinitions implements En {
contribution = new StepContribution(stepExecution); contribution = new StepContribution(stepExecution);
contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME, contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME,
LocalDate.now(ZoneId.systemDefault()).toString()); LocalDate.now(ZoneId.systemDefault()).toString());
lenient().when(customJobParameterResolver.getCustomJobParameterSet(Mockito.any())).thenReturn(Optional.empty());
if ("empty loanIds".equals(action)) { if ("empty loanIds".equals(action)) {
contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.LOAN_COB_PARAMETER, contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.LOAN_COB_PARAMETER,
new LoanCOBParameter(0L, 0L)); new LoanCOBParameter(0L, 0L));
@@ -106,7 +110,7 @@ public class FetchAndLockLoanStepDefinitions implements En {
List.of(new LoanAccountLock(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING, LocalDate.now(ZoneId.systemDefault())))); List.of(new LoanAccountLock(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING, LocalDate.now(ZoneId.systemDefault()))));
} }
lockLoanTasklet = new LockLoanTasklet(loanLockingService); lockLoanTasklet = new LockLoanTasklet(loanLockingService, customJobParameterResolver);
}); });
When("FetchAndLockLoanTasklet.execute method executed", () -> { When("FetchAndLockLoanTasklet.execute method executed", () -> {
@@ -21,6 +21,7 @@ package org.apache.fineract.cob.loan;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -32,6 +33,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.data.LoanCOBParameter; import org.apache.fineract.cob.data.LoanCOBParameter;
import org.apache.fineract.cob.exceptions.LoanReadException; import org.apache.fineract.cob.exceptions.LoanReadException;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
@@ -48,7 +50,9 @@ public class LoanItemReaderStepDefinitions implements En {
private RetrieveLoanIdService retrieveLoanIdService = mock(RetrieveLoanIdService.class); private RetrieveLoanIdService retrieveLoanIdService = mock(RetrieveLoanIdService.class);
private LoanItemReader loanItemReader = new LoanItemReader(loanRepository, retrieveLoanIdService); private CustomJobParameterResolver customJobParameterResolver = mock(CustomJobParameterResolver.class);
private LoanItemReader loanItemReader = new LoanItemReader(loanRepository, retrieveLoanIdService, customJobParameterResolver);
private Loan loan = mock(Loan.class); private Loan loan = mock(Loan.class);
@@ -83,12 +87,12 @@ public class LoanItemReaderStepDefinitions implements En {
businessDates.put(BusinessDateType.BUSINESS_DATE, businessDate); businessDates.put(BusinessDateType.BUSINESS_DATE, businessDate);
businessDates.put(BusinessDateType.COB_DATE, businessDate.minusDays(1)); businessDates.put(BusinessDateType.COB_DATE, businessDate.minusDays(1));
ThreadLocalContextUtil.setBusinessDates(businessDates); ThreadLocalContextUtil.setBusinessDates(businessDates);
lenient().when(customJobParameterResolver.getCustomJobParameterSet(any())).thenReturn(Optional.empty());
loanItemReader.beforeStep(stepExecution); loanItemReader.beforeStep(stepExecution);
lenient().when(this.loanRepository.findById(1L)).thenReturn(Optional.of(loan)); lenient().when(this.loanRepository.findById(1L)).thenReturn(Optional.of(loan));
lenient().when(this.loanRepository.findById(-1L)).thenThrow(new RuntimeException("fail")); lenient().when(this.loanRepository.findById(-1L)).thenThrow(new RuntimeException("fail"));
}); });
When("LoanItemReader.read method executed", () -> { When("LoanItemReader.read method executed", () -> {