1616
1717package org .springframework .boot .autoconfigure .ssl ;
1818
19- import java .net .URL ;
2019import java .nio .file .Path ;
21- import java .util .LinkedHashSet ;
20+ import java .util .ArrayList ;
21+ import java .util .List ;
2222import java .util .Map ;
2323import java .util .Set ;
2424import java .util .function .Function ;
25- import java .util .regex . Pattern ;
25+ import java .util .function . Supplier ;
2626import java .util .stream .Collectors ;
2727
2828import org .springframework .boot .ssl .SslBundle ;
2929import org .springframework .boot .ssl .SslBundleRegistry ;
30- import org .springframework .util .Assert ;
31- import org .springframework .util .ResourceUtils ;
32- import org .springframework .util .StringUtils ;
3330
3431/**
3532 * A {@link SslBundleRegistrar} that registers SSL bundles based
4138 */
4239class SslPropertiesBundleRegistrar implements SslBundleRegistrar {
4340
44- private static final Pattern PEM_CONTENT = Pattern .compile ("-+BEGIN\\ s+[^-]*-+" , Pattern .CASE_INSENSITIVE );
45-
4641 private final SslProperties .Bundles properties ;
4742
4843 private final FileWatcher fileWatcher ;
@@ -54,70 +49,58 @@ class SslPropertiesBundleRegistrar implements SslBundleRegistrar {
5449
5550 @ Override
5651 public void registerBundles (SslBundleRegistry registry ) {
57- registerBundles (registry , this .properties .getPem (), PropertiesSslBundle ::get , this ::getLocations );
58- registerBundles (registry , this .properties .getJks (), PropertiesSslBundle ::get , this ::getLocations );
52+ registerBundles (registry , this .properties .getPem (), PropertiesSslBundle ::get , this ::watchedPemPaths );
53+ registerBundles (registry , this .properties .getJks (), PropertiesSslBundle ::get , this ::watchedJksPaths );
5954 }
6055
6156 private <P extends SslBundleProperties > void registerBundles (SslBundleRegistry registry , Map <String , P > properties ,
62- Function <P , SslBundle > bundleFactory , Function <P , Set <Location >> locationsSupplier ) {
57+ Function <P , SslBundle > bundleFactory , Function <P , Set <Path >> watchedPaths ) {
6358 properties .forEach ((bundleName , bundleProperties ) -> {
64- SslBundle bundle = bundleFactory .apply (bundleProperties );
65- registry . registerBundle ( bundleName , bundle );
66- if ( bundleProperties . isReloadOnUpdate ()) {
67- Set < Path > paths = locationsSupplier . apply (bundleProperties )
68- . stream ()
69- . filter ( Location :: hasValue )
70- . map (( location ) -> toPath ( bundleName , location ))
71- . collect ( Collectors . toSet ());
72- this . fileWatcher . watch ( paths ,
73- () -> registry . updateBundle (bundleName , bundleFactory . apply ( bundleProperties )) );
59+ Supplier < SslBundle > bundleSupplier = () -> bundleFactory .apply (bundleProperties );
60+ try {
61+ registry . registerBundle ( bundleName , bundleSupplier . get ());
62+ if (bundleProperties . isReloadOnUpdate ()) {
63+ Supplier < Set < Path >> pathsSupplier = () -> watchedPaths . apply ( bundleProperties );
64+ watchForUpdates ( registry , bundleName , pathsSupplier , bundleSupplier );
65+ }
66+ }
67+ catch ( IllegalStateException ex ) {
68+ throw new IllegalStateException ( "Unable to register SSL bundle '%s'" . formatted (bundleName ), ex );
7469 }
7570 });
7671 }
7772
78- private Set <Location > getLocations (JksSslBundleProperties properties ) {
79- JksSslBundleProperties .Store keystore = properties .getKeystore ();
80- JksSslBundleProperties .Store truststore = properties .getTruststore ();
81- Set <Location > locations = new LinkedHashSet <>();
82- locations .add (new Location ("keystore.location" , keystore .getLocation ()));
83- locations .add (new Location ("truststore.location" , truststore .getLocation ()));
84- return locations ;
85- }
86-
87- private Set <Location > getLocations (PemSslBundleProperties properties ) {
88- PemSslBundleProperties .Store keystore = properties .getKeystore ();
89- PemSslBundleProperties .Store truststore = properties .getTruststore ();
90- Set <Location > locations = new LinkedHashSet <>();
91- locations .add (new Location ("keystore.private-key" , keystore .getPrivateKey ()));
92- locations .add (new Location ("keystore.certificate" , keystore .getCertificate ()));
93- locations .add (new Location ("truststore.private-key" , truststore .getPrivateKey ()));
94- locations .add (new Location ("truststore.certificate" , truststore .getCertificate ()));
95- return locations ;
96- }
97-
98- private Path toPath (String bundleName , Location watchableLocation ) {
99- String value = watchableLocation .value ();
100- String field = watchableLocation .field ();
101- Assert .state (!PEM_CONTENT .matcher (value ).find (),
102- () -> "SSL bundle '%s' '%s' is not a URL and can't be watched" .formatted (bundleName , field ));
73+ private void watchForUpdates (SslBundleRegistry registry , String bundleName , Supplier <Set <Path >> pathsSupplier ,
74+ Supplier <SslBundle > bundleSupplier ) {
10375 try {
104- URL url = ResourceUtils .getURL (value );
105- Assert .state ("file" .equalsIgnoreCase (url .getProtocol ()),
106- () -> "SSL bundle '%s' '%s' URL '%s' doesn't point to a file" .formatted (bundleName , field , url ));
107- return Path .of (url .toURI ()).toAbsolutePath ();
76+ this .fileWatcher .watch (pathsSupplier .get (), () -> registry .updateBundle (bundleName , bundleSupplier .get ()));
10877 }
109- catch (Exception ex ) {
110- throw new RuntimeException (
111- "SSL bundle '%s' '%s' location '%s' cannot be watched" .formatted (bundleName , field , value ), ex );
78+ catch (RuntimeException ex ) {
79+ throw new IllegalStateException ("Unable to watch for reload on update" , ex );
11280 }
11381 }
11482
115- private record Location (String field , String value ) {
83+ private Set <Path > watchedJksPaths (JksSslBundleProperties properties ) {
84+ List <BundleContentProperty > watched = new ArrayList <>();
85+ watched .add (new BundleContentProperty ("keystore.location" , properties .getKeystore ().getLocation ()));
86+ watched .add (new BundleContentProperty ("truststore.location" , properties .getTruststore ().getLocation ()));
87+ return watchedPaths (watched );
88+ }
11689
117- boolean hasValue () {
118- return StringUtils .hasText (this .value );
119- }
90+ private Set <Path > watchedPemPaths (PemSslBundleProperties properties ) {
91+ List <BundleContentProperty > watched = new ArrayList <>();
92+ watched .add (new BundleContentProperty ("keystore.private-key" , properties .getKeystore ().getPrivateKey ()));
93+ watched .add (new BundleContentProperty ("keystore.certificate" , properties .getKeystore ().getCertificate ()));
94+ watched .add (new BundleContentProperty ("truststore.private-key" , properties .getTruststore ().getPrivateKey ()));
95+ watched .add (new BundleContentProperty ("truststore.certificate" , properties .getTruststore ().getCertificate ()));
96+ return watchedPaths (watched );
97+ }
12098
99+ private Set <Path > watchedPaths (List <BundleContentProperty > properties ) {
100+ return properties .stream ()
101+ .filter (BundleContentProperty ::hasValue )
102+ .map (BundleContentProperty ::toWatchPath )
103+ .collect (Collectors .toSet ());
121104 }
122105
123106}
0 commit comments