From def677040fe9430469f32bed826ce912edb1f31e Mon Sep 17 00:00:00 2001 From: Marco Herrn Date: Mon, 4 Mar 2024 22:35:47 +0100 Subject: [PATCH] Add Coat to the compared libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As I don’t know much about gradle I wans’t able to correctly configure the annotation processor. Therefore I included the generated files - ConfigParam.java - ImmutableConfig.java directly in the source tree. --- coat/README.md | 3 + coat/build.gradle | 4 + .../config/lib/benchmarks/Benchmarks.java | 49 ++++ .../java/config/lib/benchmarks/Config.java | 11 + .../config/lib/benchmarks/ConfigParam.java | 77 ++++++ .../lib/benchmarks/ImmutableConfig.java | 238 ++++++++++++++++++ coat/src/jmh/resources/AppProps.properties | 33 +++ runAllJmh.sh | 1 + settings.gradle | 1 + 9 files changed, 417 insertions(+) create mode 100644 coat/README.md create mode 100644 coat/build.gradle create mode 100644 coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/Benchmarks.java create mode 100644 coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/Config.java create mode 100644 coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/ConfigParam.java create mode 100644 coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/ImmutableConfig.java create mode 100644 coat/src/jmh/resources/AppProps.properties diff --git a/coat/README.md b/coat/README.md new file mode 100644 index 0000000..175ef85 --- /dev/null +++ b/coat/README.md @@ -0,0 +1,3 @@ +# Project Repository + + diff --git a/coat/build.gradle b/coat/build.gradle new file mode 100644 index 0000000..ee96655 --- /dev/null +++ b/coat/build.gradle @@ -0,0 +1,4 @@ +dependencies { + annotationProcessor 'de.poiu.coat:coat-processor:1.0.0' + implementation 'de.poiu.coat:coat-runtime:1.0.0' +} diff --git a/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/Benchmarks.java b/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/Benchmarks.java new file mode 100644 index 0000000..06ebf2b --- /dev/null +++ b/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/Benchmarks.java @@ -0,0 +1,49 @@ +package io.github.joeljeremy7.java.config.lib.benchmarks; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.Properties; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import java.util.concurrent.TimeUnit; + +public abstract class Benchmarks { + + @State(Scope.Benchmark) + public static class BenchmarkState { + private ImmutableConfig config; + + @Setup + public void setup() throws IOException, URISyntaxException { + final InputStream inStream = Benchmark.class.getResourceAsStream("/AppProps.properties"); + final Properties props= new Properties(); + props.load(inStream); + this.config= ImmutableConfig.from(props); + } + } + + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class Avgt extends Benchmarks {} + + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public static class Thrpt extends Benchmarks {} + + @Benchmark + public String Coat_String(BenchmarkState state) { + return state.config.test1(); + } + + @Benchmark + public int Coat_Int(BenchmarkState state) { + return state.config.testInt1(); + } +} diff --git a/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/Config.java b/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/Config.java new file mode 100644 index 0000000..123a580 --- /dev/null +++ b/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/Config.java @@ -0,0 +1,11 @@ +package io.github.joeljeremy7.java.config.lib.benchmarks; + +import de.poiu.coat.annotation.Coat; + + +@Coat.Config +public interface Config { + public String test1(); + + public int testInt1(); +} diff --git a/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/ConfigParam.java b/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/ConfigParam.java new file mode 100644 index 0000000..cfb626e --- /dev/null +++ b/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/ConfigParam.java @@ -0,0 +1,77 @@ +package io.github.joeljeremy7.java.config.lib.benchmarks; + +import java.lang.Class; +import java.lang.Override; +import java.lang.String; +import javax.annotation.processing.Generated; + +@Generated( + value = "de.poiu.coat.processor.codegeneration.CodeGenerator", + date = "2024-03-04T22:29:49.748122957+01:00" +) +public enum ConfigParam implements de.poiu.coat.ConfigParam { + TEST1("test1", java.lang.String.class, null, null, true, null, null), + + TEST_INT1("testInt1", int.class, null, null, true, null, null); + + private final String key; + + private final Class type; + + private final Class collectionType; + + private final String defaultValue; + + private final boolean mandatory; + + private final Class converter; + + private final Class listParser; + + private ConfigParam(final String key, final Class type, final Class collectionType, + final String defaultValue, final boolean mandatory, final Class converter, + final Class listParser) { + this.key = key; + this.type = type; + this.collectionType = collectionType; + this.defaultValue = defaultValue; + this.mandatory = mandatory; + this.converter = converter; + this.listParser = listParser; + } + + @Override + public String key() { + return this.key; + } + + @Override + public Class type() { + return this.type; + } + + @Override + public Class collectionType() { + return this.collectionType; + } + + @Override + public String defaultValue() { + return this.defaultValue; + } + + @Override + public boolean mandatory() { + return this.mandatory; + } + + @Override + public Class converter() { + return this.converter; + } + + @Override + public Class listParser() { + return this.listParser; + } +} diff --git a/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/ImmutableConfig.java b/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/ImmutableConfig.java new file mode 100644 index 0000000..147c7b9 --- /dev/null +++ b/coat/src/jmh/java/io/github/joeljeremy7/java/config/lib/benchmarks/ImmutableConfig.java @@ -0,0 +1,238 @@ +package io.github.joeljeremy7.java.config.lib.benchmarks; + +import de.poiu.coat.CoatConfig; +import de.poiu.coat.c14n.KeyC14n; +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.lang.Object; +import java.lang.Override; +import java.lang.String; +import java.lang.System; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import javax.annotation.processing.Generated; + +@Generated( + value = "de.poiu.coat.processor.codegeneration.CodeGenerator", + date = "2024-03-04T22:29:49.753072188+01:00" +) +public class ImmutableConfig extends CoatConfig implements Config { + private static final System.Logger LOGGER = System.getLogger(ImmutableConfig.class.getName()); + + private ImmutableConfig(final Map props) { + super(ConfigParam.values()); + + this.add(props); + } + + /** + * Create a new ImmutableConfig from the given config entries. + * + * @param props the config entries + * @return the ImmutableConfig created with the given entries + */ + public static ImmutableConfig from(final Map props) { + return new ImmutableConfig(props); + } + + /** + * Create a new ImmutableConfig from the given config file. + * + * @param file the config file to read + * @return the ImmutableConfig created with the entries from the given file + * @throws IOException if reading the given file failed + */ + public static ImmutableConfig from(final File file) throws IOException { + return new ImmutableConfig(toMap(file)); + } + + /** + * Create a new ImmutableConfig from the given config entries. + * + * @param jup the config entries + * @return the ImmutableConfig created with the given entries + */ + public static ImmutableConfig from(final Properties jup) { + return new ImmutableConfig(toMap(jup)); + } + + /** + * Create a new ImmutableConfig from the current environment variables. + *

