[FINERACT-1678] configuration validation for COBs

This commit is contained in:
taskain7
2022-08-09 09:30:37 +02:00
committed by Aleksandar Vidakovic
parent 9e949887b9
commit 47d5a85f17
10 changed files with 295 additions and 6 deletions
+1
View File
@@ -166,5 +166,6 @@ dependencies {
exclude group: 'javax.activation'
exclude group: 'org.skyscreamer'
}
testImplementation ('org.mockito:mockito-inline:4.6.1')
}
@@ -0,0 +1,31 @@
/**
* 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.infrastructure.core.config;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.ConditionContext;
public final class ExplicitConfigurationPropertiesFactory {
private ExplicitConfigurationPropertiesFactory() {}
public static <T> T getProperty(ConditionContext context, String propertyName, Class<T> type) {
return Binder.get(context.getEnvironment()).bind(propertyName, type).orElse(null);
}
}
@@ -19,10 +19,12 @@
package org.apache.fineract.infrastructure.core.config;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
@Slf4j
public class FineractModeValidationCondition implements Condition {
@Override
@@ -33,6 +35,11 @@ public class FineractModeValidationCondition implements Condition {
.orElse(true);
boolean isBatchModeEnabled = Optional.ofNullable(context.getEnvironment().getProperty("fineract.mode.batch-enabled", Boolean.class))
.orElse(true);
return !isReadModeEnabled && !isWriteModeEnabled && !isBatchModeEnabled;
boolean isValidationFails = !isReadModeEnabled && !isWriteModeEnabled && !isBatchModeEnabled;
if (isValidationFails) {
log.error(
"The Fineract instance type is not configured properly. At least one of these environment variables should be true: FINERACT_MODE_READ_ENABLED, FINERACT_MODE_WRITE_ENABLED, FINERACT_MODE_BATCH_ENABLED");
}
return isValidationFails;
}
}
@@ -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.infrastructure.core.config;
import java.util.List;
import java.util.function.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
@Slf4j
public class FineractPartitionJobConfigValidationCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
FineractProperties.FineractPartitionedJob partitionedJobProperties = ExplicitConfigurationPropertiesFactory.getProperty(context,
"fineract.partitioned-job", FineractProperties.FineractPartitionedJob.class);
if (partitionedJobProperties != null) {
List<FineractProperties.PartitionedJobProperty> invalidConfigs = partitionedJobProperties.getPartitionedJobProperties().stream()
.filter(isAnyConfigBelowOne().or(FineractPartitionJobConfigValidationCondition::isPartitionRatioInvalid)).toList();
if (!invalidConfigs.isEmpty()) {
for (FineractProperties.PartitionedJobProperty invalidConfig : invalidConfigs) {
log.error(
"{} partitioned job is not configured properly. The partition size, chunk size and thread count must be more than 0, and partition size must be less then chunk size * thread count",
invalidConfig.getJobName());
}
}
return !invalidConfigs.isEmpty();
} else {
return false;
}
}
private static Predicate<FineractProperties.PartitionedJobProperty> isAnyConfigBelowOne() {
return partitionedJobProperty -> !(partitionedJobProperty.getPartitionSize() > 0 && partitionedJobProperty.getChunkSize() > 0
&& partitionedJobProperty.getThreadCount() > 0);
}
private static boolean isPartitionRatioInvalid(FineractProperties.PartitionedJobProperty partitionedJobProperty) {
return partitionedJobProperty.getPartitionSize() < partitionedJobProperty.getThreadCount() * partitionedJobProperty.getChunkSize();
}
}
@@ -19,6 +19,7 @@
package org.apache.fineract.infrastructure.core.config;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -36,6 +37,8 @@ public class FineractProperties {
private FineractCorrelationProperties correlation;
private FineractPartitionedJob partitionedJob;
@Getter
@Setter
public static class FineractTenantProperties {
@@ -72,4 +75,22 @@ public class FineractProperties {
private boolean enabled;
private String headerName;
}
@Getter
@Setter
public static class FineractPartitionedJob {
// TODO should be used without wrapper class
private List<PartitionedJobProperty> partitionedJobProperties;
}
@Getter
@Setter
public static class PartitionedJobProperty {
private String jobName;
private Integer chunkSize;
private Integer partitionSize;
private Integer threadCount;
}
}
@@ -32,8 +32,8 @@ import org.springframework.stereotype.Component;
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@RequiredArgsConstructor
@Conditional(FineractModeValidationCondition.class)
public class FineractModeValidationConfig implements InitializingBean {
@Conditional(FineractValidationCondition.class)
public class FineractStartupValidationConfig implements InitializingBean {
private final ApplicationContext applicationContext;
@@ -43,8 +43,7 @@ public class FineractModeValidationConfig implements InitializingBean {
}
private void terminateApplication() {
log.error(
"The Fineract instance type is not configured properly. At least one of these environment variables should be true: FINERACT_MODE_READ_ENABLED, FINERACT_MODE_WRITE_ENABLED, FINERACT_MODE_BATCH_ENABLED");
((ConfigurableApplicationContext) this.applicationContext).close();
log.error("The application startup fails on validations. Please check the log above for the details");
((ConfigurableApplicationContext) applicationContext).close();
}
}
@@ -0,0 +1,35 @@
/**
* 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.infrastructure.core.config;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.context.annotation.Conditional;
public class FineractValidationCondition extends AnyNestedCondition {
public FineractValidationCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Conditional(FineractModeValidationCondition.class)
static class FineractModeValidation {}
@Conditional(FineractPartitionJobConfigValidationCondition.class)
static class FineractPartitionedJobValidation {}
}
@@ -43,6 +43,11 @@ fineract.mode.batch-manager-enabled=${FINERACT_MODE_BATCH_MANAGER_ENABLED:true}
fineract.correlation.enabled=${FINERACT_LOGGING_HTTP_CORRELATION_ID_ENABLED:false}
fineract.correlation.header-name=${FINERACT_LOGGING_HTTP_CORRELATION_ID_HEADER_NAME:X-Correlation-ID}
fineract.partitioned-job.partitioned-job-properties[0].job-name=LOAN_COB
fineract.partitioned-job.partitioned-job-properties[0].chunk-size=${LOAN_COB_CHUNK_SIZE:100}
fineract.partitioned-job.partitioned-job-properties[0].partition-size=${LOAN_COB_PARTITION_SIZE:100}
fineract.partitioned-job.partitioned-job-properties[0].thread-count=${LOAN_COB_THREAD_COUNT:1}
# Logging pattern for the console
logging.pattern.console=${CONSOLE_LOG_PATTERN:%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(%replace([%X{correlationId}]){'\\[\\]', ''}) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}}
@@ -0,0 +1,126 @@
/**
* 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.infrastructure.core.config;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class FineractPartitionJobConfigValidationConditionTest {
@InjectMocks
private FineractPartitionJobConfigValidationCondition testObj;
@Mock
private AnnotatedTypeMetadata metadata;
private ConditionContext context;
@BeforeEach
void setUp() {
context = mock(ConditionContext.class);
}
@Test
public void testApplicationStartup_ShouldApplicationStartupFails_WhenConfigurationNumbersAreBelowOne() {
try (MockedStatic<ExplicitConfigurationPropertiesFactory> propertyFactory = Mockito
.mockStatic(ExplicitConfigurationPropertiesFactory.class)) {
propertyFactory.when(() -> ExplicitConfigurationPropertiesFactory.getProperty(context, "fineract.partitioned-job",
FineractProperties.FineractPartitionedJob.class)).thenReturn(getBelowOneConfig());
assertTrue(testObj.matches(context, metadata));
}
}
@Test
public void testApplicationStartup_ShouldApplicationStartupFails_WhenPartitionSizeInvalid() {
try (MockedStatic<ExplicitConfigurationPropertiesFactory> propertyFactory = Mockito
.mockStatic(ExplicitConfigurationPropertiesFactory.class)) {
propertyFactory.when(() -> ExplicitConfigurationPropertiesFactory.getProperty(context, "fineract.partitioned-job",
FineractProperties.FineractPartitionedJob.class)).thenReturn(getInvalidPartitionSize());
assertTrue(testObj.matches(context, metadata));
}
}
@Test
public void testApplicationStartup_ShouldApplicationStart_WhenConfigValid() {
try (MockedStatic<ExplicitConfigurationPropertiesFactory> propertyFactory = Mockito
.mockStatic(ExplicitConfigurationPropertiesFactory.class)) {
propertyFactory.when(() -> ExplicitConfigurationPropertiesFactory.getProperty(context, "fineract.partitioned-job",
FineractProperties.FineractPartitionedJob.class)).thenReturn(getValidConfig());
assertFalse(testObj.matches(context, metadata));
}
}
private FineractProperties.FineractPartitionedJob getValidConfig() {
FineractProperties.FineractPartitionedJob partitionedJob = new FineractProperties.FineractPartitionedJob();
List<FineractProperties.PartitionedJobProperty> jobProperties = new ArrayList<>();
FineractProperties.PartitionedJobProperty partitionedJobProperty = new FineractProperties.PartitionedJobProperty();
partitionedJobProperty.setJobName("LOAN_COB");
partitionedJobProperty.setPartitionSize(100);
partitionedJobProperty.setChunkSize(10);
partitionedJobProperty.setThreadCount(10);
jobProperties.add(partitionedJobProperty);
partitionedJob.setPartitionedJobProperties(jobProperties);
return partitionedJob;
}
private FineractProperties.FineractPartitionedJob getInvalidPartitionSize() {
FineractProperties.FineractPartitionedJob partitionedJob = new FineractProperties.FineractPartitionedJob();
List<FineractProperties.PartitionedJobProperty> jobProperties = new ArrayList<>();
FineractProperties.PartitionedJobProperty partitionedJobProperty = new FineractProperties.PartitionedJobProperty();
partitionedJobProperty.setJobName("LOAN_COB");
partitionedJobProperty.setPartitionSize(99);
partitionedJobProperty.setChunkSize(10);
partitionedJobProperty.setThreadCount(10);
jobProperties.add(partitionedJobProperty);
partitionedJob.setPartitionedJobProperties(jobProperties);
return partitionedJob;
}
private FineractProperties.FineractPartitionedJob getBelowOneConfig() {
FineractProperties.FineractPartitionedJob partitionedJob = new FineractProperties.FineractPartitionedJob();
List<FineractProperties.PartitionedJobProperty> jobProperties = new ArrayList<>();
FineractProperties.PartitionedJobProperty partitionedJobProperty = new FineractProperties.PartitionedJobProperty();
partitionedJobProperty.setJobName("LOAN_COB");
partitionedJobProperty.setPartitionSize(0);
partitionedJobProperty.setChunkSize(1);
partitionedJobProperty.setThreadCount(1);
jobProperties.add(partitionedJobProperty);
partitionedJob.setPartitionedJobProperties(jobProperties);
return partitionedJob;
}
}
@@ -37,6 +37,11 @@ fineract.mode.read-enabled=true
fineract.mode.write-enabled=true
fineract.mode.batch-enabled=true
fineract.partitioned-job.partitioned-job-properties[0].job-name=LOAN_COB
fineract.partitioned-job.partitioned-job-properties[0].chunk-size=100
fineract.partitioned-job.partitioned-job-properties[0].partition-size=100
fineract.partitioned-job.partitioned-job-properties[0].thread-count=1
management.health.jms.enabled=false
# FINERACT 1296