Skip to content

Commit

Permalink
Expose object_size_hint variable from S3Client (#794)
Browse files Browse the repository at this point in the history
Co-authored-by: Michael Graeb <[email protected]>
  • Loading branch information
waahm7 and graebm authored Jun 14, 2024
1 parent adfb5a0 commit 4069614
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/main/java/software/amazon/awssdk/crt/s3/S3Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ public S3MetaRequest makeMetaRequest(S3MetaRequestOptions options) {
ChecksumAlgorithm.marshallAlgorithmsForJNI(checksumConfig.getValidateChecksumAlgorithmList()),
httpRequestBytes, options.getHttpRequest().getBodyStream(), requestFilePath, signingConfig,
responseHandlerNativeAdapter, endpoint == null ? null : endpoint.toString().getBytes(UTF8),
options.getResumeToken());
options.getResumeToken(), options.getObjectSizeHint());

metaRequest.setMetaRequestNativeHandle(metaRequestNativeHandle);

Expand Down Expand Up @@ -236,5 +236,5 @@ private static native long s3ClientMakeMetaRequest(long clientId, S3MetaRequest
int[] validateAlgorithms, byte[] httpRequestBytes,
HttpRequestBodyStream httpRequestBodyStream, byte[] requestFilePath,
AwsSigningConfig signingConfig, S3MetaRequestResponseHandlerNativeAdapter responseHandlerNativeAdapter,
byte[] endpoint, ResumeToken resumeToken);
byte[] endpoint, ResumeToken resumeToken, Long objectSizeHint);
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ private static Map<Integer, MetaRequestType> buildEnumMapping() {
private AwsSigningConfig signingConfig;
private URI endpoint;
private ResumeToken resumeToken;
private Long objectSizeHint;

public S3MetaRequestOptions withMetaRequestType(MetaRequestType metaRequestType) {
this.metaRequestType = metaRequestType;
Expand Down Expand Up @@ -263,4 +264,20 @@ public S3MetaRequestOptions withResumeToken(ResumeToken resumeToken) {
public ResumeToken getResumeToken() {
return resumeToken;
}

/*
* (Optional)
* Total object size hint, in bytes.
* The optimal strategy for downloading a file depends on its size.
* Set this hint to help the S3 client choose the best strategy for this particular file.
* This is just used as an estimate, so it's okay to provide an approximate value if the exact size is unknown.
*/
public S3MetaRequestOptions withObjectSizeHint(Long objectSizeHint) {
this.objectSizeHint = objectSizeHint;
return this;
}

public Long getObjectSizeHint() {
return objectSizeHint;
}
}
24 changes: 24 additions & 0 deletions src/native/crt.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,30 @@ int aws_size_t_from_java(JNIEnv *env, size_t *out_size, jlong java_long, const c
return AWS_OP_ERR;
}

int aws_uint64_t_from_java_Long(JNIEnv *env, uint64_t *out_long, jobject java_Long, const char *errmsg_prefix) {
if (java_Long == NULL) {
aws_jni_throw_null_pointer_exception(env, "%s can't be null");
goto error;
}

jlong jlong_value = (*env)->CallLongMethod(env, java_Long, boxed_long_properties.long_value_method_id);
if ((*env)->ExceptionCheck(env)) {
goto error;
}

int64_t jlong_value_int64 = (int64_t)jlong_value;
if (jlong_value_int64 < 0) {
aws_jni_throw_illegal_argument_exception(env, "%s cannot be negative", errmsg_prefix);
goto error;
}
*out_long = (uint64_t)jlong_value_int64;
return AWS_OP_SUCCESS;

error:
*out_long = 0;
return AWS_OP_ERR;
}

jbyteArray aws_java_byte_array_new(JNIEnv *env, size_t size) {
jbyteArray jArray = (*env)->NewByteArray(env, (jsize)size);
return jArray;
Expand Down
7 changes: 7 additions & 0 deletions src/native/crt.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ bool aws_jni_get_and_clear_exception(JNIEnv *env, jthrowable *out);
******************************************************************************/
int aws_size_t_from_java(JNIEnv *env, size_t *out_size, jlong java_long, const char *errmsg_prefix);

/*******************************************************************************
* Set a uint64_t based on a Long (jobject).
* If conversion fails, a java IllegalArgumentException is thrown like
* "{errmsg_prefix} cannot be negative" and AWS_OP_ERR is returned.
******************************************************************************/
int aws_uint64_t_from_java_Long(JNIEnv *env, uint64_t *out_long, jobject java_Long, const char *errmsg_prefix);

/*******************************************************************************
* aws_java_byte_array_new - Creates a new Java byte[]
******************************************************************************/
Expand Down
11 changes: 10 additions & 1 deletion src/native/s3_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,8 @@ JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_s3_S3Client_s3ClientMake
jobject java_signing_config,
jobject java_response_handler_jobject,
jbyteArray jni_endpoint,
jobject java_resume_token_jobject) {
jobject java_resume_token_jobject,
jobject jni_object_size_hint) {
(void)jni_class;
aws_cache_jni_ids(env);

Expand Down Expand Up @@ -1039,6 +1040,13 @@ JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_s3_S3Client_s3ClientMake
checksum_config.validate_checksum_algorithms = &response_checksum_list;
}

uint64_t object_size_hint = 0;
if (jni_object_size_hint != NULL) {
if (aws_uint64_t_from_java_Long(env, &object_size_hint, jni_object_size_hint, "objectSizeHint")) {
goto done;
}
}

struct aws_s3_meta_request_options meta_request_options = {
.type = meta_request_type,
.checksum_config = &checksum_config,
Expand All @@ -1053,6 +1061,7 @@ JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_s3_S3Client_s3ClientMake
.shutdown_callback = s_on_s3_meta_request_shutdown_complete_callback,
.endpoint = jni_endpoint != NULL ? &endpoint : NULL,
.resume_token = resume_token,
.object_size_hint = jni_object_size_hint != NULL ? &object_size_hint : NULL,
};

meta_request = aws_s3_client_make_meta_request(client, &meta_request_options);
Expand Down
48 changes: 48 additions & 0 deletions src/test/java/software/amazon/awssdk/crt/test/S3ClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,54 @@ public void onFinished(S3FinishedResponseContext context) {
Assert.fail(ex.getMessage());
}
}

@Test
public void testS3GetWithSizeHint() {
skipIfAndroid();
skipIfNetworkUnavailable();
Assume.assumeTrue(hasAwsCredentials());
S3ClientOptions clientOptions = new S3ClientOptions().withRegion(REGION);
try (S3Client client = createS3Client(clientOptions)) {
CompletableFuture<Integer> onFinishedFuture = new CompletableFuture<>();
S3MetaRequestResponseHandler responseHandler = new S3MetaRequestResponseHandler() {

@Override
public int onResponseBody(ByteBuffer bodyBytesIn, long objectRangeStart, long objectRangeEnd) {
byte[] bytes = new byte[bodyBytesIn.remaining()];
bodyBytesIn.get(bytes);
Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, "Body Response: " + Arrays.toString(bytes));
return 0;
}

@Override
public void onFinished(S3FinishedResponseContext context) {
Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3,
"Meta request finished with error code " + context.getErrorCode());
if (context.getErrorCode() != 0) {
onFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context));
return;
}
onFinishedFuture.complete(Integer.valueOf(context.getErrorCode()));
}
};

HttpHeader[] headers = { new HttpHeader("Host", ENDPOINT) };
HttpRequest httpRequest = new HttpRequest("GET", PRE_EXIST_1MB_PATH, headers, null);

S3MetaRequestOptions metaRequestOptions = new S3MetaRequestOptions()
.withMetaRequestType(MetaRequestType.GET_OBJECT).withHttpRequest(httpRequest)
.withResponseHandler(responseHandler)
// Passing a 5GB size hint intentionally to verify
// that an incorrect size_hint and size_hint > UINT32.MAX work fine.
.withObjectSizeHint(5L * 1024 * 1024 * 1024);

try (S3MetaRequest metaRequest = client.makeMetaRequest(metaRequestOptions)) {
Assert.assertEquals(Integer.valueOf(0), onFinishedFuture.get());
}
} catch (InterruptedException | ExecutionException ex) {
Assert.fail(ex.getMessage());
}
}

@Test
public void testS3GetErrorHeadersAreReported() {
Expand Down

0 comments on commit 4069614

Please sign in to comment.