Skip to content

Commit 29730a8

Browse files
author
Joey Yang
committed
pump version, improve code quality, fix watch '..data' symbolic bug
1 parent 9863173 commit 29730a8

File tree

7 files changed

+81
-48
lines changed

7 files changed

+81
-48
lines changed

README-zh.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ Maven
4242
<dependency>
4343
<groupId>top.code2life</groupId>
4444
<artifactId>spring-boot-dynamic-config</artifactId>
45-
<version>1.0.5</version>
45+
<version>1.0.6</version>
4646
</dependency>
4747
```
4848

4949
Gradle
5050

5151
```groovy
52-
implementation 'top.code2life:spring-boot-dynamic-config:1.0.5'
52+
implementation 'top.code2life:spring-boot-dynamic-config:1.0.6'
5353
```
5454

5555
### 步骤二:在代码中添加 @DynamicConfig 注解

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ Maven
4242
<dependency>
4343
<groupId>top.code2life</groupId>
4444
<artifactId>spring-boot-dynamic-config</artifactId>
45-
<version>1.0.5</version>
45+
<version>1.0.6</version>
4646
</dependency>
4747
```
4848

4949
Gradle
5050

5151
```groovy
52-
implementation 'top.code2life:spring-boot-dynamic-config:1.0.5'
52+
implementation 'top.code2life:spring-boot-dynamic-config:1.0.6'
5353
```
5454

5555
### Step2. Add @DynamicConfig Annotation

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ java {
1616
}
1717

1818
group = 'top.code2life'
19-
version = '1.0.5'
19+
version = '1.0.6'
2020
sourceCompatibility = JavaVersion.VERSION_1_8
2121

2222
repositories {

example/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@ dependencies {
2727
annotationProcessor 'org.projectlombok:lombok:1.18.20'
2828
implementation 'org.springframework.boot:spring-boot-starter-web'
2929

30-
implementation 'top.code2life:spring-boot-dynamic-config:1.0.5'
30+
implementation 'top.code2life:spring-boot-dynamic-config:1.0.6'
3131
}

src/main/java/top/code2life/config/ConfigurationChangedEvent.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ public class ConfigurationChangedEvent extends ApplicationEvent {
2525
*/
2626
private PropertySource<?> current;
2727

28-
ConfigurationChangedEvent(String path) {
28+
ConfigurationChangedEvent(String path, PropertySource<?> previous, PropertySource<?> current) {
2929
super(path);
30+
this.previous = previous;
31+
this.current = current;
3032
}
3133
}

src/main/java/top/code2life/config/ConfigurationUtils.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.springframework.util.ClassUtils;
66
import org.springframework.util.StringUtils;
77

8+
import java.nio.file.Paths;
89
import java.util.ArrayList;
910
import java.util.List;
1011
import java.util.Locale;
@@ -64,4 +65,35 @@ static Class<?> getTargetClassOfBean(Object bean) {
6465
return clazz;
6566
}
6667

68+
static String normalizePath(String path, String expectedBaseDir) {
69+
path = trimRelativePathAndReplaceBackSlash(path);
70+
expectedBaseDir = trimRelativePathAndReplaceBackSlash(expectedBaseDir);
71+
if (!path.startsWith(expectedBaseDir)) {
72+
String combined = Paths.get(expectedBaseDir, path).toString();
73+
return trimRelativePathAndReplaceBackSlash(combined);
74+
}
75+
return path;
76+
}
77+
78+
static String trimRelativePathAndReplaceBackSlash(String str) {
79+
if (!StringUtils.hasText(str)) {
80+
throw new IllegalArgumentException("wrong parameters when processing path");
81+
}
82+
str = str.toLowerCase();
83+
boolean beginWithRelative = str.length() > 2 && (str.startsWith("./") || str.startsWith(".\\"));
84+
if (beginWithRelative) {
85+
return str.substring(2).replaceAll("\\\\", "/");
86+
}
87+
return str;
88+
}
89+
90+
91+
static String getFileExtension(String path) {
92+
String extension = "";
93+
int i = path.lastIndexOf('.');
94+
if (i > 0) {
95+
extension = path.substring(i + 1);
96+
}
97+
return extension;
98+
}
6799
}

src/main/java/top/code2life/config/DynamicConfigPropertiesWatcher.java

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@
2222
import java.util.List;
2323
import java.util.Map;
2424
import java.util.concurrent.Executors;
25+
import java.util.concurrent.ScheduledFuture;
2526
import java.util.concurrent.TimeUnit;
2627
import 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

Comments
 (0)