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 that class:
public class PapParameterHandler { private static final Logger LOGGER = LoggerFactory.getLogger(PapParameterHandler.class); private static final Coder CODER = new StandardCoder(); /** * Read the parameters from the parameter file. * * @param arguments the arguments passed to policy pap * @return the parameters read from the configuration file * @throws PolicyPapException on parameter exceptions */ public PapParameterGroup getParameters(final PapCommandLineArguments arguments) throws PolicyPapException { PapParameterGroup papParameterGroup = null; // Read the parameters try { // Read the parameters from JSON 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); } // The JSON processing returns null if there is an empty file if (papParameterGroup == null) { final String errorMessage = "no parameters found in \"" + arguments.getConfigurationFilePath() + "\""; LOGGER.error(errorMessage); throw new PolicyPapException(errorMessage); } // validate the parameters 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; // API, Distribution Health Check REST client parameters. private List<@NotNull @Valid RestClientParameters> healthCheckRestClientParameters; /** * Create the pap parameter group. * * @param name the parameter group name */ public PapParameterGroup(final String name) { super(name); } }
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(); }
This page is Work in Progress