+ * Since the allowed characters for environment variables are much more restricted than Coat config keys, + * a relaxed mapping is applied. + *

Dots and hyphens are treated as underscores. Also uppercase + * characters in config keys are preceded by an underscore (to convert camelCase to UPPER_CASE). + * Comparison between the environment variables and the config keys is done case insensitively.

+ * For example the environment variable + * SERVER_MQTT_HOST will match the config key server.mqttHost. + * + * @return the ImmutableConfig created with the entries in the current environment variables + */ + public static ImmutableConfig fromEnvVars() { + return builder().addEnvVars().build(); + } + + @Override + public String test1() { + return super.get(ConfigParam.TEST1); + } + + @Override + public int testInt1() { + return super.getInt(ConfigParam.TEST_INT1); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (this.getClass() != obj.getClass()) { + return false; + } + + final ImmutableConfig other = (ImmutableConfig) obj; + + if (!Objects.equals(this.test1(), other.test1())) { + return false; + } + + if (!Objects.equals(this.testInt1(), other.testInt1())) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return java.util.Objects.hash( + test1(), + testInt1()); + } + + /** + * Write an example config file to the given Writer. + * + * @param writer the Writer to write to + * @throws IOException if writing the example config file fails + */ + public static void writeExampleConfig(final Writer writer) throws IOException { + writer.append("test1 = \n" + + "\n" + + "testInt1 = \n" + + "\n"); + writer.flush(); + } + + /** + * Create a builder for {@link ImmutableConfig} instances. + *

+ * Call the add and/or addEnvVars methods for specifying the config + * sources (and the order in which they are applied), then call {@link Builder#build()} to create the + * ImmutableConfig + * + * @return an new ImmutableConfig builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for creating new {@link ImmutableConfig} instances. + *

+ * Call the add and/or addEnvVars methods for specifying the config + * sources (and the order in which they are applied), then call {@link Builder#build()} to create the + * ImmutableConfig + */ + public static class Builder { + private final Map props = new HashMap<>(); + + /** + * Add the config entries from the given Map to the built ImmutableConfig. + * Already existing config entries with the same keys will be overwritten. + * + * @param map the config entries to add + * @return this Builder + */ + public Builder add(final Map map) { + this.props.putAll(map); + return this; + } + + /** + * Add the config entries from the given file to the built ImmutableConfig. + * Already existing config entries with the same keys will be overwritten. + * + * @param file the file with the config entries to add + * @return this Builder + * @throws java.io.IOException if reading the config file failed + */ + public Builder add(final File file) throws IOException { + this.props.putAll(toMap(file)); + return this; + } + + /** + * Add the config entries from the given Properties to the built ImmutableConfig. + * Already existing config entries with the same keys will be overwritten. + * + * @param jup the config entries to add + * @return this Builder + */ + public Builder add(final Properties jup) { + this.props.putAll(toMap(jup)); + return this; + } + + /** + * Add the config entries from the current environment variables to the built ImmutableConfig. + * Already existing config entries with the same keys will be overwritten. + *

+ * Since the allowed characters for environment variables are much more restricted than Coat config keys, + * a relaxed mapping is applied. + *

Dots and hyphens are treated as underscores. Also uppercase + * characters in config keys are preceded by an underscore (to convert camelCase to UPPER_CASE). + * Comparison between the environment variables and the config keys is done case insensitively.

+ * For example the environment variable + * SERVER_MQTT_HOST will match the config key server.mqttHost. + * @return this Builder + */ + public Builder addEnvVars() { + final Map envVars= System.getenv(); + final String[] configKeys= { + "test1", + "testInt1" + }; + + for (final String envVar : envVars.keySet()) { + for (final String configKey : configKeys) { + if (envVar.toUpperCase().equals(KeyC14n.c14n(configKey))) { + LOGGER.log(System.Logger.Level.INFO, "Using environment variable {0} as config key {1}", new Object[]{envVar, configKey}); + this.props.put(configKey, envVars.get(envVar)); + } + } + } + + return this; + } + + /** + * Build a new {@link ImmutableConfig} with the config keys from this Builder. + * + * @return a new ImmutableConfig + */ + public ImmutableConfig build() { + return new ImmutableConfig(this.props); + } + } +} diff --git a/coat/src/jmh/resources/AppProps.properties b/coat/src/jmh/resources/AppProps.properties new file mode 100644 index 0000000..f8a757c --- /dev/null +++ b/coat/src/jmh/resources/AppProps.properties @@ -0,0 +1,33 @@ +test1=test.value.1 +test2=test.value.2 +test3=test.value.3 +test4=test.value.4 +test5=test.value.5 +test6=test.value.6 +test7=test.value.7 +test8=test.value.8 +test9=test.value.9 +test10=test.value.10 + +testInt1=1 +testInt2=2 +testInt3=3 +testInt4=4 +testInt5=5 +testInt6=6 +testInt7=7 +testInt8=8 +testInt9=9 +testInt10=10 + +variable.test.value.1=1 +variable.test.value.2=2 +variable.test.value.3=3 +variable.test.value.4=4 +variable.test.value.5=5 +variable.test.value.6=6 +variable.test.value.7=7 +variable.test.value.8=8 +variable.test.value.9=9 +variable.test.value.10=10 + diff --git a/runAllJmh.sh b/runAllJmh.sh index f7d514b..e1b0b50 100755 --- a/runAllJmh.sh +++ b/runAllJmh.sh @@ -3,6 +3,7 @@ ./gradlew build \ :baseline:jmh \ :cfg4j:jmh \ + :coat:jmh \ :externalized-properties:jmh \ :gestalt-config:jmh \ :lightbend-config:jmh \ diff --git a/settings.gradle b/settings.gradle index 0afd63b..c5d3c06 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,7 @@ rootProject.name = 'java-config-library-benchmarks' include 'baseline' include 'cfg4j' +include 'coat' include 'externalized-properties' include 'gestalt-config' include 'lightbend-config'