diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java index c75524811c6..43eae2baa23 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java @@ -18,6 +18,8 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationException; @@ -53,4 +55,9 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C protected String[] getSupportedTypes() { return SUFFIXES; } + + @Override + public List getSupportedExtensions() { + return Collections.emptyList(); + } } diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java index fb6f1383be5..0d4d4eedc0a 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java @@ -16,6 +16,8 @@ */ package org.apache.log4j.config; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationFactory; @@ -52,6 +54,11 @@ protected String[] getSupportedTypes() { return new String[] {FILE_EXTENSION}; } + @Override + public List getSupportedExtensions() { + return Collections.emptyList(); + } + @Override public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { final int interval = PropertiesUtil.getProperties().getIntegerProperty(Log4j1Configuration.MONITOR_INTERVAL, 0); diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/package-info.java index 396cf9bbd32..0d4aadd3374 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/package-info.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/package-info.java @@ -18,7 +18,7 @@ * Log4j 1.x compatibility layer. */ @Export -@Version("2.20.1") +@Version("2.21.0") @Open("org.apache.logging.log4j.core") package org.apache.log4j.config; diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java index a5b10fce848..210f41af49b 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java @@ -16,6 +16,8 @@ */ package org.apache.log4j.xml; +import java.util.Collections; +import java.util.List; import org.apache.log4j.config.Log4j1Configuration; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; @@ -56,6 +58,11 @@ protected String[] getSupportedTypes() { return new String[] {FILE_EXTENSION}; } + @Override + public List getSupportedExtensions() { + return Collections.emptyList(); + } + @Override public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { final int interval = PropertiesUtil.getProperties().getIntegerProperty(Log4j1Configuration.MONITOR_INTERVAL, 0); diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/package-info.java index b7c3d31598d..fd72c206c4f 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/package-info.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/package-info.java @@ -18,7 +18,7 @@ * Log4j 1.x compatibility layer. */ @Export -@Version("2.20.2") +@Version("2.21.0") package org.apache.log4j.xml; import org.osgi.annotation.bundle.Export; diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java index afb282b9c65..efc16c63ac7 100644 --- a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java +++ b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java @@ -17,6 +17,8 @@ package org.apache.log4j; import java.net.URI; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.AbstractConfiguration; @@ -35,6 +37,11 @@ public String[] getSupportedTypes() { return new String[] {"*"}; } + @Override + public List getSupportedExtensions() { + return Collections.emptyList(); + } + @Override public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { return new BasicConfiguration(loggerContext); diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/BasicConfigurationFactory.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/BasicConfigurationFactory.java index a3e06e2f6e9..707b42ad1d2 100644 --- a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/BasicConfigurationFactory.java +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/BasicConfigurationFactory.java @@ -17,6 +17,8 @@ package org.apache.logging.log4j.core.test; import java.net.URI; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.AbstractConfiguration; @@ -41,6 +43,11 @@ public String[] getSupportedTypes() { return null; } + @Override + public List getSupportedExtensions() { + return Collections.emptyList(); + } + @Override public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { return null; diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/package-info.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/package-info.java index 2199930f31f..70fbbc2b6f5 100644 --- a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/package-info.java +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/package-info.java @@ -15,7 +15,7 @@ * limitations under the license. */ @Export -@Version("2.25.0") +@Version("2.26.0") @BaselineIgnore("2.25.0") package org.apache.logging.log4j.core.test; diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationFactoryTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationFactoryTest.java index f2d81f13472..f7c0177ce74 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationFactoryTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationFactoryTest.java @@ -19,6 +19,7 @@ import static org.apache.logging.log4j.util.Unbox.box; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -36,6 +37,10 @@ import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.ConsoleAppender; +import org.apache.logging.log4j.core.config.json.JsonConfigurationFactory; +import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory; +import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory; +import org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory; import org.apache.logging.log4j.core.filter.ThreadContextMapFilter; import org.apache.logging.log4j.core.test.junit.LoggerContextSource; import org.apache.logging.log4j.test.junit.TempLoggingDir; @@ -130,4 +135,55 @@ void properties(final LoggerContext context) throws IOException { final Path logFile = loggingPath.resolve("test-properties.log"); checkFileLogger(context, logFile); } + + @Test + void getSupportedExtensions() { + final List extensions = ConfigurationFactory.getActiveFileExtensions(); + assertNotNull(extensions); + assertFalse(extensions.isEmpty()); + assertTrue(extensions.contains("xml")); + assertTrue(extensions.contains("properties")); + final long distinctCount = extensions.stream().distinct().count(); + assertEquals(extensions.size(), distinctCount); + } + + @Test + void xmlConfigurationFactoryExtensions() { + final XmlConfigurationFactory factory = new XmlConfigurationFactory(); + final List extensions = factory.getSupportedExtensions(); + assertEquals(1, extensions.size()); + assertEquals("xml", extensions.get(0)); + } + + @Test + void propertiesConfigurationFactoryExtensions() { + final PropertiesConfigurationFactory factory = new PropertiesConfigurationFactory(); + final List extensions = factory.getSupportedExtensions(); + assertEquals(1, extensions.size()); + assertEquals("properties", extensions.get(0)); + } + + @Test + @Tag("json") + void jsonConfigurationFactoryExtensions() { + final JsonConfigurationFactory factory = new JsonConfigurationFactory(); + final List extensions = factory.getSupportedExtensions(); + assertTrue(extensions.size() == 0 || extensions.size() == 2); + if (!extensions.isEmpty()) { + assertTrue(extensions.contains("json")); + assertTrue(extensions.contains("jsn")); + } + } + + @Test + @Tag("yaml") + void yamlConfigurationFactoryExtensions() { + final YamlConfigurationFactory factory = new YamlConfigurationFactory(); + final List extensions = factory.getSupportedExtensions(); + assertTrue(extensions.size() == 0 || extensions.size() == 2); + if (!extensions.isEmpty()) { + assertTrue(extensions.contains("yml")); + assertTrue(extensions.contains("yaml")); + } + } } diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/CustomConfigurationFactory.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/CustomConfigurationFactory.java index 737c2181e77..eedcec4ce60 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/CustomConfigurationFactory.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/CustomConfigurationFactory.java @@ -17,6 +17,8 @@ package org.apache.logging.log4j.core.config.builder; import java.net.URI; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.LoggerContext; @@ -86,4 +88,9 @@ public Configuration getConfiguration( protected String[] getSupportedTypes() { return new String[] {"*"}; } + + @Override + public List getSupportedExtensions() { + return Collections.emptyList(); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java index 505ca9c9cd9..b8944c2d7f8 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LoggerContext; @@ -263,6 +264,14 @@ public static void removeConfigurationFactory(final ConfigurationFactory factory protected abstract String[] getSupportedTypes(); + /** + * Returns the file extensions supported by this configuration factory. + * + * @return list of supported file extensions (e.g., ["xml", "json"]) + * @since 2.25.0 + */ + public abstract List getSupportedExtensions(); + protected String getTestPrefix() { return TEST_PREFIX; } @@ -553,6 +562,16 @@ public String[] getSupportedTypes() { return null; } + @Override + public List getSupportedExtensions() { + return getFactories().stream() + .filter(factory -> factory != this) + .filter(ConfigurationFactory::isActive) + .flatMap(factory -> factory.getSupportedExtensions().stream()) + .distinct() + .collect(Collectors.toList()); + } + @Override public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { if (source != null) { @@ -617,4 +636,19 @@ private String[] parseConfigLocations(final String configLocations) { static List getFactories() { return factories; } + + /** + * Returns all configuration file extensions currently supported at runtime. + * This aggregates extensions from all active configuration factories. + * + * @return list of supported file extensions + * @since 2.25.0 + */ + public static List getActiveFileExtensions() { + return getFactories().stream() + .filter(ConfigurationFactory::isActive) + .flatMap(factory -> factory.getSupportedExtensions().stream()) + .distinct() + .collect(Collectors.toList()); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java index ddcc61b4a60..10a7a6c0f85 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java @@ -16,6 +16,9 @@ */ package org.apache.logging.log4j.core.config.json; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationFactory; @@ -71,4 +74,22 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C public String[] getSupportedTypes() { return SUFFIXES; } + + /** + * Returns the file extensions supported by this JSON configuration factory. + * + *

This method returns JSON file extensions that this factory can process. + * The factory requires Jackson dependencies to be available on the classpath. + * If Jackson libraries are missing, this method returns an empty list.

+ * + * @return a list containing "json" and "jsn" extensions if Jackson dependencies + * are available and the factory is active, empty list otherwise + */ + @Override + public List getSupportedExtensions() { + if (!isActive()) { + return Collections.emptyList(); + } + return Arrays.asList("json", "jsn"); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java index 9d9e5d60eb3..5615f2cc73d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with JSON. */ @Export -@Version("2.20.1") +@Version("2.21.0") package org.apache.logging.log4j.core.config.json; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationFactory.java index 02d8f064048..e11f398fdfa 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationFactory.java @@ -18,6 +18,9 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Properties; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.ConfigurationException; @@ -40,6 +43,24 @@ protected String[] getSupportedTypes() { return new String[] {".properties"}; } + /** + * Returns the file extensions supported by this Properties configuration factory. + * + *

This method returns Properties file extensions that this factory can process. + * The factory supports standard Java properties files used for Log4j 2 configuration. + * No external dependencies are required for properties file support.

+ * + * @return a list containing "properties" extension if the factory is active, + * empty list if inactive + */ + @Override + public List getSupportedExtensions() { + if (!isActive()) { + return Collections.emptyList(); + } + return Arrays.asList("properties"); + } + @Override public PropertiesConfiguration getConfiguration( final LoggerContext loggerContext, final ConfigurationSource source) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java index 1563a62e0ec..57419616d65 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java @@ -18,7 +18,7 @@ * Configuration using Properties files. */ @Export -@Version("2.20.1") +@Version("2.21.0") package org.apache.logging.log4j.core.config.properties; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java index 35d433b0e9d..f39b4b9d242 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java @@ -16,6 +16,9 @@ */ package org.apache.logging.log4j.core.config.xml; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationFactory; @@ -54,4 +57,21 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C public String[] getSupportedTypes() { return SUFFIXES; } + + /** + * Returns the file extensions supported by this XML configuration factory. + * + *

This method returns XML file extensions that this factory can process. + * The factory supports standard XML configuration files used by Log4j 2.

+ * + * @return a list containing "xml" extension if the factory is active, + * empty list if inactive due to missing XML parsing dependencies + */ + @Override + public List getSupportedExtensions() { + if (!isActive()) { + return Collections.emptyList(); + } + return Arrays.asList("xml"); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java index f0a6c9d7e4f..79858cbdfd6 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with XML. */ @Export -@Version("2.20.2") +@Version("2.21.0") package org.apache.logging.log4j.core.config.xml; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfigurationFactory.java index d3cbaad03ef..1bb4e04f837 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfigurationFactory.java @@ -16,6 +16,9 @@ */ package org.apache.logging.log4j.core.config.yaml; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationFactory; @@ -72,4 +75,22 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C public String[] getSupportedTypes() { return SUFFIXES; } + + /** + * Returns the file extensions supported by this YAML configuration factory. + * + *

This method returns YAML file extensions that this factory can process. + * The factory requires Jackson YAML dependencies to be available on the classpath. + * If Jackson YAML libraries are missing, this method returns an empty list.

+ * + * @return a list containing "yml" and "yaml" extensions if Jackson YAML dependencies + * are available and the factory is active, empty list otherwise + */ + @Override + public List getSupportedExtensions() { + if (!isActive()) { + return Collections.emptyList(); + } + return Arrays.asList("yml", "yaml"); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java index c476f1d82c1..ae6af3f58b7 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with YAML. */ @Export -@Version("2.20.1") +@Version("2.21.0") package org.apache.logging.log4j.core.config.yaml; import org.osgi.annotation.bundle.Export; diff --git a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CustomConfigurationFactory.java b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CustomConfigurationFactory.java index ddbfe68ddca..fb71187ea42 100644 --- a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CustomConfigurationFactory.java +++ b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CustomConfigurationFactory.java @@ -17,6 +17,8 @@ package org.apache.logging.log4j.osgi.tests; import java.net.URI; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationFactory; @@ -60,4 +62,9 @@ public Configuration getConfiguration( public String[] getSupportedTypes() { return SUFFIXES; } + + @Override + public List getSupportedExtensions() { + return Collections.emptyList(); + } } diff --git a/src/changelog/.2.x.x/3775_add_supported_configuration_file_locations_api.xml b/src/changelog/.2.x.x/3775_add_supported_configuration_file_locations_api.xml new file mode 100644 index 00000000000..81f8b09220b --- /dev/null +++ b/src/changelog/.2.x.x/3775_add_supported_configuration_file_locations_api.xml @@ -0,0 +1,12 @@ + + + + + Add API to list supported configuration file extensions for external frameworks like Spring Boot to determine which configuration file formats are available at runtime + +