add settings refresher

This commit is contained in:
Jörg Prante 2020-04-22 16:12:25 +02:00
parent ec7fd49baa
commit 0e37917c47
5 changed files with 105 additions and 26 deletions

View file

@ -15,6 +15,8 @@ import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.DateTimeException; import java.time.DateTimeException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -24,23 +26,39 @@ import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
* *
*/ */
public class Settings { public class Settings implements AutoCloseable {
private static final Logger logger = Logger.getLogger(Settings.class.getName()); private static final Logger logger = Logger.getLogger(Settings.class.getName());
public static final Settings EMPTY_SETTINGS = new Builder().build(); public static final Settings EMPTY_SETTINGS = new Builder().build();
public static final String[] EMPTY_ARRAY = new String[0];
public static final int BUFFER_SIZE = 1024 * 8;
private final Map<String, String> map;
private Settings(Map<String, String> settings) { public static final String[] EMPTY_ARRAY = new String[0];
this.map = new HashMap<>(settings);
public static final int BUFFER_SIZE = 1024 * 8;
private DefaultSettingsRefresher refresher;
private Map<String, String> map;
private Settings(Map<String, String> map) {
this(map, null, 0L, 0L, TimeUnit.SECONDS);
}
private Settings(Map<String, String> map, Path path, long initialDelay, long period, TimeUnit timeUnit) {
this.map = new LinkedHashMap<>(map);
if (path != null && initialDelay >= 0L && period > 0L) {
this.refresher = new DefaultSettingsRefresher(path, initialDelay, period, timeUnit);
}
} }
public static Settings readSettingsFromMap(Map<String, Object> map) throws IOException { public static Settings readSettingsFromMap(Map<String, Object> map) throws IOException {
@ -175,11 +193,7 @@ public class Settings {
} }
public String get(String setting) { public String get(String setting) {
String retVal = map.get(setting); return map.get(setting);
if (retVal != null) {
return retVal;
}
return null;
} }
public String get(String setting, String defaultValue) { public String get(String setting, String defaultValue) {
@ -298,11 +312,7 @@ public class Settings {
} }
String name = nameValue.substring(0, dotIndex); String name = nameValue.substring(0, dotIndex);
String value = nameValue.substring(dotIndex + 1); String value = nameValue.substring(dotIndex + 1);
Map<String, String> groupSettings = hashMap.get(name); Map<String, String> groupSettings = hashMap.computeIfAbsent(name, k -> new LinkedHashMap<>());
if (groupSettings == null) {
groupSettings = new LinkedHashMap<>();
hashMap.put(name, groupSettings);
}
groupSettings.put(value, get(setting)); groupSettings.put(value, get(setting));
} }
} }
@ -394,6 +404,13 @@ public class Settings {
return map; return map;
} }
@Override
public void close() throws IOException {
if (refresher != null) {
refresher.stop();
}
}
/** /**
* *
*/ */
@ -401,6 +418,14 @@ public class Settings {
private final Map<String, String> map = new LinkedHashMap<>(); private final Map<String, String> map = new LinkedHashMap<>();
private Path path;
private long initialDelay;
private long period;
private TimeUnit timeUnit;
private Builder() { private Builder() {
} }
@ -709,14 +734,54 @@ public class Settings {
public Builder replacePropertyPlaceholders(PropertyPlaceholder propertyPlaceholder, public Builder replacePropertyPlaceholders(PropertyPlaceholder propertyPlaceholder,
PlaceholderResolver placeholderResolver) { PlaceholderResolver placeholderResolver) {
for (Map.Entry<String, String> entry : map.entrySet()) { map.replaceAll((k, v) -> propertyPlaceholder.replacePlaceholders(v, placeholderResolver));
map.put(entry.getKey(), propertyPlaceholder.replacePlaceholders(entry.getValue(), placeholderResolver)); return this;
} }
public Builder setRefresh(Path path, long initialDelay, long period, TimeUnit timeUnit) {
this.path = path;
this.initialDelay = initialDelay;
this.period = period;
this.timeUnit = timeUnit;
return this; return this;
} }
public Settings build() { public Settings build() {
return new Settings(map); return new Settings(map, path, initialDelay, period, timeUnit);
}
}
class DefaultSettingsRefresher implements Runnable {
private final Path path;
private final ScheduledExecutorService executorService;
private final AtomicBoolean closed;
DefaultSettingsRefresher(Path path, long initialDelay, long period, TimeUnit timeUnit) {
this.path = path;
this.executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(this, initialDelay, period, timeUnit);
this.closed = new AtomicBoolean();
}
@Override
public void run() {
try {
if (!closed.get()) {
String settingsSource = Files.readString(path);
SettingsLoader settingsLoader = SettingsLoaderService.loaderFromResource(path.toString());
map = settingsLoader.load(settingsSource);
}
} catch (IOException e) {
throw new RuntimeException("unable to refresh settings from path " + path, e);
}
}
public void stop() {
closed.set(true);
executorService.shutdownNow();
} }
} }
} }

View file

@ -12,12 +12,14 @@ import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
@ -114,7 +116,7 @@ public class SettingsTest extends Assert {
Settings settings = Settings.settingsBuilder() Settings settings = Settings.settingsBuilder()
.loadFromSystemEnvironment() .loadFromSystemEnvironment()
.build(); .build();
assertTrue(!settings.getAsMap().isEmpty()); assertFalse(settings.getAsMap().isEmpty());
} }
@Test @Test
@ -122,7 +124,7 @@ public class SettingsTest extends Assert {
Settings settings = Settings.settingsBuilder() Settings settings = Settings.settingsBuilder()
.loadFromSystemProperties() .loadFromSystemProperties()
.build(); .build();
assertTrue(!settings.getAsMap().isEmpty()); assertFalse(settings.getAsMap().isEmpty());
} }
@Test @Test
@ -176,4 +178,14 @@ public class SettingsTest extends Assert {
Map<String, String> result = loader.load(map); Map<String, String> result = loader.load(map);
assertEquals("{code.a=b, code.b=c, name.a=b, name.b=c, list.0=a, list.1=b, null=null}", result.toString()); assertEquals("{code.a=b, code.b=c, name.a=b, name.b=c, list.0=a, list.1=b, null=null}", result.toString());
} }
@Test
public void testRefresher() throws Exception {
Settings settings = Settings.settingsBuilder()
.setRefresh(Paths.get("src/test/resources/settings.json"), 0L, 1L, TimeUnit.DAYS)
.build();
Thread.sleep(1000L);
assertEquals("Jörg Prante", settings.get("name"));
settings.close();
}
} }

View file

@ -0,0 +1,3 @@
{
"name": "Jörg Prante"
}

View file

@ -1,6 +1,6 @@
group = org.xbib group = org.xbib
name = content name = content
version = 2.0.5 version = 2.0.6
xbib-net.version = 2.0.3 xbib-net.version = 2.0.3
jackson.version = 2.9.10 jackson.version = 2.9.10

View file

@ -1,6 +1,5 @@
#Mon Jan 13 14:36:01 CET 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists