diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingLoader.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingLoader.java index 2ac631fff..0bcdadfdb 100644 --- a/implementation/src/main/java/io/smallrye/config/ConfigMappingLoader.java +++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingLoader.java @@ -7,6 +7,8 @@ import java.util.ArrayList; import java.util.List; +import org.eclipse.microprofile.config.inject.ConfigProperties; + import io.smallrye.common.classloader.ClassDefiner; public final class ConfigMappingLoader { @@ -39,6 +41,8 @@ static ConfigMappingInterface getConfigMappingInterface(final Class type) { } static Class getConfigMappingClass(final Class type) { + validateAnnotations(type); + final ConfigMappingClass configMappingClass = ConfigMappingClass.getConfigurationClass(type); if (configMappingClass == null) { return type; @@ -86,6 +90,16 @@ static Class loadClass(final Class parent, final ConfigMappingMetadata con } } + static void validateAnnotations(Class type) { + if (!type.isInterface() && type.isAnnotationPresent(ConfigMapping.class)) { + throw ConfigMessages.msg.mappingAnnotationNotSupportedInClass(type); + } + + if (type.isInterface() && type.isAnnotationPresent(ConfigProperties.class)) { + throw ConfigMessages.msg.propertiesAnnotationNotSupportedInInterface(type); + } + } + /** * Do not remove this method or inline it. It is keep separate on purpose, so it is easier to substitute it with * the GraalVM API for native image compilation. diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMessages.java b/implementation/src/main/java/io/smallrye/config/ConfigMessages.java index bc9661364..7c09d0526 100644 --- a/implementation/src/main/java/io/smallrye/config/ConfigMessages.java +++ b/implementation/src/main/java/io/smallrye/config/ConfigMessages.java @@ -146,4 +146,9 @@ IllegalArgumentException converterException(@Cause Throwable converterException, @Message(id = 42, value = "Value does not match the expected map format \"=;=...\" (value was \"%s\")") NoSuchElementException valueNotMatchMapFormat(String value); + @Message(id = 43, value = "The @ConfigMapping annotation can only be placed in interfaces, %s is a class") + IllegalStateException mappingAnnotationNotSupportedInClass(Class type); + + @Message(id = 44, value = "The @ConfigProperties annotation can only be placed in classes, %s is an interface") + IllegalStateException propertiesAnnotationNotSupportedInInterface(Class type); } diff --git a/implementation/src/test/java/io/smallrye/config/ConfigMappingsTest.java b/implementation/src/test/java/io/smallrye/config/ConfigMappingsTest.java index 507030e33..d99f530c2 100644 --- a/implementation/src/test/java/io/smallrye/config/ConfigMappingsTest.java +++ b/implementation/src/test/java/io/smallrye/config/ConfigMappingsTest.java @@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Map; import java.util.Objects; @@ -128,6 +129,31 @@ void validateDisableOnConfigProperties() { assertEquals(8080, server.port); } + @Test + void validateAnnotations() { + SmallRyeConfig config = new SmallRyeConfigBuilder().build(); + IllegalStateException exception = assertThrows(IllegalStateException.class, + () -> registerConfigMappings(config, singleton(configClassWithPrefix(ServerMappingClass.class, "server")))); + assertTrue(exception.getMessage() + .startsWith("SRCFG00043: The @ConfigMapping annotation can only be placed in interfaces")); + + exception = assertThrows(IllegalStateException.class, + () -> new SmallRyeConfigBuilder().withMapping(ServerMappingClass.class, "server").build()); + assertTrue(exception.getMessage() + .startsWith("SRCFG00043: The @ConfigMapping annotation can only be placed in interfaces")); + + exception = assertThrows(IllegalStateException.class, + () -> registerConfigMappings(config, + singleton(configClassWithPrefix(ServerPropertiesInterface.class, "server")))); + assertTrue(exception.getMessage() + .startsWith("SRCFG00044: The @ConfigProperties annotation can only be placed in classes")); + + exception = assertThrows(IllegalStateException.class, + () -> new SmallRyeConfigBuilder().withMapping(ServerPropertiesInterface.class, "server").build()); + assertTrue(exception.getMessage() + .startsWith("SRCFG00044: The @ConfigProperties annotation can only be placed in classes")); + } + @ConfigMapping(prefix = "server") interface Server { String host(); @@ -150,6 +176,16 @@ static class ServerClass { Map numbers; } + @ConfigMapping(prefix = "server") + static class ServerMappingClass { + String host; + } + + @ConfigProperties(prefix = "server") + interface ServerPropertiesInterface { + String host(); + } + static class Version { int id; String name; @@ -176,7 +212,6 @@ public int hashCode() { } static class VersionConverter implements Converter { - @Override public Version convert(String value) { return new Version(Integer.parseInt(value.substring(0, 1)), value.substring(2));