Skip to content

Commit

Permalink
Add missing fields as per S3 specification (minio#1618)
Browse files Browse the repository at this point in the history
Signed-off-by: Bala.FA <bala@minio.io>
  • Loading branch information
balamurugana authored Jan 25, 2025
1 parent e17ef94 commit 0f533e8
Show file tree
Hide file tree
Showing 52 changed files with 1,074 additions and 424 deletions.
112 changes: 112 additions & 0 deletions api/src/main/java/io/minio/GenericUploadResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2025 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.minio;

import io.minio.messages.CompleteMultipartUploadResult;
import io.minio.messages.CopyObjectResult;
import okhttp3.Headers;

/** Response class of any APIs doing object/part upload. */
public class GenericUploadResponse extends GenericResponse {
private String etag;
private String checksumCRC32;
private String checksumCRC32C;
private String checksumCRC64NVME;
private String checksumSHA1;
private String checksumSHA256;
private String checksumType;

public GenericUploadResponse(
Headers headers, String bucket, String region, String object, String etag) {
super(headers, bucket, region, object);
this.etag = etag;
if (headers != null) {
this.checksumCRC32 = headers.get("x-amz-checksum-crc32");
this.checksumCRC32C = headers.get("x-amz-checksum-crc32c");
this.checksumCRC64NVME = headers.get("x-amz-checksum-crc64nvme");
this.checksumSHA1 = headers.get("x-amz-checksum-sha1");
this.checksumSHA256 = headers.get("x-amz-checksum-sha256");
this.checksumType = headers.get("x-amz-checksum-type");
}
}

public GenericUploadResponse(
Headers headers,
String bucket,
String region,
String object,
String etag,
CopyObjectResult result) {
super(headers, bucket, region, object);
this.etag = etag;
if (result != null) {
this.checksumType = result.checksumType();
this.checksumCRC32 = result.checksumCRC32();
this.checksumCRC32C = result.checksumCRC32C();
this.checksumCRC64NVME = result.checksumCRC64NVME();
this.checksumSHA1 = result.checksumSHA1();
this.checksumSHA256 = result.checksumSHA256();
}
}

public GenericUploadResponse(
Headers headers,
String bucket,
String region,
String object,
String etag,
CompleteMultipartUploadResult result) {
super(headers, bucket, region, object);
this.etag = etag;
if (result != null) {
this.checksumType = result.checksumType();
this.checksumCRC32 = result.checksumCRC32();
this.checksumCRC32C = result.checksumCRC32C();
this.checksumCRC64NVME = result.checksumCRC64NVME();
this.checksumSHA1 = result.checksumSHA1();
this.checksumSHA256 = result.checksumSHA256();
}
}

public String etag() {
return etag;
}

public String checksumCRC32() {
return checksumCRC32;
}

public String checksumCRC32C() {
return checksumCRC32C;
}

public String checksumCRC64NVME() {
return checksumCRC64NVME;
}

public String checksumSHA1() {
return checksumSHA1;
}

public String checksumSHA256() {
return checksumSHA256;
}

public String checksumType() {
return checksumType;
}
}
67 changes: 67 additions & 0 deletions api/src/main/java/io/minio/ListBucketsArgs.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,31 @@

package io.minio;

import java.util.Objects;

/** Argument class of {@link MinioAsyncClient#listBuckets} and {@link MinioClient#listBuckets}. */
public class ListBucketsArgs extends BaseArgs {
private String bucketRegion;
private int maxBuckets = 10000;
private String prefix;
private String continuationToken;

public String bucketRegion() {
return bucketRegion;
}

public int maxBuckets() {
return maxBuckets;
}

public String prefix() {
return prefix;
}

public String continuationToken() {
return continuationToken;
}

public static Builder builder() {
return new Builder();
}
Expand All @@ -26,5 +49,49 @@ public static Builder builder() {
public static final class Builder extends BaseArgs.Builder<Builder, ListBucketsArgs> {
@Override
protected void validate(ListBucketsArgs args) {}

public Builder bucketRegion(String region) {
validateNullOrNotEmptyString(region, "bucket region");
operations.add(args -> args.bucketRegion = region);
return this;
}

public Builder maxBuckets(int maxBuckets) {
if (maxBuckets < 1 || maxBuckets > 10000) {
throw new IllegalArgumentException("max buckets must be between 1 and 10000");
}

operations.add(args -> args.maxBuckets = maxBuckets);
return this;
}

public Builder prefix(String prefix) {
validateNullOrNotEmptyString(prefix, "prefix");
operations.add(args -> args.prefix = prefix);
return this;
}

public Builder continuationToken(String continuationToken) {
validateNullOrNotEmptyString(continuationToken, "continuation token");
operations.add(args -> args.continuationToken = continuationToken);
return this;
}
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ListBucketsArgs)) return false;
if (!super.equals(o)) return false;
ListBucketsArgs that = (ListBucketsArgs) o;
return Objects.equals(bucketRegion, that.bucketRegion)
&& maxBuckets == that.maxBuckets
&& Objects.equals(prefix, that.prefix)
&& Objects.equals(continuationToken, that.continuationToken);
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), bucketRegion, maxBuckets, prefix, continuationToken);
}
}
34 changes: 34 additions & 0 deletions api/src/main/java/io/minio/ListBucketsResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2025 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.minio;

