From 500eebefde922ddf2e3fe19cf6eb7b053dd1a06a Mon Sep 17 00:00:00 2001 From: Carm Date: Wed, 11 Mar 2026 11:02:11 +0800 Subject: [PATCH] feat(holder): Try to support MultiConfiguration. #169 --- core/pom.xml | 2 +- demo/pom.xml | 2 +- features/collections/pom.xml | 2 +- features/commentable/pom.xml | 2 +- features/file/pom.xml | 2 +- features/kotlin/pom.xml | 2 +- features/multi/pom.xml | 65 +++++++++ .../lib/configuration/MultiConfiguration.java | 135 ++++++++++++++++++ .../tests/ExampleUserStorage.java | 66 +++++++++ .../configuration/tests/MultiFileTests.java | 61 ++++++++ .../lib/configuration/tests/UserProfile.java | 49 +++++++ features/record/pom.xml | 2 +- features/section/pom.xml | 2 +- features/text/pom.xml | 2 +- features/validators/pom.xml | 2 +- features/versioned/pom.xml | 2 +- pom.xml | 3 +- providers/gson/pom.xml | 2 +- providers/hocon/pom.xml | 2 +- providers/mongodb/pom.xml | 2 +- providers/sql/pom.xml | 2 +- providers/temp/pom.xml | 2 +- providers/yaml/pom.xml | 2 +- 23 files changed, 395 insertions(+), 18 deletions(-) create mode 100644 features/multi/pom.xml create mode 100644 features/multi/src/main/java/cc/carm/lib/configuration/MultiConfiguration.java create mode 100644 features/multi/src/test/java/cc/carm/lib/configuration/tests/ExampleUserStorage.java create mode 100644 features/multi/src/test/java/cc/carm/lib/configuration/tests/MultiFileTests.java create mode 100644 features/multi/src/test/java/cc/carm/lib/configuration/tests/UserProfile.java diff --git a/core/pom.xml b/core/pom.xml index dda1d65..d539d3b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,7 @@ configured-parent cc.carm.lib - 4.2.0 + 4.2.1 4.0.0 diff --git a/demo/pom.xml b/demo/pom.xml index f65fc3e..4e28c80 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -5,7 +5,7 @@ configured-parent cc.carm.lib - 4.2.0 + 4.2.1 4.0.0 diff --git a/features/collections/pom.xml b/features/collections/pom.xml index 3ca6f3f..e570019 100644 --- a/features/collections/pom.xml +++ b/features/collections/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/features/commentable/pom.xml b/features/commentable/pom.xml index 26073bb..92c9a75 100644 --- a/features/commentable/pom.xml +++ b/features/commentable/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/features/file/pom.xml b/features/file/pom.xml index 4f0326b..45c1f02 100644 --- a/features/file/pom.xml +++ b/features/file/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/features/kotlin/pom.xml b/features/kotlin/pom.xml index 2f06210..f66747a 100644 --- a/features/kotlin/pom.xml +++ b/features/kotlin/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/features/multi/pom.xml b/features/multi/pom.xml new file mode 100644 index 0000000..86c38a8 --- /dev/null +++ b/features/multi/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + cc.carm.lib + configured-parent + 4.2.1 + ../../pom.xml + + + ${project.jdk.version} + ${project.jdk.version} + UTF-8 + UTF-8 + 2.3.10 + + configured-feature-multi + jar + + Configured - Record Feature + https://github.com/CarmJos/configured + Provides Java record type support for the Configured framework. + + + + + + cc.carm.lib + configured-core + ${project.version} + + + + + cc.carm.lib + configured-yaml + ${project.version} + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + diff --git a/features/multi/src/main/java/cc/carm/lib/configuration/MultiConfiguration.java b/features/multi/src/main/java/cc/carm/lib/configuration/MultiConfiguration.java new file mode 100644 index 0000000..9531b1e --- /dev/null +++ b/features/multi/src/main/java/cc/carm/lib/configuration/MultiConfiguration.java @@ -0,0 +1,135 @@ +package cc.carm.lib.configuration; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class MultiConfiguration { + + protected final ConcurrentHashMap valuesCache = new ConcurrentHashMap<>(); + + public MultiConfiguration() { + } + + /** + * Read the value of the key from the holder, and return the value. + * + * @param key The key of the value to read. + * @param holder The holder of the value to read. + * @return The value of the key, or null if the value is not exist. + */ + public abstract T read(@NotNull K key, @NotNull ConfigurationHolder holder); + + /** + * Write (and save) the value of the key to the holder, then update the cache. + * + * @param holder The holder of the value to write. + * @param value The value to write, which should not be null. + */ + public abstract void write(@NotNull ConfigurationHolder holder, @NotNull T value); + + /** + * Get all holders of the configuration, which should be a concurrent map to support concurrent access. + * + * @return All holders of the configuration. + */ + public abstract @NotNull Map> holders(); + + /** + * Get the holder of the key. + * If the holder of the key is not exist, + * it should be created then return the created holder. + * + * @param key The key of the holder to get. + * @return The holder of the key. + */ + public abstract @NotNull ConfigurationHolder holder(@NotNull K key); + + /** + * Remove the holder of the key, and remove the value of the key from the cache. + * Also delete the configuration file if necessary. + * + * @param key The key of the holder to remove. + */ + public abstract void removeHolder(@NotNull K key); + + public void loadAll() { + for (Map.Entry> entry : holders().entrySet()) { + K key = entry.getKey(); + ConfigurationHolder holder = entry.getValue(); + try { + T loaded = read(key, holder); + if (loaded != null) { + valuesCache.put(key, loaded); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + public void saveAll() { + for (Map.Entry entry : valuesCache.entrySet()) { + K key = entry.getKey(); + T value = entry.getValue(); + ConfigurationHolder holder = holder(key); + try { + write(holder, value); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + + /** + * Get all keys in the cache. + * + * @return All keys in the cache. + */ + public @NotNull Set keys() { + return valuesCache.keySet(); + } + + /** + * Get all values in the cache. + * + * @return All values in the cache. + */ + public @NotNull Map values() { + return this.valuesCache; + } + + /** + * Get the value of the key, or null if the value is not exist. + * + * @param key The key of the value to get. + * @return The value of the key, or null if the value is not exist. + */ + public @Nullable T get(@NotNull K key) { + return valuesCache.get(key); + } + + /** + * Update the value of the key, and return the old value. + * + * @param key The key of the value to update. + * @param value The new value, or null to remove the value. + * @return The old value, or null if the value is not exist. + */ + public @Nullable T update(@NotNull K key, @Nullable T value) { + if (value == null) { + T current = valuesCache.remove(key); + removeHolder(key); + return current; + } + ConfigurationHolder holder = holder(key); + write(holder, value); + return valuesCache.put(key, value); + } + +} diff --git a/features/multi/src/test/java/cc/carm/lib/configuration/tests/ExampleUserStorage.java b/features/multi/src/test/java/cc/carm/lib/configuration/tests/ExampleUserStorage.java new file mode 100644 index 0000000..666732d --- /dev/null +++ b/features/multi/src/test/java/cc/carm/lib/configuration/tests/ExampleUserStorage.java @@ -0,0 +1,66 @@ +package cc.carm.lib.configuration.tests; + +import cc.carm.lib.configuration.MultiConfiguration; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class ExampleUserStorage extends MultiConfiguration { + + protected final @NotNull File dataFolder; + protected final ConcurrentHashMap> holders = new ConcurrentHashMap<>(); + + public ExampleUserStorage(@NotNull File dataFolder) { + this.dataFolder = dataFolder; + + // Load existing configuration files + if (dataFolder.exists() && dataFolder.isDirectory()) { + File[] files = dataFolder.listFiles((dir, name) -> name.endsWith(".yml")); + if (files != null) { + for (File file : files) { + String fileName = file.getName(); + String uuidStr = fileName.substring(0, fileName.length() - 4); // Remove ".yml" + try { + UUID uuid = UUID.fromString(uuidStr); + ConfigurationHolder holder = YAMLConfigFactory.from(file).build(); + holders.put(uuid, holder); + } catch (IllegalArgumentException e) { + System.err.println("Invalid UUID in file name: " + fileName); + } + } + } + } + + loadAll(); + } + + @Override + public @NotNull Map> holders() { + return this.holders; + } + + @Override + public @NotNull ConfigurationHolder holder(@NotNull UUID key) { + ConfigurationHolder loaded = holders.get(key); + if (loaded != null) return loaded; + + ConfigurationHolder created = YAMLConfigFactory.from(new File(dataFolder, key + ".yml")).build(); + holders.put(key, created); + return created; + } + + @Override + public void removeHolder(@NotNull UUID key) { + ConfigurationHolder loaded = holders.remove(key); + if (loaded == null) return; + + File file = new File(dataFolder, key + ".yml"); + if (file.exists()) file.delete(); + } + +} diff --git a/features/multi/src/test/java/cc/carm/lib/configuration/tests/MultiFileTests.java b/features/multi/src/test/java/cc/carm/lib/configuration/tests/MultiFileTests.java new file mode 100644 index 0000000..b995f11 --- /dev/null +++ b/features/multi/src/test/java/cc/carm/lib/configuration/tests/MultiFileTests.java @@ -0,0 +1,61 @@ +package cc.carm.lib.configuration.tests; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.section.ConfigureSection; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +import java.io.File; +import java.util.UUID; + +public class MultiFileTests { + + + @Test + public void test() { + + ProfileStorage storage = new ProfileStorage(new File(new File("target"), "test-profiles")); + UserProfile profile = new UserProfile(UUID.randomUUID(), "John Doe", "john@google.com", "123123123"); + storage.update(profile.getUniqueId(), profile); + + System.out.println("Current users: "); + storage.values().forEach((k, v) -> { + System.out.println("# " + k); + System.out.println("- " + v.getName() + " (" + v.getEmail() + ", " + v.getPhone() + ")"); + }); + + } + + public static class ProfileStorage extends ExampleUserStorage { + + public ProfileStorage(@NotNull File dataFolder) { + super(dataFolder); + } + + @Override + public UserProfile read(@NotNull UUID key, @NotNull ConfigurationHolder holder) { + ConfigureSection conf = holder.config(); + return new UserProfile( + key, + conf.getString("name", "Unknown"), + conf.getString("email", "unset"), + conf.getString("phone", "123123123") + ); + } + + @Override + public void write(@NotNull ConfigurationHolder holder, @NotNull UserProfile value) { + ConfigureSection conf = holder.config(); + conf.set("name", value.getName()); + conf.set("email", value.getEmail()); + conf.set("phone", value.getPhone()); + try { + holder.save(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + +} diff --git a/features/multi/src/test/java/cc/carm/lib/configuration/tests/UserProfile.java b/features/multi/src/test/java/cc/carm/lib/configuration/tests/UserProfile.java new file mode 100644 index 0000000..e6d68bd --- /dev/null +++ b/features/multi/src/test/java/cc/carm/lib/configuration/tests/UserProfile.java @@ -0,0 +1,49 @@ +package cc.carm.lib.configuration.tests; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.UUID; + +public class UserProfile { + + protected final @NotNull UUID uuid; + protected final @NotNull String name; + protected final @NotNull String email; + protected final @NotNull String phone; + + public UserProfile(@NotNull UUID uuid, @NotNull String name, @NotNull String email, @NotNull String phone) { + this.uuid = uuid; + this.name = name; + this.email = email; + this.phone = phone; + } + + public @NotNull UUID getUniqueId() { + return uuid; + } + + public @NotNull String getName() { + return name; + } + + public @NotNull String getEmail() { + return email; + } + + public @NotNull String getPhone() { + return phone; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof UserProfile)) return false; + UserProfile that = (UserProfile) o; + return Objects.equals(name, that.name) && Objects.equals(email, that.email) && Objects.equals(phone, that.phone); + } + + @Override + public int hashCode() { + return Objects.hash(name, email, phone); + } +} diff --git a/features/record/pom.xml b/features/record/pom.xml index 9fd6a38..a9d7cde 100644 --- a/features/record/pom.xml +++ b/features/record/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/features/section/pom.xml b/features/section/pom.xml index edd628a..76c0d92 100644 --- a/features/section/pom.xml +++ b/features/section/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/features/text/pom.xml b/features/text/pom.xml index faaf5de..ab2f8c6 100644 --- a/features/text/pom.xml +++ b/features/text/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/features/validators/pom.xml b/features/validators/pom.xml index a6ec66c..0ff7e83 100644 --- a/features/validators/pom.xml +++ b/features/validators/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/features/versioned/pom.xml b/features/versioned/pom.xml index f8af61f..750b3e3 100644 --- a/features/versioned/pom.xml +++ b/features/versioned/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/pom.xml b/pom.xml index 258d1d6..2f8e281 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ cc.carm.lib configured-parent pom - 4.2.0 + 4.2.1 core features/section @@ -37,6 +37,7 @@ providers/mongodb demo + features/multi configured diff --git a/providers/gson/pom.xml b/providers/gson/pom.xml index 3fdbdbd..179219e 100644 --- a/providers/gson/pom.xml +++ b/providers/gson/pom.xml @@ -5,7 +5,7 @@ configured-parent cc.carm.lib - 4.2.0 + 4.2.1 ../../pom.xml 4.0.0 diff --git a/providers/hocon/pom.xml b/providers/hocon/pom.xml index 042e9ed..2eaf84c 100644 --- a/providers/hocon/pom.xml +++ b/providers/hocon/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/providers/mongodb/pom.xml b/providers/mongodb/pom.xml index 36785be..e5f428e 100644 --- a/providers/mongodb/pom.xml +++ b/providers/mongodb/pom.xml @@ -5,7 +5,7 @@ configured-parent cc.carm.lib - 4.2.0 + 4.2.1 ../../pom.xml 4.0.0 diff --git a/providers/sql/pom.xml b/providers/sql/pom.xml index 24497df..7873382 100644 --- a/providers/sql/pom.xml +++ b/providers/sql/pom.xml @@ -6,7 +6,7 @@ configured-parent cc.carm.lib - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/providers/temp/pom.xml b/providers/temp/pom.xml index 83a06c8..2403cb6 100644 --- a/providers/temp/pom.xml +++ b/providers/temp/pom.xml @@ -6,7 +6,7 @@ configured-parent cc.carm.lib - 4.2.0 + 4.2.1 ../../pom.xml diff --git a/providers/yaml/pom.xml b/providers/yaml/pom.xml index 734da31..58a2b57 100644 --- a/providers/yaml/pom.xml +++ b/providers/yaml/pom.xml @@ -6,7 +6,7 @@ configured-parent cc.carm.lib - 4.2.0 + 4.2.1 ../../pom.xml