aboutsummaryrefslogtreecommitdiffstats
path: root/docs/development/property-configuration.rst
blob: d89b4769bc75b095c1e3aeae7c802a47ec2f0090 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
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 parameters into the Policy Framework Components.

Not Spring boot framework
*************************
The application should have a ParameterHandler class to support the map of values from Json to a POJO; so it should load the file and convert it; performing all type conversion.

The code below shown is 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 has to implement the **org.onap.policy.common.parameters.ParameterGroup** interface or eventually extend **org.onap.policy.common.parameters.ParameterGroupImpl**. The last one already implements the **validate()** method that performs error checking using validation **org.onap.policy.common.parameters.annotations**.

The code below shows an example of the 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 shown 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 the property file automatically and makes it available under the **org.springframework.core.env.Environment** Spring component.

Environment
+++++++++++
A component can use Environment component directly.

The Environment component is not a good approach because there is no type conversion or 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 field. There is no error checking, but it can assign a 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 where we need to include the properties in a POJO, as shown before, in a class that implements **ParameterGroup** interface, we need to add the **org.onap.policy.common.parameters.validation.ParameterGroupConstraint** annotation. That annotation is configured to use **ParameterGroupValidator**, which handles the conversion of a **org.onap.policy.common.parameters.BeanValidationResult** to a Spring validation.

The code below shows how to add the TopicParameterGroup parameter into acRuntimeParameterGroup:

.. 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 acRuntimeParameterGroup parameters;
      ....

      public void method() {
        .....
        long heartBeatMs = parameters.getHeartBeatMs();
        .....
      }

The code shown below, is an example of Unit Test validation of the POJO acRuntimeParameterGroup:

.. code-block:: java

   private ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();

   @Test
   void testParameters_NullTopicParameterGroup() {
       final acRuntimeParameterGroup parameters = CommonTestData.geParameterGroup();
       parameters.setTopicParameterGroup(null);
       assertThat(validatorFactory.getValidator().validate(parameters)).isNotEmpty();
   }