From 0e37917c47347469c43473f870762b1e811239c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Wed, 22 Apr 2020 16:12:25 +0200 Subject: [PATCH] add settings refresher --- .../org/xbib/content/settings/Settings.java | 105 ++++++++++++++---- .../xbib/content/settings/SettingsTest.java | 16 ++- content-core/src/test/resources/settings.json | 3 + gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 5 +- 5 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 content-core/src/test/resources/settings.json diff --git a/content-core/src/main/java/org/xbib/content/settings/Settings.java b/content-core/src/main/java/org/xbib/content/settings/Settings.java index df1ad88..7939459 100644 --- a/content-core/src/main/java/org/xbib/content/settings/Settings.java +++ b/content-core/src/main/java/org/xbib/content/settings/Settings.java @@ -15,6 +15,8 @@ import java.io.StringWriter; import java.io.Writer; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.DateTimeException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -24,23 +26,39 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; 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.Logger; /** * */ -public class Settings { +public class Settings implements AutoCloseable { private static final Logger logger = Logger.getLogger(Settings.class.getName()); 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 map; - private Settings(Map settings) { - this.map = new HashMap<>(settings); + public static final String[] EMPTY_ARRAY = new String[0]; + + public static final int BUFFER_SIZE = 1024 * 8; + + private DefaultSettingsRefresher refresher; + + private Map map; + + private Settings(Map map) { + this(map, null, 0L, 0L, TimeUnit.SECONDS); + } + + private Settings(Map 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 map) throws IOException { @@ -175,11 +193,7 @@ public class Settings { } public String get(String setting) { - String retVal = map.get(setting); - if (retVal != null) { - return retVal; - } - return null; + return map.get(setting); } public String get(String setting, String defaultValue) { @@ -298,11 +312,7 @@ public class Settings { } String name = nameValue.substring(0, dotIndex); String value = nameValue.substring(dotIndex + 1); - Map groupSettings = hashMap.get(name); - if (groupSettings == null) { - groupSettings = new LinkedHashMap<>(); - hashMap.put(name, groupSettings); - } + Map groupSettings = hashMap.computeIfAbsent(name, k -> new LinkedHashMap<>()); groupSettings.put(value, get(setting)); } } @@ -394,6 +404,13 @@ public class Settings { return map; } + @Override + public void close() throws IOException { + if (refresher != null) { + refresher.stop(); + } + } + /** * */ @@ -401,6 +418,14 @@ public class Settings { private final Map map = new LinkedHashMap<>(); + private Path path; + + private long initialDelay; + + private long period; + + private TimeUnit timeUnit; + private Builder() { } @@ -709,14 +734,54 @@ public class Settings { public Builder replacePropertyPlaceholders(PropertyPlaceholder propertyPlaceholder, PlaceholderResolver placeholderResolver) { - for (Map.Entry entry : map.entrySet()) { - map.put(entry.getKey(), propertyPlaceholder.replacePlaceholders(entry.getValue(), placeholderResolver)); - } + map.replaceAll((k, v) -> propertyPlaceholder.replacePlaceholders(v, 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; } 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(); } } } diff --git a/content-core/src/test/java/org/xbib/content/settings/SettingsTest.java b/content-core/src/test/java/org/xbib/content/settings/SettingsTest.java index 00a6fd8..95789bd 100644 --- a/content-core/src/test/java/org/xbib/content/settings/SettingsTest.java +++ b/content-core/src/test/java/org/xbib/content/settings/SettingsTest.java @@ -12,12 +12,14 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.logging.Logger; /** @@ -114,7 +116,7 @@ public class SettingsTest extends Assert { Settings settings = Settings.settingsBuilder() .loadFromSystemEnvironment() .build(); - assertTrue(!settings.getAsMap().isEmpty()); + assertFalse(settings.getAsMap().isEmpty()); } @Test @@ -122,7 +124,7 @@ public class SettingsTest extends Assert { Settings settings = Settings.settingsBuilder() .loadFromSystemProperties() .build(); - assertTrue(!settings.getAsMap().isEmpty()); + assertFalse(settings.getAsMap().isEmpty()); } @Test @@ -176,4 +178,14 @@ public class SettingsTest extends Assert { Map 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()); } + + @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(); + } } diff --git a/content-core/src/test/resources/settings.json b/content-core/src/test/resources/settings.json new file mode 100644 index 0000000..8b412ed --- /dev/null +++ b/content-core/src/test/resources/settings.json @@ -0,0 +1,3 @@ +{ + "name": "Jörg Prante" +} diff --git a/gradle.properties b/gradle.properties index c0f98ce..1ee8ca2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group = org.xbib name = content -version = 2.0.5 +version = 2.0.6 xbib-net.version = 2.0.3 jackson.version = 2.9.10 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bb04992..7c4388a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists