From de77722a37e9899244f0203191019b73c8a946ad Mon Sep 17 00:00:00 2001 From: FrancescoFioraEst Date: Thu, 21 Oct 2021 09:32:08 +0100 Subject: [PATCH] Evaluate property-configuration mechanisms Issue-ID: POLICY-1107 Change-Id: I549b6cb67eb4d0c22698560d7db64c119e640ef4 Signed-off-by: FrancescoFioraEst --- docs/development/development.rst | 1 + docs/development/property-configuration.rst | 220 ++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 docs/development/property-configuration.rst diff --git a/docs/development/development.rst b/docs/development/development.rst index b0eef5d6..4da6f51a 100644 --- a/docs/development/development.rst +++ b/docs/development/development.rst @@ -11,3 +11,4 @@ Policy Platform Development devtools/devtools.rst pdp/pdp-pap-interaction.rst actors/actors.rst + property-configuration.rst diff --git a/docs/development/property-configuration.rst b/docs/development/property-configuration.rst new file mode 100644 index 00000000..39691ade --- /dev/null +++ b/docs/development/property-configuration.rst @@ -0,0 +1,220 @@ +.. 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(); + } -- 2.16.6