From 8bcdb4b06bdb2a012a66b44d11a8acceee08fb5c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 25 Jun 2024 17:06:10 -0700 Subject: [PATCH] Improve error message when spring.config.import fails to resolve Update `StandardConfigDataLocationResolver` to give a better error message when a location cannot be resolved. Prior to this commit, a location with a misspelling in the prefix would only give an error about the file extension being not known. Fixes gh-36243 --- .../config/ConfigDataLocationResolvers.java | 12 ++++++------ .../config/StandardConfigDataLocationResolver.java | 14 ++++++++++++-- .../StandardConfigDataLocationResolverTests.java | 12 +++++++++++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java index 00b86060c20..65477c4a884 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,17 +69,17 @@ class ConfigDataLocationResolvers { @SuppressWarnings("rawtypes") private List> reorder(List resolvers) { List> reordered = new ArrayList<>(resolvers.size()); - StandardConfigDataLocationResolver resourceResolver = null; + ConfigDataLocationResolver standardConfigDataLocationResolver = null; for (ConfigDataLocationResolver resolver : resolvers) { - if (resolver instanceof StandardConfigDataLocationResolver configDataLocationResolver) { - resourceResolver = configDataLocationResolver; + if (resolver instanceof StandardConfigDataLocationResolver) { + standardConfigDataLocationResolver = resolver; } else { reordered.add(resolver); } } - if (resourceResolver != null) { - reordered.add(resourceResolver); + if (standardConfigDataLocationResolver != null) { + reordered.add(standardConfigDataLocationResolver); } return Collections.unmodifiableList(reordered); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java index ad95e7a22ec..beb076ad869 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java @@ -45,6 +45,7 @@ import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; +import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; /** @@ -231,8 +232,17 @@ public class StandardConfigDataLocationResolver if (configDataLocation.isOptional()) { return Collections.emptySet(); } - throw new IllegalStateException("File extension is not known to any PropertySourceLoader. " - + "If the location is meant to reference a directory, it must end in '/' or File.separator"); + if (configDataLocation.hasPrefix(PREFIX) || configDataLocation.hasPrefix(ResourceUtils.FILE_URL_PREFIX) + || configDataLocation.hasPrefix(ResourceUtils.CLASSPATH_URL_PREFIX) + || configDataLocation.toString().indexOf(':') == -1) { + throw new IllegalStateException("File extension is not known to any PropertySourceLoader. " + + "If the location is meant to reference a directory, it must end in '/' or File.separator"); + } + throw new IllegalStateException( + "Incorrect ConfigDataLocationResolver chosen or file extension is not known to any PropertySourceLoader. " + + "If the location is meant to reference a directory, it must end in '/' or File.separator. " + + "The location is being resolved using the StandardConfigDataLocationResolver, " + + "check the location prefix if a different resolver is expected"); } private String getLoadableFileExtension(PropertySourceLoader loader, String file) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/StandardConfigDataLocationResolverTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/StandardConfigDataLocationResolverTests.java index 15709fb2c1e..3f39c8643ad 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/StandardConfigDataLocationResolverTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/StandardConfigDataLocationResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,6 +99,16 @@ class StandardConfigDataLocationResolverTests { .satisfies((ex) -> assertThat(ex.getCause()).hasMessageStartingWith("File extension is not known")); } + @Test + void resolveWhenLocationHasUnknownPrefixAndNoMatchingLoaderThrowsException() { + ConfigDataLocation location = ConfigDataLocation + .of("typo:src/test/resources/configdata/properties/application.unknown"); + assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location)) + .withMessageStartingWith("Unable to load config data from") + .satisfies((ex) -> assertThat(ex.getCause()).hasMessageStartingWith( + "Incorrect ConfigDataLocationResolver chosen or file extension is not known to any PropertySourceLoader")); + } + @Test void resolveWhenLocationWildcardIsSpecifiedForClasspathLocationThrowsException() { ConfigDataLocation location = ConfigDataLocation.of("classpath*:application.properties");