From 8184b11c29eb2b1b5378365025be965026f67427 Mon Sep 17 00:00:00 2001 From: taskain7 Date: Wed, 3 Aug 2022 19:41:52 +0200 Subject: [PATCH] [FINERACT-1678] execution order for Loan COB --- .../fineract/cob/COBBusinessStepService.java | 32 +++++++++ .../cob/COBBusinessStepServiceImpl.java | 66 +++++++++++++++++++ .../cob/domain/BatchBusinessStep.java | 43 ++++++++++++ .../domain/BatchBusinessStepRepository.java | 28 ++++++++ .../cob/loan/LoanCOBManagerConfiguration.java | 11 +++- .../fineract/cob/loan/LoanCOBPartitioner.java | 42 ++++++++++++ .../cob/loan/LoanCOBWorkerConfiguration.java | 5 +- .../fineract/cob/loan/LoanItemProcessor.java | 55 ++++++++++++++++ 8 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/domain/BatchBusinessStep.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/domain/BatchBusinessStepRepository.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepService.java b/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepService.java new file mode 100644 index 000000000..ade540adc --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepService.java @@ -0,0 +1,32 @@ +/** + * 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; + +import java.util.TreeMap; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.jetbrains.annotations.NotNull; + +public interface COBBusinessStepService { + + , S extends AbstractPersistableCustom> S run(TreeMap executionMap, S item); + + @NotNull + , S extends AbstractPersistableCustom> TreeMap getCOBBusinessStepMap( + Class businessStepClass, String cobJobName); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java new file mode 100644 index 000000000..03fdbf195 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java @@ -0,0 +1,66 @@ +/** + * 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; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.TreeMap; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.cob.domain.BatchBusinessStep; +import org.apache.fineract.cob.domain.BatchBusinessStepRepository; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class COBBusinessStepServiceImpl implements COBBusinessStepService { + + private final BatchBusinessStepRepository batchBusinessStepRepository; + private final ApplicationContext applicationContext; + private final ListableBeanFactory beanFactory; + + @Override + public , S extends AbstractPersistableCustom> S run(TreeMap executionMap, S item) { + for (String businessStep : executionMap.values()) { + COBBusinessStep businessStepBean = (COBBusinessStep) applicationContext.getBean(businessStep); + item = businessStepBean.execute(item); + } + return item; + } + + @NotNull + @Override + public , S extends AbstractPersistableCustom> TreeMap getCOBBusinessStepMap( + Class businessStepClass, String cobJobName) { + List cobStepConfigs = batchBusinessStepRepository.findAllByJobName(cobJobName); + List businessSteps = Arrays.stream(beanFactory.getBeanNamesForType(businessStepClass)).toList(); + TreeMap executionMap = new TreeMap<>(); + for (String businessStep : businessSteps) { + COBBusinessStep businessStepBean = (COBBusinessStep) applicationContext.getBean(businessStep); + Optional businessStepConfig = cobStepConfigs.stream() + .filter(stepConfig -> businessStepBean.getEnumStyledName().equals(stepConfig.getStepName())).findFirst(); + businessStepConfig.ifPresent(batchBusinessStep -> executionMap.put(batchBusinessStep.getStepOrder(), businessStep)); + } + return executionMap; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/domain/BatchBusinessStep.java b/fineract-provider/src/main/java/org/apache/fineract/cob/domain/BatchBusinessStep.java new file mode 100644 index 000000000..7876e56fb --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/domain/BatchBusinessStep.java @@ -0,0 +1,43 @@ +/** + * 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.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; + +@Entity +@Table(name = "m_batch_business_steps") +@NoArgsConstructor +@Getter +public class BatchBusinessStep extends AbstractPersistableCustom { + + @Column(name = "job_name", nullable = false) + private String jobName; + + @Column(name = "step_name", nullable = false) + private String stepName; + + @Column(name = "step_order", nullable = false) + private Long stepOrder; + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/domain/BatchBusinessStepRepository.java b/fineract-provider/src/main/java/org/apache/fineract/cob/domain/BatchBusinessStepRepository.java new file mode 100644 index 000000000..76475e2a3 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/domain/BatchBusinessStepRepository.java @@ -0,0 +1,28 @@ +/** + * 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.domain; + +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface BatchBusinessStepRepository extends JpaRepository, JpaSpecificationExecutor { + + List findAllByJobName(String jobName); +} 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 077602c74..30a91ee59 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 @@ -18,12 +18,15 @@ */ package org.apache.fineract.cob.loan; +import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBPropertyService; import org.apache.fineract.infrastructure.jobs.service.JobName; 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; +import org.springframework.batch.core.explore.JobExplorer; +import org.springframework.batch.core.launch.JobOperator; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.integration.config.annotation.EnableBatchIntegration; import org.springframework.batch.integration.partition.RemotePartitioningManagerStepBuilderFactory; @@ -49,10 +52,16 @@ public class LoanCOBManagerConfiguration { private COBPropertyService cobPropertyService; @Autowired private DirectChannel outboundRequests; + @Autowired + private COBBusinessStepService cobBusinessStepService; + @Autowired + private JobOperator jobOperator; + @Autowired + private JobExplorer jobExplorer; @Bean public LoanCOBPartitioner partitioner() { - return new LoanCOBPartitioner(loanRepository, cobPropertyService); + return new LoanCOBPartitioner(loanRepository, cobPropertyService, cobBusinessStepService, jobOperator, jobExplorer); } @Bean diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBPartitioner.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBPartitioner.java index 49ed65d0b..0857b6a01 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBPartitioner.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBPartitioner.java @@ -22,28 +22,56 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeMap; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBPropertyService; +import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.explore.JobExplorer; +import org.springframework.batch.core.launch.JobExecutionNotRunningException; +import org.springframework.batch.core.launch.JobOperator; +import org.springframework.batch.core.launch.NoSuchJobExecutionException; import org.springframework.batch.core.partition.support.Partitioner; import org.springframework.batch.item.ExecutionContext; +@Slf4j @RequiredArgsConstructor public class LoanCOBPartitioner implements Partitioner { private final LoanRepository loanRepository; private final COBPropertyService cobPropertyService; + private final COBBusinessStepService cobBusinessStepService; + private final JobOperator jobOperator; + private final JobExplorer jobExplorer; private static final String PARTITION_PREFIX = "partition"; private static final String JOB_NAME = "LOAN_COB"; + private static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS"; @Override public Map partition(int gridSize) { int partitionCount = cobPropertyService.getPartitionSize(JOB_NAME); + TreeMap cobBusinessStepMap = cobBusinessStepService.getCOBBusinessStepMap(LoanCOBBusinessStep.class, + LOAN_COB_JOB_NAME); + if (cobBusinessStepMap.isEmpty()) { + return stopJobExecution(); + } + return getPartitions(partitionCount, cobBusinessStepMap); + } + + @NotNull + private Map getPartitions(int partitionCount, TreeMap cobBusinessStepMap) { Map partitions = new HashMap<>(); for (int i = 0; i < partitionCount; i++) { ExecutionContext executionContext = new ExecutionContext(); executionContext.put("loanIds", new ArrayList()); + executionContext.put("BusinessStepMap", cobBusinessStepMap); partitions.put(PARTITION_PREFIX + i, executionContext); } @@ -56,4 +84,18 @@ public class LoanCOBPartitioner implements Partitioner { } return partitions; } + + @Nullable + private Map stopJobExecution() { + Set runningJobExecutions = jobExplorer.findRunningJobExecutions(JobName.LOAN_COB.name()); + for (JobExecution jobExecution : runningJobExecutions) { + try { + jobOperator.stop(jobExecution.getId()); + } catch (NoSuchJobExecutionException | JobExecutionNotRunningException e) { + log.error("There is no running execution for the given execution ID. Execution ID: {}", jobExecution.getId()); + throw new RuntimeException(e); + } + } + return null; + } } 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 18527eb63..ddee35820 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 @@ -20,6 +20,7 @@ package org.apache.fineract.cob.loan; import java.util.ArrayList; import java.util.List; +import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBPropertyService; import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.portfolio.loanaccount.domain.Loan; @@ -54,6 +55,8 @@ public class LoanCOBWorkerConfiguration { private LoanRepository loanRepository; @Autowired private QueueChannel inboundRequests; + @Autowired + private COBBusinessStepService cobBusinessStepService; @Bean(name = "Loan COB worker") public Step loanCOBWorkerStep() { @@ -81,7 +84,7 @@ public class LoanCOBWorkerConfiguration { @Bean public ItemProcessor itemProcessor() { - return null; + return new LoanItemProcessor(cobBusinessStepService); } @Bean diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java new file mode 100644 index 000000000..537a3f415 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java @@ -0,0 +1,55 @@ +/** + * 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.util.TreeMap; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.cob.COBBusinessStepService; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.springframework.batch.core.ExitStatus; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.StepExecutionListener; +import org.springframework.batch.item.ExecutionContext; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class LoanItemProcessor implements ItemProcessor, StepExecutionListener { + + private final COBBusinessStepService cobBusinessStepService; + private StepExecution stepExecution; + + @Override + public void beforeStep(StepExecution stepExecution) { + this.stepExecution = stepExecution; + } + + @Override + public Loan process(Loan item) throws Exception { + ExecutionContext executionContext = stepExecution.getJobExecution().getExecutionContext(); + TreeMap businessStepMap = (TreeMap) executionContext.get("BusinessStepMap"); + return cobBusinessStepService.run(businessStepMap, item); + } + + @Override + public ExitStatus afterStep(StepExecution stepExecution) { + return ExitStatus.COMPLETED; + } +}