2222import java .util .List ;
2323import java .util .Map ;
2424import java .util .concurrent .Executors ;
25+ import java .util .concurrent .ScheduledFuture ;
2526import java .util .concurrent .TimeUnit ;
2627import java .util .stream .Stream ;
2728
29+ import static top .code2life .config .ConfigurationUtils .normalizePath ;
30+ import static top .code2life .config .ConfigurationUtils .trimRelativePathAndReplaceBackSlash ;
31+
2832/**
2933 * Enhance PropertySource when spring.config.location is specified, it will start directory-watch,
3034 * listening any changes on configuration files, then publish ConfigurationChangedEvent.
@@ -108,7 +112,7 @@ private void normalizeAndRecordPropSource(PropertySource<?> ps) {
108112 if (pathStr .contains (FILE_COLON_SYMBOL )) {
109113 pathStr = pathStr .replace (FILE_COLON_SYMBOL , "" );
110114 }
111- PROPERTY_SOURCE_META_MAP .put (pathStr . replaceAll ( " \\ \\ " , "/" ), new PropertySourceMeta (ps , Paths .get (pathStr ), 0L ));
115+ PROPERTY_SOURCE_META_MAP .put (trimRelativePathAndReplaceBackSlash ( pathStr ), new PropertySourceMeta (ps , Paths .get (pathStr ), 0L ));
112116 log .debug ("configuration file found: {}" , pathStr );
113117 }
114118
@@ -124,7 +128,7 @@ private void startWatchDir() {
124128 Thread .sleep (50 );
125129 for (WatchEvent <?> event : key .pollEvents ()) {
126130 Path path = (Path ) event .context ();
127- reloadChangedFile (path , false );
131+ reloadChangedFile (path . toString () , false );
128132
129133 }
130134 key .reset ();
@@ -137,22 +141,25 @@ private void startWatchDir() {
137141 }
138142 }
139143
140- @ SuppressWarnings ("AlibabaThreadPoolCreation" )
141144 private void checkChangesWithPeriod () throws IOException {
142145 Path symLinkPath = Paths .get (configLocation , SYMBOL_LINK_DIR );
143146 boolean hasDotDataLinkFile = new File (configLocation , SYMBOL_LINK_DIR ).exists ();
144147 if (hasDotDataLinkFile ) {
145148 log .info ("ConfigMap/Secret mode detected, will polling symbolic link instead." );
146149 symbolicLinkModifiedTime = Files .getLastModifiedTime (symLinkPath , LinkOption .NOFOLLOW_LINKS ).toMillis ();
147- Executors .newSingleThreadScheduledExecutor (r -> new Thread (r , POLLING_THREAD )).scheduleWithFixedDelay (
148- this ::checkSymbolicLink , SYMBOL_LINK_POLLING_INTERVAL , SYMBOL_LINK_POLLING_INTERVAL , TimeUnit .MILLISECONDS );
150+ startFixedRateCheckThread (this ::checkSymbolicLink , SYMBOL_LINK_POLLING_INTERVAL );
149151 } else {
150152 // longer check for all config files, make up mechanism if WatchService doesn't work
151- Executors .newSingleThreadScheduledExecutor (r -> new Thread (r , POLLING_THREAD )).scheduleWithFixedDelay (
152- this ::reloadAllConfigFiles , NORMAL_FILE_POLLING_INTERVAL , NORMAL_FILE_POLLING_INTERVAL , TimeUnit .MILLISECONDS );
153+ startFixedRateCheckThread (this ::reloadAllConfigFiles , NORMAL_FILE_POLLING_INTERVAL );
153154 }
154155 }
155156
157+ @ SuppressWarnings ("AlibabaThreadPoolCreation" )
158+ private ScheduledFuture <?> startFixedRateCheckThread (Runnable cmd , long interval ) {
159+ return Executors .newSingleThreadScheduledExecutor (r -> new Thread (r , POLLING_THREAD ))
160+ .scheduleWithFixedDelay (cmd , interval , interval , TimeUnit .MILLISECONDS );
161+ }
162+
156163 private void checkSymbolicLink () {
157164 try {
158165 Path symLinkPath = Paths .get (configLocation , SYMBOL_LINK_DIR );
@@ -172,71 +179,63 @@ private void reloadAllConfigFiles() {
172179
173180 private void reloadAllConfigFiles (boolean forceReload ) {
174181 try (Stream <Path > paths = Files .walk (Paths .get (configLocation ))) {
175- paths .forEach ((path ) -> {
176- if (!Files .isDirectory (path )) {
177- reloadChangedFile (path , forceReload );
178- }
182+ paths .filter (path -> !Files .isDirectory (path )).forEach ((path ) -> {
183+ reloadChangedFile (path .toString (), forceReload );
179184 });
180185 } catch (IOException e ) {
181186 log .warn ("can not walk through config directory: {}" , e .getMessage ());
182187 }
183188 }
184189
185- private void reloadChangedFile (Path path , boolean forceReload ) {
186- if (SYMBOL_LINK_DIR .equals (path .toString ())) {
190+ private void reloadChangedFile (String rawPath , boolean forceReload ) {
191+ String absPathStr = normalizePath (rawPath , configLocation );
192+ Path path = Paths .get (absPathStr );
193+ if (SYMBOL_LINK_DIR .equals (path .getFileName ().toString ())) {
187194 return ;
188195 }
189- Path absPath = null ;
190196 try {
191- absPath = Paths .get (configLocation , path .toString ());
192- String absPathStr = absPath .toString ();
193- // remove ./ or .\ at the beginning of the path
194- boolean beginWithRelative = absPathStr .length () > 2 && (absPathStr .startsWith ("./" ) || absPathStr .startsWith (".\\ " ));
195- if (beginWithRelative ) {
196- absPathStr = absPathStr .substring (2 );
197- }
198- PropertySourceMeta propertySourceMeta = PROPERTY_SOURCE_META_MAP .get (absPathStr .replaceAll ("\\ \\ " , "/" ));
197+ PropertySourceMeta propertySourceMeta = PROPERTY_SOURCE_META_MAP .get (absPathStr );
199198 if (propertySourceMeta == null ) {
200199 log .debug ("changed file at config location is not recognized: {}" , absPathStr );
201200 return ;
202201 }
203- long currentModTs = Files .getLastModifiedTime (absPath ).toMillis ();
202+ long currentModTs = Files .getLastModifiedTime (path ).toMillis ();
204203 long mdt = propertySourceMeta .getLastModifyTime ();
205204 if (forceReload || mdt != currentModTs ) {
206205 doReloadConfigFile (propertySourceMeta , absPathStr , currentModTs );
207206 }
208207 } catch (Exception ex ) {
209- log .error ("reload configuration file {} failed: " , absPath , ex );
208+ log .error ("reload configuration file {} failed: " , absPathStr , ex );
210209 }
211210 }
212211
213212 private void doReloadConfigFile (PropertySourceMeta propertySourceMeta , String path , long modifyTime ) throws IOException {
214213 log .info ("config file has been changed: {}" , path );
215- String extension = "" ;
216- int i = path .lastIndexOf ('.' );
217- if (i > 0 ) {
218- extension = path .substring (i + 1 );
219- }
220- String propertySourceName = propertySourceMeta .getPropertySource ().getName ();
221- ConfigurationChangedEvent event = new ConfigurationChangedEvent (path );
222- FileSystemResource resource = new FileSystemResource (path );
214+ String extension = ConfigurationUtils .getFileExtension (path );
223215 for (PropertySourceLoader loader : propertyLoaders ) {
224216 if (Arrays .asList (loader .getFileExtensions ()).contains (extension )) {
225217 // use this loader to load config resource
226- List <PropertySource <?>> newPropsList = loader .load (propertySourceName , resource );
227- if (newPropsList .size () >= 1 ) {
228- PropertySource <?> newProps = newPropsList .get (0 );
229- event .setPrevious (env .getPropertySources ().get (propertySourceName ));
230- event .setCurrent (newProps );
231- env .getPropertySources ().replace (propertySourceName , newProps );
232- propertySourceMeta .setLastModifyTime (modifyTime );
233- eventPublisher .publishEvent (event );
234- }
218+ loadPropertiesAndPublishEvent (propertySourceMeta , loader , path , modifyTime );
235219 break ;
236220 }
237221 }
238222 }
239223
224+ private void loadPropertiesAndPublishEvent (PropertySourceMeta propertySourceMeta , PropertySourceLoader loader , String path , long modifyTime ) throws IOException {
225+ FileSystemResource resource = new FileSystemResource (path );
226+ String propertySourceName = propertySourceMeta .getPropertySource ().getName ();
227+ List <PropertySource <?>> newPropsList = loader .load (propertySourceName , resource );
228+ if (newPropsList .size () < 1 ) {
229+ log .warn ("properties not loaded after config changed: {}" , path );
230+ return ;
231+ }
232+ PropertySource <?> newProps = newPropsList .get (0 );
233+ ConfigurationChangedEvent event = new ConfigurationChangedEvent (path , env .getPropertySources ().get (propertySourceName ), newProps );
234+ env .getPropertySources ().replace (propertySourceName , newProps );
235+ propertySourceMeta .setLastModifyTime (modifyTime );
236+ eventPublisher .publishEvent (event );
237+ }
238+
240239 private void closeConfigDirectoryWatch () {
241240 if (watchService != null ) {
242241 try {
0 commit comments