diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-30203bb.json b/.changes/next-release/bugfix-AWSSDKforJavav2-30203bb.json new file mode 100644 index 000000000000..1b639f2e5f6c --- /dev/null +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-30203bb.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Fix NPE in `ProfileFileSupplier.defaultSupplier` when both credentials and config files do not exist." +} diff --git a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFile.java b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFile.java index 89ffa2b64ad2..5199e729da8f 100644 --- a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFile.java +++ b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFile.java @@ -85,6 +85,13 @@ public static Aggregator aggregator() { return new Aggregator(); } + /** + * Create an empty profile file. + */ + static ProfileFile empty() { + return new ProfileFile(Collections.emptyMap()); + } + /** * Get the default profile file, using the credentials file from "~/.aws/credentials", the config file from "~/.aws/config" * and the "default" profile. This default behavior can be customized using the @@ -310,8 +317,10 @@ public void setType(Type type) { @Override public ProfileFile build() { + Validate.isTrue(content != null || contentLocation != null, + "content or contentLocation must be set."); InputStream stream = content != null ? content : - FunctionalUtils.invokeSafely(() -> Files.newInputStream(contentLocation)); + FunctionalUtils.invokeSafely(() -> Files.newInputStream(contentLocation)); Validate.paramNotNull(type, "type"); Validate.paramNotNull(stream, "content"); diff --git a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFileSupplier.java b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFileSupplier.java index 4dec2883c814..7046c300d339 100644 --- a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFileSupplier.java +++ b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFileSupplier.java @@ -55,13 +55,15 @@ static ProfileFileSupplier defaultSupplier() { = ProfileFileLocation.configurationFileLocation() .map(path -> reloadWhenModified(path, ProfileFile.Type.CONFIGURATION)); - ProfileFileSupplier supplier = () -> ProfileFile.builder().build(); + ProfileFileSupplier supplier; if (credentialsSupplierOptional.isPresent() && configurationSupplierOptional.isPresent()) { supplier = aggregate(credentialsSupplierOptional.get(), configurationSupplierOptional.get()); } else if (credentialsSupplierOptional.isPresent()) { supplier = credentialsSupplierOptional.get(); } else if (configurationSupplierOptional.isPresent()) { supplier = configurationSupplierOptional.get(); + } else { + supplier = fixedProfileFile(ProfileFile.empty()); } return supplier; diff --git a/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java b/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java index bc39916da5d9..e5ca165840ae 100644 --- a/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java +++ b/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java @@ -48,6 +48,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; +import software.amazon.awssdk.testutils.EnvironmentVariableHelper; import software.amazon.awssdk.utils.Pair; import software.amazon.awssdk.utils.StringInputStream; import software.amazon.awssdk.testutils.LogCaptor; @@ -581,6 +582,15 @@ public void checkPermission(Permission perm) { } } + @Test + public void defaultSupplier_noCredentialsFiles_returnsEmptyProvider() { + EnvironmentVariableHelper.run(environmentVariableHelper -> { + environmentVariableHelper.set(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE, "no-such-file"); + environmentVariableHelper.set(ProfileFileSystemSetting.AWS_CONFIG_FILE, "no-such-file"); + ProfileFileSupplier supplier = ProfileFileSupplier.defaultSupplier(); + assertThat(supplier.get().profiles()).isEmpty(); + }); + } private Path writeTestFile(String contents, Path path) { try { diff --git a/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileTest.java b/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileTest.java index e827e07dbd8b..9d2310c69d2b 100644 --- a/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileTest.java +++ b/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileTest.java @@ -570,6 +570,14 @@ public void returnsEmptyMap_when_AwsFilesDoNotExist() { assertThat(missingProfile.profiles()).isInstanceOf(Map.class); } + @Test + public void builderValidatesContentRequired() { + assertThatThrownBy(() -> ProfileFile.builder().type(ProfileFile.Type.CONFIGURATION).build()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("content or contentLocation must be set."); + + } + private ProfileFile configFile(String configFile) { return ProfileFile.builder() .content(configFile)