import io.minio.messages.ListAllMyBucketsResult;
import okhttp3.Headers;

/** Response class of {@link S3Base#listBucketsAsync}. */
public class ListBucketsResponse extends GenericResponse {
private ListAllMyBucketsResult result;

public ListBucketsResponse(Headers headers, ListAllMyBucketsResult result) {
super(headers, null, null, null);
this.result = result;
}

public ListAllMyBucketsResult result() {
return result;
}
}
144 changes: 119 additions & 25 deletions api/src/main/java/io/minio/MinioAsyncClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,7 @@ public CompletableFuture<Void> removeObject(RemoveObjectArgs args)
* minioAsyncClient.removeObjects(
* RemoveObjectsArgs.builder().bucket("my-bucketname").objects(objects).build());
* for (Result<DeleteError> result : results) {
* DeleteError error = errorResult.get();
* DeleteError error = result.get();
* System.out.println(
* "Error in deleting object " + error.objectName() + "; " + error.message());
* }
Expand Down Expand Up @@ -1330,41 +1330,135 @@ public Iterable<Result<Item>> listObjects(ListObjectsArgs args) {
public CompletableFuture<List<Bucket>> listBuckets()
throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
NoSuchAlgorithmException, XmlParserException {
return listBuckets(ListBucketsArgs.builder().build());
return listBucketsAsync(null, null, null, null, null, null)
.thenApply(
response -> {
return response.result().buckets();
});
}

/**
* Lists bucket information of all buckets.
*
* <pre>Example:{@code
* CompletableFuture<List<Bucket>> future =
* minioAsyncClient.listBuckets(ListBucketsArgs.builder().extraHeaders(headers).build());
* Iterable<Result<Bucket>> results = minioAsyncClient.listBuckets(ListBucketsArgs.builder().build());
* for (Result<Bucket> result : results) {
* Bucket bucket = result.get();
* System.out.println(String.format("Bucket: %s, Region: %s, CreationDate: %s", bucket.name(), bucket.bucketRegion(), bucket.creationDate()));
* }
* }</pre>
*
* @return {@link CompletableFuture}&lt;{@link List}&lt;{@link Bucket}&gt;&gt; object.
* @throws InsufficientDataException thrown to indicate not enough data available in InputStream.
* @throws InternalException thrown to indicate internal library error.
* @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library.
* @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.
* @return {@link Iterable}&lt;{@link List}&lt;{@link Bucket}&gt;&gt; object.
*/
public CompletableFuture<List<Bucket>> listBuckets(ListBucketsArgs args)
throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
NoSuchAlgorithmException, XmlParserException {
return executeGetAsync(args, null, null)
.thenApply(
response -> {
public Iterable<Result<Bucket>> listBuckets(ListBucketsArgs args) {
return new Iterable<Result<Bucket>>() {
@Override
public Iterator<Result<Bucket>> iterator() {
return new Iterator<Result<Bucket>>() {
private ListAllMyBucketsResult result = null;
private Result<Bucket> error = null;
private Iterator<Bucket> iterator = null;
private boolean completed = false;

private synchronized void populate() {
if (completed) return;

try {
this.iterator = new LinkedList<Bucket>().iterator();
try {
ListAllMyBucketsResult result =
Xml.unmarshal(ListAllMyBucketsResult.class, response.body().charStream());
return result.buckets();
} catch (XmlParserException e) {
throw new CompletionException(e);
} finally {
response.close();
ListBucketsResponse response =
listBucketsAsync(
args.bucketRegion(),
args.maxBuckets(),
args.prefix(),
(result == null)
? args.continuationToken()
: result.continuationToken(),
args.extraHeaders(),
args.extraQueryParams())
.get();
this.result = response.result();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throwEncapsulatedException(e);
}
});
this.iterator = this.result.buckets().iterator();
} catch (ErrorResponseException
| InsufficientDataException
| InternalException
| InvalidKeyException
| InvalidResponseException
| IOException
| NoSuchAlgorithmException
| ServerException
| XmlParserException e) {
this.error = new Result<>(e);
completed = true;
}
}

@Override
public boolean hasNext() {
if (this.completed) return false;

if (this.error == null && this.iterator == null) {
populate();
}

if (this.error == null
&& !this.iterator.hasNext()
&& this.result.continuationToken() != null
&& !this.result.continuationToken().isEmpty()) {
populate();
}

if (this.error != null) return true;
if (this.iterator.hasNext()) return true;

this.completed = true;
return false;
}

@Override
public Result<Bucket> next() {
if (this.completed) throw new NoSuchElementException();
if (this.error == null && this.iterator == null) {
populate();
}

if (this.error == null
&& !this.iterator.hasNext()
&& this.result.continuationToken() != null
&& !this.result.continuationToken().isEmpty()) {
populate();
}

if (this.error != null) {
this.completed = true;
return this.error;
}

Bucket item = null;
if (this.iterator.hasNext()) {
item = this.iterator.next();
}

if (item != null) {
return new Result<>(item);
}

this.completed = true;
throw new NoSuchElementException();
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}

/**
Expand Down
Loading

0 comments on commit 0f533e8

Please sign in to comment.