Evaluate property-configuration mechanisms 17/125217/2
authorFrancescoFioraEst <francesco.fiora@est.tech>
Thu, 21 Oct 2021 08:32:08 +0000 (09:32 +0100)
committerFrancescoFioraEst <francesco.fiora@est.tech>
Fri, 22 Oct 2021 08:12:32 +0000 (09:12 +0100)
Issue-ID: POLICY-1107
Change-Id: I549b6cb67eb4d0c22698560d7db64c119e640ef4
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
docs/development/development.rst
docs/development/property-configuration.rst [new file with mode: 0644]

index b0eef5d..4da6f51 100644 (file)
@@ -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 (file)
index 0000000..39691ad
--- /dev/null
@@ -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();
+   }