22
22
import alpine .common .util .ByteFormat ;
23
23
import alpine .common .util .PathUtil ;
24
24
import alpine .common .util .SystemUtil ;
25
+ import io .smallrye .config .ConfigSourceInterceptor ;
26
+ import io .smallrye .config .ConfigSourceInterceptorContext ;
27
+ import io .smallrye .config .ConfigSourceInterceptorFactory ;
28
+ import io .smallrye .config .ExpressionConfigSourceInterceptor ;
29
+ import io .smallrye .config .Priorities ;
30
+ import io .smallrye .config .ProfileConfigSourceInterceptor ;
31
+ import io .smallrye .config .RelocateConfigSourceInterceptor ;
32
+ import io .smallrye .config .SmallRyeConfig ;
33
+ import io .smallrye .config .SmallRyeConfigBuilder ;
25
34
import org .apache .commons .lang3 .StringUtils ;
26
35
27
36
import java .io .File ;
28
- import java .io .FileNotFoundException ;
29
37
import java .io .IOException ;
30
38
import java .io .InputStream ;
31
39
import java .io .OutputStream ;
32
40
import java .nio .file .Files ;
33
- import java .util .Arrays ;
34
- import java .util .Collections ;
35
41
import java .util .HashMap ;
36
42
import java .util .List ;
37
43
import java .util .Map ;
44
+ import java .util .OptionalInt ;
38
45
import java .util .Properties ;
39
46
import java .util .UUID ;
40
-
47
+ import java .util .stream .Collectors ;
48
+
49
+ import static io .smallrye .config .PropertiesConfigSourceLoader .inFileSystem ;
50
+ import static io .smallrye .config .SmallRyeConfig .SMALLRYE_CONFIG_LOCATIONS ;
51
+ import static io .smallrye .config .SmallRyeConfig .SMALLRYE_CONFIG_LOG_VALUES ;
52
+ import static io .smallrye .config .SmallRyeConfig .SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN ;
53
+ import static io .smallrye .config .SmallRyeConfig .SMALLRYE_CONFIG_PROFILE ;
54
+ import static io .smallrye .config .SmallRyeConfig .SMALLRYE_CONFIG_PROFILE_PARENT ;
41
55
import static java .util .function .Predicate .not ;
42
56
43
57
/**
@@ -54,10 +68,10 @@ public class Config {
54
68
private static final String ALPINE_VERSION_PROP_FILE = "alpine.version" ;
55
69
private static final String APPLICATION_VERSION_PROP_FILE = "application.version" ;
56
70
private static final Config INSTANCE ;
57
- private static Properties properties ;
58
71
private static Properties alpineVersionProperties ;
59
72
private static Properties applicationVersionProperties ;
60
73
private static String systemId ;
74
+ private static org .eclipse .microprofile .config .Config delegateConfig ;
61
75
62
76
static {
63
77
LOGGER .info (StringUtils .repeat ("-" , 80 ));
@@ -218,40 +232,71 @@ public static Config getInstance() {
218
232
* Initialize the Config object. This method should only be called once.
219
233
*/
220
234
void init () {
221
- if (properties != null ) {
235
+ if (delegateConfig != null ) {
222
236
return ;
223
237
}
224
238
225
239
LOGGER .info ("Initializing Configuration" );
226
- properties = new Properties ();
227
-
240
+ final var configBuilder = new SmallRyeConfigBuilder ()
241
+ .forClassLoader (Thread .currentThread ().getContextClassLoader ())
242
+ // Enable default config sources:
243
+ //
244
+ // | Source | Priority |
245
+ // | :--------------------------------------------------- | :------- |
246
+ // | System properties | 400 |
247
+ // | Environment variables | 300 |
248
+ // | ${pwd}/.env file | 295 |
249
+ // | ${pwd}/config/application.properties | 260 |
250
+ // | ${classpath}/application.properties | 250 |
251
+ // | ${classpath}/META-INF/microprofile-config.properties | 100 |
252
+ //
253
+ // https://smallrye.io/smallrye-config/3.10.2/config/getting-started/#config-sources
254
+ .addDefaultSources ()
255
+ // Support expressions.
256
+ // https://smallrye.io/smallrye-config/3.10.2/config/expressions/
257
+ .withInterceptors (new ExpressionConfigSourceInterceptor ())
258
+ // Support profiles.
259
+ // https://smallrye.io/smallrye-config/3.10.2/config/profiles/
260
+ .withInterceptors (new ProfileConfigSourceInterceptor (List .of ("prod" , "dev" , "test" )))
261
+ // Relocate SmallRye properties to the Alpine prefix for better framework "immersion".
262
+ // https://smallrye.io/smallrye-config/3.10.2/extensions/relocate/
263
+ .withInterceptorFactories (new ConfigSourceInterceptorFactory () {
264
+
265
+ @ Override
266
+ public ConfigSourceInterceptor getInterceptor (final ConfigSourceInterceptorContext context ) {
267
+ // Properties to be relocated are documented here:
268
+ // https://smallrye.io/smallrye-config/3.10.2/config/configuration/
269
+ return new RelocateConfigSourceInterceptor (Map .ofEntries (
270
+ Map .entry (SMALLRYE_CONFIG_PROFILE , "alpine.config.profile" ),
271
+ Map .entry (SMALLRYE_CONFIG_PROFILE_PARENT , "alpine.config.profile.parent" ),
272
+ Map .entry (SMALLRYE_CONFIG_LOCATIONS , "alpine.config.locations" ),
273
+ Map .entry (SMALLRYE_CONFIG_LOG_VALUES , "alpine.config.log.values" ),
274
+ Map .entry (SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN , "alpine.config.mapping.validate-unknown" )));
275
+ }
276
+
277
+ @ Override
278
+ public OptionalInt getPriority () {
279
+ // Priority must be higher than that of ProfileConfigSourceInterceptor
280
+ // in order for profile property relocations to work.
281
+ return OptionalInt .of (Priorities .LIBRARY + 210 );
282
+ }
283
+
284
+ })
285
+ .withDefaultValue ("alpine.config.profile" , "prod" )
286
+ // Allow applications to customize the Config via SPI.
287
+ // https://smallrye.io/smallrye-config/3.10.2/config/customizer/
288
+ .addDiscoveredCustomizers ();
289
+
290
+ // If a custom properties file is specified via "alpine.application.properties" system property,
291
+ // register it as additional config source. The file has a higher priority than any of the default
292
+ // properties sources.
228
293
final String alpineAppProp = PathUtil .resolve (System .getProperty (ALPINE_APP_PROP ));
229
294
if (StringUtils .isNotBlank (alpineAppProp )) {
230
- LOGGER .info ("Loading application properties from " + alpineAppProp );
231
- try (InputStream fileInputStream = Files .newInputStream ((new File (alpineAppProp )).toPath ())) {
232
- properties .load (fileInputStream );
233
- } catch (FileNotFoundException e ) {
234
- LOGGER .error ("Could not find property file " + alpineAppProp );
235
- } catch (IOException e ) {
236
- LOGGER .error ("Unable to load " + alpineAppProp );
237
- }
238
- } else {
239
- LOGGER .info ("System property " + ALPINE_APP_PROP + " not specified" );
240
- LOGGER .info ("Loading " + PROP_FILE + " from classpath" );
241
- try (InputStream in = Thread .currentThread ().getContextClassLoader ().getResourceAsStream (PROP_FILE )) {
242
- if (in != null ) {
243
- properties .load (in );
244
- } else {
245
- LOGGER .error ("Unable to load (resourceStream is null) " + PROP_FILE );
246
- }
247
- } catch (IOException e ) {
248
- LOGGER .error ("Unable to load " + PROP_FILE );
249
- }
250
- }
251
- if (properties .size () == 0 ) {
252
- LOGGER .error ("A fatal error occurred loading application properties. Please correct the issue and restart the application." );
295
+ configBuilder .withSources (inFileSystem (alpineAppProp , 275 , Thread .currentThread ().getContextClassLoader ()));
253
296
}
254
297
298
+ delegateConfig = configBuilder .build ();
299
+
255
300
alpineVersionProperties = new Properties ();
256
301
try (InputStream in = Thread .currentThread ().getContextClassLoader ().getResourceAsStream (ALPINE_VERSION_PROP_FILE )) {
257
302
alpineVersionProperties .load (in );
@@ -313,6 +358,20 @@ void init() {
313
358
}
314
359
}
315
360
361
+ /**
362
+ * @since 5.6.0
363
+ */
364
+ public org .eclipse .microprofile .config .Config getDelegate () {
365
+ return delegateConfig ;
366
+ }
367
+
368
+ /**
369
+ * @since 5.6.0
370
+ */
371
+ public <T > T getMapping (final Class <T > mappingClass ) {
372
+ return delegateConfig .unwrap (SmallRyeConfig .class ).getConfigMapping (mappingClass );
373
+ }
374
+
316
375
/**
317
376
* Retrieves the path where the system.id is stored
318
377
* @return a File representing the path to the system.id
@@ -432,15 +491,8 @@ public File getDataDirectorty() {
432
491
* @since 1.0.0
433
492
*/
434
493
public String getProperty (Key key ) {
435
- final String envVariable = getPropertyFromEnvironment (key );
436
- if (envVariable != null ) {
437
- return envVariable ;
438
- }
439
- if (key .getDefaultValue () == null ) {
440
- return properties .getProperty (key .getPropertyName ());
441
- } else {
442
- return properties .getProperty (key .getPropertyName (), String .valueOf (key .getDefaultValue ()));
443
- }
494
+ return delegateConfig .getOptionalValue (key .getPropertyName (), String .class ).orElseGet (
495
+ () -> key .getDefaultValue () != null ? String .valueOf (key .getDefaultValue ()) : null );
444
496
}
445
497
446
498
/**
@@ -455,21 +507,17 @@ public String getProperty(Key key) {
455
507
* @since 1.7.0
456
508
*/
457
509
public String getPropertyOrFile (AlpineKey key ) {
458
- final AlpineKey fileKey = AlpineKey .valueOf (key .toString ()+"_FILE" );
459
- final String filePath = getProperty (fileKey );
460
- final String prop = getProperty (key );
461
- if (StringUtils .isNotBlank (filePath )) {
462
- if (prop != null && !prop .equals (String .valueOf (key .getDefaultValue ()))) {
463
- LOGGER .warn (fileKey .getPropertyName () + " overrides value from property " + key .getPropertyName ());
464
- }
465
- try {
466
- return new String (Files .readAllBytes (new File (PathUtil .resolve (filePath )).toPath ())).replaceAll ("\\ s+" , "" );
467
- } catch (IOException e ) {
468
- LOGGER .error (filePath + " file doesn't exist or not readable." );
469
- return null ;
470
- }
471
- }
472
- return prop ;
510
+ return delegateConfig .getOptionalValue (key .getPropertyName () + ".file" , String .class )
511
+ .map (filePath -> {
512
+ try {
513
+ return new String (Files .readAllBytes (new File (PathUtil .resolve (filePath )).toPath ())).replaceAll ("\\ s+" , "" );
514
+ } catch (IOException e ) {
515
+ LOGGER .error (filePath + " file doesn't exist or not readable." , e );
516
+ return null ;
517
+ }
518
+ })
519
+ .or (() -> delegateConfig .getOptionalValue (key .getPropertyName (), String .class ))
520
+ .orElse (null );
473
521
}
474
522
475
523
/**
@@ -520,15 +568,10 @@ public boolean getPropertyAsBoolean(Key key) {
520
568
* @since 2.2.5
521
569
*/
522
570
public List <String > getPropertyAsList (Key key ) {
523
- String property = getProperty (key );
524
- if (property == null ) {
525
- return Collections .emptyList ();
526
- } else {
527
- return Arrays .stream (property .split ("," ))
528
- .map (String ::trim )
529
- .filter (not (String ::isEmpty ))
530
- .toList ();
531
- }
571
+ return delegateConfig .getValues (key .getPropertyName (), String .class ).stream ()
572
+ .map (String ::trim )
573
+ .filter (not (String ::isEmpty ))
574
+ .collect (Collectors .toList ());
532
575
}
533
576
534
577
/**
@@ -551,82 +594,25 @@ public List<String> getPropertyAsList(Key key) {
551
594
*/
552
595
public Map <String , String > getPassThroughProperties (final String prefix ) {
553
596
final var passThroughProperties = new HashMap <String , String >();
554
- try {
555
- for (final Map .Entry <String , String > envVar : System .getenv ().entrySet ()) {
556
- if (envVar .getKey ().startsWith ("ALPINE_%s_" .formatted (prefix .toUpperCase ().replace ("." , "_" )))) {
557
- final String key = envVar .getKey ().replaceFirst ("^ALPINE_" , "" ).toLowerCase ().replace ("_" , "." );
558
- passThroughProperties .put (key , envVar .getValue ());
559
- }
560
- }
561
- } catch (SecurityException e ) {
562
- LOGGER .warn ("""
563
- Unable to retrieve pass-through properties for prefix "%s" \
564
- from environment variables. Using defaults.""" .formatted (prefix ), e );
565
- }
566
- for (final Map .Entry <Object , Object > property : properties .entrySet ()) {
567
- if (property .getKey () instanceof String key
568
- && key .startsWith ("alpine.%s." .formatted (prefix ))
569
- && property .getValue () instanceof final String value ) {
570
- key = key .replaceFirst ("^alpine\\ ." , "" );
571
- if (!passThroughProperties .containsKey (key )) { // Environment variables take precedence
572
- passThroughProperties .put (key , value );
573
- }
597
+ for (final String propertyName : delegateConfig .getPropertyNames ()) {
598
+ if (!propertyName .startsWith ("alpine.%s." .formatted (prefix ))) {
599
+ continue ;
574
600
}
601
+
602
+ final String key = propertyName .replaceFirst ("^alpine\\ ." , "" );
603
+ passThroughProperties .put (key , delegateConfig .getValue (propertyName , String .class ));
575
604
}
576
605
return passThroughProperties ;
577
606
}
578
607
579
- static void reset () {
580
- properties = null ;
581
- }
582
-
583
- /**
584
- * Return the configured value for the specified Key.
585
- * @param key The Key to return the configuration for
586
- * @return a String of the value of the configuration
587
- * @since 1.0.0
588
- * @deprecated use {{@link #getProperty(Key)}}
589
- */
590
- @ Deprecated
591
- public String getProperty (String key ) {
592
- return properties .getProperty (key );
593
- }
594
-
595
608
/**
596
- * Return the configured value for the specified Key.
597
- * @param key The String of the key to return the configuration for
598
- * @param defaultValue The default value if the key cannot be found
599
- * @return a String of the value of the configuration
600
- * @since 1.0.0
601
- * @deprecated use {{@link #getProperty(Key)}
602
- */
603
- @ Deprecated
604
- public String getProperty (String key , String defaultValue ) {
605
- return properties .getProperty (key , defaultValue );
606
- }
607
-
608
- /**
609
- * Attempts to retrieve the key via environment variable. Property names are
610
- * always upper case with periods replaced with underscores.
611
- *
612
- * alpine.worker.threads
613
- * becomes
614
- * ALPINE_WORKER_THREADS
609
+ * Reload the configuration.
615
610
*
616
- * @param key the key to retrieve from environment
617
- * @return the value of the key (if set), null otherwise.
618
- * @since 1.4.3
611
+ * @since 5.6.0
619
612
*/
620
- private String getPropertyFromEnvironment (Key key ) {
621
- final String envVariable = key .getPropertyName ().toUpperCase ().replace ("." , "_" );
622
- try {
623
- return StringUtils .trimToNull (System .getenv (envVariable ));
624
- } catch (SecurityException e ) {
625
- LOGGER .warn ("A security exception prevented access to the environment variable. Using defaults." );
626
- } catch (NullPointerException e ) {
627
- // Do nothing. The key was not specified in an environment variable. Continue along.
628
- }
629
- return null ;
613
+ static void reload () {
614
+ delegateConfig = null ;
615
+ INSTANCE .init ();
630
616
}
631
617
632
618
/**
0 commit comments