galovics commented on code in PR #2675: URL: https://github.com/apache/fineract/pull/2675#discussion_r996273175
########## fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java: ########## @@ -0,0 +1,117 @@ +/** + * 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.COBBusinessStepService; +import org.apache.fineract.cob.domain.LoanAccountLockRepository; +import org.apache.fineract.cob.listener.LoanItemListener; +import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl; +import org.apache.fineract.infrastructure.jobs.service.JobName; +import org.apache.fineract.infrastructure.springbatch.PropertyService; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +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.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.launch.support.RunIdIncrementer; +import org.springframework.batch.core.listener.ExecutionContextPromotionListener; +import org.springframework.batch.integration.config.annotation.EnableBatchIntegration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.support.TransactionTemplate; + +@Configuration +@EnableBatchIntegration +public class LoanInlineCOBConfig { + + @Autowired + private JobBuilderFactory jobBuilderFactory; + @Autowired + private StepBuilderFactory stepBuilderFactory; + @Autowired + private PropertyService propertyService; + @Autowired + private LoanRepository loanRepository; + @Autowired + private COBBusinessStepService cobBusinessStepService; + @Autowired + private LoanAccountLockRepository accountLockRepository; + @Autowired + private TransactionTemplate transactionTemplate; + @Autowired + private InlineLoanCOBExecutorServiceImpl inlineCOBExecutorService; + + @Bean + public InlineCOBBuildExecutionContextTasklet inlineCOBBuildExecutionContextTasklet() { + return new InlineCOBBuildExecutionContextTasklet(inlineCOBExecutorService, cobBusinessStepService); + } + + @Bean + protected Step inlineCOBBuildExecutionContextStep() { + return stepBuilderFactory.get(JobName.ACCOUNTING_RUNNING_BALANCE_UPDATE.name()).tasklet(inlineCOBBuildExecutionContextTasklet()) Review Comment: Copy paste error with the job name. ########## fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBBuildExecutionContextTasklet.java: ########## @@ -0,0 +1,47 @@ +/** + * 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 lombok.extern.slf4j.Slf4j; +import org.apache.fineract.cob.COBBusinessStepService; +import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl; +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; + +@Slf4j +@RequiredArgsConstructor +public class InlineCOBBuildExecutionContextTasklet implements Tasklet { + + private final InlineLoanCOBExecutorServiceImpl inlineCOBExecutorService; + private final COBBusinessStepService cobBusinessStepService; + + @Override + public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { + TreeMap<Long, String> cobBusinessStepMap = cobBusinessStepService.getCOBBusinessStepMap(LoanCOBBusinessStep.class, + LoanCOBConstant.LOAN_COB_JOB_NAME); + contribution.getStepExecution().getExecutionContext().put(LoanCOBConstant.LOAN_IDS, inlineCOBExecutorService.getLoanIds()); Review Comment: Same as above, this is LOAN_COB specific. ########## fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineCOBType.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.service; + +import java.util.Arrays; +import java.util.Optional; + +public enum InlineCOBType { + + LOAN_COB("LOAN_COB", "INLINE_LOAN_COB", InlineLoanCOBExecutorServiceImpl.class); + + final String jobName; Review Comment: Mark them as private please. ########## fineract-provider/src/main/java/org/apache/fineract/cob/api/InlineCOBResource.java: ########## @@ -0,0 +1,69 @@ +/** + * 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.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.cob.data.LoanIdsResponseDTO; +import org.apache.fineract.commands.domain.CommandWrapper; +import org.apache.fineract.commands.service.CommandWrapperBuilder; +import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.springframework.stereotype.Component; + +@Path("/jobs") +@Component +@Tag(name = "Inline COB", description = "") +@RequiredArgsConstructor +public class InlineCOBResource { + + private final PortfolioCommandSourceWritePlatformService commandWritePlatformService; + private final DefaultToApiJsonSerializer<LoanIdsResponseDTO> serializer; + + @POST + @Path("{COBJobName}/inline") Review Comment: Doesn't have to be `COBJobName`. Simply `jobName` is enough. ########## fineract-provider/src/main/java/org/apache/fineract/cob/api/InlineCOBResource.java: ########## @@ -0,0 +1,69 @@ +/** + * 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.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.cob.data.LoanIdsResponseDTO; +import org.apache.fineract.commands.domain.CommandWrapper; +import org.apache.fineract.commands.service.CommandWrapperBuilder; +import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.springframework.stereotype.Component; + +@Path("/jobs") +@Component +@Tag(name = "Inline COB", description = "") +@RequiredArgsConstructor +public class InlineCOBResource { Review Comment: Doesn't need to be COB specific. Any jobs in the future which are partitioned could have an inline alternative. ########## fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java: ########## @@ -0,0 +1,117 @@ +/** + * 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.COBBusinessStepService; +import org.apache.fineract.cob.domain.LoanAccountLockRepository; +import org.apache.fineract.cob.listener.LoanItemListener; +import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl; +import org.apache.fineract.infrastructure.jobs.service.JobName; +import org.apache.fineract.infrastructure.springbatch.PropertyService; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +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.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.launch.support.RunIdIncrementer; +import org.springframework.batch.core.listener.ExecutionContextPromotionListener; +import org.springframework.batch.integration.config.annotation.EnableBatchIntegration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.support.TransactionTemplate; + +@Configuration +@EnableBatchIntegration +public class LoanInlineCOBConfig { + + @Autowired + private JobBuilderFactory jobBuilderFactory; + @Autowired + private StepBuilderFactory stepBuilderFactory; + @Autowired + private PropertyService propertyService; + @Autowired + private LoanRepository loanRepository; + @Autowired + private COBBusinessStepService cobBusinessStepService; + @Autowired + private LoanAccountLockRepository accountLockRepository; + @Autowired + private TransactionTemplate transactionTemplate; + @Autowired + private InlineLoanCOBExecutorServiceImpl inlineCOBExecutorService; + + @Bean + public InlineCOBBuildExecutionContextTasklet inlineCOBBuildExecutionContextTasklet() { + return new InlineCOBBuildExecutionContextTasklet(inlineCOBExecutorService, cobBusinessStepService); + } + + @Bean + protected Step inlineCOBBuildExecutionContextStep() { + return stepBuilderFactory.get(JobName.ACCOUNTING_RUNNING_BALANCE_UPDATE.name()).tasklet(inlineCOBBuildExecutionContextTasklet()) + .listener(inlineCobPromotionListener()).build(); + } + + @Bean + public Step inlineLoanCOBStep() { + return stepBuilderFactory.get("Inline Loan COB Step").<Loan, Loan>chunk(propertyService.getChunkSize(JobName.LOAN_COB.name())) + .reader(inlineCobWorkerItemReader()).processor(inlineCobWorkerItemProcessor()).writer(inlineCobWorkerItemWriter()) + .faultTolerant().skip(Exception.class).skipLimit(propertyService.getChunkSize(JobName.LOAN_COB.name()) + 1) + .listener(inlineCobLoanItemListener()).build(); + } + + @Bean(name = "loanInlineCOBJob") + public Job loanInlineCOBJob() { + return jobBuilderFactory.get(LoanCOBConstant.INLINE_LOAN_COB_JOB_NAME) // + .start(inlineCOBBuildExecutionContextStep()).next(inlineLoanCOBStep()) // + .incrementer(new RunIdIncrementer()) // + .build(); + } + + @Bean + public InlineCOBLoanItemReader inlineCobWorkerItemReader() { Review Comment: Why can we reuse these beans from the regular Loan COB? I think with minimal refactorings to them it could be easily reused.. ########## fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineCOBExecutionDataParser.java: ########## @@ -0,0 +1,59 @@ +/** + * 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.service; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class InlineCOBExecutionDataParser { + + private final FromJsonHelper jsonHelper; + + public List<Long> parseExecution(JsonCommand command) { + JsonObject element = extractJsonObject(command); + JsonArray jsonArray = jsonHelper.extractJsonArrayNamed("loanIds", element); + List<Long> loanIds = new ArrayList<>(); + for (final JsonElement loanIdElement : jsonArray) { + Long loanId = jsonHelper.extractLongNamed("loanId", loanIdElement); Review Comment: Doesn't this mean that in the array we have every single element as object and inside that a loanId? Something like this: ``` { "loanIds": [ { "loanId": 1}, { "loanId": 2} ] } ``` If this is the case, why can't we just go with a plain, primitive array? ``` { "loanIds": [ 1, 2 ] } ``` ########## fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineCOBExecuteHandler.java: ########## @@ -0,0 +1,49 @@ +/** + * 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.service; + +import com.google.common.base.Splitter; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +@CommandType(entity = "INLINE_COB", action = "EXECUTE") +public class InlineCOBExecuteHandler implements NewCommandSourceHandler { + + private final ApplicationContext applicationContext; + + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + List<String> split = Splitter.on("/").splitToList(command.getUrl()); Review Comment: Why do we need this splitting? The REST layer can easily get you access to the path/query parameters with annotations, lets not reinvent the wheel. ########## fineract-provider/src/main/java/org/apache/fineract/cob/api/InlineCOBResource.java: ########## @@ -0,0 +1,69 @@ +/** + * 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.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.cob.data.LoanIdsResponseDTO; +import org.apache.fineract.commands.domain.CommandWrapper; +import org.apache.fineract.commands.service.CommandWrapperBuilder; +import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.springframework.stereotype.Component; + +@Path("/jobs") +@Component +@Tag(name = "Inline COB", description = "") +@RequiredArgsConstructor +public class InlineCOBResource { + + private final PortfolioCommandSourceWritePlatformService commandWritePlatformService; + private final DefaultToApiJsonSerializer<LoanIdsResponseDTO> serializer; + + @POST + @Path("{COBJobName}/inline") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Starts an inline COB", description = "Starts an inline COB") Review Comment: Not COB only. ########## fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBBuildExecutionContextTasklet.java: ########## @@ -0,0 +1,47 @@ +/** + * 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 lombok.extern.slf4j.Slf4j; +import org.apache.fineract.cob.COBBusinessStepService; +import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl; +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; + +@Slf4j +@RequiredArgsConstructor +public class InlineCOBBuildExecutionContextTasklet implements Tasklet { + + private final InlineLoanCOBExecutorServiceImpl inlineCOBExecutorService; + private final COBBusinessStepService cobBusinessStepService; + + @Override + public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { + TreeMap<Long, String> cobBusinessStepMap = cobBusinessStepService.getCOBBusinessStepMap(LoanCOBBusinessStep.class, + LoanCOBConstant.LOAN_COB_JOB_NAME); Review Comment: Since this tasklet is a generic one for inline execution, we can't hardcode the LOAN_COB_JOB_NAME, can we? ########## fineract-provider/src/main/java/org/apache/fineract/cob/listener/LoanItemListener.java: ########## @@ -59,6 +59,9 @@ protected void doInTransactionWithoutResult(@NotNull TransactionStatus status) { for (Long loanId : loanIds) { Optional<LoanAccountLock> loanAccountLock = accountLockRepository.findByLoanIdAndLockOwner(loanId, LockOwner.LOAN_COB_CHUNK_PROCESSING); + if (loanAccountLock.isEmpty()) { Review Comment: Not sure why this is needed, explain please. ########## fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java: ########## @@ -0,0 +1,117 @@ +/** + * 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.COBBusinessStepService; +import org.apache.fineract.cob.domain.LoanAccountLockRepository; +import org.apache.fineract.cob.listener.LoanItemListener; +import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl; +import org.apache.fineract.infrastructure.jobs.service.JobName; +import org.apache.fineract.infrastructure.springbatch.PropertyService; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +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.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.launch.support.RunIdIncrementer; +import org.springframework.batch.core.listener.ExecutionContextPromotionListener; +import org.springframework.batch.integration.config.annotation.EnableBatchIntegration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.support.TransactionTemplate; + +@Configuration +@EnableBatchIntegration +public class LoanInlineCOBConfig { + + @Autowired + private JobBuilderFactory jobBuilderFactory; + @Autowired + private StepBuilderFactory stepBuilderFactory; + @Autowired + private PropertyService propertyService; + @Autowired + private LoanRepository loanRepository; + @Autowired + private COBBusinessStepService cobBusinessStepService; + @Autowired + private LoanAccountLockRepository accountLockRepository; + @Autowired + private TransactionTemplate transactionTemplate; + @Autowired + private InlineLoanCOBExecutorServiceImpl inlineCOBExecutorService; + + @Bean + public InlineCOBBuildExecutionContextTasklet inlineCOBBuildExecutionContextTasklet() { + return new InlineCOBBuildExecutionContextTasklet(inlineCOBExecutorService, cobBusinessStepService); + } + + @Bean + protected Step inlineCOBBuildExecutionContextStep() { + return stepBuilderFactory.get(JobName.ACCOUNTING_RUNNING_BALANCE_UPDATE.name()).tasklet(inlineCOBBuildExecutionContextTasklet()) + .listener(inlineCobPromotionListener()).build(); + } + + @Bean + public Step inlineLoanCOBStep() { + return stepBuilderFactory.get("Inline Loan COB Step").<Loan, Loan>chunk(propertyService.getChunkSize(JobName.LOAN_COB.name())) + .reader(inlineCobWorkerItemReader()).processor(inlineCobWorkerItemProcessor()).writer(inlineCobWorkerItemWriter()) + .faultTolerant().skip(Exception.class).skipLimit(propertyService.getChunkSize(JobName.LOAN_COB.name()) + 1) Review Comment: I think we shouldn't allow skipping here. If the inline Loan COB fails, it should fail and respond synchronously. ########## fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineCOBType.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.service; + +import java.util.Arrays; +import java.util.Optional; + +public enum InlineCOBType { + + LOAN_COB("LOAN_COB", "INLINE_LOAN_COB", InlineLoanCOBExecutorServiceImpl.class); + + final String jobName; + final String inlineCOBJobName; + final Class<? extends InlineCOBExecutorService> executorServiceClass; + + InlineCOBType(String jobName, String inlineCOBJobName, Class<? extends InlineCOBExecutorService> executorServiceClass) { + this.jobName = jobName; + this.inlineCOBJobName = inlineCOBJobName; + this.executorServiceClass = executorServiceClass; + } + + public static InlineCOBType getInlineCOBTypeByJobName(String jobName) { + Optional<InlineCOBType> optionalCategory = Arrays.stream(InlineCOBType.values()) Review Comment: variable naming, not sure about "category", maybe just a copy paste error. ########## fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineCOBType.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.service; + +import java.util.Arrays; +import java.util.Optional; + +public enum InlineCOBType { + + LOAN_COB("LOAN_COB", "INLINE_LOAN_COB", InlineLoanCOBExecutorServiceImpl.class); + + final String jobName; + final String inlineCOBJobName; + final Class<? extends InlineCOBExecutorService> executorServiceClass; + + InlineCOBType(String jobName, String inlineCOBJobName, Class<? extends InlineCOBExecutorService> executorServiceClass) { Review Comment: Can be replaced with a RequiredArgsConstructor ########## fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java: ########## @@ -40,6 +40,7 @@ public void write(@NotNull List<? extends Loan> items) throws Exception { List<Long> loanIds = items.stream().map(AbstractPersistableCustom::getId).toList(); accountLockRepository.deleteByLoanIdInAndLockOwner(loanIds, LockOwner.LOAN_COB_CHUNK_PROCESSING); + accountLockRepository.deleteByLoanIdInAndLockOwner(loanIds, LockOwner.LOAN_INLINE_COB_PROCESSING); Review Comment: Not sure why this is needed, can you please explain? ########## fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImpl.java: ########## @@ -0,0 +1,137 @@ +/** + * 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.service; + +import java.util.ArrayList; +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.LoanAccountLockCannotBeOverruledException; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException; +import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.configuration.JobLocator; +import org.springframework.batch.core.explore.JobExplorer; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.launch.NoSuchJobException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Slf4j +@RequiredArgsConstructor +public class InlineLoanCOBExecutorServiceImpl implements InlineCOBExecutorService { + + private final LoanAccountLockRepository loanAccountLockRepository; + private final InlineCOBExecutionDataParser dataParser; + private final JobLauncher jobLauncher; + private final JobLocator jobLocator; + private final JobExplorer jobExplorer; + private List<Long> loanIds; + + @Override + @Transactional(propagation = Propagation.NOT_SUPPORTED) + public CommandProcessingResult executeInlineCOB(JsonCommand command, String jobName) throws LoanAccountLockCannotBeOverruledException { + List<Long> loanIds = dataParser.parseExecution(command); + List<LoanAccountLock> loanAccountLocks = getLoanAccountLocks(loanIds); + execute(loanAccountLocks, loanIds, jobName); + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).build(); + } + + public List<Long> getLoanIds() { + return loanIds; + } + + private List<LoanAccountLock> getLoanAccountLocks(List<Long> loanIds) { + List<LoanAccountLock> loanAccountLocks = new ArrayList<>(); + List<Long> alreadyLockedLoanIds = new ArrayList<>(); + loanIds.forEach(loanId -> { + Optional<LoanAccountLock> loanLockOptional = loanAccountLockRepository.findById(loanId); + if (loanLockOptional.isPresent()) { + LoanAccountLock loanAccountLock = loanLockOptional.get(); + if (isLockOverruleable(loanAccountLock)) { + loanAccountLocks.add(loanAccountLock); + } else { + alreadyLockedLoanIds.add(loanId); + } + } else { + loanAccountLocks.add(new LoanAccountLock(loanId, LockOwner.LOAN_INLINE_COB_PROCESSING)); + } + }); + if (!alreadyLockedLoanIds.isEmpty()) { + String message = "There is a hard lock on the loan account without any error, so it can't be overruled."; + String loanIdsMessage = " Locked loan IDs: " + alreadyLockedLoanIds; + throw new LoanAccountLockCannotBeOverruledException(message + loanIdsMessage); + } + return loanAccountLocks; + } + + private void execute(List<LoanAccountLock> loanAccountLocks, List<Long> loanIds, String jobName) { + lockLoanAccounts(loanAccountLocks); + this.loanIds = loanIds; + Job inlineLoanCOBJob; + try { + inlineLoanCOBJob = jobLocator.getJob(jobName); + } catch (NoSuchJobException e) { + throw new JobNotFoundException(jobName, e); + } + JobParameters jobParameters = new JobParametersBuilder(jobExplorer).getNextJobParameters(inlineLoanCOBJob) + .addJobParameters(new JobParameters()).toJobParameters(); Review Comment: Why don't we add the loanIds as a job parameter as well? ########## fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImpl.java: ########## @@ -0,0 +1,137 @@ +/** + * 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.service; + +import java.util.ArrayList; +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.LoanAccountLockCannotBeOverruledException; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException; +import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.configuration.JobLocator; +import org.springframework.batch.core.explore.JobExplorer; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.launch.NoSuchJobException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Slf4j +@RequiredArgsConstructor +public class InlineLoanCOBExecutorServiceImpl implements InlineCOBExecutorService { + + private final LoanAccountLockRepository loanAccountLockRepository; + private final InlineCOBExecutionDataParser dataParser; + private final JobLauncher jobLauncher; + private final JobLocator jobLocator; + private final JobExplorer jobExplorer; + private List<Long> loanIds; Review Comment: Okay, this is definitely not the way to go. You can a Spring bean that's a singleton and you just made it stateful. What happens if there are concurrent executions? It'll mess up the state. ########## fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImpl.java: ########## @@ -0,0 +1,137 @@ +/** + * 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.service; + +import java.util.ArrayList; +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.LoanAccountLockCannotBeOverruledException; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException; +import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.configuration.JobLocator; +import org.springframework.batch.core.explore.JobExplorer; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.launch.NoSuchJobException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Slf4j +@RequiredArgsConstructor +public class InlineLoanCOBExecutorServiceImpl implements InlineCOBExecutorService { + + private final LoanAccountLockRepository loanAccountLockRepository; + private final InlineCOBExecutionDataParser dataParser; + private final JobLauncher jobLauncher; + private final JobLocator jobLocator; + private final JobExplorer jobExplorer; + private List<Long> loanIds; + + @Override + @Transactional(propagation = Propagation.NOT_SUPPORTED) + public CommandProcessingResult executeInlineCOB(JsonCommand command, String jobName) throws LoanAccountLockCannotBeOverruledException { + List<Long> loanIds = dataParser.parseExecution(command); + List<LoanAccountLock> loanAccountLocks = getLoanAccountLocks(loanIds); + execute(loanAccountLocks, loanIds, jobName); + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).build(); + } + + public List<Long> getLoanIds() { + return loanIds; + } + + private List<LoanAccountLock> getLoanAccountLocks(List<Long> loanIds) { + List<LoanAccountLock> loanAccountLocks = new ArrayList<>(); + List<Long> alreadyLockedLoanIds = new ArrayList<>(); + loanIds.forEach(loanId -> { + Optional<LoanAccountLock> loanLockOptional = loanAccountLockRepository.findById(loanId); + if (loanLockOptional.isPresent()) { + LoanAccountLock loanAccountLock = loanLockOptional.get(); + if (isLockOverruleable(loanAccountLock)) { + loanAccountLocks.add(loanAccountLock); + } else { + alreadyLockedLoanIds.add(loanId); + } + } else { + loanAccountLocks.add(new LoanAccountLock(loanId, LockOwner.LOAN_INLINE_COB_PROCESSING)); + } + }); + if (!alreadyLockedLoanIds.isEmpty()) { + String message = "There is a hard lock on the loan account without any error, so it can't be overruled."; + String loanIdsMessage = " Locked loan IDs: " + alreadyLockedLoanIds; + throw new LoanAccountLockCannotBeOverruledException(message + loanIdsMessage); + } + return loanAccountLocks; + } + + private void execute(List<LoanAccountLock> loanAccountLocks, List<Long> loanIds, String jobName) { + lockLoanAccounts(loanAccountLocks); + this.loanIds = loanIds; + Job inlineLoanCOBJob; + try { + inlineLoanCOBJob = jobLocator.getJob(jobName); + } catch (NoSuchJobException e) { + throw new JobNotFoundException(jobName, e); + } + JobParameters jobParameters = new JobParametersBuilder(jobExplorer).getNextJobParameters(inlineLoanCOBJob) + .addJobParameters(new JobParameters()).toJobParameters(); + JobExecution jobExecution; + try { + jobExecution = jobLauncher.run(inlineLoanCOBJob, jobParameters); + } catch (Exception e) { + final String msg = "Job execution failed for job with name:" + jobName; + log.error("{}", msg, e); + throw new PlatformInternalServerException("error.msg.sheduler.job.execution.failed", msg, jobName, e); + } + if (!BatchStatus.COMPLETED.equals(jobExecution.getStatus())) { + final String msg = "Job execution failed for job with name:" + jobName; + log.error("{}", msg); Review Comment: What's the point of this? With the String concatenation above you're defeating the purpose of placeholders in the logger. Should inline the log message and pass the jobName as a parameter. ########## fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImpl.java: ########## @@ -0,0 +1,137 @@ +/** + * 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.service; + +import java.util.ArrayList; +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.LoanAccountLockCannotBeOverruledException; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException; +import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.configuration.JobLocator; +import org.springframework.batch.core.explore.JobExplorer; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.launch.NoSuchJobException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Slf4j +@RequiredArgsConstructor +public class InlineLoanCOBExecutorServiceImpl implements InlineCOBExecutorService { + + private final LoanAccountLockRepository loanAccountLockRepository; + private final InlineCOBExecutionDataParser dataParser; + private final JobLauncher jobLauncher; + private final JobLocator jobLocator; + private final JobExplorer jobExplorer; + private List<Long> loanIds; + + @Override + @Transactional(propagation = Propagation.NOT_SUPPORTED) + public CommandProcessingResult executeInlineCOB(JsonCommand command, String jobName) throws LoanAccountLockCannotBeOverruledException { + List<Long> loanIds = dataParser.parseExecution(command); + List<LoanAccountLock> loanAccountLocks = getLoanAccountLocks(loanIds); + execute(loanAccountLocks, loanIds, jobName); + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).build(); + } + + public List<Long> getLoanIds() { + return loanIds; + } + + private List<LoanAccountLock> getLoanAccountLocks(List<Long> loanIds) { + List<LoanAccountLock> loanAccountLocks = new ArrayList<>(); + List<Long> alreadyLockedLoanIds = new ArrayList<>(); + loanIds.forEach(loanId -> { + Optional<LoanAccountLock> loanLockOptional = loanAccountLockRepository.findById(loanId); + if (loanLockOptional.isPresent()) { + LoanAccountLock loanAccountLock = loanLockOptional.get(); + if (isLockOverruleable(loanAccountLock)) { + loanAccountLocks.add(loanAccountLock); + } else { + alreadyLockedLoanIds.add(loanId); + } + } else { + loanAccountLocks.add(new LoanAccountLock(loanId, LockOwner.LOAN_INLINE_COB_PROCESSING)); + } + }); + if (!alreadyLockedLoanIds.isEmpty()) { + String message = "There is a hard lock on the loan account without any error, so it can't be overruled."; + String loanIdsMessage = " Locked loan IDs: " + alreadyLockedLoanIds; + throw new LoanAccountLockCannotBeOverruledException(message + loanIdsMessage); + } + return loanAccountLocks; + } + + private void execute(List<LoanAccountLock> loanAccountLocks, List<Long> loanIds, String jobName) { + lockLoanAccounts(loanAccountLocks); + this.loanIds = loanIds; + Job inlineLoanCOBJob; + try { + inlineLoanCOBJob = jobLocator.getJob(jobName); + } catch (NoSuchJobException e) { + throw new JobNotFoundException(jobName, e); + } + JobParameters jobParameters = new JobParametersBuilder(jobExplorer).getNextJobParameters(inlineLoanCOBJob) + .addJobParameters(new JobParameters()).toJobParameters(); + JobExecution jobExecution; + try { + jobExecution = jobLauncher.run(inlineLoanCOBJob, jobParameters); + } catch (Exception e) { + final String msg = "Job execution failed for job with name:" + jobName; + log.error("{}", msg, e); + throw new PlatformInternalServerException("error.msg.sheduler.job.execution.failed", msg, jobName, e); + } + if (!BatchStatus.COMPLETED.equals(jobExecution.getStatus())) { + final String msg = "Job execution failed for job with name:" + jobName; + log.error("{}", msg); + throw new PlatformInternalServerException("error.msg.sheduler.job.execution.failed", msg, jobName); + } + } + + private void lockLoanAccounts(List<LoanAccountLock> loanAccountLocks) { + loanAccountLocks.forEach(loanAccountLock -> { + loanAccountLock.setNewLockOwner(LockOwner.LOAN_INLINE_COB_PROCESSING); + loanAccountLockRepository.save(loanAccountLock); Review Comment: So the public method is not allowing transactions which means that this save call is opening and closing transactions for every single loan account lock, that's definitely not the way to go. All loan accounts shall be locked in a single tx -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
