4 Commits

Author SHA1 Message Date
62b1e5a8fd extended interface: added keys() method
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2025-04-16 09:02:32 +02:00
e16bc02400 added method subset(…) to Configuration interface, added implementation in JsonConfig
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2025-04-09 16:56:49 +02:00
29faafbbdb added minimal logging
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2025-01-03 14:45:40 +01:00
bb25d61480 prepared configuration.json for publishing
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2024-12-25 23:15:25 +01:00
7 changed files with 159 additions and 8 deletions

View File

@@ -11,7 +11,6 @@ spotless {
target("**/src/**/java/**/*.java")
removeUnusedImports()
importOrder()
clangFormat("18.1.8").style("file:config/clang-format")
licenseHeader("/* © SRSoftware 2024 */")
}
}

View File

@@ -9,7 +9,7 @@ object Meta {
val COMPONENT_TYPE = "java" // "java" or "versionCatalog"
val GROUP = "de.srsoftware"
val ARTIFACT_ID = "configuration.api"
val VERSION = "1.0.0"
val VERSION = "1.0.2"
val PUBLISHING_TYPE = "AUTOMATIC" // USER_MANAGED or AUTOMATIC
val SHA_ALGORITHMS = listOf("SHA-256", "SHA-512") // sha256 and sha512 are supported but not mandatory. Only sha1 is mandatory but it is supported by default.
val DESC = "SRSoftware Configuration API"

View File

@@ -2,6 +2,7 @@
package de.srsoftware.configuration;
import java.io.IOException;
import java.util.Collection;
import java.util.Optional;
/**
@@ -35,6 +36,12 @@ public interface Configuration {
*/
<T> T get(String key, T defaultValue);
/**
* Get the configuration`s key set
* @return a collection of strings which are the keys to the configuration
*/
Collection<String> keys();
/**
* Assign a specific key with a new value. If the key was assigned with another value before, the old value is overwritten
* @param key specifies, which value is to be assigned
@@ -44,4 +51,12 @@ public interface Configuration {
* @throws IOException if altering the configuration fails
*/
<C extends Configuration> C set(String key, Object value) throws IOException;
/**
* get a subset of this configuration
*
* @param key specifies, which subset is requested
* @return the part of the Configuration which is located at the key
*/
Optional<? extends Configuration> subset(String key);
}

View File

