junit5 dependencies
[policy/parent.git] / docs / development / property-configuration.rst
1 .. This work is licensed under a
2 .. Creative Commons Attribution 4.0 International License.
3 .. http://creativecommons.org/licenses/by/4.0
4
5 .. _property-configuration:
6
7 Property-configuration mechanisms
8 #################################
9
10 .. contents::
11     :depth: 3
12
13 This article explains how to implement handling and validation of common parameters into the Policy Framework Components.
14
15 Not Spring boot framework
16 *************************
17 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.
18
19 The code below shown is an example of ParameterHandler:
20
21 .. code-block:: java
22
23    public class PapParameterHandler {
24
25        private static final Logger LOGGER = LoggerFactory.getLogger(PapParameterHandler.class);
26
27        private static final Coder CODER = new StandardCoder();
28
29     public PapParameterGroup getParameters(final PapCommandLineArguments arguments) throws PolicyPapException {
30            PapParameterGroup papParameterGroup = null;
31
32            try {
33                var file = new File(arguments.getFullConfigurationFilePath());
34                papParameterGroup = CODER.decode(file, PapParameterGroup.class);
35            } catch (final CoderException e) {
36                final String errorMessage = "error reading parameters from \"" + arguments.getConfigurationFilePath()
37                        + "\"\n" + "(" + e.getClass().getSimpleName() + ")";
38                throw new PolicyPapException(errorMessage, e);
39            }
40
41            if (papParameterGroup == null) {
42                final String errorMessage = "no parameters found in \"" + arguments.getConfigurationFilePath() + "\"";
43                LOGGER.error(errorMessage);
44                throw new PolicyPapException(errorMessage);
45            }
46
47            final ValidationResult validationResult = papParameterGroup.validate();
48            if (!validationResult.isValid()) {
49                String returnMessage =
50                        "validation error(s) on parameters from \"" + arguments.getConfigurationFilePath() + "\"\n";
51                returnMessage += validationResult.getResult();
52
53                LOGGER.error(returnMessage);
54                throw new PolicyPapException(returnMessage);
55            }
56
57            return papParameterGroup;
58        }
59    }
60
61
62 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**.
63
64 The code below shows an example of the POJO:
65
66 .. code-block:: java
67
68    @NotNull
69    @NotBlank
70    @Getter
71    public class PapParameterGroup extends ParameterGroupImpl {
72        @Valid
73        private RestServerParameters restServerParameters;
74        @Valid
75        private PdpParameters pdpParameters;
76        @Valid
77        private PolicyModelsProviderParameters databaseProviderParameters;
78        private boolean savePdpStatisticsInDb;
79        @Valid
80        private TopicParameterGroup topicParameterGroup;
81
82        private List<@NotNull @Valid RestClientParameters> healthCheckRestClientParameters;
83
84        public PapParameterGroup(final String name) {
85            super(name);
86        }
87    }
88
89
90 The code shown below, is an example of Unit Test validation of the POJO PapParameterGroup:
91
92 .. code-block:: java
93
94    private static final Coder coder = new StandardCoder();
95
96    @Test
97    void testPapParameterGroup_NullName() throws Exception {
98        String json = commonTestData.getPapParameterGroupAsString(1).replace("\"PapGroup\"", "null");
99        final PapParameterGroup papParameters = coder.decode(json, PapParameterGroup.class);
100        final ValidationResult validationResult = papParameters.validate();
101        assertFalse(validationResult.isValid());
102        assertEquals(null, papParameters.getName());
103        assertThat(validationResult.getResult()).contains("is null");
104    }
105
106
107 Using Spring boot framework
108 ***************************
109 Spring loads the property file automatically and makes it available under the **org.springframework.core.env.Environment** Spring component.
110
111 Environment
112 +++++++++++
113 A component can use Environment component directly.
114
115 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.
116
117 .. code-block:: java
118
119    @Component
120    @RequiredArgsConstructor
121    public class Example {
122
123    private Environment env;
124    ....
125
126    public void method(String pathPropertyName) {
127     .....
128     String path = env.getProperty(pathPropertyName);
129     .....
130    }
131
132 Annotation-based Spring configuration
133 +++++++++++++++++++++++++++++++++++++
134 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.
135 A documentation about SpEL could be found here: https://docs.spring.io/spring-framework/docs/3.0.x/reference/expressions.html.
136
137 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.
138
139 .. code-block:: java
140
141    @Value("${security.enable-csrf:true}")
142    private boolean csrfEnabled = true;
143
144
145 The code below shows how to inject a value of a property into @Scheduled configuration.
146
147 .. code-block:: java
148
149     @Scheduled(
150             fixedRateString = "${runtime.participantParameters.heartBeatMs}",
151             initialDelayString = "${runtime.participantParameters.heartBeatMs}")
152     public void schedule() {
153     }
154
155 ConfigurationProperties
156 +++++++++++++++++++++++
157 @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**.
158
159 .. code-block:: java
160
161    @Validated
162    @Getter
163    @Setter
164    @ConfigurationProperties(prefix = "runtime")
165    public class ClRuntimeParameterGroup {
166        @Min(100)
167        private long heartBeatMs;
168
169        @Valid
170        @Positive
171        private long reportingTimeIntervalMs;
172
173        @Valid
174        @NotNull
175        private ParticipantUpdateParameters updateParameters;
176
177        @NotBlank
178        private String description;
179    }
180
181 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.
182
183 The code below shows how to add the TopicParameterGroup parameter into acRuntimeParameterGroup:
184
185 .. code-block:: java
186
187    @NotNull
188    @ParameterGroupConstraint
189    private TopicParameterGroup topicParameterGroup;
190
191
192 A bean configured with ConfigurationProperties, is automatically a Spring component and could be injected into other Spring components. The code below shown an example:
193
194 .. code-block:: java
195
196    @Component
197    @RequiredArgsConstructor
198    public class Example {
199
200       private acRuntimeParameterGroup parameters;
201       ....
202
203       public void method() {
204         .....
205         long heartBeatMs = parameters.getHeartBeatMs();
206         .....
207       }
208
209 The code shown below, is an example of Unit Test validation of the POJO acRuntimeParameterGroup:
210
211 .. code-block:: java
212
213    private ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
214
215    @Test
216    void testParameters_NullTopicParameterGroup() {
217        final acRuntimeParameterGroup parameters = CommonTestData.geParameterGroup();
218        parameters.setTopicParameterGroup(null);
219        assertThat(validatorFactory.getValidator().validate(parameters)).isNotEmpty();
220    }