Skip to content

Commit 7cb505b

Browse files
committed
Sync APIs with S3 specification
Signed-off-by: Bala.FA <bala@minio.io>
1 parent f37052f commit 7cb505b

11 files changed

+233
-51
lines changed

api/src/main/java/io/minio/BaseS3Client.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -664,13 +664,18 @@ public CompletableFuture<AbortMultipartUploadResponse> abortMultipartUpload(
664664
public CompletableFuture<ObjectWriteResponse> completeMultipartUpload(
665665
CompleteMultipartUploadArgs args) {
666666
checkArgs(args);
667+
args.validateSsec(baseUrl.isHttps());
667668
Http.Body body = null;
668669
try {
669670
body = new Http.Body(new CompleteMultipartUpload(args.parts()), null, null, null);
670671
} catch (MinioException e) {
671672
return Utils.failedFuture(e);
672673
}
673-
return executePostAsync(args, null, new Http.QueryParameters(UPLOAD_ID, args.uploadId()), body)
674+
return executePostAsync(
675+
args,
676+
args.ssec() == null ? null : args.ssec().headers(),
677+
new Http.QueryParameters(UPLOAD_ID, args.uploadId()),
678+
body)
674679
.thenApply(
675680
response -> {
676681
try {
@@ -802,7 +807,7 @@ public CompletableFuture<GenericResponse> createBucket(CreateBucketArgs args) {
802807
if (locationConstraint.equals(Http.US_EAST_1)) {
803808
config =
804809
new CreateBucketConfiguration(
805-
locationConstraint, args.locationConfig(), args.bucketConfig());
810+
locationConstraint, args.locationConfig(), args.bucketConfig(), args.tags());
806811
}
807812

808813
Http.Body body = null;
@@ -951,6 +956,20 @@ public CompletableFuture<String> getBucketLocation(GetBucketLocationArgs args) {
951956
});
952957
}
953958

959+
/**
960+
* Do <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html">HeadBucket S3
961+
* API</a> asynchronously.
962+
*
963+
* @param args {@link HeadBucketArgs} object.
964+
* @return {@link CompletableFuture}&lt;{@link HeadBucketResponse}&gt; object.
965+
*/
966+
public CompletableFuture<HeadBucketResponse> headBucket(HeadBucketArgs args) {
967+
checkArgs(args);
968+
return executeHeadAsync(args, null, null)
969+
.thenApply(
970+
response -> new HeadBucketResponse(response.headers(), args.bucket(), args.region()));
971+
}
972+
954973
/**
955974
* Do <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html">HeadObject S3
956975
* API</a> asynchronously.

api/src/main/java/io/minio/BucketExistsArgs.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
package io.minio;
1818

1919
/** Arguments of {@link MinioAsyncClient#bucketExists} and {@link MinioClient#bucketExists}. */
20-
public class BucketExistsArgs extends BucketArgs {
20+
public class BucketExistsArgs extends HeadBucketBaseArgs {
2121
public static Builder builder() {
2222
return new Builder();
2323
}
2424

2525
/** Builder of {@link BucketExistsArgs}. */
26-
public static final class Builder extends BucketArgs.Builder<Builder, BucketExistsArgs> {}
26+
public static final class Builder extends HeadBucketBaseArgs.Builder<Builder, BucketExistsArgs> {}
2727
}

api/src/main/java/io/minio/CompleteMultipartUploadArgs.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
public class CompleteMultipartUploadArgs extends ObjectArgs {
2525
private String uploadId;
2626
private Part[] parts;
27+
private ServerSideEncryption.CustomerKey ssec;
2728

2829
protected CompleteMultipartUploadArgs() {}
2930

@@ -37,6 +38,9 @@ public CompleteMultipartUploadArgs(PutObjectBaseArgs args, String uploadId, Part
3738
super(args);
3839
this.uploadId = uploadId;
3940
this.parts = parts;
41+
if (args.sse() != null && args.sse() instanceof ServerSideEncryption.CustomerKey) {
42+
this.ssec = (ServerSideEncryption.CustomerKey) args.sse();
43+
}
4044
}
4145

4246
public String uploadId() {
@@ -47,6 +51,14 @@ public Part[] parts() {
4751
return parts;
4852
}
4953

54+
public ServerSideEncryption.CustomerKey ssec() {
55+
return ssec;
56+
}
57+
58+
public void validateSsec(boolean isHttps) {
59+
checkSse(ssec, isHttps);
60+
}
61+
5062
public static Builder builder() {
5163
return new Builder();
5264
}
@@ -72,6 +84,11 @@ public Builder parts(Part[] parts) {
7284
operations.add(args -> args.parts = parts);
7385
return this;
7486
}
87+
88+
public Builder ssec(ServerSideEncryption.CustomerKey ssec) {
89+
operations.add(args -> args.ssec = ssec);
90+
return this;
91+
}
7592
}
7693

7794
@Override
@@ -80,11 +97,13 @@ public boolean equals(Object o) {
8097
if (!(o instanceof CompleteMultipartUploadArgs)) return false;
8198
if (!super.equals(o)) return false;
8299
CompleteMultipartUploadArgs that = (CompleteMultipartUploadArgs) o;
83-
return Objects.equals(uploadId, that.uploadId) && Arrays.equals(parts, that.parts);
100+
return Objects.equals(uploadId, that.uploadId)
101+
&& Arrays.equals(parts, that.parts)
102+
&& Objects.equals(ssec, that.ssec);
84103
}
85104

86105
@Override
87106
public int hashCode() {
88-
return Objects.hash(super.hashCode(), uploadId, parts);
107+
return Objects.hash(super.hashCode(), uploadId, parts, ssec);
89108
}
90109
}

api/src/main/java/io/minio/CreateBucketBaseArgs.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
package io.minio;
1818

1919
import io.minio.messages.CreateBucketConfiguration;
20+
import io.minio.messages.Tags;
2021
import java.util.Objects;
2122

2223
/** Common arguments of {@link CreateBucketArgs} and {@link MakeBucketArgs}. */
2324
public abstract class CreateBucketBaseArgs extends BucketArgs {
2425
protected boolean objectLock;
2526
protected CreateBucketConfiguration.Location locationConfig;
2627
protected CreateBucketConfiguration.Bucket bucket;
28+
protected Tags tags;
2729

2830
protected CreateBucketBaseArgs() {}
2931

@@ -32,6 +34,7 @@ protected CreateBucketBaseArgs(CreateBucketBaseArgs args) {
3234
this.objectLock = args.objectLock;
3335
this.locationConfig = args.locationConfig;
3436
this.bucket = args.bucket;
37+
this.tags = args.tags;
3538
}
3639

3740
public boolean objectLock() {
@@ -46,6 +49,10 @@ public CreateBucketConfiguration.Bucket bucketConfig() {
4649
return bucket;
4750
}
4851

52+
public Tags tags() {
53+
return tags;
54+
}
55+
4956
/** Base argument builder of {@link CreateBucketBaseArgs}. */
5057
@SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class
5158
public abstract static class Builder<B extends Builder<B, A>, A extends CreateBucketBaseArgs>
@@ -69,6 +76,11 @@ public B bucketConfig(CreateBucketConfiguration.Bucket bucket) {
6976
operations.add(args -> args.bucket = bucket);
7077
return (B) this;
7178
}
79+
80+
public B tags(Tags tags) {
81+
operations.add(args -> args.tags = tags);
82+
return (B) this;
83+
}
7284
}
7385

7486
@Override
@@ -79,11 +91,12 @@ public boolean equals(Object o) {
7991
CreateBucketBaseArgs that = (CreateBucketBaseArgs) o;
8092
return objectLock == that.objectLock
8193
&& Objects.equals(locationConfig, that.locationConfig)
82-
&& Objects.equals(bucket, that.bucket);
94+
&& Objects.equals(bucket, that.bucket)
95+
&& Objects.equals(tags, that.tags);
8396
}
8497

8598
@Override
8699
public int hashCode() {
87-
return Objects.hash(super.hashCode(), objectLock, locationConfig, bucket);
100+
return Objects.hash(super.hashCode(), objectLock, locationConfig, bucket, tags);
88101
}
89102
}

api/src/main/java/io/minio/GenericUploadResponse.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818

1919
import io.minio.messages.CompleteMultipartUploadResult;
2020
import io.minio.messages.CopyObjectResult;
21+
import java.time.ZonedDateTime;
2122
import okhttp3.Headers;
2223

2324
/** Common response of {@link ObjectWriteResponse} and {@link PutObjectFanOutResponse}. */
2425
public class GenericUploadResponse extends GenericResponse {
2526
private String etag;
27+
private ZonedDateTime lastModified;
2628
private String checksumCRC32;
2729
private String checksumCRC32C;
2830
private String checksumCRC64NVME;
@@ -53,6 +55,7 @@ public GenericUploadResponse(
5355
CopyObjectResult result) {
5456
super(headers, bucket, region, object);
5557
this.etag = etag;
58+
this.lastModified = result.lastModified();
5659
if (result != null) {
5760
this.checksumType = result.checksumType();
5861
this.checksumCRC32 = result.checksumCRC32();
@@ -86,6 +89,10 @@ public String etag() {
8689
return etag;
8790
}
8891

92+
public ZonedDateTime lastModified() {
93+
return lastModified;
94+
}
95+
8996
public String checksumCRC32() {
9097
return checksumCRC32;
9198
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2025 MinIO, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.minio;
18+
19+
/** Arguments of {@link BaseS3Client#headBucket}. */
20+
public class HeadBucketArgs extends HeadBucketBaseArgs {
21+
public static Builder builder() {
22+
return new Builder();
23+
}
24+
25+
/** Builder of {@link HeadBucketArgs}. */
26+
public static final class Builder extends HeadBucketBaseArgs.Builder<Builder, HeadBucketArgs> {}
27+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2025 MinIO, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.minio;
18+
19+
/** Common arguments of {@link HeadBucketArgs} and {@link BucketExistsArgs}. */
20+
public abstract class HeadBucketBaseArgs extends BucketArgs {
21+
/** Builder of {@link HeadBucketBaseArgs}. */
22+
public abstract static class Builder<B extends Builder<B, A>, A extends HeadBucketBaseArgs>
23+
extends BucketArgs.Builder<B, A> {}
24+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2025 MinIO, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.minio;
18+
19+
/** Response of {@link BaseS3Client#headBucket}. */
20+
public class HeadBucketResponse extends GenericResponse {
21+
public HeadBucketResponse(okhttp3.Headers headers, String bucket, String region) {
22+
super(headers, bucket, region, null);
23+
}
24+
25+
public String bucketArn() {
26+
return this.headers().get("x-amz-bucket-arn");
27+
}
28+
29+
public String locationType() {
30+
return this.headers().get("x-amz-bucket-location-type");
31+
}
32+
33+
public String locationName() {
34+
return this.headers().get("x-amz-bucket-location-name");
35+
}
36+
37+
public String accessPointAlias() {
38+
return this.headers().get("x-amz-access-point-alias");
39+
}
40+
41+
@Override
42+
public String toString() {
43+
return "HeadObjectResponse{" + "bucket=" + bucket() + ", region=" + region() + "}";
44+
}
45+
}

api/src/main/java/io/minio/HeadObjectResponse.java

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import io.minio.messages.LegalHold;
2020
import io.minio.messages.RetentionMode;
2121
import java.time.ZonedDateTime;
22-
import java.util.ArrayList;
22+
import java.util.HashMap;
2323
import java.util.List;
2424
import java.util.Locale;
2525
import java.util.Map;
@@ -34,6 +34,7 @@ public class HeadObjectResponse extends GenericResponse {
3434
private LegalHold lockLegalHold;
3535
private boolean deleteMarker;
3636
private Http.Headers userMetadata;
37+
private Map<Checksum.Algorithm, String> checksums;
3738
private Checksum.Type checksumType;
3839

3940
public HeadObjectResponse(okhttp3.Headers headers, String bucket, String region, String object) {
@@ -72,6 +73,19 @@ public HeadObjectResponse(okhttp3.Headers headers, String bucket, String region,
7273

7374
value = headers.get("x-amz-checksum-type");
7475
this.checksumType = value == null ? null : Checksum.Type.valueOf(value);
76+
77+
Map<Checksum.Algorithm, String> checksums = new HashMap<>();
78+
value = headers.get("x-amz-checksum-crc32");
79+
if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.CRC32, value);
80+
value = headers.get("x-amz-checksum-crc32c");
81+
if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.CRC32C, value);
82+
value = headers.get("x-amz-checksum-crc64nvme");
83+
if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.CRC64NVME, value);
84+
value = headers.get("x-amz-checksum-sha1");
85+
if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.SHA1, value);
86+
value = headers.get("x-amz-checksum-sha256");
87+
if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.SHA256, value);
88+
if (checksums.size() != 0) this.checksums = checksums;
7589
}
7690

7791
public String etag() {
@@ -118,32 +132,13 @@ public Checksum.Type checksumType() {
118132
return checksumType;
119133
}
120134

121-
public List<Checksum.Algorithm> algorithms() {
122-
okhttp3.Headers headers = headers();
123-
List<Checksum.Algorithm> algorithms = new ArrayList<>();
124-
String value;
125-
126-
value = headers.get("x-amz-checksum-crc32");
127-
if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.CRC32);
128-
129-
value = headers.get("x-amz-checksum-crc32c");
130-
if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.CRC32C);
131-
132-
value = headers.get("x-amz-checksum-crc64nvme");
133-
if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.CRC64NVME);
134-
135-
value = headers.get("x-amz-checksum-sha1");
136-
if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.SHA1);
137-
138-
value = headers.get("x-amz-checksum-sha256");
139-
if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.SHA256);
140-
141-
return algorithms.size() == 0 ? null : algorithms;
135+
public Map<Checksum.Algorithm, String> checksums() {
136+
return checksums;
142137
}
143138

144139
@Override
145140
public String toString() {
146-
return "ObjectHead{"
141+
return "HeadObjectResponse{"
147142
+ "bucket="
148143
+ bucket()
149144
+ ", object="

0 commit comments

Comments
 (0)