diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-75d93e4.json b/.changes/next-release/bugfix-AWSSDKforJavav2-75d93e4.json new file mode 100644 index 000000000000..5a6cee49c5ee --- /dev/null +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-75d93e4.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Include the sdk-core mime.types resource in the native-image resource configuration so default MIME type detection works in GraalVM native images. Fixes [#7063](https://github.com/aws/aws-sdk-java-v2/issues/7063)", + "contributor": "sjh9714" +} diff --git a/core/sdk-core/src/main/resources/META-INF/native-image/software.amazon.awssdk/sdk-core/resource-config.json b/core/sdk-core/src/main/resources/META-INF/native-image/software.amazon.awssdk/sdk-core/resource-config.json index e445c5021c38..f2fc4e67315f 100644 --- a/core/sdk-core/src/main/resources/META-INF/native-image/software.amazon.awssdk/sdk-core/resource-config.json +++ b/core/sdk-core/src/main/resources/META-INF/native-image/software.amazon.awssdk/sdk-core/resource-config.json @@ -3,6 +3,9 @@ "includes": [ { "pattern": "software/amazon/awssdk/services/\\w+/execution.interceptors" + }, + { + "pattern": "\\Qsoftware/amazon/awssdk/core/util/mime.types\\E" }] } -} \ No newline at end of file +} diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/util/MimetypeTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/util/MimetypeTest.java index 95406a1f119c..a4de0fd29492 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/util/MimetypeTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/util/MimetypeTest.java @@ -19,12 +19,19 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.io.IOException; +import java.io.InputStream; import java.nio.file.Path; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import software.amazon.awssdk.utils.IoUtils; public class MimetypeTest { + private static final String MIME_TYPES_RESOURCE = "software/amazon/awssdk/core/util/mime.types"; + private static final String NATIVE_IMAGE_RESOURCE_CONFIG = + "META-INF/native-image/software.amazon.awssdk/sdk-core/resource-config.json"; + private static Mimetype mimetype; @BeforeAll @@ -58,4 +65,14 @@ public void pathWithoutFileName_defaulttoBeStream() throws Exception { when(mockPath.getFileName()).thenReturn(null); assertThat(mimetype.getMimetype(mockPath)).isEqualTo(Mimetype.MIMETYPE_OCTET_STREAM); } + + @Test + public void nativeImageResourceConfig_includesMimeTypes() throws IOException { + try (InputStream resourceConfig = Mimetype.class.getClassLoader().getResourceAsStream(NATIVE_IMAGE_RESOURCE_CONFIG)) { + assertThat(resourceConfig).isNotNull(); + + String resourceConfigContents = IoUtils.toUtf8String(resourceConfig); + assertThat(resourceConfigContents).contains(MIME_TYPES_RESOURCE); + } + } } diff --git a/test/sdk-native-image-test/src/main/java/software/amazon/awssdk/nativeimagetest/S3TestRunner.java b/test/sdk-native-image-test/src/main/java/software/amazon/awssdk/nativeimagetest/S3TestRunner.java index 4290edd7a87d..cb36c0f9db62 100644 --- a/test/sdk-native-image-test/src/main/java/software/amazon/awssdk/nativeimagetest/S3TestRunner.java +++ b/test/sdk-native-image-test/src/main/java/software/amazon/awssdk/nativeimagetest/S3TestRunner.java @@ -15,7 +15,10 @@ package software.amazon.awssdk.nativeimagetest; +import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,11 +27,14 @@ import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.CreateBucketResponse; +import software.amazon.awssdk.services.s3.model.HeadObjectResponse; public class S3TestRunner implements TestRunner { private static final String BUCKET_NAME = "v2-native-image-tests-" + UUID.randomUUID(); private static final Logger logger = LoggerFactory.getLogger(S3TestRunner.class); private static final String KEY = "key"; + private static final String MIMETYPE_KEY = "mimetype-key.txt"; + private static final String EXPECTED_TXT_CONTENT_TYPE = "text/plain"; private final S3Client s3ApacheHttpClient; private final S3Client s3UrlConnectionHttpClient; private final S3AsyncClient s3NettyClient; @@ -43,6 +49,7 @@ public S3TestRunner() { public void runTests() { logger.info("starting to run S3 tests"); CreateBucketResponse bucketResponse = null; + Path tempFile = null; try { bucketResponse = s3UrlConnectionHttpClient.createBucket(b -> b.bucket(BUCKET_NAME)); @@ -56,12 +63,42 @@ public void runTests() { s3NettyClient.getObject(b -> b.bucket(BUCKET_NAME).key(KEY), AsyncResponseTransformer.toBytes()).join(); + tempFile = createTempTextFile(); + s3ApacheHttpClient.putObject(b -> b.bucket(BUCKET_NAME).key(MIMETYPE_KEY), + RequestBody.fromFile(tempFile)); + + HeadObjectResponse head = s3ApacheHttpClient.headObject(b -> b.bucket(BUCKET_NAME).key(MIMETYPE_KEY)); + String contentType = head.contentType(); + if (contentType == null || !contentType.startsWith(EXPECTED_TXT_CONTENT_TYPE)) { + throw new RuntimeException("Expected Content-Type to start with '" + EXPECTED_TXT_CONTENT_TYPE + + "' but was '" + contentType + "'. The mime.types resource may be missing" + + " from the native image classpath."); + } + } finally { if (bucketResponse != null) { s3NettyClient.deleteObject(b -> b.bucket(BUCKET_NAME).key(KEY)).join(); + s3NettyClient.deleteObject(b -> b.bucket(BUCKET_NAME).key(MIMETYPE_KEY)).join(); s3NettyClient.deleteBucket(b -> b.bucket(BUCKET_NAME)).join(); } + if (tempFile != null) { + try { + Files.deleteIfExists(tempFile); + } catch (IOException e) { + logger.warn("Failed to delete temp file {}", tempFile, e); + } + } + } + } + + private static Path createTempTextFile() { + try { + Path file = Files.createTempFile("native-image-mimetype-", ".txt"); + Files.write(file, "helloworld".getBytes(StandardCharsets.UTF_8)); + return file; + } catch (IOException e) { + throw new RuntimeException("Failed to create temp .txt file for mimetype test", e); } } }