| .. This work is licensed under a |
| .. Creative Commons Attribution 4.0 International License. |
| .. http://creativecommons.org/licenses/by/4.0 |
| |
| .. _property-configuration: |
| |
| Property-configuration mechanisms |
| ################################# |
| |
| .. contents:: |
| :depth: 3 |
| |
| 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: |
| |
| .. code-block:: java |
| |
| 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: |
| |
| .. code-block:: java |
| |
| @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: |
| |
| .. code-block:: java |
| |
| 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. |
| |
| .. code-block:: java |
| |
| @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. |
| |
| .. code-block:: java |
| |
| @Value("${security.enable-csrf:true}") |
| private boolean csrfEnabled = true; |
| |
| |
| The code below shows how to inject a value of a property into @Scheduled configuration. |
| |
| .. code-block:: java |
| |
| @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**. |
| |
| .. code-block:: java |
| |
| @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: |
| |
| .. code-block:: java |
| |
| @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: |
| |
| .. code-block:: java |
| |
| @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: |
| |
| .. code-block:: java |
| |
| private ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); |
| |
| @Test |
| void testParameters_NullTopicParameterGroup() { |
| final ClRuntimeParameterGroup parameters = CommonTestData.geParameterGroup(); |
| parameters.setTopicParameterGroup(null); |
| assertThat(validatorFactory.getValidator().validate(parameters)).isNotEmpty(); |
| } |