.. 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();
   }