@@ -8,8 +8,8 @@ import java.nio.file.Path;
* Helper for getting a config file
*/
public class Locator {
private Locator(){}
private Locator() {
}
/**
* Get the proper configuration file for a given application name with the desired extension

View File

@@ -3,4 +3,72 @@ description = "SRSoftware Configuration | JSON Configuration"
dependencies {
implementation(project(":de.srsoftware.configuration.api"))
implementation("org.json:json:latest.release")
}
}
plugins {
id("eu.kakde.gradle.sonatype-maven-central-publisher") version "1.0.6"
}
object Meta {
val COMPONENT_TYPE = "java" // "java" or "versionCatalog"
val GROUP = "de.srsoftware"
val ARTIFACT_ID = "configuration.json"
val VERSION = "1.0.3"
val PUBLISHING_TYPE = "AUTOMATIC" // USER_MANAGED or AUTOMATIC
val SHA_ALGORITHMS = listOf("SHA-256", "SHA-512") // sha256 and sha512 are supported but not mandatory. Only sha1 is mandatory but it is supported by default.
val DESC = "SRSoftware Configuration: Json-based Implementation"
val LICENSE = "MIT License"
val LICENSE_URL = "http://www.opensource.org/licenses/mit-license.php"
val GITHUB_REPO = "srsoftware-de/de.srsoftware.configuration"
val DEVELOPER_ID = "srichter"
val DEVELOPER_NAME = "Stephan Richter"
val DEVELOPER_ORGANIZATION = "SRSoftware"
val DEVELOPER_ORGANIZATION_URL = "https://srsoftware.de"
}
val sonatypeUsername: String? by project // this is defined in ~/.gradle/gradle.properties
val sonatypePassword: String? by project // this is defined in ~/.gradle/gradle.properties
sonatypeCentralPublishExtension {
// Set group ID, artifact ID, version, and other publication details
groupId.set(Meta.GROUP)
artifactId.set(Meta.ARTIFACT_ID)
version.set(Meta.VERSION)
componentType.set(Meta.COMPONENT_TYPE) // "java" or "versionCatalog"
publishingType.set(Meta.PUBLISHING_TYPE) // USER_MANAGED or AUTOMATIC
// Set username and password for Sonatype repository
username.set(sonatypeUsername)
password.set(sonatypePassword)
// Configure POM metadata
pom {
name.set(Meta.ARTIFACT_ID)
description.set(Meta.DESC)
url.set("https://github.com/${Meta.GITHUB_REPO}")
licenses {
license {
name.set(Meta.LICENSE)
url.set(Meta.LICENSE_URL)
}
}
developers {
developer {
id.set(Meta.DEVELOPER_ID)
name.set(Meta.DEVELOPER_NAME)
organization.set(Meta.DEVELOPER_ORGANIZATION)
organizationUrl.set(Meta.DEVELOPER_ORGANIZATION_URL)
}
}
scm {
url.set("https://github.com/${Meta.GITHUB_REPO}")
connection.set("scm:git:https://github.com/${Meta.GITHUB_REPO}")
developerConnection.set("scm:git:https://github.com/${Meta.GITHUB_REPO}")
}
issueManagement {
system.set("GitHub")
url.set("https://github.com/${Meta.GITHUB_REPO}/issues")
}
}
}

View File

@@ -2,6 +2,9 @@
package de.srsoftware.configuration;
import static java.lang.System.Logger;
import static java.lang.System.getLogger;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@@ -9,20 +12,44 @@ import java.nio.file.Files;
import java.util.*;
import org.json.JSONObject;
/**
* A Configuration implementation, that stores its data in a json file.
* Altered json &lt;em&gt;is not automatically saved&gt;/em&lt; after editing!
*/
public class JsonConfig implements Configuration {
private final File file;
private final JSONObject json;
private static final Logger LOG = getLogger(JsonConfig.class.getSimpleName());
private final File file;
private final JSONObject json;
/**
* Create a new JsonConfig instance using the passed file for storage
* @param jsonConfigurationFile this file will be used to store json data
* @throws IOException if one of the file operations failed
*/
public JsonConfig(File jsonConfigurationFile) throws IOException {
file = jsonConfigurationFile;
if (file.isDirectory()) throw new IllegalArgumentException("%s is a directory, file expected".formatted(file));
if (!file.exists()) try (var out = new FileWriter(file)) {
out.write("{}\n");
}
LOG.log(Logger.Level.INFO, "Loading json config file from {0}…", file);
json = new JSONObject(Files.readString(file.toPath()));
}
/**
* create a JsonConfig instance from a Json object
* @param json the data to use
*/
public JsonConfig(JSONObject json){
file = null;
this.json = json;
}
/**
* Create a new JsonConfig using the passed applicationName
* @param applicationName this determines the name of the file, to which data are stored
* @throws IOException if one of the file operations failed
*/
public JsonConfig(String applicationName) throws IOException {
this(Locator.locateConfig(applicationName, "json"));
}
@@ -43,10 +70,18 @@ public class JsonConfig implements Configuration {
if (json.get(key) instanceof JSONObject inner) drop(inner, path);
}
/**
* returns the file object of the json storage
* @return a File object
*/
public File file() {
return file;
}
/**
* creates a one-line representation of the json of this config
* @return the config as json string
*/
public String flat() {
return json.toString();
}
@@ -95,6 +130,19 @@ public class JsonConfig implements Configuration {
}
}
@Override
public Collection<String> keys() {
return json.keySet();
}
/**
* updates the storage file with the current json data
* @throws IOException if writing the file does so
*/
public void save() throws IOException {
Files.writeString(file.toPath(), json.toString(2));
}
@Override
@SuppressWarnings("unchecked")
public <C extends Configuration> C set(String key, Object value) throws IOException {
@@ -114,6 +162,12 @@ public class JsonConfig implements Configuration {
}
}
@Override
public Optional<JsonConfig> subset(String key) {
Optional<JSONObject> json = get(key);
return json.map(JsonConfig::new);
}
private Stack<String> toPath(String key) {
var parts = key.split("\\.");
var path = new Stack<String>();

View File

@@ -117,4 +117,19 @@ public class JsonConfigTest {
config.drop("hello");
assertEquals("{}", config.flat());
}
@Test
public void testSubset() throws IOException {
config.set("a.a.a", "aaa");
config.set("a.a.b", "aab");
config.set("a.b.a", "aba");
config.set("a.b.b", "abb");
assertEquals("{\"a\":{\"a\":{\"a\":\"aaa\",\"b\":\"aab\"},\"b\":{\"a\":\"aba\",\"b\":\"abb\"}}}",config.flat());
var subset = config.subset("a");
assertTrue(subset.isPresent());
assertEquals("{\"a\":{\"a\":\"aaa\",\"b\":\"aab\"},\"b\":{\"a\":\"aba\",\"b\":\"abb\"}}",subset.get().flat());
subset = config.subset("a.b");
assertTrue(subset.isPresent());
assertEquals("{\"a\":\"aba\",\"b\":\"abb\"}",subset.get().flat());
}
}