Skip to content

Commit

Permalink
Remove configuration path syntax (#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez authored Jun 24, 2024
1 parent f62c910 commit 90ef9f5
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 34 deletions.
29 changes: 21 additions & 8 deletions api/src/main/java/jakarta/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,32 @@ public interface Config {
/**
* Return a new instance of a {@link Config} with the <em>configuration path</em> set.
*
* <p>The configuration path uses the dot symbol {@code .} as a separator.</p>
* <p>The <em>configuration</em> path may contain one or more elements. A <em>configuration path</em> element
* only includes the single name of the {@link Config} child hierarchy.</p>
*
* <br>
* For instance, if the configuration contains
* <pre> my.configuration.user=tester</pre>
* the <em>configuration path</em> for the configuration name {@code user} is {@code my.configuration}.
* For instance, if the configuration contains a <code>properties</code> file with:
* <pre>my.configuration.user=tester</pre>
* the <em>configuration path</em> for the configuration name <code>user</code> is <code>my</code> and
* <code>configuration</code>.
*
* @param path a configuration path
* @return a new instance of the {@link Config} class with a new <em>configuration path</em>
* @param paths a <code>String</code> array of configuration paths
* @return a new instance of the {@link Config} class with the new <em>configuration path</em>
*/
Config path(String... paths);

/**
* Return a new instance of a {@link Config} with the <em>configuration path</em> set.
*
* <p>The <em>configuration path</em> is the single name of the {@link Config} child hierarchy.</p>
*
* @see ConfigMapping#path() Configuration#path
* @param path a <code>String</code> of a configuration path
* @return a new instance of the {@link Config} class with the new <em>configuration path</em>
* @see Config#path(String...)
*/
Config path(String path);
default Config path(String path) {
return path(new String[] {path});
}

/**
* {@linkplain #bootstrap(ClassLoader) Bootstraps} a {@link Config} instance for subsequent usage using
Expand Down
3 changes: 2 additions & 1 deletion api/src/main/java/jakarta/config/ConfigDefault.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// TODO - Should we leave this as is, or add ways to accept other defaults types (ints, booleans, etc)
/**
* Specify the default value of a configuration member.
*/
Expand All @@ -16,7 +17,7 @@
/**
* The default value of the member.
*
* @return the default value as a string
* @return the default value as a <code>String</code>
*/
String value();
}
99 changes: 80 additions & 19 deletions api/src/main/java/jakarta/config/ConfigMapping.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,34 +25,95 @@
import java.lang.annotation.Target;

/**
* <p>This annotation is to mark an interface to contain configuration data.</p>
* Marks an interface type as capable of mapping a configuration hierarchy.
*
* <p>The <em>configuration interface</em> is <em>resolved</em> with a portion of application's
* <em>persistent configuration</em> identified by <em>configuration path</em>.
* <p>Config Mapping allows mapping configuration entries to complex object types (usually user defined).
*
* <p>This configuration annotation is ignored on all nested objects.</p>
* <ul>
* <li>A configuration path uniquely identifies object member</li>
* <li>A configuration value maps to the object member value type</li>
* </ul>
*
* <p>The terms <em>configuration interface</em>, <em>configuration key</em>, <em>configuration path</em>,
* <em>persistent configuration</em>, <em>resolve</em>, and others are used here as defined in
* the Jakarta Config specification.</p>
* <p>A complex object type uses the following rules to map configuration values to their member values:</p>
*
* <ul>
* <li>A configuration path is built by taking the object type path and the mapping member name</li>
* <li>The member name is converted to its kebab-case format</li>
* <li>If the member name is a getter, the member name is taken from its property name equivalent, and then converted to its kebab-case format</li>
* <li>The configuration value is automatically converted to the member type</li>
* <li>The configuration path is required to exist with a valid configuration value or the mapping will fail</li>
* </ul>
*
* <h2>Example</h2>
* <pre>
* &#064;ConfigMapping("server")
* interface Server {
* String host();
* int port();
* int ioThreads();
* List&lt;Endpoint&gt; endpoints();
* Optional&lt;Ssl&gt; ssl();
* Map&lt;String, String&gt; form();
*
* interface Ssl {
* int port();
* String certificate();
* List&lt;String&gt; protocols();
* }
*
* interface Endpoint {
* String path();
* List&lt;String&gt; methods();
* }
* }
* </pre>
*
* <p>The {@link ConfigMapping} members will be populated with the configuration found in the configuration paths:</p>
*
* <ul>
* <li><code>Server#host</code> in <code>server</code>, <code>host</code></li>
* <li><code>Server#port</code> in <code>server</code>, <code>port</code></li>
* <li><code>Server#ioThreads</code> in <code>server</code>, <code>io-threads</code></li>
* <li>
* <code>Server#endpoints()</code> in <code>server</code>, <code>endpoints</code>
* <ul>
* <li>Endpoint#path in <code>server</code>, <code>endpoints</code>, <code>path</code></li>
* <li>Endpoint#methods in <code>server</code>, <code>endpoints</code>, <code>methods</code></li>
* </ul>
* </li>
* <li>
* <code>Server#ssl</code> in <code>server</code>, <code>ssl</code>
* <ul>
* <li>Ssl#port in <code>server</code>, <code>ssl</code>, <code>port</code></li>
* <li>Ssl#certificate in <code>server</code>, <code>ssl</code>, <code>certificate</code></li>
* <li>Ssl#protocols in <code>server</code>, <code>ssl</code>, <code>protocols</code></li>
* </ul>
* </li>
* <li><code>Server#form</code> in <code>server</code>, <code>form</code></li>
* </ul>
*
* <h2>Usage</h2>
*
* <p>A {@link ConfigMapping} annotated interface must be retrieved with {@link Config#load(Class)}:</p>
*
* <pre>
* Config config = Config.bootstrap();
* Server server = config.load(Server.class);
* </pre>
*
* @see Config#load(Class)
* @see ConfigName
* @see ConfigDefault
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigMapping {

/**
* The <em>configuration path</em> identifies where the configuration relevant for the annotated configuration class is found
* in a given application's <em>persistent configuration</em>.
*
* <p>The configuration path uses the dot symbol as a separator.</p>
*
* <br>
* For instance, if the <em>persistent configuration</em> contains
* <pre> my.configuration.user=tester</pre>
* the <em>configuration path</em> for the configuration portion {@code user=tester} would be {@code my.configuration}.
* The <em>configuration</em> path may contain one or more elements. A <em>configuration path</em> element
* only includes the single name of the {@link Config} child hierarchy.
*
* @return a {@link String} representation of a configuration path.
* @return a <code>String</code> array of configuration paths
*/
String path() default "";
String[] value() default {};
}
2 changes: 1 addition & 1 deletion tck/src/main/java/jakarta/config/tck/ConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void testThirdLevelAnyConfigurationInterface() {

@Test
public void testOverridePathThirdLevelAnyConfigurationInterface() {
AnyConfiguration configuration = Config.bootstrap().path("other.configuration").load(AnyConfiguration.class);
AnyConfiguration configuration = Config.bootstrap().path("other", "configuration").load(AnyConfiguration.class);
assertThat(configuration.key(), equalTo(JakartaConfigValues.otherConfigurationKey));
}
}
4 changes: 2 additions & 2 deletions tck/src/main/java/jakarta/config/tck/NegativeConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class NegativeConfigTest {
@Test
public void testFailWhenNotAnnotatedInterface() {
try {
Config.bootstrap().path("my.configuration").load(NotAnnotatedConfiguration.class);
Config.bootstrap().path("my", "configuration").load(NotAnnotatedConfiguration.class);
Assertions.fail("Expected ConfigException has not been thrown when the interface does not contain @Configuration");
} catch (ConfigException configException) {
// pass
Expand All @@ -41,7 +41,7 @@ public void testFailWhenNotAnnotatedInterface() {
@Test
public void testFailWhenUnknowPath() {
try {
Config.bootstrap().path("my.config").load(AnyConfiguration.class);
Config.bootstrap().path("my", "config").load(AnyConfiguration.class);
Assertions.fail("Expected NoSuchObjectException has not been thrown when the configuration object not found");
} catch (NoSuchElementException noSuchElementException) {
// pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import jakarta.config.ConfigMapping;

@ConfigMapping(path = "my.configuration")
@ConfigMapping({"my", "configuration"})
public interface AnyConfiguration {
String key();
}
2 changes: 1 addition & 1 deletion tck/src/main/java/jakarta/config/tck/common/My.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import jakarta.config.ConfigMapping;

@ConfigMapping(path="my")
@ConfigMapping("my")
public interface My {
String username();
String password();
Expand Down
2 changes: 1 addition & 1 deletion tck/src/main/java/jakarta/config/tck/common/Other.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import jakarta.config.ConfigMapping;

@ConfigMapping(path = "other")
@ConfigMapping("other")
public interface Other {
AnyConfiguration configuration();
}

0 comments on commit 90ef9f5

Please sign in to comment.