Property-configuration mechanisms

This article explains how to implement handling and validation of common parameter into the Policy Framework Components.

Not Spring boot framework

The application should have a ParameterHandler class to support the map values from Json to a POJO, so it should be load the file, convert it performing all type conversion.

The code below shown an example of ParameterHandler:

public class PapParameterHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(PapParameterHandler.class);

    private static final Coder CODER = new StandardCoder();

 public PapParameterGroup getParameters(final PapCommandLineArguments arguments) throws PolicyPapException {
        PapParameterGroup papParameterGroup = null;

        try {
            var file = new File(arguments.getFullConfigurationFilePath());
            papParameterGroup = CODER.decode(file, PapParameterGroup.class);
        } catch (final CoderException e) {
            final String errorMessage = "error reading parameters from \"" + arguments.getConfigurationFilePath()
                    + "\"\n" + "(" + e.getClass().getSimpleName() + ")";
            throw new PolicyPapException(errorMessage, e);
        }

        if (papParameterGroup == null) {
            final String errorMessage = "no parameters found in \"" + arguments.getConfigurationFilePath() + "\"";
            LOGGER.error(errorMessage);
            throw new PolicyPapException(errorMessage);
        }

        final ValidationResult validationResult = papParameterGroup.validate();
        if (!validationResult.isValid()) {
            String returnMessage =
                    "validation error(s) on parameters from \"" + arguments.getConfigurationFilePath() + "\"\n";
            returnMessage += validationResult.getResult();

            LOGGER.error(returnMessage);
            throw new PolicyPapException(returnMessage);
        }

        return papParameterGroup;
    }
}

The POJO have to implement org.onap.policy.common.parameters.ParameterGroup interface or eventually extend org.onap.policy.common.parameters.ParameterGroupImpl. The last one already implements validate() method that performs error checking using validation org.onap.policy.common.parameters.annotations.

The code below shown an example of POJO:

@NotNull
@NotBlank
@Getter
public class PapParameterGroup extends ParameterGroupImpl {
    @Valid
    private RestServerParameters restServerParameters;
    @Valid
    private PdpParameters pdpParameters;
    @Valid
    private PolicyModelsProviderParameters databaseProviderParameters;
    private boolean savePdpStatisticsInDb;
    @Valid
    private TopicParameterGroup topicParameterGroup;

    private List<@NotNull @Valid RestClientParameters> healthCheckRestClientParameters;

    public PapParameterGroup(final String name) {
        super(name);
    }
}

The code shows below, is an example of Unit Test validation of the POJO PapParameterGroup:

private static final Coder coder = new StandardCoder();

@Test
void testPapParameterGroup_NullName() throws Exception {
    String json = commonTestData.getPapParameterGroupAsString(1).replace("\"PapGroup\"", "null");
    final PapParameterGroup papParameters = coder.decode(json, PapParameterGroup.class);
    final ValidationResult validationResult = papParameters.validate();
    assertFalse(validationResult.isValid());
    assertEquals(null, papParameters.getName());
    assertThat(validationResult.getResult()).contains("is null");
}

Using Spring boot framework

Spring loads automatically the property file and put it available under the org.springframework.core.env.Environment Spring component.

Environment

A component can use Environment component directly.

Environment component is not a good approach because there is not type conversion and error checking, but it could be useful when the name of the property you need to access changes dynamically.

@Component
@RequiredArgsConstructor
public class Example {

private Environment env;
....

public void method(String pathPropertyName) {
 .....
 String path = env.getProperty(pathPropertyName);
 .....
}

Annotation-based Spring configuration

All annotation-based Spring configurations support the Spring Expression Language (SpEL), a powerful expression language that supports querying and manipulating an object graph at runtime. A documentation about SpEL could be found here: https://docs.spring.io/spring-framework/docs/3.0.x/reference/expressions.html.

A component can use org.springframework.beans.factory.annotation.Value, which reads from properties, performs a type conversion and injects the value into the filed. There is not error checking, but it can assign default value if the property is not defined.

@Value("${security.enable-csrf:true}")
private boolean csrfEnabled = true;

The code below shows how to inject a value of a property into @Scheduled configuration.

@Scheduled(
        fixedRateString = "${runtime.participantParameters.heartBeatMs}",
        initialDelayString = "${runtime.participantParameters.heartBeatMs}")
public void schedule() {
}

ConfigurationProperties

@ConfigurationProperties can be used to map values from .properties( .yml also supported) to a POJO. It performs all type conversion and error checking using validation javax.validation.constraints.

@Validated
@Getter
@Setter
@ConfigurationProperties(prefix = "runtime")
public class ClRuntimeParameterGroup {
    @Min(100)
    private long heartBeatMs;

    @Valid
    @Positive
    private long reportingTimeIntervalMs;

    @Valid
    @NotNull
    private ParticipantUpdateParameters updateParameters;

    @NotBlank
    private String description;
}

In a scenario that we need to include into a POJO shown before, a class that implement ParameterGroup interface, we need to add the org.onap.policy.common.parameters.validation.ParameterGroupConstraint annotation. That annotation is configured to use ParameterGroupValidator that handles the conversion of a org.onap.policy.common.parameters.BeanValidationResult to a Spring validation.

The code below shown how to add TopicParameterGroup parameter into ClRuntimeParameterGroup:

@NotNull
@ParameterGroupConstraint
private TopicParameterGroup topicParameterGroup;

A bean configured with ConfigurationProperties, is automatically a Spring component and could be injected into other Spring components. The code below shown an example:

@Component
@RequiredArgsConstructor
public class Example {

   private ClRuntimeParameterGroup parameters;
   ....

   public void method() {
     .....
     long heartBeatMs = parameters.getHeartBeatMs();
     .....
   }

The code shows below, is an example of Unit Test validation of the POJO ClRuntimeParameterGroup:

private ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();

@Test
void testParameters_NullTopicParameterGroup() {
    final ClRuntimeParameterGroup parameters = CommonTestData.geParameterGroup();
    parameters.setTopicParameterGroup(null);
    assertThat(validatorFactory.getValidator().validate(parameters)).isNotEmpty();
}