From 6288dcbb2d0940d3b54ac4da784d6cb35dcfa19f Mon Sep 17 00:00:00 2001 From: "Bala.FA" Date: Sun, 7 Jun 2020 08:10:52 +0530 Subject: [PATCH 1/3] add arg builder support to copyObject API --- .../main/java/io/minio/CopyObjectArgs.java | 148 +++ api/src/main/java/io/minio/Directive.java | 23 + api/src/main/java/io/minio/MinioClient.java | 180 +++- .../main/java/io/minio/ObjectWriteArgs.java | 169 ++++ docs/API.md | 79 +- examples/CopyObject.java | 174 +++- examples/CopyObjectEncrypted.java | 92 -- examples/CopyObjectEncryptedKms.java | 85 -- examples/CopyObjectEncryptedS3.java | 80 -- examples/CopyObjectMetadata.java | 102 -- functional/FunctionalTest.java | 893 ++++++------------ 11 files changed, 987 insertions(+), 1038 deletions(-) create mode 100644 api/src/main/java/io/minio/CopyObjectArgs.java create mode 100644 api/src/main/java/io/minio/Directive.java create mode 100644 api/src/main/java/io/minio/ObjectWriteArgs.java delete mode 100644 examples/CopyObjectEncrypted.java delete mode 100644 examples/CopyObjectEncryptedKms.java delete mode 100644 examples/CopyObjectEncryptedS3.java delete mode 100644 examples/CopyObjectMetadata.java diff --git a/api/src/main/java/io/minio/CopyObjectArgs.java b/api/src/main/java/io/minio/CopyObjectArgs.java new file mode 100644 index 000000000..cbfe74292 --- /dev/null +++ b/api/src/main/java/io/minio/CopyObjectArgs.java @@ -0,0 +1,148 @@ +/* + * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2020 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; + +import java.time.ZonedDateTime; +import okhttp3.HttpUrl; + +/** Argument class of MinioClient.copyObject(). */ +public class CopyObjectArgs extends ObjectWriteArgs { + private String srcBucket; + private String srcObject; + private String srcVersionId; + private ServerSideEncryptionCustomerKey srcSsec; + private String srcMatchETag; + private String srcNotMatchETag; + private ZonedDateTime srcModifiedSince; + private ZonedDateTime srcUnmodifiedSince; + private Directive metadataDirective; + private Directive taggingDirective; + + public String srcBucket() { + return srcBucket; + } + + public String srcObject() { + return srcObject; + } + + public String srcVersionId() { + return srcVersionId; + } + + public ServerSideEncryptionCustomerKey srcSsec() { + return srcSsec; + } + + public String srcMatchETag() { + return srcMatchETag; + } + + public String srcNotMatchETag() { + return srcNotMatchETag; + } + + public ZonedDateTime srcModifiedSince() { + return srcModifiedSince; + } + + public ZonedDateTime srcUnmodifiedSince() { + return srcUnmodifiedSince; + } + + public Directive metadataDirective() { + return metadataDirective; + } + + public Directive taggingDirective() { + return taggingDirective; + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public void validateSse(HttpUrl url) { + super.validateSse(url); + checkSse(srcSsec, url); + } + + /** Argument builder of {@link CopyObjectArgs}. */ + public static final class Builder extends ObjectWriteArgs.Builder { + @Override + protected void validate(CopyObjectArgs args) { + super.validate(args); + validateBucketName(args.srcBucket); + } + + public Builder srcBucket(String srcBucket) { + validateBucketName(srcBucket); + operations.add(args -> args.srcBucket = srcBucket); + return this; + } + + public Builder srcObject(String srcObject) { + validateNullOrNotEmptyString(srcObject, "source object"); + operations.add(args -> args.srcObject = srcObject); + return this; + } + + public Builder srcVersionId(String srcVersionId) { + validateNullOrNotEmptyString(srcVersionId, "source version ID"); + operations.add(args -> args.srcVersionId = srcVersionId); + return this; + } + + public Builder srcSsec(ServerSideEncryptionCustomerKey srcSsec) { + operations.add(args -> args.srcSsec = srcSsec); + return this; + } + + public Builder srcMatchETag(String etag) { + validateNullOrNotEmptyString(etag, "etag"); + operations.add(args -> args.srcMatchETag = etag); + return this; + } + + public Builder srcNotMatchETag(String etag) { + validateNullOrNotEmptyString(etag, "etag"); + operations.add(args -> args.srcNotMatchETag = etag); + return this; + } + + public Builder srcModifiedSince(ZonedDateTime modifiedTime) { + operations.add(args -> args.srcModifiedSince = modifiedTime); + return this; + } + + public Builder srcUnmodifiedSince(ZonedDateTime modifiedTime) { + operations.add(args -> args.srcUnmodifiedSince = modifiedTime); + return this; + } + + public Builder metadataDirective(Directive directive) { + operations.add(args -> args.metadataDirective = directive); + return this; + } + + public Builder taggingDirective(Directive directive) { + operations.add(args -> args.taggingDirective = directive); + return this; + } + } +} diff --git a/api/src/main/java/io/minio/Directive.java b/api/src/main/java/io/minio/Directive.java new file mode 100644 index 000000000..e9f3eacc1 --- /dev/null +++ b/api/src/main/java/io/minio/Directive.java @@ -0,0 +1,23 @@ +/* + * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2020 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; + +/** Copy object directive for metadata and tags. */ +public enum Directive { + COPY, + REPLACE; +} diff --git a/api/src/main/java/io/minio/MinioClient.java b/api/src/main/java/io/minio/MinioClient.java index 3ac04dad3..e9aa06092 100755 --- a/api/src/main/java/io/minio/MinioClient.java +++ b/api/src/main/java/io/minio/MinioClient.java @@ -2307,7 +2307,9 @@ public void downloadObject(DownloadObjectArgs args) * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. + * @deprecated use {@link #copyObject(CopyObjectArgs)} */ + @Deprecated public void copyObject( String bucketName, String objectName, @@ -2321,48 +2323,184 @@ public void copyObject( InternalException, InvalidBucketNameException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException { - if ((bucketName == null) || (bucketName.isEmpty())) { - throw new IllegalArgumentException("bucket name cannot be empty"); + ServerSideEncryptionCustomerKey srcSsec = null; + if (srcSse instanceof ServerSideEncryptionCustomerKey) { + srcSsec = (ServerSideEncryptionCustomerKey) srcSse; } + checkReadRequestSse(srcSse); - checkObjectName(objectName); + CopyObjectArgs.Builder builder = + CopyObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .headers(headerMap) + .sse(sse) + .srcBucket(srcBucketName) + .srcObject(srcObjectName) + .srcSsec(srcSsec); - checkWriteRequestSse(sse); + if (copyConditions != null) { + Map map = copyConditions.getConditions(); + String value; + + builder.srcMatchETag(map.get("x-amz-copy-source-if-match")); + builder.srcNotMatchETag(map.get("x-amz-copy-source-if-none-match")); - if ((srcBucketName == null) || (srcBucketName.isEmpty())) { - throw new IllegalArgumentException("Source bucket name cannot be empty"); + value = map.get("x-amz-copy-source-if-modified-since"); + if (value != null) { + builder.srcModifiedSince(ZonedDateTime.parse(value, Time.HTTP_HEADER_DATE_FORMAT)); + } + + value = map.get("x-amz-copy-source-if-unmodified-since"); + if (value != null) { + builder.srcUnmodifiedSince(ZonedDateTime.parse(value, Time.HTTP_HEADER_DATE_FORMAT)); + } + + value = map.get("x-amz-metadata-directive"); + if (value != null) { + builder.metadataDirective(Directive.REPLACE); + } } + copyObject(builder.build()); + } + + /** + * Creates an object by server-side copying data from another object. + * + *
Example:{@code
+   * // Create object "my-objectname" in bucket "my-bucketname" by copying from object
+   * // "my-objectname" in bucket "my-source-bucketname".
+   * minioClient.copyObject(
+   *     CopyObjectArgs.builder()
+   *         .bucket("my-bucketname")
+   *         .object("my-objectname")
+   *         .srcBucket("my-source-bucketname")
+   *         .build());
+   *
+   * // Create object "my-objectname" in bucket "my-bucketname" by copying from object
+   * // "my-source-objectname" in bucket "my-source-bucketname".
+   * minioClient.copyObject(
+   *     CopyObjectArgs.builder()
+   *         .bucket("my-bucketname")
+   *         .object("my-objectname")
+   *         .srcBucket("my-source-bucketname")
+   *         .srcObject("my-source-objectname")
+   *         .build());
+   *
+   * // Create object "my-objectname" in bucket "my-bucketname" with server-side encryption by
+   * // copying from object "my-objectname" in bucket "my-source-bucketname".
+   * minioClient.copyObject(
+   *     CopyObjectArgs.builder()
+   *         .bucket("my-bucketname")
+   *         .object("my-objectname")
+   *         .srcBucket("my-source-bucketname")
+   *         .sse(sse)
+   *         .build());
+   *
+   * // Create object "my-objectname" in bucket "my-bucketname" by copying from SSE-C encrypted
+   * // object "my-source-objectname" in bucket "my-source-bucketname".
+   * minioClient.copyObject(
+   *     CopyObjectArgs.builder()
+   *         .bucket("my-bucketname")
+   *         .object("my-objectname")
+   *         .srcBucket("my-source-bucketname")
+   *         .srcObject("my-source-objectname")
+   *         .srcSsec(ssec)
+   *         .build());
+   *
+   * // Create object "my-objectname" in bucket "my-bucketname" with custom headers by copying from
+   * // object "my-objectname" in bucket "my-source-bucketname" using conditions.
+   * minioClient.copyObject(
+   *     CopyObjectArgs.builder()
+   *         .bucket("my-bucketname")
+   *         .object("my-objectname")
+   *         .srcBucket("my-source-bucketname")
+   *         .headers(headers)
+   *         .srcMatchETag(etag)
+   *         .build());
+   * }
+ * + * @param args {@link CopyObjectArgs} object. + * @throws ErrorResponseException thrown to indicate S3 service returned an error response. + * @throws IllegalArgumentException throws to indicate invalid argument passed. + * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. + * @throws InternalException thrown to indicate internal library error. + * @throws InvalidBucketNameException thrown to indicate invalid bucket name passed. + * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. + * @throws InvalidResponseException thrown to indicate S3 service returned invalid or no error + * response. + * @throws IOException thrown to indicate I/O error on S3 operation. + * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. + * @throws XmlParserException thrown to indicate XML parsing error. + */ + public void copyObject(CopyObjectArgs args) + throws ErrorResponseException, IllegalArgumentException, InsufficientDataException, + InternalException, InvalidBucketNameException, InvalidKeyException, + InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, + XmlParserException { + checkArgs(args); + args.validateSse(this.baseUrl); + + Multimap headers = args.genHeaders(); + // Source object name is optional, if empty default to object name. - if (srcObjectName == null) { - srcObjectName = objectName; + String srcObject = args.object(); + if (args.srcObject() != null) { + srcObject = args.srcObject(); } - checkReadRequestSse(srcSse); + String copySource = S3Escaper.encodePath("/" + args.srcBucket() + "/" + srcObject); + if (args.srcVersionId() != null) { + copySource += "?versionId=" + S3Escaper.encode(args.srcVersionId()); + } - if (headerMap == null) { - headerMap = new HashMap<>(); + headers.put("x-amz-copy-source", copySource); + + if (args.srcSsec() != null) { + headers.putAll(Multimaps.forMap(args.srcSsec().copySourceHeaders())); } - headerMap.put("x-amz-copy-source", S3Escaper.encodePath(srcBucketName + "/" + srcObjectName)); + if (args.srcMatchETag() != null) { + headers.put("x-amz-copy-source-if-match", args.srcMatchETag()); + } - if (sse != null) { - headerMap.putAll(sse.headers()); + if (args.srcNotMatchETag() != null) { + headers.put("x-amz-copy-source-if-none-match", args.srcNotMatchETag()); } - if (srcSse != null) { - headerMap.putAll(srcSse.copySourceHeaders()); + if (args.srcModifiedSince() != null) { + headers.put( + "x-amz-copy-source-if-modified-since", + args.srcModifiedSince().format(Time.HTTP_HEADER_DATE_FORMAT)); } - if (copyConditions != null) { - headerMap.putAll(copyConditions.getConditions()); + if (args.srcUnmodifiedSince() != null) { + headers.put( + "x-amz-copy-source-if-unmodified-since", + args.srcUnmodifiedSince().format(Time.HTTP_HEADER_DATE_FORMAT)); } - Response response = executePut(bucketName, objectName, headerMap, null, "", 0); + if (args.metadataDirective() != null) { + headers.put("x-amz-metadata-directive", args.metadataDirective().name()); + } - try (ResponseBody body = response.body()) { + if (args.taggingDirective() != null) { + headers.put("x-amz-tagging-directive", args.taggingDirective().name()); + } + + try (Response response = + execute( + Method.PUT, + args.bucket(), + args.object(), + getRegion(args.bucket()), + headers, + null, + null, + 0)) { // For now ignore the copyObjectResult, just read and parse it. - Xml.unmarshal(CopyObjectResult.class, body.charStream()); + Xml.unmarshal(CopyObjectResult.class, response.body().charStream()); } } diff --git a/api/src/main/java/io/minio/ObjectWriteArgs.java b/api/src/main/java/io/minio/ObjectWriteArgs.java new file mode 100644 index 000000000..7fa9a936b --- /dev/null +++ b/api/src/main/java/io/minio/ObjectWriteArgs.java @@ -0,0 +1,169 @@ +/* + * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2020 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; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import io.minio.messages.Retention; +import io.minio.messages.Tags; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; +import okhttp3.HttpUrl; + +public abstract class ObjectWriteArgs extends ObjectArgs { + protected Multimap headers = + Multimaps.unmodifiableMultimap(HashMultimap.create()); + protected Multimap userMetadata = + Multimaps.unmodifiableMultimap(HashMultimap.create()); + protected ServerSideEncryption sse; + protected Tags tags = new Tags(); + protected Retention retention; + protected boolean legalHold; + + public Multimap headers() { + return headers; + } + + public Multimap userMetadata() { + return userMetadata; + } + + public ServerSideEncryption sse() { + return sse; + } + + public Tags tags() { + return tags; + } + + public Retention retention() { + return retention; + } + + public boolean legalHold() { + return legalHold; + } + + public Multimap genHeaders() { + Multimap headers = HashMultimap.create(); + + headers.putAll(this.headers); + headers.putAll(userMetadata); + + if (sse != null) { + headers.putAll(Multimaps.forMap(sse.headers())); + } + + String tagging = + tags.get().entrySet().stream() + .map(e -> S3Escaper.encode(e.getKey()) + "=" + S3Escaper.encode(e.getValue())) + .collect(Collectors.joining("&")); + if (!tagging.isEmpty()) { + headers.put("x-amz-tagging", tagging); + } + + if (retention != null && retention.mode() != null) { + headers.put("x-amz-object-lock-mode", retention.mode().name()); + headers.put( + "x-amz-object-lock-retain-until-date", + retention.retainUntilDate().format(Time.HTTP_HEADER_DATE_FORMAT)); + } + + if (legalHold) { + headers.put("x-amz-object-lock-legal-hold", "ON"); + } + + return headers; + } + + protected void validateSse(HttpUrl url) { + checkSse(sse, url); + } + + public abstract static class Builder, A extends ObjectWriteArgs> + extends ObjectArgs.Builder { + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public B headers(Map headers) { + final Multimap headersCopy = toMultimap(headers); + operations.add(args -> args.headers = headersCopy); + return (B) this; + } + + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public B headers(Multimap headers) { + final Multimap headersCopy = copyMultimap(headers); + operations.add(args -> args.headers = headersCopy); + return (B) this; + } + + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public B userMetadata(Map userMetadata) { + return userMetadata((userMetadata == null) ? null : Multimaps.forMap(userMetadata)); + } + + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public B userMetadata(Multimap userMetadata) { + Multimap userMetadataCopy = HashMultimap.create(); + if (userMetadata != null) { + for (String key : userMetadata.keySet()) { + userMetadataCopy.putAll( + (key.toLowerCase(Locale.US).startsWith("x-amz-meta-") ? "" : "x-amz-meta-") + key, + userMetadata.get(key)); + } + } + + final Multimap finalUserMetadata = + Multimaps.unmodifiableMultimap(userMetadataCopy); + operations.add(args -> args.userMetadata = finalUserMetadata); + return (B) this; + } + + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public B sse(ServerSideEncryption sse) { + operations.add(args -> args.sse = sse); + return (B) this; + } + + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public B tags(Map map) { + final Tags tags = (map == null) ? new Tags() : Tags.newObjectTags(map); + operations.add(args -> args.tags = tags); + return (B) this; + } + + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public B tags(Tags tags) { + Tags tagsCopy = (tags == null) ? new Tags() : Tags.newObjectTags(tags.get()); + operations.add(args -> args.tags = tagsCopy); + return (B) this; + } + + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public B retention(Retention retention) { + operations.add(args -> args.retention = retention); + return (B) this; + } + + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public B legalHold(boolean flag) { + operations.add(args -> args.legalHold = flag); + return (B) this; + } + } +} diff --git a/docs/API.md b/docs/API.md index 974fcced6..a392201b5 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1017,45 +1017,69 @@ minioClient.composeObject("my-bucketname", "my-objectname", sourceObjectList, us ``` -### copyObject(String bucketName, String objectName, Map headerMap, ServerSideEncryption sse, String srcBucketName, String srcObjectName, ServerSideEncryption srcSse, CopyConditions copyConditions) -`public void copyObject(String bucketName, String objectName, Map headerMap, ServerSideEncryption sse, String srcBucketName, String srcObjectName, ServerSideEncryption srcSse, CopyConditions copyConditions)` _[[Javadoc]](http://minio.github.io/minio-java/io/minio/MinioClient.html#copyObject-java.lang.String-java.lang.String-java.util.Map-io.minio.ServerSideEncryption-java.lang.String-java.lang.String-io.minio.ServerSideEncryption-io.minio.CopyConditions-)_ +### copyObject(CopyObjectArgs args) +`public void copyObject(CopyObjectArgs args)` _[[Javadoc]](http://minio.github.io/minio-java/io/minio/MinioClient.html#copyObject-io.minio.CopyObjectArgs-)_ Creates an object by server-side copying data from another object. __Parameters__ -| Parameter | Type | Description | -|:-------------------|:-------------------------|:---------------------------------------------------------------| -| ``bucketName`` | _String_ | Name of the bucket. | -| ``objectName`` | _String_ | Object name to be created. | -| ``headerMap`` | _Map_ | (Optional) User metadata. | -| ``sse`` | _[ServerSideEncryption]_ | (Optional) Server-side encryption. | -| ``srcBucketName`` | _String_ | Source bucket name. | -| ``srcObjectName`` | _String_ | (Optional) Source object name. | -| ``srcSse`` | _[ServerSideEncryption]_ | (Optional) SSE-C type server-side encryption of source object. | -| ``copyConditions`` | _[CopyConditions]_ | (Optional) Conditiions to be used in copy operation. | +| Parameter | Type | Description | +|:----------|:-------------------|:------------| +| ``args`` | _[CopyObjectArgs]_ | Arguments. | __Example__ ```java -// Copy data from my-source-bucketname/my-objectname to my-bucketname/my-objectname. -minioClient.copyObject("my-bucketname", "my-objectname", null, null, "my-source-bucketname", null, null, - null); +// Create object "my-objectname" in bucket "my-bucketname" by copying from object +// "my-objectname" in bucket "my-source-bucketname". +minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .build()); -// Copy data from my-source-bucketname/my-source-objectname to my-bucketname/my-objectname. -minioClient.copyObject("my-bucketname", "my-objectname", null, null, "my-source-bucketname", - "my-source-objectname", null, null); +// Create object "my-objectname" in bucket "my-bucketname" by copying from object +// "my-source-objectname" in bucket "my-source-bucketname". +minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .srcObject("my-source-objectname") + .build()); -// Copy data from my-source-bucketname/my-objectname to my-bucketname/my-objectname by server-side encryption. -minioClient.copyObject("my-bucketname", "my-objectname", null, sse, "my-source-bucketname", null, null, null); +// Create object "my-objectname" in bucket "my-bucketname" with server-side encryption by +// copying from object "my-objectname" in bucket "my-source-bucketname". +minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .sse(sse) + .build()); -// Copy data from SSE-C encrypted my-source-bucketname/my-objectname to my-bucketname/my-objectname. -minioClient.copyObject("my-bucketname", "my-objectname", null, null, "my-source-bucketname", null, srcSsec, - null); +// Create object "my-objectname" in bucket "my-bucketname" by copying from SSE-C encrypted +// object "my-source-objectname" in bucket "my-source-bucketname". +minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .srcObject("my-source-objectname") + .srcSsec(ssec) + .build()); -// Copy data from my-source-bucketname/my-objectname to my-bucketname/my-objectname with user metadata and -// copy conditions. -minioClient.copyObject("my-bucketname", "my-objectname", headers, null, "my-source-bucketname", null, null, - conditions); +// Create object "my-objectname" in bucket "my-bucketname" with custom headers by copying from +// object "my-objectname" in bucket "my-source-bucketname" using conditions. +minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .headers(headers) + .srcMatchETag(etag) + .build()); ``` @@ -1747,3 +1771,4 @@ ObjectStat objectStat = [RemoveIncompleteUploadArgs]: http://minio.github.io/minio-java/io/minio/RemoveIncompleteUploadArgs.html [GetPresignedObjectUrlArgs]: http://minio.github.io/minio-java/io/minio/GetPresignedObjectUrlArgs.html [RemoveObjectsArgs]: http://minio.github.io/minio-java/io/minio/RemoveObjectsArgs.html +[CopyObjectArgs]: http://minio.github.io/minio-java/io/minio/CopyObjectArgs.html diff --git a/examples/CopyObject.java b/examples/CopyObject.java index 5d97544e1..610361060 100644 --- a/examples/CopyObject.java +++ b/examples/CopyObject.java @@ -15,13 +15,17 @@ * limitations under the License. */ +import io.minio.CopyObjectArgs; import io.minio.MinioClient; -import io.minio.PutObjectOptions; +import io.minio.ServerSideEncryption; +import io.minio.ServerSideEncryptionCustomerKey; import io.minio.errors.MinioException; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; +import javax.crypto.KeyGenerator; public class CopyObject { /** MinioClient.copyObject() example. */ @@ -39,52 +43,130 @@ public static void main(String[] args) // MinioClient minioClient = new MinioClient("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", // "YOUR-SECRETACCESSKEY"); - // Create some content for the object. - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - builder.append( - "Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); - builder.append("(29 letters)\n"); - builder.append( - "Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); - builder.append("(31 letters)\n"); - builder.append( - "Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); - builder.append("NASAs Space Shuttle. (32 letters)\n"); - builder.append( - "The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. "); - builder.append("(39 letters)\n"); - builder.append( - "How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity "); - builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); - builder.append( - "Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac "); - builder.append("computers after System 7.\n"); - builder.append( - "A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my "); - builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); - builder.append("---\n"); + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + ServerSideEncryptionCustomerKey ssec = + ServerSideEncryption.withCustomerKey(keyGen.generateKey()); + + Map myContext = new HashMap<>(); + myContext.put("key1", "value1"); + ServerSideEncryption sseKms = ServerSideEncryption.withManagedKeys("Key-Id", myContext); + + ServerSideEncryption sseS3 = ServerSideEncryption.atRest(); + + String versionId = "ac38316c-fe14-4f96-9f76-8f675ae5a79e"; + + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + headers.put("x-amz-meta-my-project", "Project One"); + + String etag = "9855d05ab7a1cfd5ea304f0547c24496"; + + { + // Create object "my-objectname" in bucket "my-bucketname" by copying from object + // "my-objectname" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .build()); + System.out.println( + "my-source-bucketname/my-objectname copied " + + "to my-bucketname/my-objectname successfully"); + } + + { + // Create object "my-objectname" in bucket "my-bucketname" by copying from object + // "my-source-objectname" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .srcObject("my-source-objectname") + .build()); + System.out.println( + "my-source-bucketname/my-source-objectname copied " + + "to my-bucketname/my-objectname successfully"); } - // Create a InputStream for object upload. - ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - - // Create object 'my-objectname' in 'my-bucketname' with content from the input stream. - minioClient.putObject( - "my-bucketname", "my-objectname", bais, new PutObjectOptions(bais.available(), -1)); - bais.close(); - System.out.println("my-objectname is uploaded successfully"); - - minioClient.copyObject( - "my-destbucketname", - "my-objectname-copy", - null, - null, - "my-bucketname", - "my-objectname", - null, - null); - System.out.println("my-objectname-copy copied to my-destbucketname successfully"); + { + // Create object "my-objectname" in bucket "my-bucketname" with SSE-KMS server-side + // encryption by copying from object "my-objectname" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .sse(sseKms) // Replace with actual key. + .build()); + System.out.println( + "my-source-bucketname/my-objectname copied " + + "to my-bucketname/my-objectname successfully"); + } + + { + // Create object "my-objectname" in bucket "my-bucketname" with SSE-S3 server-side + // encryption by copying from object "my-objectname" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .sse(sseS3) // Replace with actual key. + .build()); + System.out.println( + "my-source-bucketname/my-objectname copied " + + "to my-bucketname/my-objectname successfully"); + } + + { + // Create object "my-objectname" in bucket "my-bucketname" with SSE-C server-side encryption + // by copying from object "my-objectname" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .sse(ssec) // Replace with actual key. + .build()); + System.out.println( + "my-source-bucketname/my-objectname copied " + + "to my-bucketname/my-objectname successfully"); + } + + { + // Create object "my-objectname" in bucket "my-bucketname" by copying from SSE-C encrypted + // object "my-source-objectname" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .srcObject("my-source-objectname") + .srcSsec(ssec) // Replace with actual key. + .build()); + System.out.println( + "my-source-bucketname/my-source-objectname copied " + + "to my-bucketname/my-objectname successfully"); + } + + { + // Create object "my-objectname" in bucket "my-bucketname" with custom headers conditionally + // by copying from object "my-objectname" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .srcBucket("my-source-bucketname") + .headers(headers) // Replace with actual headers. + .srcMatchETag(etag) // Replace with actual ETag. + .build()); + System.out.println( + "my-source-bucketname/my-objectname copied " + + "to my-bucketname/my-objectname successfully"); + } } catch (MinioException e) { System.out.println("Error occurred: " + e); } diff --git a/examples/CopyObjectEncrypted.java b/examples/CopyObjectEncrypted.java deleted file mode 100644 index 9f1bd5b7e..000000000 --- a/examples/CopyObjectEncrypted.java +++ /dev/null @@ -1,92 +0,0 @@ -import io.minio.MinioClient; -import io.minio.PutObjectOptions; -import io.minio.ServerSideEncryption; -import io.minio.errors.MinioException; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import javax.crypto.spec.SecretKeySpec; - -public class CopyObjectEncrypted { - /** MinioClient.copyObject() example using SSE_C. */ - public static void main(String[] args) - throws NoSuchAlgorithmException, IOException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - new MinioClient( - "https://play.min.io", - "Q3AM3UQ867SPQQA43P2F", - "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); - - /* Amazon S3: */ - // MinioClient minioClient = new MinioClient("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", - // "YOUR-SECRETACCESSKEY"); - - // Create some content for the object. - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - builder.append( - "Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); - builder.append("(29 letters)\n"); - builder.append( - "Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); - builder.append("(31 letters)\n"); - builder.append( - "Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); - builder.append("NASAs Space Shuttle. (32 letters)\n"); - builder.append( - "The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. "); - builder.append("(39 letters)\n"); - builder.append( - "How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity "); - builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); - builder.append( - "Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac "); - builder.append("computers after System 7.\n"); - builder.append( - "A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my "); - builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); - builder.append("---\n"); - } - // Create a InputStream for object upload. - ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - - // Generate a new 256 bit AES key - This key must be remembered by the client. - byte[] key = "01234567890123456789012345678901".getBytes("UTF-8"); - SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); - - ServerSideEncryption ssePut = ServerSideEncryption.withCustomerKey(secretKeySpec); - ServerSideEncryption sseSource = ServerSideEncryption.withCustomerKey(secretKeySpec); - - byte[] keyTarget = "98765432100123456789012345678901".getBytes("UTF-8"); - SecretKeySpec secretKeySpecTarget = new SecretKeySpec(keyTarget, "AES"); - - ServerSideEncryption sseTarget = ServerSideEncryption.withCustomerKey(secretKeySpecTarget); - - // Create object 'my-objectname' in 'my-bucketname' with content from the input stream. - PutObjectOptions options = new PutObjectOptions(bais.available(), -1); - options.setSse(ssePut); - minioClient.putObject("my-bucketname", "my-objectname", bais, options); - System.out.println("my-objectname is uploaded successfully"); - - minioClient.copyObject( - "my-destbucketname", - "my-objectname-copy", - null, - sseTarget, - "my-bucketname", - "my-objectname", - sseSource, - null); - - bais.close(); - - System.out.println("my-objectname-copy copied to my-destbucketname successfully."); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } - System.out.println("finished"); - } -} diff --git a/examples/CopyObjectEncryptedKms.java b/examples/CopyObjectEncryptedKms.java deleted file mode 100644 index 01f670149..000000000 --- a/examples/CopyObjectEncryptedKms.java +++ /dev/null @@ -1,85 +0,0 @@ -import io.minio.MinioClient; -import io.minio.PutObjectOptions; -import io.minio.ServerSideEncryption; -import io.minio.errors.MinioException; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.HashMap; -import java.util.Map; - -public class CopyObjectEncryptedKms { - /** MinioClient.copyObject() example using SSE_KMS. */ - public static void main(String[] args) - throws NoSuchAlgorithmException, IOException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - new MinioClient( - "https://play.min.io", - "Q3AM3UQ867SPQQA43P2F", - "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); - - /* Amazon S3: */ - // MinioClient minioClient = new MinioClient("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", - // "YOUR-SECRETACCESSKEY"); - - // Create some content for the object. - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - builder.append( - "Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); - builder.append("(29 letters)\n"); - builder.append( - "Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); - builder.append("(31 letters)\n"); - builder.append( - "Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); - builder.append("NASAs Space Shuttle. (32 letters)\n"); - builder.append( - "The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. "); - builder.append("(39 letters)\n"); - builder.append( - "How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity "); - builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); - builder.append( - "Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac "); - builder.append("computers after System 7.\n"); - builder.append( - "A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my "); - builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); - builder.append("---\n"); - } - - // Create a InputStream for object upload. - ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - - Map myContext = new HashMap<>(); - myContext.put("key1", "value1"); - - ServerSideEncryption sse = ServerSideEncryption.withManagedKeys("Key-Id", myContext); - - PutObjectOptions options = new PutObjectOptions(bais.available(), -1); - options.setSse(sse); - minioClient.putObject("my-bucketname", "my-objectname", bais, options); - bais.close(); - System.out.println("my-objectname is uploaded successfully"); - - minioClient.copyObject( - "my-destbucketname", - "my-objectname-copy", - null, - sse, - "my-bucketname", - "my-objectname", - null, - null); - System.out.println("my-objectname-copy copied to my-destbucketname successfully"); - - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } - System.out.println("finished"); - } -} diff --git a/examples/CopyObjectEncryptedS3.java b/examples/CopyObjectEncryptedS3.java deleted file mode 100644 index da9add9c6..000000000 --- a/examples/CopyObjectEncryptedS3.java +++ /dev/null @@ -1,80 +0,0 @@ -import io.minio.MinioClient; -import io.minio.PutObjectOptions; -import io.minio.ServerSideEncryption; -import io.minio.errors.MinioException; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -public class CopyObjectEncryptedS3 { - /** MinioClient.copyObject() example using SSE_S3. */ - public static void main(String[] args) - throws NoSuchAlgorithmException, IOException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - new MinioClient( - "https://play.min.io", - "Q3AM3UQ867SPQQA43P2F", - "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); - - /* Amazon S3: */ - // MinioClient minioClient = new MinioClient("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", - // "YOUR-SECRETACCESSKEY"); - - // Create some content for the object. - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - builder.append( - "Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); - builder.append("(29 letters)\n"); - builder.append( - "Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); - builder.append("(31 letters)\n"); - builder.append( - "Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); - builder.append("NASAs Space Shuttle. (32 letters)\n"); - builder.append( - "The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. "); - builder.append("(39 letters)\n"); - builder.append( - "How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity "); - builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); - builder.append( - "Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac "); - builder.append("computers after System 7.\n"); - builder.append( - "A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my "); - builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); - builder.append("---\n"); - } - - // Create a InputStream for object upload. - ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - - ServerSideEncryption sse = ServerSideEncryption.atRest(); - - // Create object 'my-objectname' in 'my-bucketname' with content from the input stream. - PutObjectOptions options = new PutObjectOptions(bais.available(), -1); - options.setSse(sse); - minioClient.putObject("my-bucketname", "my-objectname", bais, options); - bais.close(); - System.out.println("my-objectname is uploaded successfully"); - - minioClient.copyObject( - "my-destbucketname", - "my-objectname-copy", - null, - sse, - "my-bucketname", - "my-objectname", - null, - null); - System.out.println("my-objectname-copy copied to my-destbucketname successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } - System.out.println("finished"); - } -} diff --git a/examples/CopyObjectMetadata.java b/examples/CopyObjectMetadata.java deleted file mode 100644 index 50f580746..000000000 --- a/examples/CopyObjectMetadata.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, - * (C) 2017 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 - * - * https://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. - */ - -import io.minio.CopyConditions; -import io.minio.MinioClient; -import io.minio.PutObjectOptions; -import io.minio.errors.MinioException; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.HashMap; -import java.util.Map; - -public class CopyObjectMetadata { - /** MinioClient.copyObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - new MinioClient( - "https://play.min.io", - "Q3AM3UQ867SPQQA43P2F", - "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); - - /* Amazon S3: */ - // MinioClient minioClient = new MinioClient("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", - // "YOUR-SECRETACCESSKEY"); - - // Create some content for the object. - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - builder.append( - "Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); - builder.append("(29 letters)\n"); - builder.append( - "Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); - builder.append("(31 letters)\n"); - builder.append( - "Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); - builder.append("NASAs Space Shuttle. (32 letters)\n"); - builder.append( - "The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. "); - builder.append("(39 letters)\n"); - builder.append( - "How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity "); - builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); - builder.append( - "Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac "); - builder.append("computers after System 7.\n"); - builder.append( - "A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my "); - builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); - builder.append("---\n"); - } - - // Create a InputStream for object upload. - ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - - // Create object 'my-objectname' in 'my-bucketname' with content from the input stream. - minioClient.putObject( - "my-bucketname", "my-objectname", bais, new PutObjectOptions(bais.available(), -1)); - bais.close(); - System.out.println("my-objectname is uploaded successfully"); - - CopyConditions copyConditions = new CopyConditions(); - // Replace metadata on destination object with custom metadata. - copyConditions.setReplaceMetadataDirective(); - - Map metadata = new HashMap<>(); - metadata.put("Content-Type", "application/javascript"); - - minioClient.copyObject( - "my-destbucketname", - "my-objectname-copy", - metadata, - null, - "my-bucketname", - "my-objectname", - null, - copyConditions); - System.out.println("my-objectname-copy copied to my-destbucketname successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } - } -} diff --git a/functional/FunctionalTest.java b/functional/FunctionalTest.java index 19cc6fb68..1dc9fd833 100644 --- a/functional/FunctionalTest.java +++ b/functional/FunctionalTest.java @@ -22,7 +22,7 @@ import io.minio.BucketExistsArgs; import io.minio.CloseableIterator; import io.minio.ComposeSource; -import io.minio.CopyConditions; +import io.minio.CopyObjectArgs; import io.minio.DeleteBucketEncryptionArgs; import io.minio.DeleteBucketLifeCycleArgs; import io.minio.DeleteBucketNotificationArgs; @@ -30,6 +30,7 @@ import io.minio.DeleteBucketTagsArgs; import io.minio.DeleteDefaultRetentionArgs; import io.minio.DeleteObjectTagsArgs; +import io.minio.Directive; import io.minio.DisableObjectLegalHoldArgs; import io.minio.DisableVersioningArgs; import io.minio.DownloadObjectArgs; @@ -158,6 +159,8 @@ public class FunctionalTest { private static String accessKey; private static String secretKey; private static String region; + private static boolean isSecureEndpoint = false; + private static String kmsKeyName = null; private static String sqsArn = null; private static MinioClient client = null; @@ -1190,11 +1193,16 @@ public static void statObject_test1() throws Exception { /** Test: with SSE-C: statObject(StatObjectArgs args). */ public static void statObject_test2() throws Exception { + long startTime = System.currentTimeMillis(); + if (!isSecureEndpoint) { + mintIgnoredLog("statObject(StatObjectArgs args) using SSE_C.", null, startTime); + return; + } + if (!mintEnv) { System.out.println("Test: with SSE-C: statObject(StatObjectArgs args)"); } - long startTime = System.currentTimeMillis(); try { String objectName = getRandomName(); // Generate a new 256 bit AES key - This key must be remembered by the client. @@ -2299,660 +2307,368 @@ public static void threadedPutObject() throws Exception { } } - /** Test: copyObject(String bucketName, String objectName, String destBucketName). */ - public static void copyObject_test1() throws Exception { - String methodName = "copyObject(String bucketName, String objectName, String destBucketName)"; + public static void testCopyObject( + String testTags, ServerSideEncryption sse, CopyObjectArgs args, boolean negativeCase) + throws Exception { + String methodName = "copyObject()"; if (!mintEnv) { - System.out.println("Test: " + methodName); - } - - long startTime = System.currentTimeMillis(); - try { - String objectName = getRandomName(); - try (final InputStream is = new ContentInputStream(1 * KB)) { - client.putObject(bucketName, objectName, is, new PutObjectOptions(1 * KB, -1)); - } - - String destBucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(destBucketName).build()); - client.copyObject(destBucketName, objectName, null, null, bucketName, null, null, null); - client - .getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()) - .close(); - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(destBucketName).object(objectName).build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(destBucketName).build()); - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); + System.out.println("Test: " + methodName + " " + testTags); } - } - /** - * Test: copyObject(String bucketName, String objectName, String destBucketName, CopyConditions - * copyConditions) with ETag to match. - */ - public static void copyObject_test2() throws Exception { - if (!mintEnv) { - System.out.println( - "Test: copyObject(String bucketName, String objectName, String destBucketName," - + "CopyConditions copyConditions) with Matching ETag (Negative Case)"); + String srcObject = args.object(); + if (args.srcObject() != null) { + srcObject = args.srcObject(); } long startTime = System.currentTimeMillis(); try { - String objectName = getRandomName(); - try (final InputStream is = new ContentInputStream(1 * KB)) { - client.putObject(bucketName, objectName, is, new PutObjectOptions(1 * KB, -1)); - } - - String destBucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(destBucketName).build()); - - CopyConditions invalidETag = new CopyConditions(); - invalidETag.setMatchETag("TestETag"); - + client.makeBucket(MakeBucketArgs.builder().bucket(args.srcBucket()).build()); try { - client.copyObject( - destBucketName, objectName, null, null, bucketName, null, null, invalidETag); - } catch (ErrorResponseException e) { - if (e.errorResponse().errorCode() != ErrorCode.PRECONDITION_FAILED) { - throw e; + PutObjectOptions options = new PutObjectOptions(1 * KB, -1); + if (sse != null) { + options.setSse(sse); } - } - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(destBucketName).build()); + client.putObject(args.srcBucket(), srcObject, new ContentInputStream(1 * KB), options); + + if (negativeCase) { + try { + client.copyObject(args); + } catch (ErrorResponseException e) { + if (e.errorResponse().errorCode() != ErrorCode.PRECONDITION_FAILED) { + throw e; + } + } + } else { + client.copyObject(args); - mintSuccessLog( - "copyObject(String bucketName, String objectName, String destBucketName," - + " CopyConditions copyConditions)", - "CopyConditions: invalidETag", - startTime); + ServerSideEncryptionCustomerKey ssec = null; + if (sse instanceof ServerSideEncryptionCustomerKey) { + ssec = (ServerSideEncryptionCustomerKey) sse; + } + client.statObject( + StatObjectArgs.builder() + .bucket(args.bucket()) + .object(args.object()) + .ssec(ssec) + .build()); + } + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(args.srcBucket()).object(srcObject).build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); + client.removeBucket(RemoveBucketArgs.builder().bucket(args.srcBucket()).build()); + } } catch (Exception e) { - mintFailedLog( - "copyObject(String bucketName, String objectName, String destBucketName, " - + "CopyConditions copyConditions)", - "CopyConditions: invalidETag", - startTime, - null, - e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); - throw e; + handleException(methodName, testTags, startTime, e); } } - /** - * Test: copyObject(String bucketName, String objectName, String destBucketName, CopyConditions - * copyConditions) with ETag to match. - */ + /** Test: copyObject(). */ + public static void copyObject_test1() throws Exception { + testCopyObject( + "", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .srcBucket(getRandomName()) + .build(), + false); + } + + /** Test: copyObject() match ETag (Negative case). */ + public static void copyObject_test2() throws Exception { + testCopyObject( + "[negative match etag]", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .srcBucket(getRandomName()) + .srcObject(getRandomName()) + .srcMatchETag("invalid-etag") + .build(), + true); + } + + /** Test: copyObject() match ETag. */ public static void copyObject_test3() throws Exception { - String methodName = - "copyObject(String bucketName, String objectName, String destBucketName," - + " CopyConditions copyConditions) with Matching ETag (Positive Case)"; + String methodName = "copyObject()"; + String testTags = "[match etag]"; if (!mintEnv) { - System.out.println("Test: " + methodName); + System.out.println("Test: " + methodName + " " + testTags); } long startTime = System.currentTimeMillis(); + String srcBucketName = getRandomName(); + String srcObjectName = getRandomName(); try { - String objectName = getRandomName(); - try (final InputStream is = new ContentInputStream(1 * KB)) { - client.putObject(bucketName, objectName, is, new PutObjectOptions(1 * KB, -1)); - } - - String destBucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(destBucketName).build()); + client.makeBucket(MakeBucketArgs.builder().bucket(srcBucketName).build()); + try { + client.putObject( + srcBucketName, + srcObjectName, + new ContentInputStream(1 * KB), + new PutObjectOptions(1 * KB, -1)); + ObjectStat stat = + client.statObject( + StatObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); - ObjectStat stat = - client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); - CopyConditions copyConditions = new CopyConditions(); - copyConditions.setMatchETag(stat.etag()); + client.copyObject( + CopyObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .srcBucket(srcBucketName) + .srcObject(srcObjectName) + .srcMatchETag(stat.etag()) + .build()); - // File should be copied as ETag set in copyConditions matches object's ETag. - client.copyObject( - destBucketName, objectName, null, null, bucketName, null, null, copyConditions); - client - .getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()) - .close(); + client.statObject( + StatObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(destBucketName).object(objectName).build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(destBucketName).build()); - mintSuccessLog(methodName, null, startTime); + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); + client.removeBucket(RemoveBucketArgs.builder().bucket(srcBucketName).build()); + } } catch (Exception e) { - handleException(methodName, null, startTime, e); + handleException(methodName, testTags, startTime, e); } } - /** - * Test: copyObject(String bucketName, String objectName, String destBucketName, CopyConditions - * copyConditions) with ETag to not match. - */ + /** Test: copyObject() not match ETag. */ public static void copyObject_test4() throws Exception { - String methodName = - "Test: copyObject(String bucketName, String objectName, String destBucketName," - + "CopyConditions copyConditions) with not matching ETag" - + " (Positive Case)"; - if (!mintEnv) { - System.out.println("Test: " + methodName); - } - - long startTime = System.currentTimeMillis(); - try { - String objectName = getRandomName(); - try (final InputStream is = new ContentInputStream(1 * KB)) { - client.putObject(bucketName, objectName, is, new PutObjectOptions(1 * KB, -1)); - } - - String destBucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(destBucketName).build()); - - CopyConditions copyConditions = new CopyConditions(); - copyConditions.setMatchETagNone("TestETag"); - - // File should be copied as ETag set in copyConditions doesn't match object's - // ETag. - client.copyObject( - destBucketName, objectName, null, null, bucketName, null, null, copyConditions); - client - .getObject(GetObjectArgs.builder().bucket(destBucketName).object(objectName).build()) - .close(); - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(destBucketName).object(objectName).build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(destBucketName).build()); - - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } + testCopyObject( + "[not match etag]", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .srcBucket(getRandomName()) + .srcObject(getRandomName()) + .srcNotMatchETag("not-etag-of-source-object") + .build(), + false); } - /** - * Test: copyObject(String bucketName, String objectName, String destBucketName, CopyConditions - * copyConditions) with ETag to not match. - */ + /** Test: copyObject() modified since. */ public static void copyObject_test5() throws Exception { - if (!mintEnv) { - System.out.println( - "Test: copyObject(String bucketName, String objectName, String destBucketName," - + "CopyConditions copyConditions) with not matching ETag" - + " (Negative Case)"); - } - - long startTime = System.currentTimeMillis(); - try { - String objectName = getRandomName(); - try (final InputStream is = new ContentInputStream(1 * KB)) { - client.putObject(bucketName, objectName, is, new PutObjectOptions(1 * KB, -1)); - } - - String destBucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(destBucketName).build()); - - ObjectStat stat = - client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); - CopyConditions matchingETagNone = new CopyConditions(); - matchingETagNone.setMatchETagNone(stat.etag()); - - try { - client.copyObject( - destBucketName, objectName, null, null, bucketName, null, null, matchingETagNone); - } catch (ErrorResponseException e) { - // File should not be copied as ETag set in copyConditions matches object's - // ETag. - if (e.errorResponse().errorCode() != ErrorCode.PRECONDITION_FAILED) { - throw e; - } - } - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(destBucketName).build()); - - mintSuccessLog( - "copyObject(String bucketName, String objectName, String destBucketName, " - + "CopyConditions copyConditions)", - null, - startTime); - } catch (Exception e) { - mintFailedLog( - "copyObject(String bucketName, String objectName, String destBucketName, " - + "CopyConditions copyConditions)", - null, - startTime, - null, - e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); - throw e; - } + testCopyObject( + "[modified since]", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .srcBucket(getRandomName()) + .srcObject(getRandomName()) + .srcModifiedSince(ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)) + .build(), + false); } - /** - * Test: copyObject(String bucketName, String objectName, String destBucketName, CopyConditions - * copyConditions) with object modified after condition. - */ + /** Test: copyObject() unmodified since (Negative case). */ public static void copyObject_test6() throws Exception { - String methodName = - "Test: copyObject(String bucketName, String objectName, String destBucketName," - + "CopyConditions copyConditions) with modified after " - + "condition (Positive Case)"; - String args = "CopyCondition: modifiedDateCondition"; - if (!mintEnv) { - System.out.println("Test: " + methodName); - } - - long startTime = System.currentTimeMillis(); - try { - String objectName = getRandomName(); - try (final InputStream is = new ContentInputStream(1 * KB)) { - client.putObject(bucketName, objectName, is, new PutObjectOptions(1 * KB, -1)); - } - - String destBucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(destBucketName).build()); - - CopyConditions modifiedDateCondition = new CopyConditions(); - modifiedDateCondition.setModified(ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)); - - // File should be copied as object was modified after the set date. - client.copyObject( - destBucketName, objectName, null, null, bucketName, null, null, modifiedDateCondition); - client - .getObject(GetObjectArgs.builder().bucket(destBucketName).object(objectName).build()) - .close(); - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(destBucketName).object(objectName).build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(destBucketName).build()); - mintSuccessLog(methodName, args, startTime); - } catch (Exception e) { - handleException(methodName, args, startTime, e); - } + testCopyObject( + "[negative unmodified since]", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .srcBucket(getRandomName()) + .srcObject(getRandomName()) + .srcUnmodifiedSince(ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)) + .build(), + true); } - /** - * Test: copyObject(String bucketName, String objectName, String destBucketName, CopyConditions - * copyConditions) with object modified after condition. - */ + /** Test: copyObject() metadata replace. */ public static void copyObject_test7() throws Exception { + String methodName = "copyObject()"; + String testTags = "[metadata replace]"; if (!mintEnv) { - System.out.println( - "Test: copyObject(String bucketName, String objectName, String destBucketName," - + "CopyConditions copyConditions) with modified after" - + " condition (Negative Case)"); + System.out.println("Test: " + methodName + " " + testTags); } long startTime = System.currentTimeMillis(); + String srcBucketName = getRandomName(); + String srcObjectName = getRandomName(); try { - String objectName = getRandomName(); - try (final InputStream is = new ContentInputStream(1 * KB)) { - client.putObject(bucketName, objectName, is, new PutObjectOptions(1 * KB, -1)); - } - - String destBucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(destBucketName).build()); - - CopyConditions invalidUnmodifiedCondition = new CopyConditions(); - invalidUnmodifiedCondition.setUnmodified( - ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)); - + client.makeBucket(MakeBucketArgs.builder().bucket(srcBucketName).build()); try { + client.putObject( + srcBucketName, + srcObjectName, + new ContentInputStream(1 * KB), + new PutObjectOptions(1 * KB, -1)); + + Map headers = new HashMap<>(); + headers.put("Content-Type", customContentType); + headers.put("X-Amz-Meta-My-Project", "Project One"); client.copyObject( - destBucketName, - objectName, - null, - null, - bucketName, - null, - null, - invalidUnmodifiedCondition); - } catch (ErrorResponseException e) { - // File should not be copied as object was modified after date set in - // copyConditions. - if (e.errorResponse().errorCode() != ErrorCode.PRECONDITION_FAILED) { - throw e; + CopyObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .srcBucket(srcBucketName) + .srcObject(srcObjectName) + .headers(headers) + .metadataDirective(Directive.REPLACE) + .build()); + + ObjectStat stat = + client.statObject( + StatObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .build()); + if (!customContentType.equals(stat.contentType())) { + throw new Exception( + "content type differs. expected: " + + customContentType + + ", got: " + + stat.contentType()); } - } - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - // Destination bucket is expected to be empty, otherwise it will trigger an - // exception. - client.removeBucket(RemoveBucketArgs.builder().bucket(destBucketName).build()); - mintSuccessLog( - "copyObject(String bucketName, String objectName, String destBucketName, " - + "CopyConditions copyConditions)", - "CopyCondition: invalidUnmodifiedCondition", - startTime); + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); + client.removeBucket(RemoveBucketArgs.builder().bucket(srcBucketName).build()); + } } catch (Exception e) { - mintFailedLog( - "copyObject(String bucketName, String objectName, String destBucketName, " - + "CopyConditions copyConditions)", - "CopyCondition: invalidUnmodifiedCondition", - startTime, - null, - e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); - throw e; + handleException(methodName, testTags, startTime, e); } } - /** - * Test: copyObject(String bucketName, String objectName, String destBucketName, CopyConditions - * copyConditions, Map metadata) replace object metadata. - */ + /** Test: copyObject() empty metadata replace. */ public static void copyObject_test8() throws Exception { + String methodName = "copyObject()"; + String testTags = "[empty metadata replace]"; if (!mintEnv) { - System.out.println( - "Test: copyObject(String bucketName, String objectName, String destBucketName," - + "CopyConditions copyConditions, Map metadata)" - + " replace object metadata"); + System.out.println("Test: " + methodName + " " + testTags); } long startTime = System.currentTimeMillis(); + String srcBucketName = getRandomName(); + String srcObjectName = getRandomName(); try { - String objectName = getRandomName(); - try (final InputStream is = new ContentInputStream(1 * KB)) { - client.putObject(bucketName, objectName, is, new PutObjectOptions(1 * KB, -1)); - } - - String destBucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(destBucketName).build()); - - CopyConditions copyConditions = new CopyConditions(); - copyConditions.setReplaceMetadataDirective(); + client.makeBucket(MakeBucketArgs.builder().bucket(srcBucketName).build()); + try { + Map headers = new HashMap<>(); + headers.put("Content-Type", customContentType); + headers.put("X-Amz-Meta-My-Project", "Project One"); + PutObjectOptions options = new PutObjectOptions(1 * KB, -1); + options.setHeaders(headers); + client.putObject(srcBucketName, srcObjectName, new ContentInputStream(1 * KB), options); - Map metadata = new HashMap<>(); - metadata.put("Content-Type", customContentType); + client.copyObject( + CopyObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .srcBucket(srcBucketName) + .srcObject(srcObjectName) + .metadataDirective(Directive.REPLACE) + .build()); - client.copyObject( - destBucketName, objectName, metadata, null, bucketName, objectName, null, copyConditions); + ObjectStat stat = + client.statObject( + StatObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .build()); + if (stat.httpHeaders().containsKey("X-Amz-Meta-My-Project")) { + throw new Exception("expected user metadata to be removed in new object"); + } - ObjectStat objectStat = - client.statObject( - StatObjectArgs.builder().bucket(destBucketName).object(objectName).build()); - if (!customContentType.equals(objectStat.contentType())) { - throw new Exception( - "content type differs. expected: " - + customContentType - + ", got: " - + objectStat.contentType()); + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); + client.removeBucket(RemoveBucketArgs.builder().bucket(srcBucketName).build()); } - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(destBucketName).object(objectName).build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(destBucketName).build()); - mintSuccessLog( - "copyObject(String bucketName, String objectName, String destBucketName, " - + "CopyConditions copyConditions, Map metadata)", - null, - startTime); } catch (Exception e) { - mintFailedLog( - "copyObject(String bucketName, String objectName, String destBucketName, " - + "CopyConditions copyConditions, Map metadata)", - null, - startTime, - null, - e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); - throw e; + handleException(methodName, testTags, startTime, e); } } - /** - * Test: copyObject(String bucketName, String objectName, String destBucketName, CopyConditions - * copyConditions, Map metadata) remove object metadata. - */ + /** Test: copyObject() SSE-C. */ public static void copyObject_test9() throws Exception { - if (!mintEnv) { - System.out.println( - "Test: copyObject(String bucketName, String objectName, String destBucketName," - + "CopyConditions copyConditions, Map metadata)" - + " remove object metadata"); + String testTags = "[SSE-C]"; + if (!isSecureEndpoint) { + mintIgnoredLog("copyObject()", testTags, System.currentTimeMillis()); + return; } - long startTime = System.currentTimeMillis(); - try { - String objectName = getRandomName(); - Map headerMap = new HashMap<>(); - headerMap.put("Test", "testValue"); - - try (final InputStream is = new ContentInputStream(1)) { - PutObjectOptions options = new PutObjectOptions(1, -1); - options.setHeaders(headerMap); - client.putObject(bucketName, objectName, is, options); - } - - // Attempt to remove the user-defined metadata from the object - CopyConditions copyConditions = new CopyConditions(); - copyConditions.setReplaceMetadataDirective(); - - client.copyObject( - bucketName, - objectName, - new HashMap(), - null, - bucketName, - objectName, - null, - copyConditions); - ObjectStat objectStat = - client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); - if (objectStat.httpHeaders().containsKey("X-Amz-Meta-Test")) { - throw new Exception("expected user-defined metadata has been removed"); - } - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - mintSuccessLog( - "copyObject(String bucketName, String objectName, String destBucketName, " - + "CopyConditions copyConditions, Map metadata)", - null, - startTime); - } catch (Exception e) { - mintFailedLog( - "copyObject(String bucketName, String objectName, String destBucketName, " - + "CopyConditions copyConditions, Map metadata)", - null, - startTime, - null, - e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); - throw e; - } + ServerSideEncryptionCustomerKey ssec = + ServerSideEncryption.withCustomerKey( + new SecretKeySpec( + "01234567890123456789012345678901".getBytes(StandardCharsets.UTF_8), "AES")); + testCopyObject( + testTags, + ssec, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sse(ssec) + .srcBucket(getRandomName()) + .srcObject(getRandomName()) + .srcSsec(ssec) + .build(), + false); } - /** - * Test: copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, String - * destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget) To test using - * SSE_C. - */ + /** Test: copyObject() SSE-S3. */ public static void copyObject_test10() throws Exception { - if (!mintEnv) { - System.out.println( - "Test: copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, " - + "String destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget)" - + " using SSE_C. "); + String testTags = "[SSE-S3]"; + if (!isSecureEndpoint) { + mintIgnoredLog("copyObject()", testTags, System.currentTimeMillis()); + return; } - long startTime = System.currentTimeMillis(); - try { - String objectName = getRandomName(); - - // Generate a new 256 bit AES key - This key must be remembered by the client. - byte[] key = "01234567890123456789012345678901".getBytes(StandardCharsets.UTF_8); - SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); - - ServerSideEncryption ssePut = ServerSideEncryption.withCustomerKey(secretKeySpec); - ServerSideEncryption sseSource = ServerSideEncryption.withCustomerKey(secretKeySpec); - - byte[] keyTarget = "98765432100123456789012345678901".getBytes(StandardCharsets.UTF_8); - SecretKeySpec secretKeySpecTarget = new SecretKeySpec(keyTarget, "AES"); - - ServerSideEncryptionCustomerKey sseTarget = - ServerSideEncryption.withCustomerKey(secretKeySpecTarget); - - try (final InputStream is = new ContentInputStream(1)) { - PutObjectOptions options = new PutObjectOptions(1, -1); - options.setSse(ssePut); - client.putObject(bucketName, objectName, is, options); - } - - // Attempt to remove the user-defined metadata from the object - CopyConditions copyConditions = new CopyConditions(); - copyConditions.setReplaceMetadataDirective(); - - client.copyObject( - bucketName, - objectName, - null, - sseTarget, - bucketName, - objectName, - sseSource, - copyConditions); - - client.statObject( - StatObjectArgs.builder() - .bucket(bucketName) - .object(objectName) - .ssec(sseTarget) - .build()); // Check for object existence. - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - mintSuccessLog( - "copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, " - + "String destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget)" - + " using SSE_C.", - null, - startTime); - } catch (Exception e) { - mintFailedLog( - "copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, " - + "String destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget)" - + " using SSE_C.", - null, - startTime, - null, - e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); - throw e; - } + ServerSideEncryption sseS3 = ServerSideEncryption.atRest(); + testCopyObject( + testTags, + sseS3, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sse(sseS3) + .srcBucket(getRandomName()) + .srcObject(getRandomName()) + .build(), + false); } - /** - * Test: copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, String - * destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget) To test using - * SSE_S3. - */ + /** Test: copyObject() SSE-KMS. */ public static void copyObject_test11() throws Exception { - if (!mintEnv) { - System.out.println( - "Test: copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, " - + "String destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget)" - + " using SSE_S3. "); - } - - long startTime = System.currentTimeMillis(); - try { - String objectName = getRandomName(); - - ServerSideEncryption sse = ServerSideEncryption.atRest(); - - try (final InputStream is = new ContentInputStream(1)) { - PutObjectOptions options = new PutObjectOptions(1, -1); - options.setSse(sse); - client.putObject(bucketName, objectName, is, options); - } - - // Attempt to remove the user-defined metadata from the object - CopyConditions copyConditions = new CopyConditions(); - copyConditions.setReplaceMetadataDirective(); - - client.copyObject( - bucketName, objectName, null, sse, bucketName, objectName, null, copyConditions); - ObjectStat objectStat = - client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); - if (objectStat.httpHeaders().containsKey("X-Amz-Meta-Test")) { - throw new Exception("expected user-defined metadata has been removed"); - } - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - mintSuccessLog( - "copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, " - + "String destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget)" - + " using SSE_S3.", - null, - startTime); - } catch (Exception e) { - mintFailedLog( - "copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, " - + "String destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget)" - + " using SSE_S3.", - null, - startTime, - null, - e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); - throw e; - } - } - - /** - * Test: copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, String - * destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget) To test using - * SSE_KMS. - */ - public static void copyObject_test12() throws Exception { - String methodName = - "SSE-KMS: copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, " - + "String destBucketName, CopyConditions copyConditions, ServerSideEncryption sseTarget)"; - if (!mintEnv) { - System.out.println("Test: " + methodName); + String testTags = "[SSE-KMS]"; + if (!isSecureEndpoint || kmsKeyName == null) { + mintIgnoredLog("copyObject()", testTags, System.currentTimeMillis()); + return; } - long startTime = System.currentTimeMillis(); - try { - String objectName = getRandomName(); - - Map myContext = new HashMap<>(); - myContext.put("key1", "value1"); - - String keyId = ""; - keyId = System.getenv("MINT_KEY_ID"); - if (keyId.equals("")) { - mintIgnoredLog(methodName, null, startTime); - } - ServerSideEncryption sse = ServerSideEncryption.withManagedKeys("keyId", myContext); - - try (final InputStream is = new ContentInputStream(1)) { - PutObjectOptions options = new PutObjectOptions(1, -1); - options.setSse(sse); - client.putObject(bucketName, objectName, is, options); - } - - // Attempt to remove the user-defined metadata from the object - CopyConditions copyConditions = new CopyConditions(); - copyConditions.setReplaceMetadataDirective(); - - client.copyObject( - bucketName, objectName, null, sse, bucketName, objectName, null, copyConditions); - ObjectStat objectStat = - client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); - if (objectStat.httpHeaders().containsKey("X-Amz-Meta-Test")) { - throw new Exception("expected user-defined metadata has been removed"); - } - - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - mintFailedLog( - methodName, - null, - startTime, - null, - e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); - throw e; - } + Map myContext = new HashMap<>(); + myContext.put("key1", "value1"); + ServerSideEncryption sseKms = ServerSideEncryption.withManagedKeys(kmsKeyName, myContext); + testCopyObject( + testTags, + sseKms, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sse(sseKms) + .srcBucket(getRandomName()) + .srcObject(getRandomName()) + .build(), + false); } /** @@ -4476,7 +4192,7 @@ public static void runTests() throws Exception { putObject_test11(); statObject_test1(); - if (endpoint.toLowerCase(Locale.US).contains("https://")) statObject_test2(); + statObject_test2(); statObject_test3(); statObject_test4(); @@ -4522,6 +4238,8 @@ public static void runTests() throws Exception { copyObject_test7(); copyObject_test8(); copyObject_test9(); + copyObject_test10(); + copyObject_test11(); composeObject_test1(); composeObject_test2(); composeObject_test3(); @@ -4548,12 +4266,11 @@ public static void runTests() throws Exception { deleteObjectTags_test(); // SSE_C tests will only work over TLS connection - if (endpoint.toLowerCase(Locale.US).contains("https://")) { + if (isSecureEndpoint) { getObject_test7(); getObject_test9(); putObject_test12(); putObject_test13(); - copyObject_test10(); composeObject_test4(); composeObject_test5(); composeObject_test6(); @@ -4564,8 +4281,6 @@ public static void runTests() throws Exception { if (requestUrl.contains(".amazonaws.com")) { putObject_test14(); putObject_test15(); - copyObject_test11(); - copyObject_test12(); setBucketLifeCycle_test1(); getBucketLifeCycle_test1(); deleteBucketLifeCycle_test1(); @@ -4677,13 +4392,15 @@ public static Process runMinio() throws Exception { File binaryPath = new File(new File(System.getProperty("user.dir")), MINIO_BINARY); ProcessBuilder pb = new ProcessBuilder(binaryPath.getPath(), "server", "d1"); + kmsKeyName = "my-minio-key"; + Map env = pb.environment(); env.put("MINIO_ACCESS_KEY", "minio"); env.put("MINIO_SECRET_KEY", "minio123"); env.put("MINIO_KMS_KES_ENDPOINT", "https://play.min.io:7373"); env.put("MINIO_KMS_KES_KEY_FILE", "play.min.io.kes.root.key"); env.put("MINIO_KMS_KES_CERT_FILE", "play.min.io.kes.root.cert"); - env.put("MINIO_KMS_KES_KEY_NAME", "my-minio-key"); + env.put("MINIO_KMS_KES_KEY_NAME", kmsKeyName); env.put("MINIO_NOTIFY_WEBHOOK_ENABLE_miniojavatest", "on"); env.put("MINIO_NOTIFY_WEBHOOK_ENDPOINT_miniojavatest", "http://example.org/"); sqsArn = "arn:minio:sqs::miniojavatest:webhook"; @@ -4711,6 +4428,10 @@ public static void main(String[] args) throws Exception { accessKey = "minio"; secretKey = "minio123"; region = "us-east-1"; + kmsKeyName = System.getenv("MINIO_JAVA_TEST_KMS_KEY_NAME"); + if (kmsKeyName == null) { + kmsKeyName = System.getenv("MINT_KEY_ID"); + } sqsArn = System.getenv("MINIO_JAVA_TEST_SQS_ARN"); if (!downloadMinio()) { @@ -4741,6 +4462,8 @@ public static void main(String[] args) throws Exception { region = args[3]; } + isSecureEndpoint = endpoint.toLowerCase(Locale.US).contains("https://"); + int exitValue = 0; try { client = new MinioClient(endpoint, accessKey, secretKey); From d4ccd41e74b329a0ca0136af682b2112d72f957d Mon Sep 17 00:00:00 2001 From: "Bala.FA" Date: Tue, 9 Jun 2020 20:13:43 +0530 Subject: [PATCH 2/3] address review comments --- api/src/main/java/io/minio/MinioClient.java | 6 ++---- api/src/main/java/io/minio/ObjectWriteArgs.java | 10 +--------- functional/FunctionalTest.java | 6 ++---- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/api/src/main/java/io/minio/MinioClient.java b/api/src/main/java/io/minio/MinioClient.java index e9aa06092..f1d5fa8ed 100755 --- a/api/src/main/java/io/minio/MinioClient.java +++ b/api/src/main/java/io/minio/MinioClient.java @@ -110,6 +110,7 @@ import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Optional; import java.util.Scanner; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -2445,10 +2446,7 @@ public void copyObject(CopyObjectArgs args) Multimap headers = args.genHeaders(); // Source object name is optional, if empty default to object name. - String srcObject = args.object(); - if (args.srcObject() != null) { - srcObject = args.srcObject(); - } + String srcObject = Optional.ofNullable(args.srcObject()).orElse(args.object()); String copySource = S3Escaper.encodePath("/" + args.srcBucket() + "/" + srcObject); if (args.srcVersionId() != null) { diff --git a/api/src/main/java/io/minio/ObjectWriteArgs.java b/api/src/main/java/io/minio/ObjectWriteArgs.java index 7fa9a936b..fc941cef0 100644 --- a/api/src/main/java/io/minio/ObjectWriteArgs.java +++ b/api/src/main/java/io/minio/ObjectWriteArgs.java @@ -96,28 +96,25 @@ protected void validateSse(HttpUrl url) { checkSse(sse, url); } + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public abstract static class Builder, A extends ObjectWriteArgs> extends ObjectArgs.Builder { - @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public B headers(Map headers) { final Multimap headersCopy = toMultimap(headers); operations.add(args -> args.headers = headersCopy); return (B) this; } - @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public B headers(Multimap headers) { final Multimap headersCopy = copyMultimap(headers); operations.add(args -> args.headers = headersCopy); return (B) this; } - @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public B userMetadata(Map userMetadata) { return userMetadata((userMetadata == null) ? null : Multimaps.forMap(userMetadata)); } - @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public B userMetadata(Multimap userMetadata) { Multimap userMetadataCopy = HashMultimap.create(); if (userMetadata != null) { @@ -134,33 +131,28 @@ public B userMetadata(Multimap userMetadata) { return (B) this; } - @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public B sse(ServerSideEncryption sse) { operations.add(args -> args.sse = sse); return (B) this; } - @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public B tags(Map map) { final Tags tags = (map == null) ? new Tags() : Tags.newObjectTags(map); operations.add(args -> args.tags = tags); return (B) this; } - @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public B tags(Tags tags) { Tags tagsCopy = (tags == null) ? new Tags() : Tags.newObjectTags(tags.get()); operations.add(args -> args.tags = tagsCopy); return (B) this; } - @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public B retention(Retention retention) { operations.add(args -> args.retention = retention); return (B) this; } - @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public B legalHold(boolean flag) { operations.add(args -> args.legalHold = flag); return (B) this; diff --git a/functional/FunctionalTest.java b/functional/FunctionalTest.java index 1dc9fd833..d1da729b1 100644 --- a/functional/FunctionalTest.java +++ b/functional/FunctionalTest.java @@ -123,6 +123,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -2315,10 +2316,7 @@ public static void testCopyObject( System.out.println("Test: " + methodName + " " + testTags); } - String srcObject = args.object(); - if (args.srcObject() != null) { - srcObject = args.srcObject(); - } + String srcObject = Optional.ofNullable(args.srcObject()).orElse(args.object()); long startTime = System.currentTimeMillis(); try { From 3fd11fea7244b2f2ef7893e6d1a901f1e3b1a483 Mon Sep 17 00:00:00 2001 From: "Bala.FA" Date: Tue, 9 Jun 2020 21:37:32 +0530 Subject: [PATCH 3/3] address review comments --- api/src/main/java/io/minio/MinioClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/io/minio/MinioClient.java b/api/src/main/java/io/minio/MinioClient.java index f1d5fa8ed..e410890ab 100755 --- a/api/src/main/java/io/minio/MinioClient.java +++ b/api/src/main/java/io/minio/MinioClient.java @@ -2359,7 +2359,7 @@ public void copyObject( value = map.get("x-amz-metadata-directive"); if (value != null) { - builder.metadataDirective(Directive.REPLACE); + builder.metadataDirective(Directive.valueOf(value)); } }