Skip to content

Commit

Permalink
add arg builder support to copyObject API
Browse files Browse the repository at this point in the history
  • Loading branch information
balamurugana committed Jun 8, 2020
1 parent 6ce2f28 commit 82d45dc
Show file tree
Hide file tree
Showing 11 changed files with 987 additions and 1,038 deletions.
148 changes: 148 additions & 0 deletions api/src/main/java/io/minio/CopyObjectArgs.java
Original file line number Diff line number Diff line change
@@ -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<Builder, CopyObjectArgs> {
@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;
}
}
}
23 changes: 23 additions & 0 deletions api/src/main/java/io/minio/Directive.java
Original file line number Diff line number Diff line change
@@ -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;
}
180 changes: 159 additions & 21 deletions api/src/main/java/io/minio/MinioClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2305,7 +2305,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,
Expand All @@ -2319,48 +2321,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<String, String> 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.
*
* <pre>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());
* }</pre>
*
* @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<String, String> 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());
}
}

Expand Down
Loading

0 comments on commit 82d45dc

Please sign in to comment.