22
22
import java .util .Collection ;
23
23
import java .util .Collections ;
24
24
import java .util .HashMap ;
25
+ import java .util .LinkedHashMap ;
25
26
import java .util .List ;
26
27
import java .util .Map ;
27
28
import java .util .stream .Collectors ;
55
56
import org .springframework .boot .actuate .endpoint .annotation .ReadOperation ;
56
57
import org .springframework .boot .context .properties .ConfigurationProperties ;
57
58
import org .springframework .boot .context .properties .ConfigurationPropertiesBean ;
59
+ import org .springframework .boot .context .properties .ConfigurationPropertiesBoundPropertiesHolder ;
58
60
import org .springframework .boot .context .properties .ConstructorBinding ;
61
+ import org .springframework .boot .context .properties .source .ConfigurationProperty ;
62
+ import org .springframework .boot .context .properties .source .ConfigurationPropertyName ;
59
63
import org .springframework .context .ApplicationContext ;
60
64
import org .springframework .context .ApplicationContextAware ;
61
65
import org .springframework .core .KotlinDetector ;
77
81
* @author Christian Dupuis
78
82
* @author Dave Syer
79
83
* @author Stephane Nicoll
84
+ * @author Madhura Bhave
85
+ * @author Andy Wilkinson
80
86
* @since 2.0.0
81
87
*/
82
88
@ Endpoint (id = "configprops" )
@@ -120,8 +126,10 @@ private ContextConfigurationProperties describeConfigurationProperties(Applicati
120
126
Map <String , ConfigurationPropertiesBeanDescriptor > descriptors = new HashMap <>();
121
127
beans .forEach ((beanName , bean ) -> {
122
128
String prefix = bean .getAnnotation ().prefix ();
123
- descriptors .put (beanName , new ConfigurationPropertiesBeanDescriptor (prefix ,
124
- sanitize (prefix , safeSerialize (mapper , bean .getInstance (), prefix ))));
129
+ descriptors .put (beanName ,
130
+ new ConfigurationPropertiesBeanDescriptor (prefix ,
131
+ sanitize (prefix , safeSerialize (mapper , bean .getInstance (), prefix )),
132
+ getInputs (prefix , safeSerialize (mapper , bean .getInstance (), prefix ))));
125
133
});
126
134
return new ContextConfigurationProperties (descriptors ,
127
135
(context .getParent () != null ) ? context .getParent ().getId () : null );
@@ -229,6 +237,67 @@ else if (item instanceof List) {
229
237
return sanitized ;
230
238
}
231
239
240
+ @ SuppressWarnings ("unchecked" )
241
+ private Map <String , Object > getInputs (String prefix , Map <String , Object > map ) {
242
+ map .forEach ((key , value ) -> {
243
+ String qualifiedKey = (prefix .isEmpty () ? prefix : prefix + "." ) + key ;
244
+ if (value instanceof Map ) {
245
+ map .put (key , getInputs (qualifiedKey , (Map <String , Object >) value ));
246
+ }
247
+ else if (value instanceof List ) {
248
+ map .put (key , getInputs (qualifiedKey , (List <Object >) value ));
249
+ }
250
+ else {
251
+ map .put (key , applyInput (qualifiedKey ));
252
+ }
253
+ });
254
+ return map ;
255
+ }
256
+
257
+ @ SuppressWarnings ("unchecked" )
258
+ private List <Object > getInputs (String prefix , List <Object > list ) {
259
+ List <Object > augmented = new ArrayList <>();
260
+ int index = 0 ;
261
+ for (Object item : list ) {
262
+ String name = prefix + "[" + index ++ + "]" ;
263
+ if (item instanceof Map ) {
264
+ augmented .add (getInputs (name , (Map <String , Object >) item ));
265
+ }
266
+ else if (item instanceof List ) {
267
+ augmented .add (getInputs (name , (List <Object >) item ));
268
+ }
269
+ else {
270
+ augmented .add (applyInput (name ));
271
+ }
272
+ }
273
+ return augmented ;
274
+ }
275
+
276
+ private Map <String , Object > applyInput (String qualifiedKey ) {
277
+ if (!this .context .containsBean (ConfigurationPropertiesBoundPropertiesHolder .BEAN_NAME )) {
278
+ return Collections .emptyMap ();
279
+ }
280
+ ConfigurationPropertiesBoundPropertiesHolder bean = this .context .getBean (
281
+ ConfigurationPropertiesBoundPropertiesHolder .BEAN_NAME ,
282
+ ConfigurationPropertiesBoundPropertiesHolder .class );
283
+ Map <ConfigurationPropertyName , ConfigurationProperty > boundProperties = bean .getProperties ();
284
+ ConfigurationPropertyName currentName = ConfigurationPropertyName .adapt (qualifiedKey , '.' );
285
+ ConfigurationProperty candidate = boundProperties .get (currentName );
286
+ if (candidate == null && currentName .isLastElementIndexed ()) {
287
+ candidate = boundProperties .get (currentName .chop (currentName .getNumberOfElements () - 1 ));
288
+ }
289
+ return (candidate != null ) ? getInput (currentName .toString (), candidate ) : Collections .emptyMap ();
290
+ }
291
+
292
+ private Map <String , Object > getInput (String property , ConfigurationProperty candidate ) {
293
+ Map <String , Object > input = new LinkedHashMap <>();
294
+ String origin = (candidate .getOrigin () != null ) ? candidate .getOrigin ().toString () : "none" ;
295
+ Object value = candidate .getValue ();
296
+ input .put ("origin" , origin );
297
+ input .put ("value" , this .sanitizer .sanitize (property , value ));
298
+ return input ;
299
+ }
300
+
232
301
/**
233
302
* Extension to {@link JacksonAnnotationIntrospector} to suppress CGLIB generated bean
234
303
* properties.
@@ -457,9 +526,13 @@ public static final class ConfigurationPropertiesBeanDescriptor {
457
526
458
527
private final Map <String , Object > properties ;
459
528
460
- private ConfigurationPropertiesBeanDescriptor (String prefix , Map <String , Object > properties ) {
529
+ private final Map <String , Object > inputs ;
530
+
531
+ private ConfigurationPropertiesBeanDescriptor (String prefix , Map <String , Object > properties ,
532
+ Map <String , Object > inputs ) {
461
533
this .prefix = prefix ;
462
534
this .properties = properties ;
535
+ this .inputs = inputs ;
463
536
}
464
537
465
538
public String getPrefix () {
@@ -470,6 +543,10 @@ public Map<String, Object> getProperties() {
470
543
return this .properties ;
471
544
}
472
545
546
+ public Map <String , Object > getInputs () {
547
+ return this .inputs ;
548
+ }
549
+
473
550
}
474
551
475
552
}
0 commit comments