diff --git a/README.md b/README.md index a10780c4a..f4b4a9cad 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ MinIO Java SDK is Simple Storage Service (aka S3) client to perform bucket and object operations to any Amazon S3 compatible object storage service. -For a complete list of APIs and examples, please take a look at the [Java Client API Reference](https://min.io/docs/minio/linux/developers/java/API.html) documentation. +For a complete list of APIs and examples, please take a look at the [Java Client API Reference](https://docs.min.io/enterprise/aistor-object-store/developers/sdk/java/api/) documentation. ## Minimum Requirements Java 1.8 or above. @@ -105,12 +105,12 @@ $ mc ls play/asiatrip/ ``` ## More References -* [Java Client API Reference](https://min.io/docs/minio/linux/developers/java/API.html) +* [Java Client API Reference](https://docs.min.io/enterprise/aistor-object-store/developers/sdk/java/api/) * [Javadoc](https://minio-java.min.io/) * [Examples](https://github.com/minio/minio-java/tree/release/examples) ## Explore Further -* [Complete Documentation](https://min.io/docs/minio/kubernetes/upstream/index.html) +* [Complete Documentation](https://docs.min.io/enterprise/aistor-object-store/) * [Build your own Photo API Service - Full Application Example ](https://github.com/minio/minio-java-rest-example) ## Contribute diff --git a/api/src/main/java/io/minio/BaseS3Client.java b/api/src/main/java/io/minio/BaseS3Client.java index af01d2aff..1ac0e301b 100644 --- a/api/src/main/java/io/minio/BaseS3Client.java +++ b/api/src/main/java/io/minio/BaseS3Client.java @@ -413,6 +413,10 @@ private void onResponse(final Response response) throws IOException { code = result[0]; message = result[1]; break; + case 403: + code = "AccessDenied"; + message = "Access denied"; + break; case 404: if (s3request.object() != null) { code = "NoSuchKey"; @@ -425,8 +429,8 @@ private void onResponse(final Response response) throws IOException { message = "Request resource not found"; } break; - case 501: case 405: + case 501: code = "MethodNotAllowed"; message = "The specified method is not allowed against this resource"; break; @@ -439,10 +443,6 @@ private void onResponse(final Response response) throws IOException { message = "Request resource conflicts"; } break; - case 403: - code = "AccessDenied"; - message = "Access denied"; - break; case 412: code = "PreconditionFailed"; message = "At least one of the preconditions you specified did not hold"; @@ -664,13 +664,18 @@ public CompletableFuture abortMultipartUpload( public CompletableFuture completeMultipartUpload( CompleteMultipartUploadArgs args) { checkArgs(args); + args.validateSsec(baseUrl.isHttps()); Http.Body body = null; try { body = new Http.Body(new CompleteMultipartUpload(args.parts()), null, null, null); } catch (MinioException e) { return Utils.failedFuture(e); } - return executePostAsync(args, null, new Http.QueryParameters(UPLOAD_ID, args.uploadId()), body) + return executePostAsync( + args, + args.ssec() == null ? null : args.ssec().headers(), + new Http.QueryParameters(UPLOAD_ID, args.uploadId()), + body) .thenApply( response -> { try { @@ -794,16 +799,14 @@ public CompletableFuture createBucket(CreateBucketArgs args) { region = Http.US_EAST_1; } - Http.Headers headers = - args.objectLock() ? new Http.Headers("x-amz-bucket-object-lock-enabled", "true") : null; - final String locationConstraint = region; + Http.Headers headers = new Http.Headers(); + if (args.objectLock()) headers.put("x-amz-bucket-object-lock-enabled", "true"); + if (args.forceCreate()) headers.put("x-minio-force-create", "true"); - CreateBucketConfiguration config = null; - if (locationConstraint.equals(Http.US_EAST_1)) { - config = - new CreateBucketConfiguration( - locationConstraint, args.locationConfig(), args.bucketConfig()); - } + final String locationConstraint = region; + CreateBucketConfiguration config = + new CreateBucketConfiguration( + locationConstraint, args.locationConfig(), args.bucketConfig(), args.tags()); Http.Body body = null; try { @@ -951,6 +954,20 @@ public CompletableFuture getBucketLocation(GetBucketLocationArgs args) { }); } + /** + * Do HeadBucket S3 + * API asynchronously. + * + * @param args {@link HeadBucketArgs} object. + * @return {@link CompletableFuture}<{@link HeadBucketResponse}> object. + */ + public CompletableFuture headBucket(HeadBucketArgs args) { + checkArgs(args); + return executeHeadAsync(args, null, null) + .thenApply( + response -> new HeadBucketResponse(response.headers(), args.bucket(), args.region())); + } + /** * Do HeadObject S3 * API asynchronously. diff --git a/api/src/main/java/io/minio/BucketExistsArgs.java b/api/src/main/java/io/minio/BucketExistsArgs.java index 2dd41a4ea..ce2f416d8 100644 --- a/api/src/main/java/io/minio/BucketExistsArgs.java +++ b/api/src/main/java/io/minio/BucketExistsArgs.java @@ -17,11 +17,11 @@ package io.minio; /** Arguments of {@link MinioAsyncClient#bucketExists} and {@link MinioClient#bucketExists}. */ -public class BucketExistsArgs extends BucketArgs { +public class BucketExistsArgs extends HeadBucketBaseArgs { public static Builder builder() { return new Builder(); } /** Builder of {@link BucketExistsArgs}. */ - public static final class Builder extends BucketArgs.Builder {} + public static final class Builder extends HeadBucketBaseArgs.Builder {} } diff --git a/api/src/main/java/io/minio/CompleteMultipartUploadArgs.java b/api/src/main/java/io/minio/CompleteMultipartUploadArgs.java index 824ea42d1..0ba072892 100644 --- a/api/src/main/java/io/minio/CompleteMultipartUploadArgs.java +++ b/api/src/main/java/io/minio/CompleteMultipartUploadArgs.java @@ -24,6 +24,7 @@ public class CompleteMultipartUploadArgs extends ObjectArgs { private String uploadId; private Part[] parts; + private ServerSideEncryption.CustomerKey ssec; protected CompleteMultipartUploadArgs() {} @@ -37,6 +38,9 @@ public CompleteMultipartUploadArgs(PutObjectBaseArgs args, String uploadId, Part super(args); this.uploadId = uploadId; this.parts = parts; + if (args.sse() != null && args.sse() instanceof ServerSideEncryption.CustomerKey) { + this.ssec = (ServerSideEncryption.CustomerKey) args.sse(); + } } public String uploadId() { @@ -47,6 +51,14 @@ public Part[] parts() { return parts; } + public ServerSideEncryption.CustomerKey ssec() { + return ssec; + } + + public void validateSsec(boolean isHttps) { + checkSse(ssec, isHttps); + } + public static Builder builder() { return new Builder(); } @@ -72,6 +84,11 @@ public Builder parts(Part[] parts) { operations.add(args -> args.parts = parts); return this; } + + public Builder ssec(ServerSideEncryption.CustomerKey ssec) { + operations.add(args -> args.ssec = ssec); + return this; + } } @Override @@ -80,11 +97,13 @@ public boolean equals(Object o) { if (!(o instanceof CompleteMultipartUploadArgs)) return false; if (!super.equals(o)) return false; CompleteMultipartUploadArgs that = (CompleteMultipartUploadArgs) o; - return Objects.equals(uploadId, that.uploadId) && Arrays.equals(parts, that.parts); + return Objects.equals(uploadId, that.uploadId) + && Arrays.equals(parts, that.parts) + && Objects.equals(ssec, that.ssec); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), uploadId, parts); + return Objects.hash(super.hashCode(), uploadId, parts, ssec); } } diff --git a/api/src/main/java/io/minio/CreateBucketBaseArgs.java b/api/src/main/java/io/minio/CreateBucketBaseArgs.java index bf4f7cb08..a44f34d02 100644 --- a/api/src/main/java/io/minio/CreateBucketBaseArgs.java +++ b/api/src/main/java/io/minio/CreateBucketBaseArgs.java @@ -17,6 +17,7 @@ package io.minio; import io.minio.messages.CreateBucketConfiguration; +import io.minio.messages.Tags; import java.util.Objects; /** Common arguments of {@link CreateBucketArgs} and {@link MakeBucketArgs}. */ @@ -24,6 +25,8 @@ public abstract class CreateBucketBaseArgs extends BucketArgs { protected boolean objectLock; protected CreateBucketConfiguration.Location locationConfig; protected CreateBucketConfiguration.Bucket bucket; + protected Tags tags; + protected boolean forceCreate; protected CreateBucketBaseArgs() {} @@ -32,6 +35,8 @@ protected CreateBucketBaseArgs(CreateBucketBaseArgs args) { this.objectLock = args.objectLock; this.locationConfig = args.locationConfig; this.bucket = args.bucket; + this.tags = args.tags; + this.forceCreate = args.forceCreate; } public boolean objectLock() { @@ -46,6 +51,14 @@ public CreateBucketConfiguration.Bucket bucketConfig() { return bucket; } + public Tags tags() { + return tags; + } + + public boolean forceCreate() { + return forceCreate; + } + /** Base argument builder of {@link CreateBucketBaseArgs}. */ @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public abstract static class Builder, A extends CreateBucketBaseArgs> @@ -69,6 +82,16 @@ public B bucketConfig(CreateBucketConfiguration.Bucket bucket) { operations.add(args -> args.bucket = bucket); return (B) this; } + + public B tags(Tags tags) { + operations.add(args -> args.tags = tags); + return (B) this; + } + + public B forceCreate(boolean forceCreate) { + operations.add(args -> args.forceCreate = forceCreate); + return (B) this; + } } @Override @@ -79,11 +102,13 @@ public boolean equals(Object o) { CreateBucketBaseArgs that = (CreateBucketBaseArgs) o; return objectLock == that.objectLock && Objects.equals(locationConfig, that.locationConfig) - && Objects.equals(bucket, that.bucket); + && Objects.equals(bucket, that.bucket) + && Objects.equals(tags, that.tags) + && forceCreate == that.forceCreate; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), objectLock, locationConfig, bucket); + return Objects.hash(super.hashCode(), objectLock, locationConfig, bucket, tags, forceCreate); } } diff --git a/api/src/main/java/io/minio/DownloadObjectArgs.java b/api/src/main/java/io/minio/DownloadObjectArgs.java index e4e96ebec..46b994344 100644 --- a/api/src/main/java/io/minio/DownloadObjectArgs.java +++ b/api/src/main/java/io/minio/DownloadObjectArgs.java @@ -16,6 +16,7 @@ package io.minio; +import java.time.ZonedDateTime; import java.util.Objects; /** @@ -25,6 +26,10 @@ public class DownloadObjectArgs extends ObjectReadArgs { private String filename; private boolean overwrite; + protected String matchETag; + protected String notMatchETag; + protected ZonedDateTime modifiedSince; + protected ZonedDateTime unmodifiedSince; public String filename() { return filename; @@ -34,6 +39,22 @@ public boolean overwrite() { return overwrite; } + public String matchETag() { + return matchETag; + } + + public String notMatchETag() { + return notMatchETag; + } + + public ZonedDateTime modifiedSince() { + return modifiedSince; + } + + public ZonedDateTime unmodifiedSince() { + return unmodifiedSince; + } + public static Builder builder() { return new Builder(); } @@ -54,6 +75,28 @@ public Builder overwrite(boolean flag) { operations.add(args -> args.overwrite = flag); return this; } + + public Builder matchETag(String etag) { + Utils.validateNullOrNotEmptyString(etag, "etag"); + operations.add(args -> args.matchETag = etag); + return this; + } + + public Builder notMatchETag(String etag) { + Utils.validateNullOrNotEmptyString(etag, "etag"); + operations.add(args -> args.notMatchETag = etag); + return this; + } + + public Builder modifiedSince(ZonedDateTime modifiedTime) { + operations.add(args -> args.modifiedSince = modifiedTime); + return this; + } + + public Builder unmodifiedSince(ZonedDateTime unmodifiedTime) { + operations.add(args -> args.unmodifiedSince = unmodifiedTime); + return this; + } } @Override @@ -62,12 +105,23 @@ public boolean equals(Object o) { if (!(o instanceof DownloadObjectArgs)) return false; if (!super.equals(o)) return false; DownloadObjectArgs that = (DownloadObjectArgs) o; - if (!Objects.equals(filename, that.filename)) return false; - return overwrite == that.overwrite; + return Objects.equals(filename, that.filename) + && overwrite == that.overwrite + && Objects.equals(matchETag, that.matchETag) + && Objects.equals(notMatchETag, that.notMatchETag) + && Objects.equals(modifiedSince, that.modifiedSince) + && Objects.equals(unmodifiedSince, that.unmodifiedSince); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), filename, overwrite); + return Objects.hash( + super.hashCode(), + filename, + overwrite, + matchETag, + notMatchETag, + modifiedSince, + unmodifiedSince); } } diff --git a/api/src/main/java/io/minio/GenericUploadResponse.java b/api/src/main/java/io/minio/GenericUploadResponse.java index d62c1d83d..fbb15e6b4 100644 --- a/api/src/main/java/io/minio/GenericUploadResponse.java +++ b/api/src/main/java/io/minio/GenericUploadResponse.java @@ -18,11 +18,13 @@ import io.minio.messages.CompleteMultipartUploadResult; import io.minio.messages.CopyObjectResult; +import java.time.ZonedDateTime; import okhttp3.Headers; /** Common response of {@link ObjectWriteResponse} and {@link PutObjectFanOutResponse}. */ public class GenericUploadResponse extends GenericResponse { private String etag; + private ZonedDateTime lastModified; private String checksumCRC32; private String checksumCRC32C; private String checksumCRC64NVME; @@ -54,6 +56,7 @@ public GenericUploadResponse( super(headers, bucket, region, object); this.etag = etag; if (result != null) { + this.lastModified = result.lastModified(); this.checksumType = result.checksumType(); this.checksumCRC32 = result.checksumCRC32(); this.checksumCRC32C = result.checksumCRC32C(); @@ -86,6 +89,10 @@ public String etag() { return etag; } + public ZonedDateTime lastModified() { + return lastModified; + } + public String checksumCRC32() { return checksumCRC32; } diff --git a/api/src/main/java/io/minio/GetObjectArgs.java b/api/src/main/java/io/minio/GetObjectArgs.java index eecf41ca5..8fb1ef937 100644 --- a/api/src/main/java/io/minio/GetObjectArgs.java +++ b/api/src/main/java/io/minio/GetObjectArgs.java @@ -21,13 +21,7 @@ public class GetObjectArgs extends ObjectConditionalReadArgs { protected GetObjectArgs() {} public GetObjectArgs(DownloadObjectArgs args) { - this.extraHeaders = args.extraHeaders; - this.extraQueryParams = args.extraQueryParams; - this.bucketName = args.bucketName; - this.region = args.region; - this.objectName = args.objectName; - this.versionId = args.versionId; - this.ssec = args.ssec; + super(args); } public static Builder builder() { diff --git a/api/src/main/java/io/minio/HeadBucketArgs.java b/api/src/main/java/io/minio/HeadBucketArgs.java new file mode 100644 index 000000000..9c534f435 --- /dev/null +++ b/api/src/main/java/io/minio/HeadBucketArgs.java @@ -0,0 +1,27 @@ +/* + * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2025 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.minio; + +/** Arguments of {@link BaseS3Client#headBucket}. */ +public class HeadBucketArgs extends HeadBucketBaseArgs { + public static Builder builder() { + return new Builder(); + } + + /** Builder of {@link HeadBucketArgs}. */ + public static final class Builder extends HeadBucketBaseArgs.Builder {} +} diff --git a/api/src/main/java/io/minio/HeadBucketBaseArgs.java b/api/src/main/java/io/minio/HeadBucketBaseArgs.java new file mode 100644 index 000000000..b2d796a52 --- /dev/null +++ b/api/src/main/java/io/minio/HeadBucketBaseArgs.java @@ -0,0 +1,24 @@ +/* + * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2025 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.minio; + +/** Common arguments of {@link HeadBucketArgs} and {@link BucketExistsArgs}. */ +public abstract class HeadBucketBaseArgs extends BucketArgs { + /** Builder of {@link HeadBucketBaseArgs}. */ + public abstract static class Builder, A extends HeadBucketBaseArgs> + extends BucketArgs.Builder {} +} diff --git a/api/src/main/java/io/minio/HeadBucketResponse.java b/api/src/main/java/io/minio/HeadBucketResponse.java new file mode 100644 index 000000000..83ec4c8b1 --- /dev/null +++ b/api/src/main/java/io/minio/HeadBucketResponse.java @@ -0,0 +1,45 @@ +/* + * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2025 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.minio; + +/** Response of {@link BaseS3Client#headBucket}. */ +public class HeadBucketResponse extends GenericResponse { + public HeadBucketResponse(okhttp3.Headers headers, String bucket, String region) { + super(headers, bucket, region, null); + } + + public String bucketArn() { + return this.headers().get("x-amz-bucket-arn"); + } + + public String locationType() { + return this.headers().get("x-amz-bucket-location-type"); + } + + public String locationName() { + return this.headers().get("x-amz-bucket-location-name"); + } + + public String accessPointAlias() { + return this.headers().get("x-amz-access-point-alias"); + } + + @Override + public String toString() { + return "HeadBucketResponse{" + "bucket=" + bucket() + ", region=" + region() + "}"; + } +} diff --git a/api/src/main/java/io/minio/HeadObjectResponse.java b/api/src/main/java/io/minio/HeadObjectResponse.java index c34bcd71b..0d4401b3e 100644 --- a/api/src/main/java/io/minio/HeadObjectResponse.java +++ b/api/src/main/java/io/minio/HeadObjectResponse.java @@ -19,7 +19,7 @@ import io.minio.messages.LegalHold; import io.minio.messages.RetentionMode; import java.time.ZonedDateTime; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -34,6 +34,7 @@ public class HeadObjectResponse extends GenericResponse { private LegalHold lockLegalHold; private boolean deleteMarker; private Http.Headers userMetadata; + private Map checksums; private Checksum.Type checksumType; public HeadObjectResponse(okhttp3.Headers headers, String bucket, String region, String object) { @@ -72,6 +73,19 @@ public HeadObjectResponse(okhttp3.Headers headers, String bucket, String region, value = headers.get("x-amz-checksum-type"); this.checksumType = value == null ? null : Checksum.Type.valueOf(value); + + Map checksums = new HashMap<>(); + value = headers.get("x-amz-checksum-crc32"); + if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.CRC32, value); + value = headers.get("x-amz-checksum-crc32c"); + if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.CRC32C, value); + value = headers.get("x-amz-checksum-crc64nvme"); + if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.CRC64NVME, value); + value = headers.get("x-amz-checksum-sha1"); + if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.SHA1, value); + value = headers.get("x-amz-checksum-sha256"); + if (value != null && !value.isEmpty()) checksums.put(Checksum.Algorithm.SHA256, value); + if (!checksums.isEmpty()) this.checksums = checksums; } public String etag() { @@ -118,32 +132,13 @@ public Checksum.Type checksumType() { return checksumType; } - public List algorithms() { - okhttp3.Headers headers = headers(); - List algorithms = new ArrayList<>(); - String value; - - value = headers.get("x-amz-checksum-crc32"); - if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.CRC32); - - value = headers.get("x-amz-checksum-crc32c"); - if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.CRC32C); - - value = headers.get("x-amz-checksum-crc64nvme"); - if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.CRC64NVME); - - value = headers.get("x-amz-checksum-sha1"); - if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.SHA1); - - value = headers.get("x-amz-checksum-sha256"); - if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.SHA256); - - return algorithms.size() == 0 ? null : algorithms; + public Map checksums() { + return checksums; } @Override public String toString() { - return "ObjectHead{" + return "HeadObjectResponse{" + "bucket=" + bucket() + ", object=" diff --git a/api/src/main/java/io/minio/MinioAsyncClient.java b/api/src/main/java/io/minio/MinioAsyncClient.java index b02effdd0..72cc5a484 100644 --- a/api/src/main/java/io/minio/MinioAsyncClient.java +++ b/api/src/main/java/io/minio/MinioAsyncClient.java @@ -74,6 +74,7 @@ import java.nio.file.StandardOpenOption; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; @@ -583,14 +584,37 @@ protected CompletableFuture calculatePartCount(List sourc sources.set(index, source); } - long size = source.length() != null ? source.length() : source.objectSize(); - size -= source.offset() != null ? source.offset() : 0; - if (size < ObjectWriteArgs.MIN_MULTIPART_SIZE && interimPart) { + List keys = + new ArrayList<>( + Arrays.asList("source", source.bucket() + "/" + source.object())); + if (source.versionId() != null && !source.versionId().isEmpty()) { + keys.add("version_id=" + source.versionId()); + } + if (source.offset() != null) keys.add("offset=" + source.offset()); + if (source.length() != null) keys.add("length=" + source.length()); + String sourceMessage = String.join(" ", keys); + if ((source.offset() != null ? source.offset() : 0) >= source.objectSize()) { + throw new IllegalArgumentException( + "source " + + sourceMessage + + ": offset is beyond object size " + + source.objectSize()); + } + + long size = source.objectSize() - (source.offset() != null ? source.offset() : 0); + + if (size < (source.length() != null ? source.length() : 0)) { + throw new IllegalArgumentException( + "source " + + sourceMessage + + ": insufficient object size " + + source.objectSize()); + } + size = source.length() != null ? source.length() : size; + if (interimPart && size < ObjectWriteArgs.MIN_MULTIPART_SIZE) { throw new IllegalArgumentException( - "compose source " - + source.bucket() - + "/" - + source.object() + "source " + + sourceMessage + ": size " + size + " must be greater than " @@ -600,7 +624,7 @@ protected CompletableFuture calculatePartCount(List sourc objectSize[0] += size; if (objectSize[0] > ObjectWriteArgs.MAX_OBJECT_SIZE) { throw new IllegalArgumentException( - "destination object size must be less than " + "source objects yield destination object size greater than " + ObjectWriteArgs.MAX_OBJECT_SIZE); } @@ -615,14 +639,11 @@ protected CompletableFuture calculatePartCount(List sourc if (lastPartSize < ObjectWriteArgs.MIN_MULTIPART_SIZE && interimPart) { throw new IllegalArgumentException( - "compose source " - + source.bucket() - + "/" - + source.object() - + ": " - + "for multipart split upload of " + "source " + + sourceMessage + + ": multipart split upload for " + size - + ", last part size is less than " + + " yields last part size less than " + ObjectWriteArgs.MIN_MULTIPART_SIZE); } partCount += (int) count; @@ -632,7 +653,9 @@ protected CompletableFuture calculatePartCount(List sourc if (partCount > ObjectWriteArgs.MAX_MULTIPART_COUNT) { throw new IllegalArgumentException( - "Compose sources create more than allowed multipart count " + "source objects yield multipart count " + + partCount + + " more than allowed multipart count " + ObjectWriteArgs.MAX_MULTIPART_COUNT); } return partCount; @@ -3332,7 +3355,7 @@ public CompletableFuture putObjectFanOut(PutObjectFanOu // Build POST object data String objectName = - "pan-out-" + "fan-out-" + new BigInteger(32, RANDOM).toString(32) + "-" + System.currentTimeMillis(); @@ -3549,8 +3572,10 @@ public CompletableFuture appendObject(AppendObjectArgs args "append object does not support checksum type " + response.checksumType())); } - List algorithms = response.algorithms(); - if (algorithms != null) algorithm = algorithms.get(0); + Map checksums = response.checksums(); + if (checksums != null && !checksums.isEmpty()) { + algorithm = checksums.keySet().iterator().next(); + } } long writeOffset = response.size(); diff --git a/api/src/main/java/io/minio/ObjectConditionalReadArgs.java b/api/src/main/java/io/minio/ObjectConditionalReadArgs.java index e8ab88432..33342df40 100644 --- a/api/src/main/java/io/minio/ObjectConditionalReadArgs.java +++ b/api/src/main/java/io/minio/ObjectConditionalReadArgs.java @@ -35,6 +35,10 @@ protected ObjectConditionalReadArgs() {} protected ObjectConditionalReadArgs(SourceObject args) { super(args); + this.matchETag = args.matchETag(); + this.notMatchETag = args.notMatchETag(); + this.modifiedSince = args.modifiedSince(); + this.unmodifiedSince = args.unmodifiedSince(); } protected ObjectConditionalReadArgs(AppendObjectArgs args) { @@ -44,6 +48,10 @@ protected ObjectConditionalReadArgs(AppendObjectArgs args) { protected ObjectConditionalReadArgs(DownloadObjectArgs args) { super(args); + this.matchETag = args.matchETag(); + this.notMatchETag = args.notMatchETag(); + this.modifiedSince = args.modifiedSince(); + this.unmodifiedSince = args.unmodifiedSince(); } protected ObjectConditionalReadArgs(ObjectConditionalReadArgs args) { @@ -59,7 +67,7 @@ protected ObjectConditionalReadArgs(ObjectConditionalReadArgs args) { protected ObjectConditionalReadArgs(ObjectConditionalReadArgs args, String matchETag) { this(args); - this.matchETag = args.matchETag; + this.matchETag = matchETag; } public Long offset() { diff --git a/api/src/main/java/io/minio/SourceObject.java b/api/src/main/java/io/minio/SourceObject.java index 5d00d427e..0a497ba62 100644 --- a/api/src/main/java/io/minio/SourceObject.java +++ b/api/src/main/java/io/minio/SourceObject.java @@ -29,6 +29,12 @@ public SourceObject(SourceObject args, long objectSize, String etag) { super(args, etag); validateSize(objectSize); this.objectSize = objectSize; + if (args.matchETag() == null) { + if (etag == null) throw new IllegalArgumentException("etag must be provided"); + } else if (etag != null && !args.matchETag().equals(etag)) { + throw new IllegalArgumentException( + "matchETag " + args.matchETag() + " and passed etag " + etag + " does not match"); + } } private void throwException(long objectsize, long arg, String argName) { diff --git a/api/src/main/java/io/minio/messages/CreateBucketConfiguration.java b/api/src/main/java/io/minio/messages/CreateBucketConfiguration.java index 5d3063346..1a9e4e9fd 100644 --- a/api/src/main/java/io/minio/messages/CreateBucketConfiguration.java +++ b/api/src/main/java/io/minio/messages/CreateBucketConfiguration.java @@ -38,15 +38,20 @@ public class CreateBucketConfiguration { @Element(name = "Bucket", required = false) private Bucket bucket; + @Element(name = "Tags", required = false) + private Tags tags; + /** Constructs a new CreateBucketConfiguration object with given location constraint. */ public CreateBucketConfiguration(String locationConstraint) { this.locationConstraint = locationConstraint; } - public CreateBucketConfiguration(String locationConstraint, Location location, Bucket bucket) { + public CreateBucketConfiguration( + String locationConstraint, Location location, Bucket bucket, Tags tags) { this.locationConstraint = locationConstraint; this.location = location; this.bucket = bucket; + this.tags = tags; } /** Bucket location information of {@link CreateBucketConfiguration}. */