From 367550007c833e89264a17665a0463281da4b9f8 Mon Sep 17 00:00:00 2001 From: Janos Haber Date: Mon, 6 Mar 2023 23:04:03 +0100 Subject: [PATCH] FINERACT-1724 - Optimistic locking fix - [x] ReloadService for lead entities - [x] ReloaderService iterating over ReloadService and try to find and reload the entity - [x] Fix COBBusinessStepServiceImpl to reload the entity before business step --- .../starter/TestDefaultConfiguration.java | 5 ++- .../cob/COBBusinessStepServiceImpl.java | 4 ++ .../cob/service/LoanReloadService.java | 43 +++++++++++++++++++ .../fineract/cob/service/ReloadService.java | 28 ++++++++++++ .../fineract/cob/service/ReloaderService.java | 40 +++++++++++++++++ ...COBBusinessStepServiceStepDefinitions.java | 9 +++- .../COBBulkEventConfigurationTest.java | 4 ++ 7 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanReloadService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/service/ReloadService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/service/ReloaderService.java diff --git a/custom/acme/loan/starter/src/test/java/com/acme/fineract/loan/starter/TestDefaultConfiguration.java b/custom/acme/loan/starter/src/test/java/com/acme/fineract/loan/starter/TestDefaultConfiguration.java index a49d06133..a503a713a 100644 --- a/custom/acme/loan/starter/src/test/java/com/acme/fineract/loan/starter/TestDefaultConfiguration.java +++ b/custom/acme/loan/starter/src/test/java/com/acme/fineract/loan/starter/TestDefaultConfiguration.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.mock; import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBBusinessStepServiceImpl; import org.apache.fineract.cob.domain.BatchBusinessStepRepository; +import org.apache.fineract.cob.service.ReloaderService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; @@ -48,9 +49,9 @@ public class TestDefaultConfiguration { @Bean public COBBusinessStepService cobBusinessStepService(BatchBusinessStepRepository batchBusinessStepRepository, ApplicationContext context, ListableBeanFactory beanFactory, BusinessEventNotifierService businessEventNotifierService, - ConfigurationDomainService configurationDomainService) { + ConfigurationDomainService configurationDomainService, ReloaderService reloaderService) { return new COBBusinessStepServiceImpl(batchBusinessStepRepository, context, beanFactory, businessEventNotifierService, - configurationDomainService); + configurationDomainService, reloaderService); } @Bean 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 index 51d1c7ee0..75cc8c896 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStepServiceImpl.java @@ -30,6 +30,7 @@ import org.apache.fineract.cob.data.BusinessStepNameAndOrder; import org.apache.fineract.cob.domain.BatchBusinessStep; import org.apache.fineract.cob.domain.BatchBusinessStepRepository; import org.apache.fineract.cob.exceptions.BusinessStepException; +import org.apache.fineract.cob.service.ReloaderService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.ActionContext; @@ -51,6 +52,8 @@ public class COBBusinessStepServiceImpl implements COBBusinessStepService { private final BusinessEventNotifierService businessEventNotifierService; private final ConfigurationDomainService configurationDomainService; + private final ReloaderService reloaderService; + @Override public , S extends AbstractPersistableCustom> S run(TreeMap executionMap, S item) { if (executionMap == null || executionMap.isEmpty()) { @@ -67,6 +70,7 @@ public class COBBusinessStepServiceImpl implements COBBusinessStepService { try { ThreadLocalContextUtil.setActionContext(ActionContext.COB); COBBusinessStep businessStepBean = (COBBusinessStep) applicationContext.getBean(businessStep); + item = reloaderService.reload(item); item = businessStepBean.execute(item); } catch (Exception e) { throw new BusinessStepException("Error happened during business step execution", e); diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanReloadService.java b/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanReloadService.java new file mode 100644 index 000000000..b4fb9f244 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanReloadService.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 lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class LoanReloadService implements ReloadService { + + private final LoanRepository loanRepository; + + @Override + public boolean canReload(S input) { + return input instanceof Loan; + } + + @Override + public Loan reload(Loan input) { + return loanRepository.findById(input.getId()).get(); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/service/ReloadService.java b/fineract-provider/src/main/java/org/apache/fineract/cob/service/ReloadService.java new file mode 100644 index 000000000..eec0033fc --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/service/ReloadService.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.service; + +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; + +public interface ReloadService { + + boolean canReload(X input); + + S reload(S input); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/service/ReloaderService.java b/fineract-provider/src/main/java/org/apache/fineract/cob/service/ReloaderService.java new file mode 100644 index 000000000..85905d1f3 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/service/ReloaderService.java @@ -0,0 +1,40 @@ +/** + * 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.List; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ReloaderService { + + private final List reloadServices; + + public S reload(S input) { + for (ReloadService reloadService : reloadServices) { + if (reloadService.canReload(input)) { + return (S) reloadService.reload(input); + } + } + return input; + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/COBBusinessStepServiceStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/cob/COBBusinessStepServiceStepDefinitions.java index 2d4c41614..d085ebc2e 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/cob/COBBusinessStepServiceStepDefinitions.java +++ b/fineract-provider/src/test/java/org/apache/fineract/cob/COBBusinessStepServiceStepDefinitions.java @@ -20,6 +20,7 @@ package org.apache.fineract.cob; import static org.junit.jupiter.api.Assertions.assertEquals; 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.mock; import static org.mockito.Mockito.verify; @@ -37,6 +38,7 @@ import org.apache.fineract.cob.domain.BatchBusinessStep; import org.apache.fineract.cob.domain.BatchBusinessStepRepository; import org.apache.fineract.cob.exceptions.BusinessStepException; import org.apache.fineract.cob.loan.LoanCOBBusinessStep; +import org.apache.fineract.cob.service.ReloaderService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom; import org.apache.fineract.infrastructure.core.domain.ActionContext; @@ -55,8 +57,10 @@ public class COBBusinessStepServiceStepDefinitions implements En { private BatchBusinessStepRepository batchBusinessStepRepository = mock(BatchBusinessStepRepository.class); private BusinessEventNotifierService businessEventNotifierService = mock(BusinessEventNotifierService.class); private ConfigurationDomainService configurationDomainService = mock(ConfigurationDomainService.class); + + private ReloaderService reloaderService = mock(ReloaderService.class); private final COBBusinessStepService businessStepService = new COBBusinessStepServiceImpl(batchBusinessStepRepository, - applicationContext, beanFactory, businessEventNotifierService, configurationDomainService); + applicationContext, beanFactory, businessEventNotifierService, configurationDomainService, reloaderService); private COBBusinessStep cobBusinessStep = mock(COBBusinessStep.class); private COBBusinessStep notRegistereCobBusinessStep = mock(COBBusinessStep.class); private TreeMap executionMap; @@ -92,6 +96,7 @@ public class COBBusinessStepServiceStepDefinitions implements En { lenient().when(this.applicationContext.getBean("test")).thenReturn(cobBusinessStep); lenient().when(this.applicationContext.getBean("notExist")).thenThrow(BeanCreationException.class); lenient().when(this.cobBusinessStep.execute(this.item)).thenReturn(outputItem); + lenient().when(this.reloaderService.reload(any())).thenAnswer(invocation -> invocation.getArgument(0)); lenient().when(this.configurationDomainService.isCOBBulkEventEnabled()).thenReturn(true); ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT); @@ -114,7 +119,7 @@ public class COBBusinessStepServiceStepDefinitions implements En { lenient().when(this.batchBusinessStepRepository.findAllByJobName("exist")) .thenReturn(Collections.singletonList(this.batchBusinessStep)); lenient().when(this.batchBusinessStepRepository.findAllByJobName("notExist")).thenReturn(Collections.emptyList()); - + lenient().when(this.reloaderService.reload(any())).thenAnswer(invocation -> invocation.getArgument(0)); lenient().when(this.beanFactory.getBeanNamesForType((Class) null)).thenReturn(new String[] { "notExist" }); lenient().when(this.beanFactory.getBeanNamesForType(Object.class)).thenReturn(new String[] { "testNotRegistered" }); lenient().when(this.beanFactory.getBeanNamesForType(String.class)).thenReturn(new String[] {}); diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/service/COBBulkEventConfigurationTest.java b/fineract-provider/src/test/java/org/apache/fineract/cob/service/COBBulkEventConfigurationTest.java index aec27ddfe..3089c61db 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/cob/service/COBBulkEventConfigurationTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/cob/service/COBBulkEventConfigurationTest.java @@ -69,11 +69,15 @@ public class COBBulkEventConfigurationTest { @InjectMocks private COBBusinessStepServiceImpl underTest; + @Mock + private ReloaderService reloaderService; + @BeforeEach public void setUp() { ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); ThreadLocalContextUtil .setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault())))); + when(reloaderService.reload(any())).thenAnswer(invocation -> invocation.getArgument(0)); } @AfterEach