From c9e40cc2f9788b33d3d493bf5f2d3e5341199ffe Mon Sep 17 00:00:00 2001 From: "Bala.FA" Date: Thu, 23 Jan 2025 19:03:41 +0530 Subject: [PATCH 1/2] remove deprecated Signed-off-by: Bala.FA --- api/src/main/java/io/minio/MinioClient.java | 20 - api/src/main/java/io/minio/S3Base.java | 1053 +------------------ 2 files changed, 9 insertions(+), 1064 deletions(-) diff --git a/api/src/main/java/io/minio/MinioClient.java b/api/src/main/java/io/minio/MinioClient.java index 3b2bf5e19..16e00f7c8 100644 --- a/api/src/main/java/io/minio/MinioClient.java +++ b/api/src/main/java/io/minio/MinioClient.java @@ -2660,26 +2660,6 @@ public void traceOff() throws IOException { asyncClient.traceOff(); } - /** - * Enables accelerate endpoint for Amazon S3 endpoint. - * - * @deprecated This method is no longer supported. - */ - @Deprecated - public void enableAccelerateEndpoint() { - asyncClient.enableAccelerateEndpoint(); - } - - /** - * Disables accelerate endpoint for Amazon S3 endpoint. - * - * @deprecated This method is no longer supported. - */ - @Deprecated - public void disableAccelerateEndpoint() { - asyncClient.disableAccelerateEndpoint(); - } - /** Enables dual-stack endpoint for Amazon S3 endpoint. */ public void enableDualStackEndpoint() { asyncClient.enableDualStackEndpoint(); diff --git a/api/src/main/java/io/minio/S3Base.java b/api/src/main/java/io/minio/S3Base.java index 324637ccb..1595913cd 100644 --- a/api/src/main/java/io/minio/S3Base.java +++ b/api/src/main/java/io/minio/S3Base.java @@ -148,29 +148,6 @@ public abstract class S3Base implements AutoCloseable { protected OkHttpClient httpClient; protected boolean closeHttpClient; - /** @deprecated This method is no longer supported. */ - @Deprecated - protected S3Base( - HttpUrl baseUrl, - String awsS3Prefix, - String awsDomainSuffix, - boolean awsDualstack, - boolean useVirtualStyle, - String region, - Provider provider, - OkHttpClient httpClient) { - this( - baseUrl, - awsS3Prefix, - awsDomainSuffix, - awsDualstack, - useVirtualStyle, - region, - provider, - httpClient, - false); - } - protected S3Base( HttpUrl baseUrl, String awsS3Prefix, @@ -192,35 +169,6 @@ protected S3Base( this.closeHttpClient = closeHttpClient; } - /** @deprecated This method is no longer supported. */ - @Deprecated - protected S3Base( - HttpUrl baseUrl, - String region, - boolean isAwsHost, - boolean isFipsHost, - boolean isAccelerateHost, - boolean isDualStackHost, - boolean useVirtualStyle, - Provider provider, - OkHttpClient httpClient) { - this.baseUrl = baseUrl; - if (isAwsHost) this.awsS3Prefix = "s3."; - if (isFipsHost) this.awsS3Prefix = "s3-fips."; - if (isAccelerateHost) this.awsS3Prefix = "s3-accelerate."; - if (isAwsHost || isFipsHost || isAccelerateHost) { - String host = baseUrl.host(); - if (host.endsWith(".amazonaws.com")) this.awsDomainSuffix = "amazonaws.com"; - if (host.endsWith(".amazonaws.com.cn")) this.awsDomainSuffix = "amazonaws.com.cn"; - } - this.awsDualstack = isDualStackHost; - this.useVirtualStyle = useVirtualStyle; - this.region = region; - this.provider = provider; - this.httpClient = httpClient; - this.closeHttpClient = false; - } - protected S3Base(S3Base client) { this.baseUrl = client.baseUrl; this.awsS3Prefix = client.awsS3Prefix; @@ -872,74 +820,6 @@ protected CompletableFuture executeAsync( }); } - /** - * Execute HTTP request for given parameters. - * - * @deprecated This method is no longer supported. Use {@link #executeAsync}. - */ - @Deprecated - protected Response execute( - Method method, - String bucketName, - String objectName, - String region, - Headers headers, - Multimap queryParamMap, - Object body, - int length) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - CompletableFuture completableFuture = - executeAsync(method, bucketName, objectName, region, headers, queryParamMap, body, length); - try { - return completableFuture.get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - - /** - * Execute HTTP request for given args and parameters. - * - * @deprecated This method is no longer supported. Use {@link #executeAsync}. - */ - @Deprecated - protected Response execute( - Method method, - BaseArgs args, - Multimap headers, - Multimap queryParams, - Object body, - int length) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - String bucketName = null; - String region = null; - String objectName = null; - - if (args instanceof BucketArgs) { - bucketName = ((BucketArgs) args).bucket(); - region = ((BucketArgs) args).region(); - } - - if (args instanceof ObjectArgs) objectName = ((ObjectArgs) args).object(); - - return execute( - method, - bucketName, - objectName, - getRegion(bucketName, region), - httpHeaders(merge(args.extraHeaders(), headers)), - merge(args.extraQueryParams(), queryParams), - body, - length); - } - /** Returns region of given bucket either from region cache or set in constructor. */ protected CompletableFuture getRegionAsync(String bucketName, String region) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, @@ -987,26 +867,6 @@ protected CompletableFuture getRegionAsync(String bucketName, String reg }); } - /** - * Returns region of given bucket either from region cache or set in constructor. - * - * @deprecated This method is no longer supported. Use {@link #getRegionAsync}. - */ - @Deprecated - protected String getRegion(String bucketName, String region) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - try { - return getRegionAsync(bucketName, region).get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** Execute asynchronously GET HTTP request for given parameters. */ protected CompletableFuture executeGetAsync( BaseArgs args, Multimap headers, Multimap queryParams) @@ -1015,27 +875,6 @@ protected CompletableFuture executeGetAsync( return executeAsync(Method.GET, args, headers, queryParams, null, 0); } - /** - * Execute GET HTTP request for given parameters. - * - * @deprecated This method is no longer supported. Use {@link #executeGetAsync}. - */ - @Deprecated - protected Response executeGet( - BaseArgs args, Multimap headers, Multimap queryParams) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - try { - return executeGetAsync(args, headers, queryParams).get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** Execute asynchronously HEAD HTTP request for given parameters. */ protected CompletableFuture executeHeadAsync( BaseArgs args, Multimap headers, Multimap queryParams) @@ -1071,27 +910,6 @@ protected CompletableFuture executeHeadAsync( }); } - /** - * Execute HEAD HTTP request for given parameters. - * - * @deprecated This method is no longer supported. Use {@link #executeHeadAsync}. - */ - @Deprecated - protected Response executeHead( - BaseArgs args, Multimap headers, Multimap queryParams) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - try { - return executeHeadAsync(args, headers, queryParams).get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** Execute asynchronously DELETE HTTP request for given parameters. */ protected CompletableFuture executeDeleteAsync( BaseArgs args, Multimap headers, Multimap queryParams) @@ -1105,27 +923,6 @@ protected CompletableFuture executeDeleteAsync( }); } - /** - * Execute DELETE HTTP request for given parameters. - * - * @deprecated This method is no longer supported. Use {@link #executeDeleteAsync}. - */ - @Deprecated - protected Response executeDelete( - BaseArgs args, Multimap headers, Multimap queryParams) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - try { - return executeDeleteAsync(args, headers, queryParams).get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** Execute asynchronously POST HTTP request for given parameters. */ protected CompletableFuture executePostAsync( BaseArgs args, @@ -1137,30 +934,6 @@ protected CompletableFuture executePostAsync( return executeAsync(Method.POST, args, headers, queryParams, data, 0); } - /** - * Execute POST HTTP request for given parameters. - * - * @deprecated This method is no longer supported. Use {@link #executePostAsync}. - */ - @Deprecated - protected Response executePost( - BaseArgs args, - Multimap headers, - Multimap queryParams, - Object data) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - try { - return executePostAsync(args, headers, queryParams, data).get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** Execute asynchronously PUT HTTP request for given parameters. */ protected CompletableFuture executePutAsync( BaseArgs args, @@ -1173,31 +946,6 @@ protected CompletableFuture executePutAsync( return executeAsync(Method.PUT, args, headers, queryParams, data, length); } - /** - * Execute PUT HTTP request for given parameters. - * - * @deprecated This method is no longer supported. Use {@link #executePutAsync}. - */ - @Deprecated - protected Response executePut( - BaseArgs args, - Multimap headers, - Multimap queryParams, - Object data, - int length) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - try { - return executePutAsync(args, headers, queryParams, data, length).get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - protected CompletableFuture calculatePartCountAsync(List sources) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { @@ -1282,91 +1030,6 @@ protected CompletableFuture calculatePartCountAsync(List return completableFuture; } - /** Calculate part count of given compose sources. */ - @Deprecated - protected int calculatePartCount(List sources) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - long objectSize = 0; - int partCount = 0; - int i = 0; - for (ComposeSource src : sources) { - i++; - StatObjectResponse stat = null; - try { - stat = statObjectAsync(new StatObjectArgs((ObjectReadArgs) src)).get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - } - - src.buildHeaders(stat.size(), stat.etag()); - - long size = stat.size(); - if (src.length() != null) { - size = src.length(); - } else if (src.offset() != null) { - size -= src.offset(); - } - - if (size < ObjectWriteArgs.MIN_MULTIPART_SIZE && sources.size() != 1 && i != sources.size()) { - throw new IllegalArgumentException( - "source " - + src.bucket() - + "/" - + src.object() - + ": size " - + size - + " must be greater than " - + ObjectWriteArgs.MIN_MULTIPART_SIZE); - } - - objectSize += size; - if (objectSize > ObjectWriteArgs.MAX_OBJECT_SIZE) { - throw new IllegalArgumentException( - "destination object size must be less than " + ObjectWriteArgs.MAX_OBJECT_SIZE); - } - - if (size > ObjectWriteArgs.MAX_PART_SIZE) { - long count = size / ObjectWriteArgs.MAX_PART_SIZE; - long lastPartSize = size - (count * ObjectWriteArgs.MAX_PART_SIZE); - if (lastPartSize > 0) { - count++; - } else { - lastPartSize = ObjectWriteArgs.MAX_PART_SIZE; - } - - if (lastPartSize < ObjectWriteArgs.MIN_MULTIPART_SIZE - && sources.size() != 1 - && i != sources.size()) { - throw new IllegalArgumentException( - "source " - + src.bucket() - + "/" - + src.object() - + ": " - + "for multipart split upload of " - + size - + ", last part size is less than " - + ObjectWriteArgs.MIN_MULTIPART_SIZE); - } - partCount += (int) count; - } else { - partCount++; - } - - if (partCount > ObjectWriteArgs.MAX_MULTIPART_COUNT) { - throw new IllegalArgumentException( - "Compose sources create more than allowed multipart count " - + ObjectWriteArgs.MAX_MULTIPART_COUNT); - } - } - - return partCount; - } - private abstract class ObjectIterator implements Iterator> { protected Result error; protected Iterator itemIterator; @@ -1634,32 +1297,6 @@ protected PartReader newPartReader(Object data, long objectSize, long partSize, return null; } - /** - * Execute put object. - * - * @deprecated This method is no longer supported. Use {@link #putObjectAsync}. - */ - @Deprecated - protected ObjectWriteResponse putObject( - PutObjectBaseArgs args, - Object data, - long objectSize, - long partSize, - int partCount, - String contentType) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - try { - return putObjectAsync(args, data, objectSize, partSize, partCount, contentType).get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** Notification result records representation. */ protected static class NotificationResultRecords { Response response = null; @@ -1827,26 +1464,6 @@ public void traceOff() throws IOException { this.traceStream = null; } - /** - * Enables accelerate endpoint for Amazon S3 endpoint. - * - * @deprecated This method is no longer supported. - */ - @Deprecated - public void enableAccelerateEndpoint() { - this.awsS3Prefix = "s3-accelerate."; - } - - /** - * Disables accelerate endpoint for Amazon S3 endpoint. - * - * @deprecated This method is no longer supported. - */ - @Deprecated - public void disableAccelerateEndpoint() { - this.awsS3Prefix = "s3."; - } - /** Enables dual-stack endpoint for Amazon S3 endpoint. */ public void enableDualStackEndpoint() { this.awsDualstack = true; @@ -1953,52 +1570,6 @@ public CompletableFuture abortMultipartUploadAsync }); } - /** - * Do AbortMultipartUpload - * S3 API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket. - * @param objectName Object name in the bucket. - * @param uploadId Upload ID. - * @param extraHeaders Extra headers (Optional). - * @param extraQueryParams Extra query parameters (Optional). - * @return {@link AbortMultipartUploadResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #abortMultipartUploadAsync}. - */ - @Deprecated - protected AbortMultipartUploadResponse abortMultipartUpload( - String bucketName, - String region, - String objectName, - String uploadId, - Multimap extraHeaders, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return abortMultipartUploadAsync( - bucketName, region, objectName, uploadId, extraHeaders, extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** * Do CompleteMultipartUpload @@ -2105,54 +1676,6 @@ public CompletableFuture completeMultipartUploadAsync( }); } - /** - * Do CompleteMultipartUpload - * S3 API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket. - * @param objectName Object name in the bucket. - * @param uploadId Upload ID. - * @param parts List of parts. - * @param extraHeaders Extra headers (Optional). - * @param extraQueryParams Extra query parameters (Optional). - * @return {@link ObjectWriteResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #completeMultipartUploadAsync}. - */ - @Deprecated - protected ObjectWriteResponse completeMultipartUpload( - String bucketName, - String region, - String objectName, - String uploadId, - Part[] parts, - Multimap extraHeaders, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return completeMultipartUploadAsync( - bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** * Do CreateMultipartUpload @@ -2228,68 +1751,25 @@ public CompletableFuture createMultipartUploadAsy /** * Do CreateMultipartUpload - * S3 API. + * href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html">DeleteObjects S3 + * API asynchronously. * * @param bucketName Name of the bucket. - * @param region Region name of buckets in S3 service. - * @param objectName Object name in the bucket. - * @param headers Request headers. + * @param region Region of the bucket (Optional). + * @param objectList List of object names. + * @param quiet Quiet flag. + * @param bypassGovernanceMode Bypass Governance retention mode. + * @param extraHeaders Extra headers for request (Optional). * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CreateMultipartUploadResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. + * @return {@link CompletableFuture}<{@link DeleteObjectsResponse}> 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 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. - * @deprecated This method is no longer supported. Use {@link #createMultipartUploadAsync}. */ - @Deprecated - protected CreateMultipartUploadResponse createMultipartUpload( - String bucketName, - String region, - String objectName, - Multimap headers, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return createMultipartUploadAsync(bucketName, region, objectName, headers, extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - - /** - * Do DeleteObjects S3 - * API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param objectList List of object names. - * @param quiet Quiet flag. - * @param bypassGovernanceMode Bypass Governance retention mode. - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link DeleteObjectsResponse}> 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. - */ - protected CompletableFuture deleteObjectsAsync( + protected CompletableFuture deleteObjectsAsync( String bucketName, String region, List objectList, @@ -2359,60 +1839,6 @@ protected CompletableFuture deleteObjectsAsync( }); } - /** - * Do DeleteObjects S3 - * API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param objectList List of object names. - * @param quiet Quiet flag. - * @param bypassGovernanceMode Bypass Governance retention mode. - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link DeleteObjectsResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #deleteObjectsAsync}. - */ - @Deprecated - protected DeleteObjectsResponse deleteObjects( - String bucketName, - String region, - List objectList, - boolean quiet, - boolean bypassGovernanceMode, - Multimap extraHeaders, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return deleteObjectsAsync( - bucketName, - region, - objectList, - quiet, - bypassGovernanceMode, - extraHeaders, - extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** * Do ListObjects * version 2 S3 API asynchronously. @@ -2498,74 +1924,6 @@ protected CompletableFuture listObjectsV2Async( }); } - /** - * Do ListObjects - * version 1 S3 API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param delimiter Delimiter (Optional). - * @param encodingType Encoding type (Optional). - * @param startAfter Fetch listing after this key (Optional). - * @param maxKeys Maximum object information to fetch (Optional). - * @param prefix Prefix (Optional). - * @param continuationToken Continuation token (Optional). - * @param fetchOwner Flag to fetch owner information (Optional). - * @param includeUserMetadata MinIO extension flag to include user metadata (Optional). - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link ListObjectsV2Response} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #listObjectsV2Async}. - */ - @Deprecated - protected ListObjectsV2Response listObjectsV2( - String bucketName, - String region, - String delimiter, - String encodingType, - String startAfter, - Integer maxKeys, - String prefix, - String continuationToken, - boolean fetchOwner, - boolean includeUserMetadata, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException, IOException { - try { - return listObjectsV2Async( - bucketName, - region, - delimiter, - encodingType, - startAfter, - maxKeys, - prefix, - continuationToken, - fetchOwner, - includeUserMetadata, - extraHeaders, - extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** * Do ListObjects * version 1 S3 API asynchronously. @@ -2641,65 +1999,6 @@ protected CompletableFuture listObjectsV1Async( }); } - /** - * Do ListObjects - * version 1 S3 API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param delimiter Delimiter (Optional). - * @param encodingType Encoding type (Optional). - * @param marker Marker (Optional). - * @param maxKeys Maximum object information to fetch (Optional). - * @param prefix Prefix (Optional). - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link ListObjectsV1Response} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #listObjectsV1Async}. - */ - @Deprecated - protected ListObjectsV1Response listObjectsV1( - String bucketName, - String region, - String delimiter, - String encodingType, - String marker, - Integer maxKeys, - String prefix, - Multimap extraHeaders, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return listObjectsV1Async( - bucketName, - region, - delimiter, - encodingType, - marker, - maxKeys, - prefix, - extraHeaders, - extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** * Do ListObjectVersions @@ -2781,69 +2080,6 @@ protected CompletableFuture listObjectVersionsAsync( }); } - /** - * Do ListObjectVersions - * API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param delimiter Delimiter (Optional). - * @param encodingType Encoding type (Optional). - * @param keyMarker Key marker (Optional). - * @param maxKeys Maximum object information to fetch (Optional). - * @param prefix Prefix (Optional). - * @param versionIdMarker Version ID marker (Optional). - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link ListObjectVersionsResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #listObjectVersionsAsync}. - */ - @Deprecated - protected ListObjectVersionsResponse listObjectVersions( - String bucketName, - String region, - String delimiter, - String encodingType, - String keyMarker, - Integer maxKeys, - String prefix, - String versionIdMarker, - Multimap extraHeaders, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return listObjectVersionsAsync( - bucketName, - region, - delimiter, - encodingType, - keyMarker, - maxKeys, - prefix, - versionIdMarker, - extraHeaders, - extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - private Part[] uploadParts( PutObjectBaseArgs args, String uploadId, PartReader partReader, PartSource firstPartSource) throws InterruptedException, ExecutionException, InsufficientDataException, InternalException, @@ -3165,51 +2401,6 @@ protected CompletableFuture putObjectAsync( }); } - /** - * Do PutObject S3 - * API. - * - * @param bucketName Name of the bucket. - * @param objectName Object name in the bucket. - * @param data Object data must be InputStream, RandomAccessFile, byte[] or String. - * @param length Length of object data. - * @param headers Additional headers. - * @param extraQueryParams Additional query parameters if any. - * @return {@link ObjectWriteResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #putObjectAsync}. - */ - @Deprecated - protected ObjectWriteResponse putObject( - String bucketName, - String region, - String objectName, - Object data, - long length, - Multimap headers, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return putObjectAsync(bucketName, region, objectName, data, length, headers, extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** * Do ListMultipartUploads @@ -3299,69 +2490,6 @@ public CompletableFuture listMultipartUploadsAsync }); } - /** - * Do ListMultipartUploads - * S3 API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param delimiter Delimiter (Optional). - * @param encodingType Encoding type (Optional). - * @param keyMarker Key marker (Optional). - * @param maxUploads Maximum upload information to fetch (Optional). - * @param prefix Prefix (Optional). - * @param uploadIdMarker Upload ID marker (Optional). - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link ListMultipartUploadsResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #listMultipartUploadsAsync}. - */ - @Deprecated - protected ListMultipartUploadsResponse listMultipartUploads( - String bucketName, - String region, - String delimiter, - String encodingType, - String keyMarker, - Integer maxUploads, - String prefix, - String uploadIdMarker, - Multimap extraHeaders, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return listMultipartUploadsAsync( - bucketName, - region, - delimiter, - encodingType, - keyMarker, - maxUploads, - prefix, - uploadIdMarker, - extraHeaders, - extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** * Do ListParts S3 * API asynchronously. @@ -3442,62 +2570,6 @@ public CompletableFuture listPartsAsync( }); } - /** - * Do ListParts S3 - * API. - * - * @param bucketName Name of the bucket. - * @param region Name of the bucket (Optional). - * @param objectName Object name in the bucket. - * @param maxParts Maximum parts information to fetch (Optional). - * @param partNumberMarker Part number marker (Optional). - * @param uploadId Upload ID. - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link ListPartsResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #listPartsAsync}. - */ - @Deprecated - protected ListPartsResponse listParts( - String bucketName, - String region, - String objectName, - Integer maxParts, - Integer partNumberMarker, - String uploadId, - Multimap extraHeaders, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return listPartsAsync( - bucketName, - region, - objectName, - maxParts, - partNumberMarker, - uploadId, - extraHeaders, - extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** * Do UploadPart S3 * API for PartSource asynchronously. @@ -3668,113 +2740,6 @@ public CompletableFuture uploadPartAsync( }); } - /** - * Do UploadPart S3 - * API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param objectName Object name in the bucket. - * @param data Object data must be InputStream, RandomAccessFile, byte[] or String. - * @param length Length of object data. - * @param uploadId Upload ID. - * @param partNumber Part number. - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link UploadPartResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #uploadPartAsync}. - */ - @Deprecated - protected UploadPartResponse uploadPart( - String bucketName, - String region, - String objectName, - Object data, - long length, - String uploadId, - int partNumber, - Multimap extraHeaders, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return uploadPartAsync( - bucketName, - region, - objectName, - data, - length, - uploadId, - partNumber, - extraHeaders, - extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - - /** - * Do UploadPartCopy - * S3 API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param objectName Object name in the bucket. - * @param uploadId Upload ID. - * @param partNumber Part number. - * @param headers Request headers with source object definitions. - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link UploadPartCopyResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @deprecated This method is no longer supported. Use {@link #uploadPartCopyAsync}. - */ - @Deprecated - protected UploadPartCopyResponse uploadPartCopy( - String bucketName, - String region, - String objectName, - String uploadId, - int partNumber, - Multimap headers, - Multimap extraQueryParams) - throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, - ServerException, XmlParserException, ErrorResponseException, InternalException, - InvalidResponseException { - try { - return uploadPartCopyAsync( - bucketName, region, objectName, uploadId, partNumber, headers, extraQueryParams) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - return null; - } - } - /** * Do UploadPartCopy From 9579f5be66e414be09e139bbbfc3a4d9efcc6efe Mon Sep 17 00:00:00 2001 From: "Bala.FA" Date: Thu, 23 Jan 2025 21:24:13 +0530 Subject: [PATCH 2/2] Refactor messages by subclasses merge SelectObjectContentRequest merge ListVersionsResult Remove unused CompleteMultipartUploadOutput.java merge ListMultipartUploadsResult merge NotificationRecords merge SseConfiguration merge DeleteRequest, DeleteResult merge RestoreRequest merge NotificationConfiguration merge LifecycleConfiguration merge ReplicationConfiguration merge Filter merge ObjectLockConfiguration merge ListAllMyBucketsResult merge Item merge ListObjectsResult Move common fields for ListPartsResult and GetObjectAttributesOutput Signed-off-by: Bala.FA --- .../src/main/java/io/minio/admin/Crypto.java | 71 +- .../java/io/minio/admin/MinioAdminClient.java | 347 +- .../io/minio/AbortMultipartUploadArgs.java | 76 + api/src/main/java/io/minio/BaseArgs.java | 92 +- .../main/java/io/minio/BaseClient.java.old | 173 + api/src/main/java/io/minio/BaseS3Client.java | 1385 ++++++++ api/src/main/java/io/minio/BucketArgs.java | 16 +- api/src/main/java/io/minio/ByteBuffer.java | 128 + .../main/java/io/minio/ByteBufferPool.java | 58 + api/src/main/java/io/minio/Checksum.java | 578 ++++ .../io/minio/CompleteMultipartUploadArgs.java | 93 + .../main/java/io/minio/ComposeObjectArgs.java | 41 +- api/src/main/java/io/minio/ComposeSource.java | 134 - .../main/java/io/minio/CopyObjectArgs.java | 42 +- api/src/main/java/io/minio/CopySource.java | 46 - .../DateDays.java => CreateBucketArgs.java} | 29 +- .../java/io/minio/CreateBucketBaseArgs.java | 88 + .../io/minio/CreateMultipartUploadArgs.java | 75 + .../main/java/io/minio/DeleteObjectsArgs.java | 94 + api/src/main/java/io/minio/Digest.java | 157 - .../java/io/minio/DownloadObjectArgs.java | 2 +- ...zation.java => GetBucketLocationArgs.java} | 20 +- .../io/minio/GetObjectAttributesArgs.java | 2 +- .../io/minio/GetPresignedObjectUrlArgs.java | 11 +- ...eEncryptionS3.java => HeadObjectArgs.java} | 35 +- ...iguration.java => HeadObjectBaseArgs.java} | 33 +- .../java/io/minio/HeadObjectResponse.java | 125 + api/src/main/java/io/minio/Http.java | 1443 +++++++++ .../main/java/io/minio/HttpRequestBody.java | 62 - .../main/java/io/minio/ListBucketsArgs.java | 6 +- .../io/minio/ListMultipartUploadsArgs.java | 117 + .../java/io/minio/ListObjectVersionsArgs.java | 132 + .../main/java/io/minio/ListObjectsArgs.java | 6 +- .../main/java/io/minio/ListObjectsV1Args.java | 118 + .../main/java/io/minio/ListObjectsV2Args.java | 163 + api/src/main/java/io/minio/ListPartsArgs.java | 84 + .../minio/ListenBucketNotificationArgs.java | 2 +- .../main/java/io/minio/MakeBucketArgs.java | 33 +- .../main/java/io/minio/MinioAsyncClient.java | 2605 ++++++++------- api/src/main/java/io/minio/MinioClient.java | 988 ++---- .../main/java/io/minio/MinioProperties.java | 81 - api/src/main/java/io/minio/ObjectArgs.java | 9 +- .../io/minio/ObjectConditionalReadArgs.java | 50 +- .../main/java/io/minio/ObjectReadArgs.java | 18 +- .../main/java/io/minio/ObjectVersionArgs.java | 7 + .../main/java/io/minio/ObjectWriteArgs.java | 91 +- api/src/main/java/io/minio/PartReader.java | 229 +- api/src/main/java/io/minio/PartSource.java | 52 +- api/src/main/java/io/minio/PostPolicy.java | 14 +- .../main/java/io/minio/PromptObjectArgs.java | 12 +- .../main/java/io/minio/PutObjectAPIArgs.java | 106 + .../java/io/minio/PutObjectAPIBaseArgs.java | 153 + api/src/main/java/io/minio/PutObjectArgs.java | 78 +- .../main/java/io/minio/PutObjectBaseArgs.java | 106 +- .../java/io/minio/PutObjectFanOutArgs.java | 11 +- .../java/io/minio/PutObjectFanOutEntry.java | 4 +- .../io/minio/PutObjectFanOutResponse.java | 8 +- api/src/main/java/io/minio/Reader.java | 116 + .../main/java/io/minio/RemoveObjectsArgs.java | 10 +- .../main/java/io/minio/RestoreObjectArgs.java | 2 +- api/src/main/java/io/minio/S3Base.java | 2874 ----------------- api/src/main/java/io/minio/S3Escaper.java | 110 - .../io/minio/SelectObjectContentArgs.java | 6 +- .../java/io/minio/ServerSideEncryption.java | 141 +- .../ServerSideEncryptionCustomerKey.java | 93 - .../io/minio/ServerSideEncryptionKms.java | 60 - .../main/java/io/minio/SetBucketCorsArgs.java | 2 +- .../io/minio/SetBucketEncryptionArgs.java | 2 +- .../java/io/minio/SetBucketLifecycleArgs.java | 2 +- .../io/minio/SetBucketNotificationArgs.java | 2 +- .../java/io/minio/SetBucketPolicyArgs.java | 2 +- .../io/minio/SetBucketReplicationArgs.java | 4 +- .../main/java/io/minio/SetBucketTagsArgs.java | 4 +- .../io/minio/SetBucketVersioningArgs.java | 2 +- .../minio/SetObjectLockConfigurationArgs.java | 2 +- .../java/io/minio/SetObjectRetentionArgs.java | 2 +- .../main/java/io/minio/SetObjectTagsArgs.java | 4 +- api/src/main/java/io/minio/Signer.java | 93 +- api/src/main/java/io/minio/SourceObject.java | 101 + .../main/java/io/minio/StatObjectArgs.java | 17 +- .../java/io/minio/StatObjectResponse.java | 114 +- api/src/main/java/io/minio/Time.java | 62 +- .../main/java/io/minio/UploadObjectArgs.java | 28 +- .../main/java/io/minio/UploadPartArgs.java | 92 + .../java/io/minio/UploadPartCopyArgs.java | 101 + .../java/io/minio/UploadPartCopyResponse.java | 5 + .../java/io/minio/UploadPartResponse.java | 23 +- .../io/minio/UploadSnowballObjectsArgs.java | 2 +- api/src/main/java/io/minio/Utils.java | 704 ++++ .../minio/credentials/AssumeRoleProvider.java | 16 +- .../io/minio/credentials/Credentials.java | 12 +- .../io/minio/credentials/IamAwsProvider.java | 7 +- .../minio/errors/ErrorResponseException.java | 39 +- .../io/minio/errors/InternalException.java | 5 + .../java/io/minio/errors/MinioException.java | 10 + .../main/java/io/minio/http/HttpUtils.java | 335 -- api/src/main/java/io/minio/http/Method.java | 26 - .../AbortIncompleteMultipartUpload.java | 38 - .../io/minio/messages/AccessControlList.java | 195 +- .../minio/messages/AccessControlPolicy.java | 34 +- .../messages/AccessControlTranslation.java | 39 - .../java/io/minio/messages/AndOperator.java | 110 - .../io/minio/messages/BasePartsResult.java | 78 + ...estBase.java => BaseSelectParameters.java} | 8 +- .../main/java/io/minio/messages/Bucket.java | 50 - .../io/minio/messages/BucketMetadata.java | 46 - .../io/minio/messages/CORSConfiguration.java | 21 +- .../java/io/minio/messages/CannedAcl.java | 72 - .../main/java/io/minio/messages/Checksum.java | 73 +- .../messages/CloudFunctionConfiguration.java | 41 - .../messages/CompleteMultipartUpload.java | 6 +- .../CompleteMultipartUploadOutput.java | 29 - .../CompleteMultipartUploadResult.java | 54 +- .../io/minio/messages/CompressionType.java | 24 - .../main/java/io/minio/messages/Contents.java | 8 +- .../io/minio/messages/CopyObjectResult.java | 55 +- .../io/minio/messages/CopyPartResult.java | 9 +- .../messages/CreateBucketConfiguration.java | 4 +- .../minio/messages/CsvInputSerialization.java | 67 - .../messages/CsvOutputSerialization.java | 57 - .../java/io/minio/messages/DeleteError.java | 27 - .../java/io/minio/messages/DeleteMarker.java | 31 - .../messages/DeleteMarkerReplication.java | 38 - .../java/io/minio/messages/DeleteObject.java | 95 - .../io/minio/messages/DeleteReplication.java | 39 - .../java/io/minio/messages/DeleteRequest.java | 86 +- .../java/io/minio/messages/DeleteResult.java | 83 +- .../java/io/minio/messages/DeletedObject.java | 54 - .../java/io/minio/messages/Encryption.java | 47 - .../messages/EncryptionConfiguration.java | 39 - .../java/io/minio/messages/ErrorResponse.java | 40 +- .../main/java/io/minio/messages/Event.java | 164 - .../java/io/minio/messages/EventMetadata.java | 106 - .../java/io/minio/messages/EventType.java | 2 +- .../messages/ExistingObjectReplication.java | 37 - .../java/io/minio/messages/Expiration.java | 76 - .../io/minio/messages/FileHeaderInfo.java | 24 - .../main/java/io/minio/messages/Filter.java | 251 +- .../java/io/minio/messages/FilterRule.java | 50 - .../messages/GetObjectAttributesOutput.java | 65 +- .../minio/messages/GlacierJobParameters.java | 34 - .../main/java/io/minio/messages/Grant.java | 58 - .../main/java/io/minio/messages/Grantee.java | 93 - .../java/io/minio/messages/GranteeType.java | 66 - .../main/java/io/minio/messages/Identity.java | 32 - .../InitiateMultipartUploadResult.java | 10 +- .../java/io/minio/messages/Initiator.java | 11 +- .../io/minio/messages/InputSerialization.java | 115 +- api/src/main/java/io/minio/messages/Item.java | 112 +- .../messages/JsonInputSerialization.java | 36 - .../messages/JsonOutputSerialization.java | 36 - .../main/java/io/minio/messages/JsonType.java | 23 - .../java/io/minio/messages/LegalHold.java | 8 +- .../messages/LifecycleConfiguration.java | 354 +- .../java/io/minio/messages/LifecycleRule.java | 123 - .../messages/ListAllMyBucketsResult.java | 49 +- .../io/minio/messages/ListBucketResultV1.java | 12 +- .../io/minio/messages/ListBucketResultV2.java | 15 +- .../messages/ListMultipartUploadsResult.java | 122 +- .../io/minio/messages/ListObjectsResult.java | 37 +- .../io/minio/messages/ListPartsResult.java | 66 +- .../io/minio/messages/ListVersionsResult.java | 48 +- .../io/minio/messages/LocationConstraint.java | 8 +- .../main/java/io/minio/messages/Metadata.java | 77 - .../main/java/io/minio/messages/Metrics.java | 48 - .../messages/NoncurrentVersionExpiration.java | 51 - .../messages/NoncurrentVersionTransition.java | 53 - .../NotificationCommonConfiguration.java | 84 - .../messages/NotificationConfiguration.java | 280 +- .../minio/messages/NotificationRecords.java | 239 +- .../messages/ObjectLockConfiguration.java | 120 +- .../io/minio/messages/ObjectMetadata.java | 56 - .../io/minio/messages/OutputLocation.java | 35 - .../minio/messages/OutputSerialization.java | 76 +- .../main/java/io/minio/messages/Owner.java | 9 +- api/src/main/java/io/minio/messages/Part.java | 71 +- .../java/io/minio/messages/Permission.java | 29 - .../main/java/io/minio/messages/Prefix.java | 36 - .../main/java/io/minio/messages/Progress.java | 17 +- .../java/io/minio/messages/QuoteFields.java | 23 - .../minio/messages/ReplicaModifications.java | 37 - .../messages/ReplicationConfiguration.java | 516 ++- .../messages/ReplicationDestination.java | 96 - .../io/minio/messages/ReplicationRule.java | 165 - .../io/minio/messages/ReplicationTime.java | 47 - .../minio/messages/ReplicationTimeValue.java | 37 - .../io/minio/messages/RequestProgress.java | 34 - .../java/io/minio/messages/ResponseDate.java | 75 - .../io/minio/messages/RestoreRequest.java | 227 +- .../java/io/minio/messages/Retention.java | 19 +- .../io/minio/messages/RetentionDuration.java | 24 - .../minio/messages/RetentionDurationDays.java | 50 - .../minio/messages/RetentionDurationUnit.java | 23 - .../messages/RetentionDurationYears.java | 50 - api/src/main/java/io/minio/messages/Rule.java | 63 - .../java/io/minio/messages/RuleFilter.java | 103 - .../io/minio/messages/S3OutputLocation.java | 71 - .../java/io/minio/messages/ScanRange.java | 40 - .../messages/SelectObjectContentRequest.java | 29 +- .../io/minio/messages/SelectParameters.java | 32 - .../main/java/io/minio/messages/Source.java | 46 - .../messages/SourceSelectionCriteria.java | 52 - .../io/minio/messages/SseConfiguration.java | 60 +- .../minio/messages/SseConfigurationRule.java | 55 - .../messages/SseKmsEncryptedObjects.java | 39 - .../main/java/io/minio/messages/Stats.java | 46 +- api/src/main/java/io/minio/messages/Tag.java | 51 - api/src/main/java/io/minio/messages/Tags.java | 11 +- api/src/main/java/io/minio/messages/Tier.java | 68 - .../io/minio/messages/TopicConfiguration.java | 41 - .../java/io/minio/messages/Transition.java | 54 - .../main/java/io/minio/messages/Upload.java | 112 - .../java/io/minio/messages/UserMetadata.java | 48 - .../main/java/io/minio/messages/Version.java | 31 - .../messages/VersioningConfiguration.java | 10 +- .../routines/InetAddressValidator.java | 201 -- .../validator/routines/RegexValidator.java | 223 -- .../java/io/minio/MakeBucketArgsTest.java | 8 +- .../test/java/io/minio/MinioClientTest.java | 62 +- .../java/io/minio/StatObjectArgsTest.java | 14 +- examples/ComposeObject.java | 29 +- examples/CopyObject.java | 27 +- examples/DownloadObject.java | 6 +- examples/GetPresignedObjectUrl.java | 4 +- examples/ListBuckets.java | 9 +- examples/ListenBucketNotification.java | 6 +- examples/PresignedGetObject.java | 4 +- examples/PresignedPutObject.java | 4 +- examples/PutObject.java | 69 +- examples/PutObjectProgressBar.java | 2 +- examples/PutObjectUiProgressBar.java | 2 +- examples/RemoveObjects.java | 18 +- examples/SelectObjectContent.java | 16 +- examples/SetBucketLifecycle.java | 12 +- examples/SetBucketNotification.java | 38 +- examples/SetBucketReplication.java | 18 +- examples/SetObjectLockConfiguration.java | 4 +- examples/StatObject.java | 6 +- examples/UploadObject.java | 6 +- functional/FunctionalTest.java | 483 ++- 240 files changed, 12561 insertions(+), 12387 deletions(-) create mode 100644 api/src/main/java/io/minio/AbortMultipartUploadArgs.java create mode 100644 api/src/main/java/io/minio/BaseClient.java.old create mode 100644 api/src/main/java/io/minio/BaseS3Client.java create mode 100644 api/src/main/java/io/minio/ByteBuffer.java create mode 100644 api/src/main/java/io/minio/ByteBufferPool.java create mode 100644 api/src/main/java/io/minio/Checksum.java create mode 100644 api/src/main/java/io/minio/CompleteMultipartUploadArgs.java delete mode 100644 api/src/main/java/io/minio/ComposeSource.java delete mode 100644 api/src/main/java/io/minio/CopySource.java rename api/src/main/java/io/minio/{messages/DateDays.java => CreateBucketArgs.java} (54%) create mode 100644 api/src/main/java/io/minio/CreateBucketBaseArgs.java create mode 100644 api/src/main/java/io/minio/CreateMultipartUploadArgs.java create mode 100644 api/src/main/java/io/minio/DeleteObjectsArgs.java delete mode 100644 api/src/main/java/io/minio/Digest.java rename api/src/main/java/io/minio/{messages/ParquetInputSerialization.java => GetBucketLocationArgs.java} (60%) rename api/src/main/java/io/minio/{ServerSideEncryptionS3.java => HeadObjectArgs.java} (52%) rename api/src/main/java/io/minio/{messages/QueueConfiguration.java => HeadObjectBaseArgs.java} (50%) create mode 100644 api/src/main/java/io/minio/HeadObjectResponse.java create mode 100644 api/src/main/java/io/minio/Http.java delete mode 100644 api/src/main/java/io/minio/HttpRequestBody.java create mode 100644 api/src/main/java/io/minio/ListMultipartUploadsArgs.java create mode 100644 api/src/main/java/io/minio/ListObjectVersionsArgs.java create mode 100644 api/src/main/java/io/minio/ListObjectsV1Args.java create mode 100644 api/src/main/java/io/minio/ListObjectsV2Args.java create mode 100644 api/src/main/java/io/minio/ListPartsArgs.java delete mode 100644 api/src/main/java/io/minio/MinioProperties.java create mode 100644 api/src/main/java/io/minio/PutObjectAPIArgs.java create mode 100644 api/src/main/java/io/minio/PutObjectAPIBaseArgs.java create mode 100644 api/src/main/java/io/minio/Reader.java delete mode 100644 api/src/main/java/io/minio/S3Base.java delete mode 100644 api/src/main/java/io/minio/S3Escaper.java delete mode 100644 api/src/main/java/io/minio/ServerSideEncryptionCustomerKey.java delete mode 100644 api/src/main/java/io/minio/ServerSideEncryptionKms.java create mode 100644 api/src/main/java/io/minio/SourceObject.java create mode 100644 api/src/main/java/io/minio/UploadPartArgs.java create mode 100644 api/src/main/java/io/minio/UploadPartCopyArgs.java delete mode 100644 api/src/main/java/io/minio/http/HttpUtils.java delete mode 100644 api/src/main/java/io/minio/http/Method.java delete mode 100644 api/src/main/java/io/minio/messages/AbortIncompleteMultipartUpload.java delete mode 100644 api/src/main/java/io/minio/messages/AccessControlTranslation.java delete mode 100644 api/src/main/java/io/minio/messages/AndOperator.java create mode 100644 api/src/main/java/io/minio/messages/BasePartsResult.java rename api/src/main/java/io/minio/messages/{SelectObjectContentRequestBase.java => BaseSelectParameters.java} (89%) delete mode 100644 api/src/main/java/io/minio/messages/Bucket.java delete mode 100644 api/src/main/java/io/minio/messages/BucketMetadata.java delete mode 100644 api/src/main/java/io/minio/messages/CannedAcl.java delete mode 100644 api/src/main/java/io/minio/messages/CloudFunctionConfiguration.java delete mode 100644 api/src/main/java/io/minio/messages/CompleteMultipartUploadOutput.java delete mode 100644 api/src/main/java/io/minio/messages/CompressionType.java delete mode 100644 api/src/main/java/io/minio/messages/CsvInputSerialization.java delete mode 100644 api/src/main/java/io/minio/messages/CsvOutputSerialization.java delete mode 100644 api/src/main/java/io/minio/messages/DeleteError.java delete mode 100644 api/src/main/java/io/minio/messages/DeleteMarker.java delete mode 100644 api/src/main/java/io/minio/messages/DeleteMarkerReplication.java delete mode 100644 api/src/main/java/io/minio/messages/DeleteObject.java delete mode 100644 api/src/main/java/io/minio/messages/DeleteReplication.java delete mode 100644 api/src/main/java/io/minio/messages/DeletedObject.java delete mode 100644 api/src/main/java/io/minio/messages/Encryption.java delete mode 100644 api/src/main/java/io/minio/messages/EncryptionConfiguration.java delete mode 100644 api/src/main/java/io/minio/messages/Event.java delete mode 100644 api/src/main/java/io/minio/messages/EventMetadata.java delete mode 100644 api/src/main/java/io/minio/messages/ExistingObjectReplication.java delete mode 100644 api/src/main/java/io/minio/messages/Expiration.java delete mode 100644 api/src/main/java/io/minio/messages/FileHeaderInfo.java delete mode 100644 api/src/main/java/io/minio/messages/FilterRule.java delete mode 100644 api/src/main/java/io/minio/messages/GlacierJobParameters.java delete mode 100644 api/src/main/java/io/minio/messages/Grant.java delete mode 100644 api/src/main/java/io/minio/messages/Grantee.java delete mode 100644 api/src/main/java/io/minio/messages/GranteeType.java delete mode 100644 api/src/main/java/io/minio/messages/Identity.java delete mode 100644 api/src/main/java/io/minio/messages/JsonInputSerialization.java delete mode 100644 api/src/main/java/io/minio/messages/JsonOutputSerialization.java delete mode 100644 api/src/main/java/io/minio/messages/JsonType.java delete mode 100644 api/src/main/java/io/minio/messages/LifecycleRule.java delete mode 100644 api/src/main/java/io/minio/messages/Metadata.java delete mode 100644 api/src/main/java/io/minio/messages/Metrics.java delete mode 100644 api/src/main/java/io/minio/messages/NoncurrentVersionExpiration.java delete mode 100644 api/src/main/java/io/minio/messages/NoncurrentVersionTransition.java delete mode 100644 api/src/main/java/io/minio/messages/NotificationCommonConfiguration.java delete mode 100644 api/src/main/java/io/minio/messages/ObjectMetadata.java delete mode 100644 api/src/main/java/io/minio/messages/OutputLocation.java delete mode 100644 api/src/main/java/io/minio/messages/Permission.java delete mode 100644 api/src/main/java/io/minio/messages/Prefix.java delete mode 100644 api/src/main/java/io/minio/messages/QuoteFields.java delete mode 100644 api/src/main/java/io/minio/messages/ReplicaModifications.java delete mode 100644 api/src/main/java/io/minio/messages/ReplicationDestination.java delete mode 100644 api/src/main/java/io/minio/messages/ReplicationRule.java delete mode 100644 api/src/main/java/io/minio/messages/ReplicationTime.java delete mode 100644 api/src/main/java/io/minio/messages/ReplicationTimeValue.java delete mode 100644 api/src/main/java/io/minio/messages/RequestProgress.java delete mode 100644 api/src/main/java/io/minio/messages/ResponseDate.java delete mode 100644 api/src/main/java/io/minio/messages/RetentionDuration.java delete mode 100644 api/src/main/java/io/minio/messages/RetentionDurationDays.java delete mode 100644 api/src/main/java/io/minio/messages/RetentionDurationUnit.java delete mode 100644 api/src/main/java/io/minio/messages/RetentionDurationYears.java delete mode 100644 api/src/main/java/io/minio/messages/Rule.java delete mode 100644 api/src/main/java/io/minio/messages/RuleFilter.java delete mode 100644 api/src/main/java/io/minio/messages/S3OutputLocation.java delete mode 100644 api/src/main/java/io/minio/messages/ScanRange.java delete mode 100644 api/src/main/java/io/minio/messages/SelectParameters.java delete mode 100644 api/src/main/java/io/minio/messages/Source.java delete mode 100644 api/src/main/java/io/minio/messages/SourceSelectionCriteria.java delete mode 100644 api/src/main/java/io/minio/messages/SseConfigurationRule.java delete mode 100644 api/src/main/java/io/minio/messages/SseKmsEncryptedObjects.java delete mode 100644 api/src/main/java/io/minio/messages/Tag.java delete mode 100644 api/src/main/java/io/minio/messages/Tier.java delete mode 100644 api/src/main/java/io/minio/messages/TopicConfiguration.java delete mode 100644 api/src/main/java/io/minio/messages/Transition.java delete mode 100644 api/src/main/java/io/minio/messages/Upload.java delete mode 100644 api/src/main/java/io/minio/messages/UserMetadata.java delete mode 100644 api/src/main/java/io/minio/messages/Version.java delete mode 100644 api/src/main/java/io/minio/org/apache/commons/validator/routines/InetAddressValidator.java delete mode 100644 api/src/main/java/io/minio/org/apache/commons/validator/routines/RegexValidator.java diff --git a/adminapi/src/main/java/io/minio/admin/Crypto.java b/adminapi/src/main/java/io/minio/admin/Crypto.java index c42c51f32..8902e6d74 100644 --- a/adminapi/src/main/java/io/minio/admin/Crypto.java +++ b/adminapi/src/main/java/io/minio/admin/Crypto.java @@ -17,6 +17,7 @@ package io.minio.admin; +import io.minio.errors.MinioException; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; @@ -156,22 +157,25 @@ private static byte[] generateKey(byte[] secret, byte[] salt) { } private static byte[] generateEncryptDecryptAdditionalData( - boolean encryptFlag, int aeadId, byte[] key, byte[] paddedNonce) - throws InvalidCipherTextException { - AEADCipher cipher = getEncryptCipher(aeadId, key, paddedNonce); - int outputLength = cipher.getMac().length; - byte[] additionalData = new byte[outputLength]; - cipher.doFinal(additionalData, 0); - return appendBytes(new byte[] {0}, additionalData); + boolean encryptFlag, int aeadId, byte[] key, byte[] paddedNonce) throws MinioException { + try { + AEADCipher cipher = getEncryptCipher(aeadId, key, paddedNonce); + int outputLength = cipher.getMac().length; + byte[] additionalData = new byte[outputLength]; + cipher.doFinal(additionalData, 0); + return appendBytes(new byte[] {0}, additionalData); + } catch (InvalidCipherTextException e) { + throw new MinioException(e); + } } private static byte[] generateEncryptAdditionalData(int aeadId, byte[] key, byte[] paddedNonce) - throws InvalidCipherTextException { + throws MinioException { return generateEncryptDecryptAdditionalData(true, aeadId, key, paddedNonce); } private static byte[] generateDecryptAdditionalData(int aeadId, byte[] key, byte[] paddedNonce) - throws InvalidCipherTextException { + throws MinioException { return generateEncryptDecryptAdditionalData(false, aeadId, key, paddedNonce); } @@ -190,7 +194,7 @@ private static byte[] updateNonceId(byte[] nonce, int idx) { } /** Encrypt data payload. */ - public static byte[] encrypt(byte[] payload, String password) throws InvalidCipherTextException { + public static byte[] encrypt(byte[] payload, String password) throws MinioException { byte[] nonce = random(NONCE_LENGTH); byte[] salt = random(SALT_LENGTH); @@ -219,7 +223,11 @@ public static byte[] encrypt(byte[] payload, String password) throws InvalidCiph int outputLength = cipher.getOutputSize(chunk.length); byte[] encryptedData = new byte[outputLength]; int outputOffset = cipher.processBytes(chunk, 0, chunk.length, encryptedData, 0); - cipher.doFinal(encryptedData, outputOffset); + try { + cipher.doFinal(encryptedData, outputOffset); + } catch (InvalidCipherTextException e) { + throw new MinioException(e); + } result = appendBytes(result, encryptedData); @@ -243,20 +251,24 @@ public static class DecryptReader { private byte[] oneByte = null; private boolean eof = false; - public DecryptReader(InputStream inputStream, byte[] secret) - throws EOFException, IOException, InvalidCipherTextException { + public DecryptReader(InputStream inputStream, byte[] secret) throws MinioException { this.inputStream = inputStream; this.secret = secret; - readFully(this.inputStream, this.salt, true); - readFully(this.inputStream, this.aeadId, true); - readFully(this.inputStream, this.nonce, true); + try { + readFully(this.inputStream, this.salt, true); + readFully(this.inputStream, this.aeadId, true); + readFully(this.inputStream, this.nonce, true); + } catch (EOFException e) { + throw new MinioException(e); + } catch (IOException e) { + throw new MinioException(e); + } this.key = generateKey(this.secret, this.salt); byte[] paddedNonce = appendBytes(this.nonce, new byte[] {0, 0, 0, 0}); this.additionalData = generateDecryptAdditionalData(this.aeadId[0], this.key, paddedNonce); } - private byte[] decrypt(byte[] encryptedData, boolean lastChunk) - throws InvalidCipherTextException { + private byte[] decrypt(byte[] encryptedData, boolean lastChunk) throws MinioException { this.count++; if (lastChunk) { this.additionalData = markAsLast(this.additionalData); @@ -268,12 +280,16 @@ private byte[] decrypt(byte[] encryptedData, boolean lastChunk) byte[] decryptedData = new byte[outputLength]; int outputOffset = cipher.processBytes(encryptedData, 0, encryptedData.length, decryptedData, 0); - cipher.doFinal(decryptedData, outputOffset); + try { + cipher.doFinal(decryptedData, outputOffset); + } catch (InvalidCipherTextException e) { + throw new MinioException(e); + } return decryptedData; } /** Read a chunk at least one byte more than chunk size. */ - private byte[] readChunk() throws IOException { + private byte[] readChunk() throws EOFException, IOException { if (this.eof) { return new byte[] {}; } @@ -302,19 +318,24 @@ private byte[] readChunk() throws IOException { return baos.toByteArray(); } - public byte[] readAllBytes() throws IOException, InvalidCipherTextException { + public byte[] readAllBytes() throws MinioException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); while (!this.eof) { - byte[] payload = this.readChunk(); - baos.write(this.decrypt(payload, this.eof)); + try { + byte[] payload = this.readChunk(); + baos.write(this.decrypt(payload, this.eof)); + } catch (EOFException e) { + throw new MinioException(e); + } catch (IOException e) { + throw new MinioException(e); + } } return baos.toByteArray(); } } /** Decrypt data stream. */ - public static byte[] decrypt(InputStream inputStream, String password) - throws EOFException, IOException, InvalidCipherTextException { + public static byte[] decrypt(InputStream inputStream, String password) throws MinioException { DecryptReader reader = new DecryptReader(inputStream, password.getBytes(StandardCharsets.UTF_8)); return reader.readAllBytes(); diff --git a/adminapi/src/main/java/io/minio/admin/MinioAdminClient.java b/adminapi/src/main/java/io/minio/admin/MinioAdminClient.java index de11bd275..625b1b42c 100644 --- a/adminapi/src/main/java/io/minio/admin/MinioAdminClient.java +++ b/adminapi/src/main/java/io/minio/admin/MinioAdminClient.java @@ -17,6 +17,7 @@ package io.minio.admin; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -27,27 +28,23 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.minio.Digest; -import io.minio.MinioProperties; -import io.minio.S3Escaper; +import io.minio.Checksum; +import io.minio.Http; import io.minio.Signer; import io.minio.Time; +import io.minio.Utils; import io.minio.admin.messages.DataUsageInfo; import io.minio.admin.messages.info.Message; import io.minio.credentials.Credentials; import io.minio.credentials.Provider; import io.minio.credentials.StaticProvider; -import io.minio.http.HttpUtils; -import io.minio.http.Method; +import io.minio.errors.MinioException; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.HashMap; @@ -65,7 +62,6 @@ import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; -import org.bouncycastle.crypto.InvalidCipherTextException; /** Client to perform MinIO administration operations. */ public class MinioAdminClient { @@ -115,7 +111,7 @@ public String toString() { OBJECT_MAPPER.registerModule(new JavaTimeModule()); } - private String userAgent = MinioProperties.INSTANCE.getDefaultUserAgent(); + private String userAgent = Utils.getDefaultUserAgent(); private PrintWriter traceStream; private HttpUrl baseUrl; @@ -137,41 +133,42 @@ private Credentials getCredentials() { return creds; } - private Response execute( - Method method, Command command, Multimap queryParamMap, byte[] body) - throws InvalidKeyException, IOException, NoSuchAlgorithmException { + private Response httpExecute( + Http.Method method, Command command, Multimap queryParamMap, byte[] body) + throws IOException, MinioException { Credentials creds = getCredentials(); HttpUrl.Builder urlBuilder = this.baseUrl .newBuilder() .host(this.baseUrl.host()) - .addEncodedPathSegments(S3Escaper.encodePath("minio/admin/v3/" + command.toString())); + .addEncodedPathSegments(Utils.encodePath("minio/admin/v3/" + command.toString())); if (queryParamMap != null) { for (Map.Entry entry : queryParamMap.entries()) { urlBuilder.addEncodedQueryParameter( - S3Escaper.encode(entry.getKey()), S3Escaper.encode(entry.getValue())); + Utils.encode(entry.getKey()), Utils.encode(entry.getValue())); } } HttpUrl url = urlBuilder.build(); Request.Builder requestBuilder = new Request.Builder(); requestBuilder.url(url); - requestBuilder.header("Host", HttpUtils.getHostHeader(url)); + requestBuilder.header("Host", Utils.getHostHeader(url)); requestBuilder.header("Accept-Encoding", "identity"); // Disable default gzip compression. requestBuilder.header("User-Agent", this.userAgent); requestBuilder.header("x-amz-date", ZonedDateTime.now().format(Time.AMZ_DATE_FORMAT)); if (creds.sessionToken() != null) { requestBuilder.header("X-Amz-Security-Token", creds.sessionToken()); } - if (body == null && (method != Method.GET && method != Method.HEAD)) { - body = HttpUtils.EMPTY_BODY; + if (body == null && (method != Http.Method.GET && method != Http.Method.HEAD)) { + body = Utils.EMPTY_BODY; } if (body != null) { - requestBuilder.header("x-amz-content-sha256", Digest.sha256Hash(body, body.length)); + requestBuilder.header( + "x-amz-content-sha256", Checksum.hexString(Checksum.SHA256.sum(body, 0, body.length))); requestBuilder.method(method.toString(), RequestBody.create(body, DEFAULT_MEDIA_TYPE)); } else { - requestBuilder.header("x-amz-content-sha256", Digest.ZERO_SHA256_HASH); + requestBuilder.header("x-amz-content-sha256", Checksum.ZERO_SHA256_HASH); } Request request = requestBuilder.build(); @@ -222,6 +219,16 @@ private Response execute( throw new RuntimeException("Request failed with response: " + response.body().string()); } + private Response execute( + Http.Method method, Command command, Multimap queryParamMap, byte[] body) + throws MinioException { + try { + return httpExecute(method, command, queryParamMap, body); + } catch (IOException e) { + throw new MinioException(e); + } + } + /** * Adds a user with the specified access and secret key. * @@ -230,10 +237,7 @@ private Response execute( * @param secretKey Secret key. * @param policyName Policy name. * @param memberOf List of group. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. - * @throws InvalidCipherTextException thrown to indicate data cannot be encrypted/decrypted. + * @throws MinioException thrown to indicate SDK exception. */ public void addUser( @Nonnull String accessKey, @@ -241,8 +245,7 @@ public void addUser( @Nullable String secretKey, @Nullable String policyName, @Nullable List memberOf) - throws NoSuchAlgorithmException, InvalidKeyException, IOException, - InvalidCipherTextException { + throws MinioException { if (accessKey == null || accessKey.isEmpty()) { throw new IllegalArgumentException("access key must be provided"); } @@ -251,10 +254,13 @@ public void addUser( Credentials creds = getCredentials(); try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.ADD_USER, ImmutableMultimap.of("accessKey", accessKey), - Crypto.encrypt(OBJECT_MAPPER.writeValueAsBytes(userInfo), creds.secretKey()))) {} + Crypto.encrypt(OBJECT_MAPPER.writeValueAsBytes(userInfo), creds.secretKey()))) { + } catch (JsonProcessingException e) { + throw new MinioException(e); + } } /** @@ -262,17 +268,19 @@ public void addUser( * * @param accessKey Access Key. * @return {@link UserInfo} - user info for the specified accessKey. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public UserInfo getUserInfo(String accessKey) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + public UserInfo getUserInfo(String accessKey) throws MinioException { try (Response response = execute( - Method.GET, Command.USER_INFO, ImmutableMultimap.of("accessKey", accessKey), null)) { + Http.Method.GET, + Command.USER_INFO, + ImmutableMultimap.of("accessKey", accessKey), + null)) { byte[] jsonData = response.body().bytes(); return OBJECT_MAPPER.readValue(jsonData, UserInfo.class); + } catch (IOException e) { + throw new MinioException(e); } } @@ -280,22 +288,21 @@ public UserInfo getUserInfo(String accessKey) * Obtains a list of all MinIO users. * * @return {@link Map} - List of all users. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. - * @throws InvalidCipherTextException thrown to indicate data cannot be encrypted/decrypted. + * @throws MinioException thrown to indicate SDK exception. */ - public Map listUsers() - throws NoSuchAlgorithmException, InvalidKeyException, IOException, - InvalidCipherTextException { - try (Response response = execute(Method.GET, Command.LIST_USERS, null, null)) { + public Map listUsers() throws MinioException { + try (Response response = execute(Http.Method.GET, Command.LIST_USERS, null, null)) { Credentials creds = getCredentials(); byte[] jsonData = Crypto.decrypt(response.body().byteStream(), creds.secretKey()); MapType mapType = OBJECT_MAPPER .getTypeFactory() .constructMapType(HashMap.class, String.class, UserInfo.class); - return OBJECT_MAPPER.readValue(jsonData, mapType); + try { + return OBJECT_MAPPER.readValue(jsonData, mapType); + } catch (IOException e) { + throw new MinioException(e); + } } } @@ -303,19 +310,16 @@ public Map listUsers() * Deletes a user by it's access key * * @param accessKey Access Key. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public void deleteUser(@Nonnull String accessKey) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + public void deleteUser(@Nonnull String accessKey) throws MinioException { if (accessKey == null || accessKey.isEmpty()) { throw new IllegalArgumentException("access key must be provided"); } try (Response response = execute( - Method.DELETE, + Http.Method.DELETE, Command.REMOVE_USER, ImmutableMultimap.of("accessKey", accessKey), null)) {} @@ -327,13 +331,11 @@ public void deleteUser(@Nonnull String accessKey) * @param group Group name. * @param groupStatus Status. * @param members Members of group. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ public void addUpdateGroup( @Nonnull String group, @Nullable Status groupStatus, @Nullable List members) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + throws MinioException { if (group == null || group.isEmpty()) { throw new IllegalArgumentException("group must be provided"); } @@ -342,10 +344,13 @@ public void addUpdateGroup( try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.ADD_UPDATE_REMOVE_GROUP, null, - OBJECT_MAPPER.writeValueAsBytes(groupAddUpdateRemoveInfo))) {} + OBJECT_MAPPER.writeValueAsBytes(groupAddUpdateRemoveInfo))) { + } catch (JsonProcessingException e) { + throw new MinioException(e); + } } /** @@ -353,16 +358,15 @@ public void addUpdateGroup( * * @param group Group name. * @return {@link GroupInfo} - group info for the specified group. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public GroupInfo getGroupInfo(String group) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + public GroupInfo getGroupInfo(String group) throws MinioException { try (Response response = - execute(Method.GET, Command.GROUP_INFO, ImmutableMultimap.of("group", group), null)) { + execute(Http.Method.GET, Command.GROUP_INFO, ImmutableMultimap.of("group", group), null)) { byte[] jsonData = response.body().bytes(); return OBJECT_MAPPER.readValue(jsonData, GroupInfo.class); + } catch (IOException e) { + throw new MinioException(e); } } @@ -370,17 +374,16 @@ public GroupInfo getGroupInfo(String group) * Obtains a list of all MinIO groups. * * @return {@link List} - List of all groups. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public List listGroups() - throws NoSuchAlgorithmException, InvalidKeyException, IOException { - try (Response response = execute(Method.GET, Command.LIST_GROUPS, null, null)) { + public List listGroups() throws MinioException { + try (Response response = execute(Http.Method.GET, Command.LIST_GROUPS, null, null)) { byte[] jsonData = response.body().bytes(); CollectionType mapType = OBJECT_MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, String.class); return OBJECT_MAPPER.readValue(jsonData, mapType); + } catch (IOException e) { + throw new MinioException(e); } } @@ -388,12 +391,9 @@ public List listGroups() * Removes a group. * * @param group Group name. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public void removeGroup(@Nonnull String group) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + public void removeGroup(@Nonnull String group) throws MinioException { if (group == null || group.isEmpty()) { throw new IllegalArgumentException("group must be provided"); } @@ -402,10 +402,13 @@ public void removeGroup(@Nonnull String group) try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.ADD_UPDATE_REMOVE_GROUP, null, - OBJECT_MAPPER.writeValueAsBytes(groupAddUpdateRemoveInfo))) {} + OBJECT_MAPPER.writeValueAsBytes(groupAddUpdateRemoveInfo))) { + } catch (JsonProcessingException e) { + throw new MinioException(e); + } } /** @@ -414,12 +417,10 @@ public void removeGroup(@Nonnull String group) * @param bucketName bucketName * @param size the capacity of the bucket * @param unit the quota unit of the size argument - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ public void setBucketQuota(@Nonnull String bucketName, long size, @Nonnull QuotaUnit unit) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { + throws MinioException { Map quotaEntity = new HashMap<>(); if (size > 0) { quotaEntity.put("quotatype", "hard"); @@ -427,10 +428,13 @@ public void setBucketQuota(@Nonnull String bucketName, long size, @Nonnull Quota quotaEntity.put("quota", unit.toBytes(size)); try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.SET_BUCKET_QUOTA, ImmutableMultimap.of("bucket", bucketName), - OBJECT_MAPPER.writeValueAsBytes(quotaEntity))) {} + OBJECT_MAPPER.writeValueAsBytes(quotaEntity))) { + } catch (JsonProcessingException e) { + throw new MinioException(e); + } } /** @@ -438,15 +442,12 @@ public void setBucketQuota(@Nonnull String bucketName, long size, @Nonnull Quota * * @param bucketName bucketName * @return bytes of bucket - * @throws IOException - * @throws NoSuchAlgorithmException - * @throws InvalidKeyException + * @throws MinioException thrown to indicate SDK exception. */ - public long getBucketQuota(String bucketName) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { + public long getBucketQuota(String bucketName) throws MinioException { try (Response response = execute( - Method.GET, + Http.Method.GET, Command.GET_BUCKET_QUOTA, ImmutableMultimap.of("bucket", bucketName), null)) { @@ -460,6 +461,8 @@ public long getBucketQuota(String bucketName) .findFirst() .map(entry -> Long.valueOf(entry.getValue().toString())) .orElseThrow(() -> new IllegalArgumentException("found not quota")); + } catch (IOException e) { + throw new MinioException(e); } } @@ -467,12 +470,9 @@ public long getBucketQuota(String bucketName) * Reset bucket quota * * @param bucketName bucketName - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public void clearBucketQuota(@Nonnull String bucketName) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { + public void clearBucketQuota(@Nonnull String bucketName) throws MinioException { setBucketQuota(bucketName, 0, QuotaUnit.KB); } @@ -498,12 +498,9 @@ public void clearBucketQuota(@Nonnull String bucketName) * * @param name Policy name. * @param policy Policy as JSON string. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public void addCannedPolicy(@Nonnull String name, @Nonnull String policy) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + public void addCannedPolicy(@Nonnull String name, @Nonnull String policy) throws MinioException { if (name == null || name.isEmpty()) { throw new IllegalArgumentException("name must be provided"); } @@ -513,7 +510,7 @@ public void addCannedPolicy(@Nonnull String name, @Nonnull String policy) try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.ADD_CANNED_POLICY, ImmutableMultimap.of("name", name), policy.getBytes(StandardCharsets.UTF_8))) {} @@ -525,13 +522,11 @@ public void addCannedPolicy(@Nonnull String name, @Nonnull String policy) * @param userOrGroupName User/Group name. * @param isGroup Flag to denote userOrGroupName is a group name. * @param policyName Policy name or comma separated policy names. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ public void setPolicy( @Nonnull String userOrGroupName, boolean isGroup, @Nonnull String policyName) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + throws MinioException { if (userOrGroupName == null || userOrGroupName.isEmpty()) { throw new IllegalArgumentException("user/group name must be provided"); } @@ -541,7 +536,7 @@ public void setPolicy( try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.SET_USER_OR_GROUP_POLICY, ImmutableMultimap.of( "userOrGroup", @@ -558,13 +553,10 @@ public void setPolicy( * * @return {@link Map} - Map of policies, keyed by their name, with their actual * policy as their value. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public Map listCannedPolicies() - throws NoSuchAlgorithmException, InvalidKeyException, IOException { - try (Response response = execute(Method.GET, Command.LIST_CANNED_POLICIES, null, null)) { + public Map listCannedPolicies() throws MinioException { + try (Response response = execute(Http.Method.GET, Command.LIST_CANNED_POLICIES, null, null)) { MapType mapType = OBJECT_MAPPER .getTypeFactory() @@ -574,6 +566,8 @@ public Map listCannedPolicies() .>readValue(response.body().bytes(), mapType) .forEach((key, value) -> policies.put(key, value.toString())); return policies; + } catch (IOException e) { + throw new MinioException(e); } } @@ -581,19 +575,16 @@ public Map listCannedPolicies() * Removes canned policy by name. * * @param name Policy name. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public void removeCannedPolicy(@Nonnull String name) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + public void removeCannedPolicy(@Nonnull String name) throws MinioException { if (name == null || name.isEmpty()) { throw new IllegalArgumentException("name must be provided"); } try (Response response = execute( - Method.DELETE, + Http.Method.DELETE, Command.REMOVE_CANNED_POLICY, ImmutableMultimap.of("name", name), null)) {} @@ -603,14 +594,13 @@ public void removeCannedPolicy(@Nonnull String name) * Get server/cluster data usage info * * @return {@link DataUsageInfo} - DataUsageInfo object - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public DataUsageInfo getDataUsageInfo() - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try (Response response = execute(Method.GET, Command.DATA_USAGE_INFO, null, null)) { + public DataUsageInfo getDataUsageInfo() throws MinioException { + try (Response response = execute(Http.Method.GET, Command.DATA_USAGE_INFO, null, null)) { return OBJECT_MAPPER.readValue(response.body().bytes(), DataUsageInfo.class); + } catch (IOException e) { + throw new MinioException(e); } } @@ -618,13 +608,13 @@ public DataUsageInfo getDataUsageInfo() * Obtains admin info for the Minio server. * * @return {@link Message} - admin info for the Minio server. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public Message getServerInfo() throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try (Response response = execute(Method.GET, Command.INFO, null, null)) { + public Message getServerInfo() throws MinioException { + try (Response response = execute(Http.Method.GET, Command.INFO, null, null)) { return OBJECT_MAPPER.readValue(response.body().charStream(), Message.class); + } catch (IOException e) { + throw new MinioException(e); } } @@ -639,10 +629,7 @@ public Message getServerInfo() throws IOException, NoSuchAlgorithmException, Inv * @param description Description for this access key. * @param expiration Expiry time. * @return {@link Credentials} - Service account info for the specified accessKey. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. - * @throws InvalidCipherTextException thrown to indicate data cannot be encrypted/decrypted. + * @throws MinioException thrown to indicate SDK exception. */ public Credentials addServiceAccount( @Nonnull String accessKey, @@ -652,8 +639,7 @@ public Credentials addServiceAccount( @Nullable String name, @Nullable String description, @Nullable ZonedDateTime expiration) - throws NoSuchAlgorithmException, InvalidKeyException, IOException, - InvalidCipherTextException { + throws MinioException { if (accessKey == null || accessKey.isEmpty()) { throw new IllegalArgumentException("access key must be provided"); } @@ -685,18 +671,22 @@ public Credentials addServiceAccount( serviceAccount.put("description", description); } if (expiration != null) { - serviceAccount.put("expiration", expiration.format(Time.EXPIRATION_DATE_FORMAT)); + serviceAccount.put("expiration", expiration.format(Time.ISO8601UTC_FORMAT)); } Credentials creds = getCredentials(); try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.ADD_SERVICE_ACCOUNT, null, Crypto.encrypt(OBJECT_MAPPER.writeValueAsBytes(serviceAccount), creds.secretKey()))) { byte[] jsonData = Crypto.decrypt(response.body().byteStream(), creds.secretKey()); return OBJECT_MAPPER.readValue(jsonData, AddServiceAccountResp.class).credentials(); + } catch (JsonProcessingException e) { + throw new MinioException(e); + } catch (IOException e) { + throw new MinioException(e); } } @@ -710,10 +700,7 @@ public Credentials addServiceAccount( * @param newName New service account name. * @param newDescription New description. * @param newExpiration New expiry time. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. - * @throws InvalidCipherTextException thrown to indicate data cannot be encrypted/decrypted. + * @throws MinioException thrown to indicate SDK exception. */ public void updateServiceAccount( @Nonnull String accessKey, @@ -723,8 +710,7 @@ public void updateServiceAccount( @Nullable String newName, @Nullable String newDescription, @Nullable ZonedDateTime newExpiration) - throws NoSuchAlgorithmException, InvalidKeyException, IOException, - InvalidCipherTextException { + throws MinioException { if (accessKey == null || accessKey.isEmpty()) { throw new IllegalArgumentException("access key must be provided"); } @@ -752,35 +738,35 @@ public void updateServiceAccount( serviceAccount.put("newDescription", newDescription); } if (newExpiration != null) { - serviceAccount.put("newExpiration", newExpiration.format(Time.EXPIRATION_DATE_FORMAT)); + serviceAccount.put("newExpiration", newExpiration.format(Time.ISO8601UTC_FORMAT)); } Credentials creds = getCredentials(); try (Response response = execute( - Method.POST, + Http.Method.POST, Command.UPDATE_SERVICE_ACCOUNT, ImmutableMultimap.of("accessKey", accessKey), - Crypto.encrypt(OBJECT_MAPPER.writeValueAsBytes(serviceAccount), creds.secretKey()))) {} + Crypto.encrypt(OBJECT_MAPPER.writeValueAsBytes(serviceAccount), creds.secretKey()))) { + } catch (JsonProcessingException e) { + throw new MinioException(e); + } } /** * Deletes a service account by it's access key * * @param accessKey Access Key. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. + * @throws MinioException thrown to indicate SDK exception. */ - public void deleteServiceAccount(@Nonnull String accessKey) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + public void deleteServiceAccount(@Nonnull String accessKey) throws MinioException { if (accessKey == null || accessKey.isEmpty()) { throw new IllegalArgumentException("access key must be provided"); } try (Response response = execute( - Method.DELETE, + Http.Method.DELETE, Command.DELETE_SERVICE_ACCOUNT, ImmutableMultimap.of("accessKey", accessKey), null)) {} @@ -791,27 +777,24 @@ public void deleteServiceAccount(@Nonnull String accessKey) * * @param username user name. * @return {@link ListServiceAccountResp} - List of minio service account. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. - * @throws InvalidCipherTextException thrown to indicate data cannot be encrypted/decrypted. + * @throws MinioException thrown to indicate SDK exception. */ - public ListServiceAccountResp listServiceAccount(@Nonnull String username) - throws NoSuchAlgorithmException, InvalidKeyException, IOException, - InvalidCipherTextException { + public ListServiceAccountResp listServiceAccount(@Nonnull String username) throws MinioException { if (username == null || username.isEmpty()) { throw new IllegalArgumentException("user name must be provided"); } try (Response response = execute( - Method.GET, + Http.Method.GET, Command.LIST_SERVICE_ACCOUNTS, ImmutableMultimap.of("user", username), null)) { Credentials creds = getCredentials(); byte[] jsonData = Crypto.decrypt(response.body().byteStream(), creds.secretKey()); return OBJECT_MAPPER.readValue(jsonData, ListServiceAccountResp.class); + } catch (IOException e) { + throw new MinioException(e); } } @@ -820,26 +803,24 @@ public ListServiceAccountResp listServiceAccount(@Nonnull String username) * * @param accessKey Access Key. * @return {@link GetServiceAccountInfoResp} - Service account info for the specified accessKey. - * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. - * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. - * @throws IOException thrown to indicate I/O error on MinIO REST operation. - * @throws InvalidCipherTextException thrown to indicate data cannot be encrypted/decrypted. + * @throws MinioException thrown to indicate SDK exception. */ public GetServiceAccountInfoResp getServiceAccountInfo(@Nonnull String accessKey) - throws NoSuchAlgorithmException, InvalidKeyException, IOException, - InvalidCipherTextException { + throws MinioException { if (accessKey == null || accessKey.isEmpty()) { throw new IllegalArgumentException("access key must be provided"); } try (Response response = execute( - Method.GET, + Http.Method.GET, Command.INFO_SERVICE_ACCOUNT, ImmutableMultimap.of("accessKey", accessKey), null)) { Credentials creds = getCredentials(); byte[] jsonData = Crypto.decrypt(response.body().byteStream(), creds.secretKey()); return OBJECT_MAPPER.readValue(jsonData, GetServiceAccountInfoResp.class); + } catch (IOException e) { + throw new MinioException(e); } } @@ -857,8 +838,7 @@ public GetServiceAccountInfoResp getServiceAccountInfo(@Nonnull String accessKey * @param readTimeout HTTP read timeout in milliseconds. */ public void setTimeout(long connectTimeout, long writeTimeout, long readTimeout) { - this.httpClient = - HttpUtils.setTimeout(this.httpClient, connectTimeout, writeTimeout, readTimeout); + this.httpClient = Http.setTimeout(this.httpClient, connectTimeout, writeTimeout, readTimeout); } /** @@ -868,12 +848,11 @@ public void setTimeout(long connectTimeout, long writeTimeout, long readTimeout) * client.ignoreCertCheck(); * } * - * @throws KeyManagementException thrown to indicate key management error. - * @throws NoSuchAlgorithmException thrown to indicate missing of SSL library. + * @throws MinioException thrown to indicate SDK exception. */ @SuppressFBWarnings(value = "SIC", justification = "Should not be used in production anyways.") - public void ignoreCertCheck() throws KeyManagementException, NoSuchAlgorithmException { - this.httpClient = HttpUtils.disableCertCheck(this.httpClient); + public void ignoreCertCheck() throws MinioException { + this.httpClient = Http.disableCertCheck(this.httpClient); } /** @@ -885,8 +864,7 @@ public void ignoreCertCheck() throws KeyManagementException, NoSuchAlgorithmExce */ public void setAppInfo(String name, String version) { if (name == null || version == null) return; - this.userAgent = - MinioProperties.INSTANCE.getDefaultUserAgent() + " " + name.trim() + "/" + version.trim(); + this.userAgent = Utils.getDefaultUserAgent() + " " + name.trim() + "/" + version.trim(); } /** @@ -905,9 +883,8 @@ public void traceOn(OutputStream traceStream) { * Disables HTTP call tracing previously enabled. * * @see #traceOn - * @throws IOException upon connection error */ - public void traceOff() throws IOException { + public void traceOff() { this.traceStream = null; } @@ -923,12 +900,12 @@ public static final class Builder { private OkHttpClient httpClient; public Builder endpoint(String endpoint) { - this.baseUrl = HttpUtils.getBaseUrl(endpoint); + this.baseUrl = Utils.getBaseUrl(endpoint); return this; } public Builder endpoint(String endpoint, int port, boolean secure) { - HttpUrl url = HttpUtils.getBaseUrl(endpoint); + HttpUrl url = Utils.getBaseUrl(endpoint); if (port < 1 || port > 65535) { throw new IllegalArgumentException("port must be in range of 1 to 65535"); } @@ -938,20 +915,20 @@ public Builder endpoint(String endpoint, int port, boolean secure) { } public Builder endpoint(HttpUrl url) { - HttpUtils.validateNotNull(url, "url"); - HttpUtils.validateUrl(url); + Utils.validateNotNull(url, "url"); + Utils.validateUrl(url); this.baseUrl = url; return this; } public Builder endpoint(URL url) { - HttpUtils.validateNotNull(url, "url"); + Utils.validateNotNull(url, "url"); return endpoint(HttpUrl.get(url)); } public Builder region(String region) { - HttpUtils.validateNotNull(region, "region"); + Utils.validateNotNull(region, "region"); this.region = region; return this; } @@ -962,24 +939,22 @@ public Builder credentials(String accessKey, String secretKey) { } public Builder credentialsProvider(Provider provider) { - HttpUtils.validateNotNull(provider, "credential provider"); + Utils.validateNotNull(provider, "credential provider"); this.provider = provider; return this; } public Builder httpClient(OkHttpClient httpClient) { - HttpUtils.validateNotNull(httpClient, "http client"); + Utils.validateNotNull(httpClient, "http client"); this.httpClient = httpClient; return this; } public MinioAdminClient build() { - HttpUtils.validateNotNull(baseUrl, "base url"); - HttpUtils.validateNotNull(provider, "credential provider"); + Utils.validateNotNull(baseUrl, "base url"); + Utils.validateNotNull(provider, "credential provider"); if (httpClient == null) { - httpClient = - HttpUtils.newDefaultHttpClient( - DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); + httpClient = Http.newDefaultClient(); } return new MinioAdminClient(baseUrl, region, provider, httpClient); } diff --git a/api/src/main/java/io/minio/AbortMultipartUploadArgs.java b/api/src/main/java/io/minio/AbortMultipartUploadArgs.java new file mode 100644 index 000000000..74f2c9011 --- /dev/null +++ b/api/src/main/java/io/minio/AbortMultipartUploadArgs.java @@ -0,0 +1,76 @@ +/* + * 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 java.util.Objects; + +/** + * Argument class of {@link MinioAsyncClient#abortMultipartUpload} and {@link + * MinioClient#abortMultipartUpload}. + */ +public class AbortMultipartUploadArgs extends ObjectArgs { + private String uploadId; + + protected AbortMultipartUploadArgs() {} + + public AbortMultipartUploadArgs(ComposeObjectArgs args, String uploadId) { + super(args); + this.uploadId = uploadId; + } + + public AbortMultipartUploadArgs(PutObjectBaseArgs args, String uploadId) { + super(args); + this.uploadId = uploadId; + } + + public String uploadId() { + return uploadId; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link AbortMultipartUploadArgs}. */ + public static final class Builder extends ObjectArgs.Builder { + @Override + protected void validate(AbortMultipartUploadArgs args) { + super.validate(args); + Utils.validateNotEmptyString(args.uploadId, "upload ID"); + } + + public Builder uploadId(String uploadId) { + Utils.validateNotEmptyString(uploadId, "upload ID"); + operations.add(args -> args.uploadId = uploadId); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AbortMultipartUploadArgs)) return false; + if (!super.equals(o)) return false; + AbortMultipartUploadArgs that = (AbortMultipartUploadArgs) o; + return Objects.equals(uploadId, that.uploadId); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), uploadId); + } +} diff --git a/api/src/main/java/io/minio/BaseArgs.java b/api/src/main/java/io/minio/BaseArgs.java index c8857ec55..f5dc2ae27 100644 --- a/api/src/main/java/io/minio/BaseArgs.java +++ b/api/src/main/java/io/minio/BaseArgs.java @@ -16,9 +16,6 @@ package io.minio; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -26,29 +23,43 @@ import java.util.Map; import java.util.Objects; import java.util.function.Consumer; -import okhttp3.HttpUrl; /** Base argument class. */ public abstract class BaseArgs { - protected Multimap extraHeaders = - Multimaps.unmodifiableMultimap(HashMultimap.create()); - protected Multimap extraQueryParams = - Multimaps.unmodifiableMultimap(HashMultimap.create()); + protected String location; + protected Http.Headers extraHeaders; + protected Http.QueryParameters extraQueryParams; - public Multimap extraHeaders() { + protected BaseArgs() {} + + protected BaseArgs(BaseArgs args) { + this.location = args.location; + this.extraHeaders = args.extraHeaders; + this.extraQueryParams = args.extraQueryParams; + } + + public void setLocation(String location) { + this.location = location; + } + + public String location() { + return location; + } + + public Http.Headers extraHeaders() { return extraHeaders; } - public Multimap extraQueryParams() { + public Http.QueryParameters extraQueryParams() { return extraQueryParams; } - protected void checkSse(ServerSideEncryption sse, HttpUrl url) { + protected void checkSse(ServerSideEncryption sse, boolean isHttps) { if (sse == null) { return; } - if (sse.tlsRequired() && !url.isHttps()) { + if (sse.tlsRequired() && !isHttps) { throw new IllegalArgumentException( sse + " operations must be performed over a secure connection."); } @@ -60,77 +71,32 @@ public abstract static class Builder, A extends BaseArgs protected abstract void validate(A args); - protected void validateNotNull(Object arg, String argName) { - if (arg == null) { - throw new IllegalArgumentException(argName + " must not be null."); - } - } - - protected void validateNotEmptyString(String arg, String argName) { - validateNotNull(arg, argName); - if (arg.isEmpty()) { - throw new IllegalArgumentException(argName + " must be a non-empty string."); - } - } - - protected void validateNullOrNotEmptyString(String arg, String argName) { - if (arg != null && arg.isEmpty()) { - throw new IllegalArgumentException(argName + " must be a non-empty string."); - } - } - - protected void validateNullOrPositive(Number arg, String argName) { - if (arg != null && arg.longValue() < 0) { - throw new IllegalArgumentException(argName + " cannot be non-negative."); - } - } - public Builder() { this.operations = new ArrayList<>(); } - protected Multimap copyMultimap(Multimap multimap) { - Multimap multimapCopy = HashMultimap.create(); - if (multimap != null) { - multimapCopy.putAll(multimap); - } - return Multimaps.unmodifiableMultimap(multimapCopy); - } - - protected Multimap toMultimap(Map map) { - Multimap multimap = HashMultimap.create(); - if (map != null) { - multimap.putAll(Multimaps.forMap(map)); - } - return Multimaps.unmodifiableMultimap(multimap); - } - @SuppressWarnings("unchecked") // Its safe to type cast to B as B extends this class. - public B extraHeaders(Multimap headers) { - final Multimap extraHeaders = copyMultimap(headers); + public B extraHeaders(Http.Headers headers) { + final Http.Headers extraHeaders = new Http.Headers(headers); operations.add(args -> args.extraHeaders = extraHeaders); return (B) this; } @SuppressWarnings("unchecked") // Its safe to type cast to B as B extends this class. - public B extraQueryParams(Multimap queryParams) { - final Multimap extraQueryParams = copyMultimap(queryParams); + public B extraQueryParams(Http.QueryParameters queryParams) { + final Http.QueryParameters extraQueryParams = new Http.QueryParameters(queryParams); operations.add(args -> args.extraQueryParams = extraQueryParams); return (B) this; } @SuppressWarnings("unchecked") // Its safe to type cast to B as B extends this class. public B extraHeaders(Map headers) { - final Multimap extraHeaders = toMultimap(headers); - operations.add(args -> args.extraHeaders = extraHeaders); - return (B) this; + return extraHeaders(new Http.Headers(headers)); } @SuppressWarnings("unchecked") // Its safe to type cast to B as B extends this class. public B extraQueryParams(Map queryParams) { - final Multimap extraQueryParams = toMultimap(queryParams); - operations.add(args -> args.extraQueryParams = extraQueryParams); - return (B) this; + return extraQueryParams(new Http.QueryParameters(queryParams)); } @SuppressWarnings("unchecked") // safe as B will always be the builder of the current args class diff --git a/api/src/main/java/io/minio/BaseClient.java.old b/api/src/main/java/io/minio/BaseClient.java.old new file mode 100644 index 000000000..7061d2e84 --- /dev/null +++ b/api/src/main/java/io/minio/BaseClient.java.old @@ -0,0 +1,173 @@ +/* + * 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 com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import io.minio.errors.InsufficientDataException; +import io.minio.errors.InternalException; +import io.minio.errors.XmlParserException; +import io.minio.messages.Part; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; + +/** Core S3 API client. */ +public abstract class BaseClient extends BaseS3Client { + ///////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + + private Part[] uploadParts( + PutObjectBaseArgs args, String uploadId, PartReader partReader, PartSource firstPartSource) + throws InterruptedException, ExecutionException, InsufficientDataException, InternalException, + InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { + Part[] parts = new Part[ObjectWriteArgs.MAX_MULTIPART_COUNT]; + int partNumber = 0; + PartSource partSource = firstPartSource; + while (true) { + partNumber++; + + Multimap ssecHeaders = null; + // set encryption headers in the case of SSE-C. + if (args.sse() != null && args.sse() instanceof ServerSideEncryption.CustomerKey) { + ssecHeaders = Multimaps.forMap(args.sse().headers()); + } + + UploadPartResponse response = + uploadPart( + UploadPartArgs.builder() + .bucket(args.bucket()) + .region(args.region()) + .object(args.object()) + .buffer(partSource, partSource.size()) + .partNumber(partNumber) + .uploadId(uploadId) + .headers(ssecHeaders) + .build()) + .get(); + parts[partNumber - 1] = new Part(partNumber, response.etag()); + + partSource = partReader.getPart(); + if (partSource == null) break; + } + + return parts; + } + + private CompletableFuture putMultipartObjectAsync( + PutObjectBaseArgs args, + Multimap headers, + PartReader partReader, + PartSource firstPartSource) + throws InsufficientDataException, InternalException, InvalidKeyException, IOException, + NoSuchAlgorithmException, XmlParserException { + return CompletableFuture.supplyAsync( + () -> { + String uploadId = null; + ObjectWriteResponse response = null; + try { + CreateMultipartUploadResponse createMultipartUploadResponse = + createMultipartUpload( + CreateMultipartUploadArgs.builder() + .extraQueryParams(args.extraQueryParams()) + .bucket(args.bucket()) + .region(args.region()) + .object(args.object()) + .headers(headers) + .build()) + .get(); + uploadId = createMultipartUploadResponse.result().uploadId(); + Part[] parts = uploadParts(args, uploadId, partReader, firstPartSource); + response = + completeMultipartUpload( + CompleteMultipartUploadArgs.builder() + .bucket(args.bucket()) + .region(args.region()) + .object(args.object()) + .uploadId(uploadId) + .parts(parts) + .build()) + .get(); + } catch (InsufficientDataException + | InternalException + | InvalidKeyException + | IOException + | NoSuchAlgorithmException + | XmlParserException + | InterruptedException + | ExecutionException e) { + Throwable throwable = e; + if (throwable instanceof ExecutionException) { + throwable = ((ExecutionException) throwable).getCause(); + } + if (throwable instanceof CompletionException) { + throwable = ((CompletionException) throwable).getCause(); + } + if (uploadId == null) { + throw new CompletionException(throwable); + } + try { + abortMultipartUpload( + AbortMultipartUploadArgs.builder() + .bucket(args.bucket()) + .region(args.region()) + .object(args.object()) + .uploadId(uploadId) + .build()) + .get(); + } catch (InsufficientDataException + | InternalException + | InvalidKeyException + | IOException + | NoSuchAlgorithmException + | XmlParserException + | InterruptedException + | ExecutionException ex) { + throwable = ex; + if (throwable instanceof ExecutionException) { + throwable = ((ExecutionException) throwable).getCause(); + } + if (throwable instanceof CompletionException) { + throwable = ((CompletionException) throwable).getCause(); + } + } + throw new CompletionException(throwable); + } + return response; + }); + } + + protected PartReader newPartReader( + Object data, long objectSize, long partSize, int partCount, Checksum.Algorithm... algorithms) + throws NoSuchAlgorithmException { + if (data instanceof RandomAccessFile) { + return new PartReader((RandomAccessFile) data, objectSize, partSize, partCount, algorithms); + } + + if (data instanceof InputStream) { + return new PartReader((InputStream) data, objectSize, partSize, partCount, algorithms); + } + + return null; + } +} diff --git a/api/src/main/java/io/minio/BaseS3Client.java b/api/src/main/java/io/minio/BaseS3Client.java new file mode 100644 index 000000000..25ce44583 --- /dev/null +++ b/api/src/main/java/io/minio/BaseS3Client.java @@ -0,0 +1,1385 @@ +/* + * 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 com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.google.common.collect.ImmutableSet; +import io.minio.credentials.Credentials; +import io.minio.credentials.Provider; +import io.minio.errors.ErrorResponseException; +import io.minio.errors.InternalException; +import io.minio.errors.InvalidResponseException; +import io.minio.errors.MinioException; +import io.minio.errors.ServerException; +import io.minio.errors.XmlParserException; +import io.minio.messages.CompleteMultipartUpload; +import io.minio.messages.CompleteMultipartUploadResult; +import io.minio.messages.CopyObjectResult; +import io.minio.messages.CopyPartResult; +import io.minio.messages.CreateBucketConfiguration; +import io.minio.messages.DeleteRequest; +import io.minio.messages.DeleteResult; +import io.minio.messages.ErrorResponse; +import io.minio.messages.InitiateMultipartUploadResult; +import io.minio.messages.ListAllMyBucketsResult; +import io.minio.messages.ListBucketResultV1; +import io.minio.messages.ListBucketResultV2; +import io.minio.messages.ListMultipartUploadsResult; +import io.minio.messages.ListPartsResult; +import io.minio.messages.ListVersionsResult; +import io.minio.messages.LocationConstraint; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; +import javax.annotation.Nonnull; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** Core S3 API client. */ +public abstract class BaseS3Client implements AutoCloseable { + static { + try { + RequestBody.create(new byte[] {}, null); + } catch (NoSuchMethodError ex) { + throw new RuntimeException("Unsupported OkHttp library found. Must use okhttp >= 4.11.0", ex); + } + } + + protected static final String NO_SUCH_BUCKET_MESSAGE = "Bucket does not exist"; + protected static final String NO_SUCH_BUCKET = "NoSuchBucket"; + protected static final String NO_SUCH_BUCKET_POLICY = "NoSuchBucketPolicy"; + protected static final String NO_SUCH_OBJECT_LOCK_CONFIGURATION = "NoSuchObjectLockConfiguration"; + protected static final String SERVER_SIDE_ENCRYPTION_CONFIGURATION_NOT_FOUND_ERROR = + "ServerSideEncryptionConfigurationNotFoundError"; + // maximum allowed bucket policy size is 20KiB + protected static final int MAX_BUCKET_POLICY_SIZE = 20 * 1024; + protected final Map regionCache = new ConcurrentHashMap<>(); + protected static final Random random = new Random(new SecureRandom().nextLong()); + protected static final ObjectMapper objectMapper = + JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) + .build(); + + private static final String RETRY_HEAD = "RetryHead"; + private static final String END_HTTP = "----------END-HTTP----------"; + private static final String UPLOAD_ID = "uploadId"; + private static final Set TRACE_QUERY_PARAMS = + ImmutableSet.of("retention", "legal-hold", "tagging", UPLOAD_ID, "acl", "attributes"); + private PrintWriter traceStream; + protected String userAgent = Utils.getDefaultUserAgent(); + + protected Http.BaseUrl baseUrl; + protected Provider provider; + protected OkHttpClient httpClient; + protected boolean closeHttpClient; + + protected BaseS3Client( + Http.BaseUrl baseUrl, Provider provider, OkHttpClient httpClient, boolean closeHttpClient) { + this.baseUrl = baseUrl; + this.provider = provider; + this.httpClient = httpClient; + this.closeHttpClient = closeHttpClient; + } + + protected BaseS3Client(BaseS3Client client) { + this.baseUrl = client.baseUrl; + this.provider = client.provider; + this.httpClient = client.httpClient; + this.closeHttpClient = client.closeHttpClient; + } + + @Override + public void close() throws Exception { + if (closeHttpClient) { + httpClient.dispatcher().executorService().shutdown(); + httpClient.connectionPool().evictAll(); + } + } + + /** + * Sets HTTP connect, write and read timeouts. A value of 0 means no timeout, otherwise values + * must be between 1 and Integer.MAX_VALUE when converted to milliseconds. + * + *
Example:{@code
+   * minioClient.setTimeout(TimeUnit.SECONDS.toMillis(10), TimeUnit.SECONDS.toMillis(10),
+   *     TimeUnit.SECONDS.toMillis(30));
+   * }
+ * + * @param connectTimeout HTTP connect timeout in milliseconds. + * @param writeTimeout HTTP write timeout in milliseconds. + * @param readTimeout HTTP read timeout in milliseconds. + */ + public void setTimeout(long connectTimeout, long writeTimeout, long readTimeout) { + this.httpClient = Http.setTimeout(this.httpClient, connectTimeout, writeTimeout, readTimeout); + } + + /** + * Ignores check on server certificate for HTTPS connection. + * + *
Example:{@code
+   * minioClient.ignoreCertCheck();
+   * }
+ * + * @throws MinioException thrown to indicate SDK exception. + */ + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "SIC", + justification = "Should not be used in production anyways.") + public void ignoreCertCheck() throws MinioException { + this.httpClient = Http.disableCertCheck(this.httpClient); + } + + /** + * Sets application's name/version to user agent. For more information about user agent refer
#rfc2616. + * + * @param name Your application name. + * @param version Your application version. + */ + public void setAppInfo(String name, String version) { + if (name == null || version == null) return; + this.userAgent = Utils.getDefaultUserAgent() + " " + name.trim() + "/" + version.trim(); + } + + /** + * Enables HTTP call tracing and written to traceStream. + * + * @param traceStream {@link OutputStream} for writing HTTP call tracing. + * @see #traceOff + */ + public void traceOn(OutputStream traceStream) { + if (traceStream == null) throw new IllegalArgumentException("trace stream must be provided"); + this.traceStream = + new PrintWriter(new OutputStreamWriter(traceStream, StandardCharsets.UTF_8), true); + } + + /** + * Disables HTTP call tracing previously enabled. + * + * @see #traceOn + */ + public void traceOff() { + this.traceStream = null; + } + + /** Enables dual-stack endpoint for Amazon S3 endpoint. */ + public void enableDualStackEndpoint() { + baseUrl.enableDualStackEndpoint(); + } + + /** Disables dual-stack endpoint for Amazon S3 endpoint. */ + public void disableDualStackEndpoint() { + baseUrl.disableDualStackEndpoint(); + } + + /** Enables virtual-style endpoint. */ + public void enableVirtualStyleEndpoint() { + baseUrl.enableVirtualStyleEndpoint(); + } + + /** Disables virtual-style endpoint. */ + public void disableVirtualStyleEndpoint() { + baseUrl.disableVirtualStyleEndpoint(); + } + + /** Sets AWS S3 domain prefix. */ + public void setAwsS3Prefix(@Nonnull String awsS3Prefix) { + baseUrl.setAwsS3Prefix(awsS3Prefix); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////// HTTP execution methods //////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + + private String[] handleRedirectResponse( + Http.Method method, String bucketName, Response response, boolean retry) { + String code = null; + String message = null; + + if (response.code() == 301) { + code = "PermanentRedirect"; + message = "Moved Permanently"; + } else if (response.code() == 307) { + code = "Redirect"; + message = "Temporary redirect"; + } else if (response.code() == 400) { + code = "BadRequest"; + message = "Bad request"; + } + + String region = response.headers().get("x-amz-bucket-region"); + if (message != null && region != null) message += ". Use region " + region; + + if (retry + && region != null + && method.equals(Http.Method.HEAD) + && bucketName != null + && regionCache.get(bucketName) != null) { + code = RETRY_HEAD; + message = null; + } + + return new String[] {code, message}; + } + + /** Execute HTTP request asynchronously for given parameters. */ + protected CompletableFuture executeAsync(Http.S3Request s3request, String region) + throws MinioException { + Credentials credentials = (provider == null) ? null : provider.fetch(); + Http.Request request = s3request.toRequest(baseUrl, region, credentials); + + StringBuilder traceBuilder = new StringBuilder(request.httpTraces()); + PrintWriter traceStream = this.traceStream; + if (traceStream != null) traceStream.print(request.httpTraces()); + + OkHttpClient httpClient = this.httpClient; + // FIXME: enable retry for all request. + // if (!s3request.retryFailure()) { + // httpClient = httpClient.newBuilder().retryOnConnectionFailure(false).build(); + // } + + okhttp3.Request httpRequest = request.httpRequest(); + CompletableFuture completableFuture = new CompletableFuture<>(); + httpClient + .newCall(httpRequest) + .enqueue( + new Callback() { + @Override + public void onFailure(final Call call, IOException e) { + completableFuture.completeExceptionally(e); + } + + @Override + public void onResponse(Call call, final Response response) throws IOException { + try { + onResponse(response); + } catch (Exception e) { + completableFuture.completeExceptionally(e); + } + } + + private void onResponse(final Response response) throws IOException { + String trace = + String.format( + "%s %d %s%n%s%n%n", + response.protocol().toString().toUpperCase(Locale.US), + response.code(), + response.message(), + response.headers().toString()); + traceBuilder.append(trace); + if (traceStream != null) traceStream.print(trace); + + if (response.isSuccessful()) { + if (traceStream != null) { + // Trace response body only if the request is not + // GetObject/ListenBucketNotification + // S3 API. + Set keys = s3request.queryParams().keySet(); + if ((s3request.method() != Http.Method.GET + || s3request.object() == null + || !Collections.disjoint(keys, TRACE_QUERY_PARAMS)) + && !(keys.contains("events") + && (keys.contains("prefix") || keys.contains("suffix")))) { + ResponseBody responseBody = response.peekBody(1024 * 1024); + traceStream.println(responseBody.string()); + } + traceStream.println(END_HTTP); + } + + completableFuture.complete(response); + return; + } + + String errorXml = null; + try (ResponseBody responseBody = response.body()) { + errorXml = responseBody.string(); + } + + if (!("".equals(errorXml) && s3request.method().equals(Http.Method.HEAD))) { + traceBuilder.append(errorXml); + if (traceStream != null) traceStream.print(errorXml); + if (!errorXml.endsWith("\n")) { + traceBuilder.append("\n"); + if (traceStream != null) traceStream.println(); + } + } + traceBuilder.append(END_HTTP).append("\n"); + if (traceStream != null) traceStream.println(END_HTTP); + + // Error out for Non-XML response from server for non-HEAD requests. + String contentType = response.headers().get(Http.Headers.CONTENT_TYPE); + if (!s3request.method().equals(Http.Method.HEAD) + && (contentType == null + || !Arrays.asList(contentType.split(";")).contains("application/xml"))) { + if (response.code() == 304 && response.body().contentLength() == 0) { + completableFuture.completeExceptionally( + new ServerException( + "server failed with HTTP status code " + response.code(), + response.code(), + traceBuilder.toString())); + } + + completableFuture.completeExceptionally( + new InvalidResponseException( + response.code(), + contentType, + errorXml.substring( + 0, errorXml.length() > 1024 ? 1024 : errorXml.length()), + traceBuilder.toString())); + return; + } + + ErrorResponse errorResponse = null; + if (!"".equals(errorXml)) { + try { + errorResponse = Xml.unmarshal(ErrorResponse.class, errorXml); + } catch (XmlParserException e) { + completableFuture.completeExceptionally(e); + return; + } + } else if (!s3request.method().equals(Http.Method.HEAD)) { + completableFuture.completeExceptionally( + new InvalidResponseException( + response.code(), contentType, errorXml, traceBuilder.toString())); + return; + } + + if (errorResponse == null) { + String code = null; + String message = null; + switch (response.code()) { + case 301: + case 307: + case 400: + String[] result = + handleRedirectResponse( + s3request.method(), s3request.bucket(), response, true); + code = result[0]; + message = result[1]; + break; + case 404: + if (s3request.object() != null) { + code = "NoSuchKey"; + message = "Object does not exist"; + } else if (s3request.bucket() != null) { + code = NO_SUCH_BUCKET; + message = NO_SUCH_BUCKET_MESSAGE; + } else { + code = "ResourceNotFound"; + message = "Request resource not found"; + } + break; + case 501: + case 405: + code = "MethodNotAllowed"; + message = "The specified method is not allowed against this resource"; + break; + case 409: + if (s3request.bucket() != null) { + code = NO_SUCH_BUCKET; + message = NO_SUCH_BUCKET_MESSAGE; + } else { + code = "ResourceConflict"; + message = "Request resource conflicts"; + } + break; + case 403: + code = "AccessDenied"; + message = "Access denied"; + break; + case 412: + code = "PreconditionFailed"; + message = "At least one of the preconditions you specified did not hold"; + break; + case 416: + code = "InvalidRange"; + message = "The requested range cannot be satisfied"; + break; + default: + completableFuture.completeExceptionally( + new ServerException( + "server failed with HTTP status code " + response.code(), + response.code(), + traceBuilder.toString())); + return; + } + + errorResponse = + new ErrorResponse( + code, + message, + s3request.bucket(), + s3request.object(), + httpRequest.url().encodedPath(), + response.header("x-amz-request-id"), + response.header("x-amz-id-2")); + } + + // invalidate region cache if needed + if (errorResponse.code().equals(NO_SUCH_BUCKET) + || errorResponse.code().equals(RETRY_HEAD)) { + regionCache.remove(s3request.bucket()); + } + + ErrorResponseException e = + new ErrorResponseException(errorResponse, response, traceBuilder.toString()); + completableFuture.completeExceptionally(e); + } + }); + return completableFuture; + } + + /** Execute HTTP request asynchronously for given args and parameters. */ + protected CompletableFuture executeAsync(Http.S3Request s3request) + throws MinioException { + return getRegion(s3request.bucket(), s3request.region()) + .thenCompose( + location -> { + s3request.args().setLocation(location); + try { + return executeAsync(s3request, location); + } catch (MinioException e) { + throw new CompletionException(e); + } + }); + } + + /** Execute asynchronously GET HTTP request for given parameters. */ + protected CompletableFuture executeGetAsync( + BaseArgs args, Http.Headers headers, Http.QueryParameters queryParams) throws MinioException { + return executeAsync( + Http.S3Request.builder() + .userAgent(userAgent) + .method(Http.Method.GET) + .headers(headers) + .queryParams(queryParams) + .args(args) + .build()); + } + + /** Execute asynchronously HEAD HTTP request for given parameters. */ + protected CompletableFuture executeHeadAsync( + BaseArgs args, Http.Headers headers, Http.QueryParameters queryParams) throws MinioException { + Http.S3Request s3request = + Http.S3Request.builder() + .userAgent(userAgent) + .method(Http.Method.HEAD) + .headers(headers) + .queryParams(queryParams) + .args(args) + .build(); + return executeAsync(s3request) + .exceptionally( + e -> { + if (e instanceof ErrorResponseException) { + ErrorResponseException ex = (ErrorResponseException) e; + if (ex.errorResponse().code().equals(RETRY_HEAD)) { + return null; + } + } + throw new CompletionException(e); + }) + .thenCompose( + response -> { + if (response != null) { + return CompletableFuture.completedFuture(response); + } + + try { + return executeAsync(s3request); + } catch (MinioException e) { + throw new CompletionException(e); + } + }); + } + + /** Execute asynchronously DELETE HTTP request for given parameters. */ + protected CompletableFuture executeDeleteAsync( + BaseArgs args, Http.Headers headers, Http.QueryParameters queryParams) throws MinioException { + return executeAsync( + Http.S3Request.builder() + .userAgent(userAgent) + .method(Http.Method.DELETE) + .headers(headers) + .queryParams(queryParams) + .args(args) + .build()) + .thenApply( + response -> { + if (response != null) response.body().close(); + return response; + }); + } + + /** Execute asynchronously POST HTTP request for given parameters. */ + protected CompletableFuture executePostAsync( + BaseArgs args, Http.Headers headers, Http.QueryParameters queryParams, Http.Body body) + throws MinioException { + return executeAsync( + Http.S3Request.builder() + .userAgent(userAgent) + .method(Http.Method.POST) + .headers(headers) + .queryParams(queryParams) + .body(body) + .args(args) + .build()); + } + + /** Execute asynchronously PUT HTTP request for given parameters. */ + protected CompletableFuture executePutAsync( + BaseArgs args, Http.Headers headers, Http.QueryParameters queryParams, Http.Body body) + throws MinioException { + return executeAsync( + Http.S3Request.builder() + .userAgent(userAgent) + .method(Http.Method.PUT) + .headers(headers) + .queryParams(queryParams) + .body(body) + .args(args) + .build()); + } + + /** Returns region of given bucket either from region cache or set in constructor. */ + protected CompletableFuture getRegion(String bucket, String region) + throws MinioException { + String thisRegion = this.baseUrl.region(); + if (region != null) { + // Error out if region does not match with region passed via constructor. + if (thisRegion != null && !thisRegion.equals(region)) { + throw new IllegalArgumentException( + "region must be " + thisRegion + ", but passed " + region); + } + return CompletableFuture.completedFuture(region); + } + + if (thisRegion != null && !thisRegion.equals("")) { + return CompletableFuture.completedFuture(thisRegion); + } + if (bucket == null || this.provider == null) { + return CompletableFuture.completedFuture(Http.US_EAST_1); + } + region = regionCache.get(bucket); + if (region != null) return CompletableFuture.completedFuture(region); + + return getBucketLocation(GetBucketLocationArgs.builder().bucket(bucket).build()); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////// S3 APIs and their helpers are added here /////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + + /** Check whether argument is valid or not. */ + protected void checkArgs(BaseArgs args) { + if (args == null) throw new IllegalArgumentException("null arguments"); + + if ((baseUrl.awsDomainSuffix() != null) && (args instanceof BucketArgs)) { + String bucketName = ((BucketArgs) args).bucket(); + if (bucketName.startsWith("xn--") + || bucketName.endsWith("--s3alias") + || bucketName.endsWith("--ol-s3")) { + throw new IllegalArgumentException( + "bucket name '" + + bucketName + + "' must not start with 'xn--' and must not end with '--s3alias' or '--ol-s3'"); + } + } + } + + /** + * Do AbortMultipartUpload + * S3 API asynchronously. + * + * @param args {@link AbortMultipartUploadArgs} object. + * @return {@link CompletableFuture}<{@link AbortMultipartUploadResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture abortMultipartUpload( + AbortMultipartUploadArgs args) throws MinioException { + checkArgs(args); + return executeDeleteAsync(args, null, new Http.QueryParameters(UPLOAD_ID, args.uploadId())) + .thenApply( + response -> { + try { + return new AbortMultipartUploadResponse( + response.headers(), + args.bucket(), + args.location(), + args.object(), + args.uploadId()); + } finally { + response.close(); + } + }); + } + + /** + * Do CompleteMultipartUpload + * S3 API asynchronously. + * + * @param args {@link CompleteMultipartUploadArgs} object. + * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture completeMultipartUpload( + CompleteMultipartUploadArgs args) throws MinioException { + checkArgs(args); + return executePostAsync( + args, + null, + new Http.QueryParameters(UPLOAD_ID, args.uploadId()), + new Http.Body(new CompleteMultipartUpload(args.parts()), null, null, null)) + .thenApply( + response -> { + try { + String bodyContent = response.body().string(); + bodyContent = bodyContent.trim(); + if (!bodyContent.isEmpty()) { + try { + if (Xml.validate(ErrorResponse.class, bodyContent)) { + ErrorResponse errorResponse = Xml.unmarshal(ErrorResponse.class, bodyContent); + throw new CompletionException( + new ErrorResponseException(errorResponse, response, null)); + } + } catch (XmlParserException e) { + // As it is not message, fallback to parse CompleteMultipartUploadOutput + // XML. + } + + try { + CompleteMultipartUploadResult result = + Xml.unmarshal(CompleteMultipartUploadResult.class, bodyContent); + return new ObjectWriteResponse( + response.headers(), + result.bucket(), + result.location(), + result.object(), + result.etag(), + response.header("x-amz-version-id"), + result); + } catch (XmlParserException e) { + // As this CompleteMultipartUpload REST call succeeded, just log it. + Logger.getLogger(BaseS3Client.class.getName()) + .warning( + "S3 service returned unknown XML for CompleteMultipartUpload REST API. " + + bodyContent); + } + } + + return new ObjectWriteResponse( + response.headers(), + args.bucket(), + args.location(), + args.object(), + null, + response.header("x-amz-version-id")); + } catch (IOException e) { + throw new CompletionException(new MinioException(e)); + } finally { + response.close(); + } + }); + } + + public CompletableFuture copyObject(CopyObjectArgs args) + throws MinioException { + checkArgs(args); + args.validateSse(this.baseUrl.isHttps()); + if (args.source().offset() != null || args.source().length() != null) { + throw new IllegalArgumentException("copy object with offset/length is unsupported"); + } + + Http.Headers headers = Http.Headers.merge(args.makeHeaders(), args.source().makeCopyHeaders()); + if (args.metadataDirective() != null) { + headers.put("x-amz-metadata-directive", args.metadataDirective().toString()); + } + if (args.taggingDirective() != null) { + headers.put("x-amz-tagging-directive", args.taggingDirective().toString()); + } + + return executePutAsync(args, headers, null, null) + .thenApply( + response -> { + try { + CopyObjectResult result = + Xml.unmarshal(CopyObjectResult.class, response.body().charStream()); + return new ObjectWriteResponse( + response.headers(), + args.bucket(), + args.region(), + args.object(), + result.etag(), + response.header("x-amz-version-id"), + result); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } + + /** + * Do CreateBucket + * S3 API asynchronously. + * + * @param args {@link CreateBucketArgs} object. + * @return {@link CompletableFuture}<{@link GenericResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture createBucket(CreateBucketArgs args) + throws MinioException { + checkArgs(args); + + String region = args.region(); + String baseUrlRegion = baseUrl.region(); + if (baseUrlRegion != null && !baseUrlRegion.isEmpty()) { + // Error out if region does not match with region passed via constructor. + if (region != null && !region.equals(baseUrlRegion)) { + throw new IllegalArgumentException( + "region must be " + baseUrlRegion + ", but passed " + region); + } + region = baseUrlRegion; + } + if (region == null) { + region = Http.US_EAST_1; + } + + Http.Headers headers = + args.objectLock() ? new Http.Headers("x-amz-bucket-object-lock-enabled", "true") : null; + final String locationConstraint = region; + + CreateBucketConfiguration config = null; + if (locationConstraint.equals(Http.US_EAST_1)) { + config = + new CreateBucketConfiguration( + locationConstraint, args.locationConfig(), args.bucketConfig()); + } + + return executeAsync( + Http.S3Request.builder() + .userAgent(userAgent) + .method(Http.Method.PUT) + .headers(headers) + .body(new Http.Body(config, null, null, null)) + .args(args) + .build(), + locationConstraint) + .thenApply( + response -> { + try { + return new GenericResponse( + response.headers(), args.bucket(), args.location(), null); + } finally { + response.close(); + } + }); + } + + /** + * Do CreateMultipartUpload + * S3 API asynchronously. + * + * @param args {@link CreateMultipartUploadArgs} object. + * @return {@link CompletableFuture}<{@link CreateMultipartUploadResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture createMultipartUpload( + CreateMultipartUploadArgs args) throws MinioException { + checkArgs(args); + return executePostAsync(args, args.headers(), new Http.QueryParameters("uploads", ""), null) + .thenApply( + response -> { + try { + InitiateMultipartUploadResult result = + Xml.unmarshal( + InitiateMultipartUploadResult.class, response.body().charStream()); + return new CreateMultipartUploadResponse( + response.headers(), args.bucket(), args.location(), args.object(), result); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } + + /** + * Do DeleteObjects S3 + * API asynchronously. + * + * @param args {@link DeleteObjectsArgs} object. + * @return {@link CompletableFuture}<{@link DeleteObjectsResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture deleteObjects(DeleteObjectsArgs args) + throws MinioException { + checkArgs(args); + return executePostAsync( + args, + args.bypassGovernanceMode() + ? new Http.Headers("x-amz-bypass-governance-retention", "true") + : null, + new Http.QueryParameters("delete", ""), + new Http.Body(new DeleteRequest(args.objects(), args.quiet()), null, null, null)) + .thenApply( + response -> { + try { + String bodyContent = response.body().string(); + try { + if (Xml.validate(DeleteResult.Error.class, bodyContent)) { + DeleteResult.Error error = Xml.unmarshal(DeleteResult.Error.class, bodyContent); + DeleteResult result = new DeleteResult(error); + return new DeleteObjectsResponse( + response.headers(), args.bucket(), args.region(), result); + } + } catch (XmlParserException e) { + // Ignore this exception as it is not message, + // but parse it as message below. + } + + DeleteResult result = Xml.unmarshal(DeleteResult.class, bodyContent); + return new DeleteObjectsResponse( + response.headers(), args.bucket(), args.region(), result); + } catch (IOException e) { + throw new CompletionException(new MinioException(e)); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } + + /** + * Do GetBucketLocation + * S3 API asynchronously. + * + * @param args {@link GetBucketLocationArgs} object. + * @return {@link CompletableFuture}<{@link String}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture getBucketLocation(GetBucketLocationArgs args) + throws MinioException { + checkArgs(args); + return executeAsync( + Http.S3Request.builder() + .userAgent(userAgent) + .method(Http.Method.GET) + .args(args) + .queryParams(new Http.QueryParameters("location", null)) + .build(), + Http.US_EAST_1) + .thenApply( + response -> { + String location; + try (ResponseBody body = response.body()) { + LocationConstraint lc = Xml.unmarshal(LocationConstraint.class, body.charStream()); + if (lc.location() == null || lc.location().equals("")) { + location = Http.US_EAST_1; + } else if (lc.location().equals("EU") && this.baseUrl.awsDomainSuffix() != null) { + location = "eu-west-1"; // eu-west-1 is also referred as 'EU'. + } else { + location = lc.location(); + } + } catch (XmlParserException e) { + throw new CompletionException(e); + } + + regionCache.put(args.bucket(), location); + return location; + }); + } + + /** + * Do HeadObject S3 + * API asynchronously. + * + * @param args {@link HeadObjectArgs} object. + * @return {@link CompletableFuture}<{@link HeadObjectResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture headObject(HeadObjectArgs args) + throws MinioException { + checkArgs(args); + args.validateSsec(baseUrl.isHttps()); + return executeHeadAsync( + args, + args.makeHeaders(), + (args.versionId() != null) + ? new Http.QueryParameters("versionId", args.versionId()) + : null) + .thenApply( + response -> + new HeadObjectResponse( + response.headers(), args.bucket(), args.region(), args.object())); + } + + /** + * Do ListBuckets + * S3 API asynchronously. + * + * @param args {@link ListBucketsArgs} object. + * @return {@link CompletableFuture}<{@link ListBucketsResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture listBucketsApi(ListBucketsArgs args) + throws MinioException { + checkArgs(args); + + Http.QueryParameters queryParams = new Http.QueryParameters(); + if (args.bucketRegion() != null) queryParams.put("bucket-region", args.bucketRegion()); + queryParams.put( + "max-buckets", Integer.toString(args.maxBuckets() > 0 ? args.maxBuckets() : 10000)); + if (args.prefix() != null) queryParams.put("prefix", args.prefix()); + if (args.continuationToken() != null) { + queryParams.put("continuation-token", args.continuationToken()); + } + + return executeGetAsync(args, null, queryParams) + .thenApply( + response -> { + try { + ListAllMyBucketsResult result = + Xml.unmarshal(ListAllMyBucketsResult.class, response.body().charStream()); + return new ListBucketsResponse(response.headers(), result); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } + + /** + * Do ListMultipartUploads + * S3 API asynchronously. + * + * @param args {@link ListMultipartUploadsArgs} object. + * @return {@link CompletableFuture}<{@link ListMultipartUploadsResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture listMultipartUploads( + ListMultipartUploadsArgs args) throws MinioException { + checkArgs(args); + Http.QueryParameters queryParams = + new Http.QueryParameters( + "uploads", + "", + "delimiter", + (args.delimiter() != null) ? args.delimiter() : "", + "max-uploads", + (args.maxUploads() != null) ? args.maxUploads().toString() : "1000", + "prefix", + (args.prefix() != null) ? args.prefix() : ""); + if (args.encodingType() != null) queryParams.put("encoding-type", args.encodingType()); + if (args.keyMarker() != null) queryParams.put("key-marker", args.keyMarker()); + if (args.uploadIdMarker() != null) queryParams.put("upload-id-marker", args.uploadIdMarker()); + + return executeGetAsync(args, null, queryParams) + .thenApply( + response -> { + try { + ListMultipartUploadsResult result = + Xml.unmarshal(ListMultipartUploadsResult.class, response.body().charStream()); + return new ListMultipartUploadsResponse( + response.headers(), args.bucket(), args.region(), result); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } + + private Http.QueryParameters getCommonListObjectsQueryParams( + String delimiter, String encodingType, Integer maxKeys, String prefix) { + Http.QueryParameters queryParams = + new Http.QueryParameters( + "delimiter", + (delimiter == null) ? "" : delimiter, + "max-keys", + Integer.toString(maxKeys > 0 ? maxKeys : 1000), + "prefix", + (prefix == null) ? "" : prefix); + if (encodingType != null) queryParams.put("encoding-type", encodingType); + return queryParams; + } + + /** + * Do ListObjects + * version 1 S3 API asynchronously. + * + * @param args {@link ListObjectsV1Args} object. + * @return {@link CompletableFuture}<{@link ListObjectsV1Response}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture listObjectsV1(ListObjectsV1Args args) + throws MinioException { + checkArgs(args); + + Http.QueryParameters queryParams = + getCommonListObjectsQueryParams( + args.delimiter(), args.encodingType(), args.maxKeys(), args.prefix()); + if (args.marker() != null) queryParams.put("marker", args.marker()); + + return executeGetAsync(args, null, queryParams) + .thenApply( + response -> { + try { + ListBucketResultV1 result = + Xml.unmarshal(ListBucketResultV1.class, response.body().charStream()); + return new ListObjectsV1Response( + response.headers(), args.bucket(), args.region(), result); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } + + /** + * Do ListObjects + * version 2 S3 API asynchronously. + * + * @param args {@link ListObjectsV2Args} object. + * @return {@link CompletableFuture}<{@link ListObjectsV2Response}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture listObjectsV2(ListObjectsV2Args args) + throws MinioException { + checkArgs(args); + + Http.QueryParameters queryParams = + getCommonListObjectsQueryParams( + args.delimiter(), args.encodingType(), args.maxKeys(), args.prefix()); + if (args.startAfter() != null) queryParams.put("start-after", args.startAfter()); + if (args.continuationToken() != null) + queryParams.put("continuation-token", args.continuationToken()); + if (args.fetchOwner()) queryParams.put("fetch-owner", "true"); + if (args.includeUserMetadata()) queryParams.put("metadata", "true"); + queryParams.put("list-type", "2"); + + return executeGetAsync(args, null, queryParams) + .thenApply( + response -> { + try { + ListBucketResultV2 result = + Xml.unmarshal(ListBucketResultV2.class, response.body().charStream()); + return new ListObjectsV2Response( + response.headers(), args.bucket(), args.region(), result); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } + + /** + * Do ListObjectVersions + * API asynchronously. + * + * @param args {@link ListObjectVersionsArgs} object. + * @return {@link CompletableFuture}<{@link ListObjectVersionsResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture listObjectVersions( + ListObjectVersionsArgs args) throws MinioException { + checkArgs(args); + + Http.QueryParameters queryParams = + getCommonListObjectsQueryParams( + args.delimiter(), args.encodingType(), args.maxKeys(), args.prefix()); + if (args.keyMarker() != null) queryParams.put("key-marker", args.keyMarker()); + if (args.versionIdMarker() != null) { + queryParams.put("version-id-marker", args.versionIdMarker()); + } + queryParams.put("versions", ""); + + return executeGetAsync(args, null, queryParams) + .thenApply( + response -> { + try { + ListVersionsResult result = + Xml.unmarshal(ListVersionsResult.class, response.body().charStream()); + return new ListObjectVersionsResponse( + response.headers(), args.bucket(), args.region(), result); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } + + /** + * Do ListParts S3 + * API asynchronously. + * + * @param args {@link ListPartsArgs} object. + * @return {@link CompletableFuture}<{@link ListPartsResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture listParts(ListPartsArgs args) throws MinioException { + Http.QueryParameters queryParams = + new Http.QueryParameters( + UPLOAD_ID, + args.uploadId(), + "max-parts", + (args.maxParts() != null) ? args.maxParts().toString() : "1000"); + if (args.partNumberMarker() != null) { + queryParams.put("part-number-marker", args.partNumberMarker().toString()); + } + + return executeGetAsync(args, null, queryParams) + .thenApply( + response -> { + try { + ListPartsResult result = + Xml.unmarshal(ListPartsResult.class, response.body().charStream()); + return new ListPartsResponse( + response.headers(), args.bucket(), args.region(), args.object(), result); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } + + private Object[] createBody(PutObjectAPIBaseArgs args, MediaType contentType) + throws MinioException { + Http.Headers headers = new Http.Headers(args.headers()); + String sha256HexString = headers.getFirst(Http.Headers.X_AMZ_CONTENT_SHA256); + String sha256Base64String = headers.getFirst(Http.Headers.X_AMZ_CHECKSUM_SHA256); + boolean checksumHeader = headers.namePrefixAny("x-amz-checksum-"); + String md5Hash = headers.getFirst(Http.Headers.CONTENT_MD5); + + if (sha256HexString == null && sha256Base64String == null) { + if (!baseUrl.isHttps()) { + Checksum.Hasher hasher = Checksum.Algorithm.SHA256.hasher(); + Map hashers = new HashMap<>(); + hashers.put(Checksum.Algorithm.SHA256, hasher); + if (args.file() != null) { + Checksum.update(hashers, args.file(), args.length()); + } else if (args.buffer() != null) { + Checksum.update(hashers, args.buffer()); + } else if (args.data() != null) { + Checksum.update(hashers, args.data(), args.length().intValue()); + } + byte[] sum = hasher.sum(); + sha256HexString = Checksum.hexString(sum); + } else { + sha256HexString = Checksum.UNSIGNED_PAYLOAD; + } + headers.put(Http.Headers.X_AMZ_CONTENT_SHA256, sha256HexString); + } + + if (sha256HexString == null && sha256Base64String != null) { + sha256HexString = Checksum.UNSIGNED_PAYLOAD; + if (!baseUrl.isHttps()) { + sha256HexString = Checksum.hexString(Checksum.base64StringToSum(sha256Base64String)); + } + headers.put(Http.Headers.X_AMZ_CONTENT_SHA256, sha256HexString); + } + + if (sha256HexString != null + && sha256Base64String == null + && !checksumHeader + && md5Hash == null) { + if (Checksum.UNSIGNED_PAYLOAD.equals(sha256HexString)) { + Checksum.Hasher hasher = Checksum.Algorithm.CRC32C.hasher(); + Map hashers = new HashMap<>(); + hashers.put(Checksum.Algorithm.CRC32C, hasher); + if (args.file() != null) { + Checksum.update(hashers, args.file(), args.length()); + } else if (args.buffer() != null) { + Checksum.update(hashers, args.buffer()); + } else if (args.data() != null) { + Checksum.update(hashers, args.data(), args.length().intValue()); + } + byte[] sum = hasher.sum(); + headers.put(Checksum.Algorithm.CRC32C.header(), Checksum.base64String(sum)); + headers.put( + Http.Headers.X_AMZ_SDK_CHECKSUM_ALGORITHM, Checksum.Algorithm.CRC32C.toString()); + } else { + sha256Base64String = Checksum.base64String(Checksum.hexStringToSum(sha256HexString)); + headers.put(Http.Headers.X_AMZ_CHECKSUM_SHA256, sha256Base64String); + headers.put( + Http.Headers.X_AMZ_SDK_CHECKSUM_ALGORITHM, Checksum.Algorithm.SHA256.toString()); + } + } + + Http.Body body = null; + if (args.file() != null) { + body = new Http.Body(args.file(), args.length(), contentType, sha256HexString, md5Hash); + } else if (args.buffer() != null) { + body = new Http.Body(args.buffer(), contentType, sha256HexString, md5Hash); + } else if (args.data() != null) { + body = + new Http.Body( + args.data(), args.length().intValue(), contentType, sha256HexString, md5Hash); + } else { + throw new InternalException("unknown body found; this should not happen"); + } + + return new Object[] {body, headers}; + } + + /** + * Do PutObject S3 + * API asynchronously. + * + * @param args {@link PutObjectAPIArgs} object. + * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture putObject(PutObjectAPIArgs args) + throws MinioException { + checkArgs(args); + + Object[] result = createBody(args, args.contentType()); + Http.Body body = (Http.Body) result[0]; + Http.Headers headers = (Http.Headers) result[1]; + return executePutAsync(args, headers, null, body) + .thenApply( + response -> { + try { + return new ObjectWriteResponse( + response.headers(), + args.bucket(), + args.region(), + args.object(), + response.header("ETag").replaceAll("\"", ""), + response.header("x-amz-version-id")); + } finally { + response.close(); + } + }); + } + + /** + * Do UploadPart S3 + * API asynchronously. + * + * @param args {@link UploadPartArgs} object. + * @return {@link CompletableFuture}<{@link UploadPartResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture uploadPart(UploadPartArgs args) + throws MinioException { + checkArgs(args); + + Object[] result = createBody(args, null); + Http.Body body = (Http.Body) result[0]; + Http.Headers headers = (Http.Headers) result[1]; + return executePutAsync( + args, + headers, + new Http.QueryParameters( + "partNumber", Integer.toString(args.partNumber()), "uploadId", args.uploadId()), + body) + .thenApply( + response -> { + try { + return new UploadPartResponse( + response.headers(), + args.bucket(), + args.region(), + args.object(), + args.uploadId(), + args.partNumber(), + response.header("ETag").replaceAll("\"", "")); + } finally { + response.close(); + } + }); + } + + /** + * Do UploadPartCopy + * S3 API. + * + * @param args {@link UploadPartCopyArgs} object. + * @return {@link CompletableFuture}<{@link UploadPartCopyResponse}> object. + * @throws MinioException thrown to indicate SDK exception. + */ + public CompletableFuture uploadPartCopy(UploadPartCopyArgs args) + throws MinioException { + checkArgs(args); + return executePutAsync( + args, + args.headers(), + new Http.QueryParameters( + "partNumber", Integer.toString(args.partNumber()), "uploadId", args.uploadId()), + null) + .thenApply( + response -> { + try { + CopyPartResult result = + Xml.unmarshal(CopyPartResult.class, response.body().charStream()); + return new UploadPartCopyResponse( + response.headers(), + args.bucket(), + args.region(), + args.object(), + args.uploadId(), + args.partNumber(), + result); + } catch (XmlParserException e) { + throw new CompletionException(e); + } finally { + response.close(); + } + }); + } +} diff --git a/api/src/main/java/io/minio/BucketArgs.java b/api/src/main/java/io/minio/BucketArgs.java index c50bcdfda..940215c1a 100644 --- a/api/src/main/java/io/minio/BucketArgs.java +++ b/api/src/main/java/io/minio/BucketArgs.java @@ -16,8 +16,6 @@ package io.minio; -import io.minio.http.HttpUtils; -import io.minio.org.apache.commons.validator.routines.InetAddressValidator; import java.util.Objects; import java.util.regex.Pattern; @@ -26,6 +24,14 @@ public abstract class BucketArgs extends BaseArgs { protected String bucketName; protected String region; + protected BucketArgs() {} + + protected BucketArgs(BucketArgs args) { + super(args); + this.bucketName = args.bucketName; + this.region = args.region; + } + public String bucket() { return bucketName; } @@ -42,7 +48,7 @@ public abstract static class Builder, A extends BucketAr protected boolean skipValidation = false; protected void validateBucketName(String name) { - validateNotNull(name, "bucket name"); + Utils.validateNotNull(name, "bucket name"); if (skipValidation) { return; } @@ -55,7 +61,7 @@ protected void validateBucketName(String name) { + "https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html"); } - if (InetAddressValidator.getInstance().isValidInet4Address(name)) { + if (Utils.isValidIPv4(name)) { throw new IllegalArgumentException( "bucket name '" + name + "' must not be formatted as an IP address"); } @@ -67,7 +73,7 @@ protected void validateBucketName(String name) { } private void validateRegion(String region) { - if (!skipValidation && region != null && !HttpUtils.REGION_REGEX.matcher(region).find()) { + if (!skipValidation && region != null && !Utils.REGION_REGEX.matcher(region).find()) { throw new IllegalArgumentException("invalid region " + region); } } diff --git a/api/src/main/java/io/minio/ByteBuffer.java b/api/src/main/java/io/minio/ByteBuffer.java new file mode 100644 index 000000000..c359b8bb1 --- /dev/null +++ b/api/src/main/java/io/minio/ByteBuffer.java @@ -0,0 +1,128 @@ +/* + * 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 java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.SequenceInputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ByteBuffer extends OutputStream { + private static class Buffer extends ByteArrayOutputStream { + public InputStream inputStream() { + return (count > 0) ? new ByteArrayInputStream(buf, 0, count) : null; + } + } + + private static final long MAX_SIZE = 5L * 1024 * 1024 * 1024; + private static final int CHUNK_SIZE = Integer.MAX_VALUE; + private final long totalSize; + private final List buffers = new ArrayList<>(); + private int index = 0; + private long writtenBytes = 0; + private boolean isClosed = false; + + public ByteBuffer(long totalSize) { + if (totalSize > MAX_SIZE) { + throw new IllegalArgumentException("Total size cannot exceed 5GiB"); + } + this.totalSize = totalSize; + } + + private void updateIndex() { + if (buffers.isEmpty()) { + index = 0; + buffers.add(new Buffer()); + } else if (writtenBytes >= (long) (index + 1) * CHUNK_SIZE) { + index++; + if (index > buffers.size() - 1) buffers.add(new Buffer()); + } + } + + @Override + public void write(int b) throws IOException { + if (isClosed) throw new IOException("Stream is closed"); + if (writtenBytes >= totalSize) throw new IOException("Exceeded total size limit"); + updateIndex(); + buffers.get(index).write(b); + writtenBytes++; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (isClosed) throw new IOException("Stream is closed"); + if (len > (totalSize - writtenBytes)) throw new IOException("Exceeded total size limit"); + int remaining = len; + while (remaining > 0) { + updateIndex(); + Buffer currentBuffer = buffers.get(index); + int bytesToWrite = Math.min(remaining, CHUNK_SIZE - currentBuffer.size()); + currentBuffer.write(b, off + (len - remaining), bytesToWrite); + writtenBytes += bytesToWrite; + remaining -= bytesToWrite; + } + } + + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + public long length() { + return writtenBytes; + } + + public long size() { + return totalSize; + } + + public void reset() throws IOException { + if (isClosed) throw new IOException("Cannot reset a closed stream"); + writtenBytes = 0; + index = 0; + for (Buffer buffer : buffers) buffer.reset(); + } + + public void close() throws IOException { + if (!isClosed) { + isClosed = true; + buffers.clear(); + } + } + + public InputStream inputStream() { + List streams = new ArrayList<>(); + for (Buffer buffer : buffers) { + InputStream stream = buffer.inputStream(); + if (stream != null) streams.add(stream); + } + switch (streams.size()) { + case 0: + return new ByteArrayInputStream(Utils.EMPTY_BODY); + case 1: + return streams.get(0); + default: + return new SequenceInputStream(Collections.enumeration(streams)); + } + } +} diff --git a/api/src/main/java/io/minio/ByteBufferPool.java b/api/src/main/java/io/minio/ByteBufferPool.java new file mode 100644 index 000000000..d3be22053 --- /dev/null +++ b/api/src/main/java/io/minio/ByteBufferPool.java @@ -0,0 +1,58 @@ +/* + * 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 java.io.IOException; +import java.lang.ref.SoftReference; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class ByteBufferPool { + private final ConcurrentLinkedQueue> pool = + new ConcurrentLinkedQueue<>(); + private final int maxSize; + private final long bufferSize; + + public ByteBufferPool(int maxSize, long bufferSize) { + this.maxSize = maxSize; + this.bufferSize = bufferSize; + } + + public ByteBuffer get() { + while (!pool.isEmpty()) { + SoftReference ref = pool.poll(); + if (ref != null) { + ByteBuffer buffer = ref.get(); + if (buffer != null) { + return buffer; + } + // SoftReference cleared + } + } + return new ByteBuffer(bufferSize); + } + + public void put(ByteBuffer buffer) throws IOException { + if (buffer == null) return; + + buffer.reset(); // Reset buffer before reuse + + if (pool.size() < maxSize) { + pool.offer(new SoftReference<>(buffer)); + } + } +} diff --git a/api/src/main/java/io/minio/Checksum.java b/api/src/main/java/io/minio/Checksum.java new file mode 100644 index 000000000..b6aa4eddc --- /dev/null +++ b/api/src/main/java/io/minio/Checksum.java @@ -0,0 +1,578 @@ +/* + * 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.errors.MinioException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** Collection of checksum algorithms. */ +public class Checksum { + // MD5 hash of zero length byte array. + public static final String ZERO_MD5_HASH = "1B2M2Y8AsgTpgAmY7PhCfg=="; + // SHA-256 hash of zero length byte array. + public static final String ZERO_SHA256_HASH = + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + public static final String UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD"; + + public static String base64String(byte[] sum) { + return Base64.getEncoder().encodeToString(sum); + } + + public static byte[] base64StringToSum(String sum) { + return Base64.getDecoder().decode(sum); + } + + public static String hexString(byte[] sum) { + StringBuilder builder = new StringBuilder(); + for (byte b : sum) builder.append(String.format("%02x", b)); + return builder.toString(); + } + + public static byte[] hexStringToSum(String sum) { + byte[] data = new byte[sum.length() / 2]; + for (int i = 0; i < sum.length(); i += 2) { + data[i / 2] = + (byte) + ((Character.digit(sum.charAt(i), 16) << 4) + Character.digit(sum.charAt(i + 1), 16)); + } + return data; + } + + public static Map newHasherMap(Algorithm[] algorithms) throws MinioException { + Map hashers = null; + if (algorithms != null) { + for (Checksum.Algorithm algorithm : algorithms) { + if (algorithm != null) { + if (hashers == null) hashers = new HashMap<>(); + if (!hashers.containsKey(algorithm)) { + hashers.put(algorithm, algorithm.hasher()); + } + } + } + } + return (hashers == null || hashers.size() == 0) ? null : hashers; + } + + private static void update( + Set> entries, + ExecutorService executor, + byte[] data, + int length) { + for (Map.Entry entry : entries) { + final Hasher hasher = entry.getValue(); + if (executor != null) { + if (executor.submit(() -> hasher.update(data, 0, length)) == null) { + throw new RuntimeException("this should not happen"); + } + } else { + hasher.update(data, 0, length); + } + } + } + + public static void update(Map hashers, byte[] data, int length) { + if (hashers == null || hashers.size() == 0) return; + + Set> entries = hashers.entrySet(); + ExecutorService executor = null; + if (entries.size() != 1) executor = Executors.newFixedThreadPool(hashers.size()); + + update(entries, executor, data, length); + if (executor != null) { + executor.shutdown(); + try { + executor.awaitTermination(5, TimeUnit.MINUTES); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + private static void update( + Map hashers, ByteBuffer buffer, RandomAccessFile file, Long size) + throws MinioException { + if (hashers == null || hashers.size() == 0) return; + + InputStream stream = null; + if (buffer != null) { + stream = buffer.inputStream(); + size = buffer.length(); + } + + Set> entries = hashers.entrySet(); + ExecutorService executor = null; + if (entries.size() != 1) executor = Executors.newFixedThreadPool(hashers.size()); + + byte[] buf16k = new byte[16384]; + long bytesRead = 0; + while (bytesRead != size) { + try { + int length = (int) Math.min(size - bytesRead, buf16k.length); + int n = file != null ? file.read(buf16k, 0, length) : stream.read(buf16k, 0, length); + if (n < 0) throw new MinioException("unexpected EOF"); + if (n != 0) { + bytesRead += n; + update(entries, executor, buf16k, n); + } + } catch (IOException e) { + throw new MinioException(e); + } + } + + if (executor != null) { + executor.shutdown(); + try { + executor.awaitTermination(5, TimeUnit.MINUTES); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + public static void update(Map hashers, RandomAccessFile file, long size) + throws MinioException { + update(hashers, null, file, size); + } + + public static void update(Map hashers, ByteBuffer buffer) + throws MinioException { + update(hashers, buffer, null, null); + } + + public static Http.Headers makeHeaders( + Map hashers, boolean computeSha256, boolean sha256Algorithm) { + if (hashers == null) return null; + + Http.Headers checksumHeaders = new Http.Headers(); + for (Map.Entry entry : hashers.entrySet()) { + byte[] sum = entry.getValue().sum(); + if (entry.getKey() == Checksum.Algorithm.SHA256) { + if (computeSha256) { + checksumHeaders.put(Http.Headers.X_AMZ_CONTENT_SHA256, Checksum.hexString(sum)); + } + if (!sha256Algorithm) continue; + } + checksumHeaders.put("x-amz-sdk-checksum-algorithm", entry.getKey().toString()); + checksumHeaders.put(entry.getKey().header(), Checksum.base64String(sum)); + } + + return checksumHeaders; + } + + /** Algorithm type. */ + public static enum Type { + COMPOSITE, + FULL_OBJECT; + } + + /** Checksum algorithm. */ + public static enum Algorithm { + CRC32, + CRC32C, + CRC64NVME, + SHA1, + SHA256, + MD5; + + @Override + public String toString() { + return name().toLowerCase(Locale.US); + } + + public String header() { + if (this == MD5) return Http.Headers.CONTENT_MD5; + return "x-amz-checksum-" + name().toLowerCase(Locale.US); + } + + public boolean fullObjectSupport() { + return this == CRC32 || this == CRC32C || this == CRC64NVME; + } + + public boolean compositeSupport() { + return this == CRC32 || this == CRC32C || this == SHA1 || this == SHA256; + } + + public void validate(Type type) { + if (!(compositeSupport() && type == Type.COMPOSITE + || fullObjectSupport() && type == Type.FULL_OBJECT)) { + throw new IllegalArgumentException( + "algorithm " + name() + " does not support " + type + " type"); + } + } + + public Hasher hasher() throws MinioException { + if (this == CRC32) return new CRC32(); + if (this == CRC32C) return new CRC32C(); + if (this == CRC64NVME) return new CRC64NVME(); + if (this == SHA1) return new SHA1(); + if (this == SHA256) return new SHA256(); + if (this == MD5) return new MD5(); + return null; + } + } + + public abstract static class Hasher extends OutputStream { + public abstract void update(byte[] b, int off, int len); + + public abstract byte[] sum(); + + public abstract void reset(); + + public void update(byte[] b) { + update(b, 0, b.length); + } + + @Override + public void write(int b) { + update(new byte[] {(byte) b}); + } + + @Override + public void write(byte[] b) { + update(b); + } + + @Override + public void write(byte[] b, int off, int len) { + update(b, off, len); + } + } + + /** CRC32 checksum is java.util.zip.CRC32 compatible to Hasher. */ + public static class CRC32 extends Hasher { + private java.util.zip.CRC32 hasher; + + public CRC32() { + hasher = new java.util.zip.CRC32(); + } + + @Override + public void update(byte[] b, int off, int len) { + hasher.update(b, off, len); + } + + @Override + public void update(byte[] b) { + hasher.update(b); + } + + @Override + public byte[] sum() { + int value = (int) hasher.getValue(); + return new byte[] { + (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value + }; + } + + @Override + public void reset() { + hasher.reset(); + } + + @Override + public String toString() { + return "CRC32{" + hexString(sum()) + "}"; + } + } + + /** CRC32C checksum. */ + public static class CRC32C extends Hasher implements java.util.zip.Checksum { + private static final int[] CRC32C_TABLE = new int[256]; + private int crc = 0xFFFFFFFF; + + static { + for (int i = 0; i < 256; i++) { + int crc = i; + for (int j = 0; j < 8; j++) { + crc = (crc >>> 1) ^ ((crc & 1) != 0 ? 0x82F63B78 : 0); + } + CRC32C_TABLE[i] = crc; + } + } + + @Override + public void update(int b) { + crc = CRC32C_TABLE[(crc ^ b) & 0xFF] ^ (crc >>> 8); + } + + @Override + public void update(byte[] b, int off, int len) { + for (int i = off; i < off + len; i++) { + update(b[i]); + } + } + + @Override + public long getValue() { + return (crc ^ 0xFFFFFFFFL) & 0xFFFFFFFFL; + } + + @Override + public void reset() { + crc = 0xFFFFFFFF; + } + + @Override + public byte[] sum() { + int value = (int) this.getValue(); + return new byte[] { + (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value + }; + } + + @Override + public String toString() { + return "CRC32C{" + hexString(sum()) + "}"; + } + } + + /** CRC64NVME checksum logic copied from https://github.com/minio/crc64nvme. */ + public static class CRC64NVME extends Hasher implements java.util.zip.Checksum { + private static final long[] CRC64_TABLE = new long[256]; + private static final long[][] SLICING8_TABLE_NVME = new long[8][256]; + + static { + long polynomial = 0x9A6C9329AC4BC9B5L; + for (int i = 0; i < 256; i++) { + long crc = i; + for (int j = 0; j < 8; j++) { + if ((crc & 1) == 1) { + crc = (crc >>> 1) ^ polynomial; + } else { + crc >>>= 1; + } + } + CRC64_TABLE[i] = crc; + } + + SLICING8_TABLE_NVME[0] = CRC64_TABLE; + for (int i = 0; i < 256; i++) { + long crc = CRC64_TABLE[i]; + for (int j = 1; j < 8; j++) { + crc = CRC64_TABLE[(int) crc & 0xFF] ^ (crc >>> 8); + SLICING8_TABLE_NVME[j][i] = crc; + } + } + } + + private long crc = 0; + + public CRC64NVME() {} + + @Override + public void update(byte[] p, int off, int len) { + java.nio.ByteBuffer byteBuffer = java.nio.ByteBuffer.wrap(p, off, len); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + int offset = byteBuffer.position(); + + crc = ~crc; + while (p.length >= 64 && (p.length - offset) > 8) { + long value = byteBuffer.getLong(); + crc ^= value; + crc = + SLICING8_TABLE_NVME[7][(int) (crc & 0xFF)] + ^ SLICING8_TABLE_NVME[6][(int) ((crc >>> 8) & 0xFF)] + ^ SLICING8_TABLE_NVME[5][(int) ((crc >>> 16) & 0xFF)] + ^ SLICING8_TABLE_NVME[4][(int) ((crc >>> 24) & 0xFF)] + ^ SLICING8_TABLE_NVME[3][(int) ((crc >>> 32) & 0xFF)] + ^ SLICING8_TABLE_NVME[2][(int) ((crc >>> 40) & 0xFF)] + ^ SLICING8_TABLE_NVME[1][(int) ((crc >>> 48) & 0xFF)] + ^ SLICING8_TABLE_NVME[0][(int) (crc >>> 56)]; + offset = byteBuffer.position(); + } + + for (; offset < len; offset++) { + crc = CRC64_TABLE[(int) ((crc ^ (long) p[offset]) & 0xFF)] ^ (crc >>> 8); + } + + crc = ~crc; + } + + @Override + public void update(int b) { + update(new byte[] {(byte) b}, 0, 1); + } + + @Override + public long getValue() { + return crc; + } + + @Override + public void reset() { + crc = 0; + } + + @Override + public byte[] sum() { + long value = this.getValue(); + return new byte[] { + (byte) (value >>> 56), + (byte) (value >>> 48), + (byte) (value >>> 40), + (byte) (value >>> 32), + (byte) (value >>> 24), + (byte) (value >>> 16), + (byte) (value >>> 8), + (byte) value + }; + } + + @Override + public String toString() { + return "CRC64NVME{" + hexString(sum()) + "}"; + } + } + + public static class SHA1 extends Hasher { + MessageDigest hasher; + + public SHA1() throws MinioException { + try { + this.hasher = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new MinioException(e); + } + } + + public void update(byte[] b, int off, int len) { + hasher.update(b, off, len); + } + + public void update(byte[] b) { + hasher.update(b); + } + + public byte[] sum() { + return hasher.digest(); + } + + public void reset() { + hasher.reset(); + } + + @Override + public String toString() { + return "SHA1{" + hexString(sum()) + "}"; + } + } + + public static class SHA256 extends Hasher { + MessageDigest hasher; + + public SHA256() throws MinioException { + try { + this.hasher = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new MinioException(e); + } + } + + public void update(byte[] b, int off, int len) { + hasher.update(b, off, len); + } + + public void update(byte[] b) { + hasher.update(b); + } + + public byte[] sum() { + return hasher.digest(); + } + + public void reset() { + hasher.reset(); + } + + @Override + public String toString() { + return "SHA256{" + hexString(sum()) + "}"; + } + + public static byte[] sum(byte[] b, int off, int len) throws MinioException { + SHA256 sha256 = new SHA256(); + sha256.update(b, off, len); + return sha256.sum(); + } + + public static byte[] sum(byte[] b) throws MinioException { + return sum(b, 0, b.length); + } + + public static byte[] sum(String value) throws MinioException { + return sum(value.getBytes(StandardCharsets.UTF_8)); + } + } + + public static class MD5 extends Hasher { + MessageDigest hasher; + + public MD5() throws MinioException { + try { + this.hasher = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new MinioException(e); + } + } + + public void update(byte[] b, int off, int len) { + hasher.update(b, off, len); + } + + public void update(byte[] b) { + hasher.update(b); + } + + public byte[] sum() { + return hasher.digest(); + } + + public void reset() { + hasher.reset(); + } + + @Override + public String toString() { + return "MD5{" + base64String(sum()) + "}"; + } + + public static byte[] sum(byte[] b, int off, int len) throws MinioException { + MD5 md5 = new MD5(); + md5.update(b, off, len); + return md5.sum(); + } + + public static byte[] sum(byte[] b) throws MinioException { + return sum(b, 0, b.length); + } + } +} diff --git a/api/src/main/java/io/minio/CompleteMultipartUploadArgs.java b/api/src/main/java/io/minio/CompleteMultipartUploadArgs.java new file mode 100644 index 000000000..95530f69c --- /dev/null +++ b/api/src/main/java/io/minio/CompleteMultipartUploadArgs.java @@ -0,0 +1,93 @@ +/* + * 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.Part; +import java.util.Arrays; +import java.util.Objects; + +/** + * Argument class of {@link MinioAsyncClient#completeMultipartUpload} and {@link + * MinioClient#completeMultipartUpload}. + */ +public class CompleteMultipartUploadArgs extends ObjectArgs { + private String uploadId; + private Part[] parts; + + protected CompleteMultipartUploadArgs() {} + + public CompleteMultipartUploadArgs(ComposeObjectArgs args, String uploadId, Part[] parts) { + super(args); + this.uploadId = uploadId; + this.parts = parts; + } + + public CompleteMultipartUploadArgs(PutObjectBaseArgs args, String uploadId, Part[] parts) { + super(args); + this.uploadId = uploadId; + this.parts = parts; + } + + public String uploadId() { + return uploadId; + } + + public Part[] parts() { + return parts; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link CompleteMultipartUploadArgs}. */ + public static final class Builder + extends ObjectArgs.Builder { + @Override + protected void validate(CompleteMultipartUploadArgs args) { + super.validate(args); + Utils.validateNotEmptyString(args.uploadId, "upload ID"); + Utils.validateNotNull(args.parts, "parts"); + } + + public Builder uploadId(String uploadId) { + Utils.validateNotEmptyString(uploadId, "upload ID"); + operations.add(args -> args.uploadId = uploadId); + return this; + } + + public Builder parts(Part[] parts) { + Utils.validateNotNull(parts, "parts"); + operations.add(args -> args.parts = parts); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CompleteMultipartUploadArgs)) return false; + if (!super.equals(o)) return false; + CompleteMultipartUploadArgs that = (CompleteMultipartUploadArgs) o; + return Objects.equals(uploadId, that.uploadId) && Arrays.equals(parts, that.parts); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), uploadId, parts); + } +} diff --git a/api/src/main/java/io/minio/ComposeObjectArgs.java b/api/src/main/java/io/minio/ComposeObjectArgs.java index 8f0c42905..a340c2d64 100644 --- a/api/src/main/java/io/minio/ComposeObjectArgs.java +++ b/api/src/main/java/io/minio/ComposeObjectArgs.java @@ -16,36 +16,29 @@ package io.minio; -import java.util.LinkedList; +import java.util.Collections; import java.util.List; import java.util.Objects; -import okhttp3.HttpUrl; /** * Argument class of {@link MinioAsyncClient#composeObject} and {@link MinioClient#composeObject}. */ public class ComposeObjectArgs extends ObjectWriteArgs { - List sources; + List sources; protected ComposeObjectArgs() {} + public ComposeObjectArgs(ComposeObjectArgs args, List sources) { + super(args); + this.sources = sources; + } + public ComposeObjectArgs(CopyObjectArgs args) { - this.extraHeaders = args.extraHeaders; - this.extraQueryParams = args.extraQueryParams; - this.bucketName = args.bucketName; - this.region = args.region; - this.objectName = args.objectName; - this.headers = args.headers; - this.userMetadata = args.userMetadata; - this.sse = args.sse; - this.tags = args.tags; - this.retention = args.retention; - this.legalHold = args.legalHold; - this.sources = new LinkedList<>(); - this.sources.add(new ComposeSource(args.source())); + super(args); + this.sources = Collections.singletonList(args.source()); } - public List sources() { + public List sources() { return sources; } @@ -54,18 +47,18 @@ public static Builder builder() { } @Override - public void validateSse(HttpUrl url) { - super.validateSse(url); - for (ComposeSource source : sources) { - source.validateSsec(url); + public void validateSse(boolean isHttps) { + super.validateSse(isHttps); + for (SourceObject source : sources) { + source.validateSsec(isHttps); } } /** Argument builder of {@link ComposeObjectArgs}. */ public static final class Builder extends ObjectWriteArgs.Builder { - private void validateSources(List sources) { + private void validateSources(List sources) { if (sources == null || sources.isEmpty()) { - throw new IllegalArgumentException("compose sources cannot be empty"); + throw new IllegalArgumentException("source objects cannot be empty"); } } @@ -75,7 +68,7 @@ protected void validate(ComposeObjectArgs args) { validateSources(args.sources); } - public Builder sources(List sources) { + public Builder sources(List sources) { validateSources(sources); operations.add(args -> args.sources = sources); return this; diff --git a/api/src/main/java/io/minio/ComposeSource.java b/api/src/main/java/io/minio/ComposeSource.java deleted file mode 100644 index a9449d3f1..000000000 --- a/api/src/main/java/io/minio/ComposeSource.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, - * (C) 2019 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.Multimap; -import com.google.common.collect.Multimaps; -import io.minio.errors.InternalException; -import java.util.Objects; - -/** A source object defintion for {@link ComposeObjectArgs}. */ -public class ComposeSource extends ObjectConditionalReadArgs { - private Long objectSize = null; - private Multimap headers = null; - - protected ComposeSource() {} - - public ComposeSource(ObjectConditionalReadArgs args) { - this.extraHeaders = args.extraHeaders; - this.extraQueryParams = args.extraQueryParams; - this.bucketName = args.bucketName; - this.region = args.region; - this.objectName = args.objectName; - this.versionId = args.versionId; - this.ssec = args.ssec; - this.offset = args.offset; - this.length = args.length; - this.matchETag = args.matchETag; - this.notMatchETag = args.notMatchETag; - this.modifiedSince = args.modifiedSince; - this.unmodifiedSince = args.unmodifiedSince; - } - - private void throwException(long objectsize, long arg, String argName) { - StringBuilder builder = - new StringBuilder().append("source ").append(bucketName).append("/").append(objectName); - - if (versionId != null) { - builder.append("?versionId=").append(versionId); - } - - builder - .append(": ") - .append(argName) - .append(" ") - .append(arg) - .append(" is beyond object size ") - .append(objectSize); - - throw new IllegalArgumentException(builder.toString()); - } - - private void validateSize(long objectSize) { - if (offset != null && offset >= objectSize) { - throwException(objectSize, offset, "offset"); - } - - if (length != null) { - if (length > objectSize) { - throwException(objectSize, length, "length"); - } - - if (offset + length > objectSize) { - throwException(objectSize, offset + length, "compose size"); - } - } - } - - public void buildHeaders(long objectSize, String etag) { - validateSize(objectSize); - this.objectSize = Long.valueOf(objectSize); - Multimap headers = genCopyHeaders(); - if (!headers.containsKey("x-amz-copy-source-if-match")) { - headers.put("x-amz-copy-source-if-match", etag); - } - this.headers = Multimaps.unmodifiableMultimap(headers); - } - - public long objectSize() throws InternalException { - if (this.objectSize == null) { - throw new InternalException( - "buildHeaders(long objectSize, String etag) must be called prior to this method invocation", - null); - } - - return this.objectSize; - } - - public Multimap headers() throws InternalException { - if (this.headers == null) { - throw new InternalException( - "buildHeaders(long objectSize, String etag) must be called prior to this method invocation", - null); - } - - return this.headers; - } - - public static Builder builder() { - return new Builder(); - } - - /** Argument builder of {@link ComposeSource}. */ - public static final class Builder - extends ObjectConditionalReadArgs.Builder {} - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ComposeSource)) return false; - if (!super.equals(o)) return false; - ComposeSource that = (ComposeSource) o; - return Objects.equals(objectSize, that.objectSize) && Objects.equals(headers, that.headers); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), objectSize, headers); - } -} diff --git a/api/src/main/java/io/minio/CopyObjectArgs.java b/api/src/main/java/io/minio/CopyObjectArgs.java index 2108ab531..c4264fa53 100644 --- a/api/src/main/java/io/minio/CopyObjectArgs.java +++ b/api/src/main/java/io/minio/CopyObjectArgs.java @@ -17,32 +17,28 @@ package io.minio; import java.util.Objects; -import okhttp3.HttpUrl; /** Argument class of {@link MinioAsyncClient#copyObject} and {@link MinioClient#copyObject}. */ public class CopyObjectArgs extends ObjectWriteArgs { - private CopySource source = null; + private SourceObject source; private Directive metadataDirective; private Directive taggingDirective; protected CopyObjectArgs() {} + public CopyObjectArgs(CopyObjectArgs args, SourceObject source) { + super(args); + this.metadataDirective = args.metadataDirective; + this.taggingDirective = args.taggingDirective; + this.source = source; + } + public CopyObjectArgs(ComposeObjectArgs args) { - this.extraHeaders = args.extraHeaders; - this.extraQueryParams = args.extraQueryParams; - this.bucketName = args.bucketName; - this.region = args.region; - this.objectName = args.objectName; - this.headers = args.headers; - this.userMetadata = args.userMetadata; - this.sse = args.sse; - this.tags = args.tags; - this.retention = args.retention; - this.legalHold = args.legalHold; - this.source = new CopySource(args.sources().get(0)); + super(args); + this.source = args.sources().get(0); } - public CopySource source() { + public SourceObject source() { return source; } @@ -59,9 +55,9 @@ public static Builder builder() { } @Override - public void validateSse(HttpUrl url) { - super.validateSse(url); - source.validateSsec(url); + public void validateSse(boolean isHttps) { + super.validateSse(isHttps); + source.validateSsec(isHttps); } /** Argument builder of {@link CopyObjectArgs}. */ @@ -69,21 +65,21 @@ public static final class Builder extends ObjectWriteArgs.Builder args.source = source); return this; } diff --git a/api/src/main/java/io/minio/CopySource.java b/api/src/main/java/io/minio/CopySource.java deleted file mode 100644 index bf3eca2cf..000000000 --- a/api/src/main/java/io/minio/CopySource.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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; - -/** A source object defintion for {@link CopyObjectArgs}. */ -public class CopySource extends ObjectConditionalReadArgs { - protected CopySource() {} - - public CopySource(ObjectConditionalReadArgs args) { - this.extraHeaders = args.extraHeaders; - this.extraQueryParams = args.extraQueryParams; - this.bucketName = args.bucketName; - this.region = args.region; - this.objectName = args.objectName; - this.versionId = args.versionId; - this.ssec = args.ssec; - this.offset = args.offset; - this.length = args.length; - this.matchETag = args.matchETag; - this.notMatchETag = args.notMatchETag; - this.modifiedSince = args.modifiedSince; - this.unmodifiedSince = args.unmodifiedSince; - } - - public static Builder builder() { - return new Builder(); - } - - /** Argument builder of {@link CopySource}. */ - public static final class Builder - extends ObjectConditionalReadArgs.Builder {} -} diff --git a/api/src/main/java/io/minio/messages/DateDays.java b/api/src/main/java/io/minio/CreateBucketArgs.java similarity index 54% rename from api/src/main/java/io/minio/messages/DateDays.java rename to api/src/main/java/io/minio/CreateBucketArgs.java index 03eda56ad..1ebb4703f 100644 --- a/api/src/main/java/io/minio/messages/DateDays.java +++ b/api/src/main/java/io/minio/CreateBucketArgs.java @@ -1,5 +1,5 @@ /* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2020 MinIO, Inc. + * 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. @@ -14,24 +14,21 @@ * limitations under the License. */ -package io.minio.messages; +package io.minio; -import java.time.ZonedDateTime; -import org.simpleframework.xml.Element; +/** Argument class of {@link MinioAsyncClient#createBucket} and {@link MinioClient#createBucket}. */ +public class CreateBucketArgs extends CreateBucketBaseArgs { + protected CreateBucketArgs() {} -/** Base class for {@link Transition} and {@link Expiration}. */ -public abstract class DateDays { - @Element(name = "Date", required = false) - protected ResponseDate date; - - @Element(name = "Days", required = false) - protected Integer days; - - public ZonedDateTime date() { - return (date != null) ? date.zonedDateTime() : null; + public CreateBucketArgs(CreateBucketBaseArgs args) { + super(args); } - public Integer days() { - return days; + public static Builder builder() { + return new Builder(); } + + /** Argument builder of {@link CreateBucketArgs}. */ + public static final class Builder + extends CreateBucketBaseArgs.Builder {} } diff --git a/api/src/main/java/io/minio/CreateBucketBaseArgs.java b/api/src/main/java/io/minio/CreateBucketBaseArgs.java new file mode 100644 index 000000000..b7a54d33d --- /dev/null +++ b/api/src/main/java/io/minio/CreateBucketBaseArgs.java @@ -0,0 +1,88 @@ +/* + * 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.CreateBucketConfiguration; +import java.util.Objects; + +public abstract class CreateBucketBaseArgs extends BucketArgs { + protected boolean objectLock; + protected CreateBucketConfiguration.Location locationConfig; + protected CreateBucketConfiguration.Bucket bucket; + + protected CreateBucketBaseArgs() {} + + protected CreateBucketBaseArgs(CreateBucketBaseArgs args) { + super(args); + this.objectLock = args.objectLock; + this.locationConfig = args.locationConfig; + this.bucket = args.bucket; + } + + public boolean objectLock() { + return objectLock; + } + + public CreateBucketConfiguration.Location locationConfig() { + return locationConfig; + } + + public CreateBucketConfiguration.Bucket bucketConfig() { + return bucket; + } + + /** Base argument builder of {@link CreateBucketBaseArgs}. */ + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public abstract static class Builder, A extends CreateBucketBaseArgs> + extends BucketArgs.Builder { + public B objectLock(boolean objectLock) { + operations.add(args -> args.objectLock = objectLock); + return (B) this; + } + + public B locationConstraint(String region) { + operations.add(args -> args.region = region); + return (B) this; + } + + public B locationConfig(CreateBucketConfiguration.Location locationConfig) { + operations.add(args -> args.locationConfig = locationConfig); + return (B) this; + } + + public B bucketConfig(CreateBucketConfiguration.Bucket bucket) { + operations.add(args -> args.bucket = bucket); + return (B) this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CreateBucketBaseArgs)) return false; + if (!super.equals(o)) return false; + CreateBucketBaseArgs that = (CreateBucketBaseArgs) o; + return objectLock == that.objectLock + && Objects.equals(locationConfig, that.locationConfig) + && Objects.equals(bucket, that.bucket); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), objectLock, locationConfig, bucket); + } +} diff --git a/api/src/main/java/io/minio/CreateMultipartUploadArgs.java b/api/src/main/java/io/minio/CreateMultipartUploadArgs.java new file mode 100644 index 000000000..9867fc944 --- /dev/null +++ b/api/src/main/java/io/minio/CreateMultipartUploadArgs.java @@ -0,0 +1,75 @@ +/* + * 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 java.util.Objects; +import okhttp3.MediaType; + +/** + * Argument class of {@link MinioAsyncClient#createMultipartUpload} and {@link + * MinioClient#createMultipartUpload}. + */ +public class CreateMultipartUploadArgs extends ObjectArgs { + private Http.Headers headers; + + protected CreateMultipartUploadArgs() {} + + public CreateMultipartUploadArgs(PutObjectBaseArgs args, MediaType contentType) { + super(args); + this.headers = args.makeHeaders(contentType); + } + + public CreateMultipartUploadArgs(ComposeObjectArgs args) { + super(args); + this.headers = args.makeHeaders(); + } + + public Http.Headers headers() { + return headers; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link CreateMultipartUploadArgs}. */ + public static final class Builder extends ObjectArgs.Builder { + @Override + protected void validate(CreateMultipartUploadArgs args) { + super.validate(args); + } + + public Builder headers(Http.Headers headers) { + operations.add(args -> args.headers = headers); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CreateMultipartUploadArgs)) return false; + if (!super.equals(o)) return false; + CreateMultipartUploadArgs that = (CreateMultipartUploadArgs) o; + return Objects.equals(headers, that.headers); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), headers); + } +} diff --git a/api/src/main/java/io/minio/DeleteObjectsArgs.java b/api/src/main/java/io/minio/DeleteObjectsArgs.java new file mode 100644 index 000000000..501fcb6b8 --- /dev/null +++ b/api/src/main/java/io/minio/DeleteObjectsArgs.java @@ -0,0 +1,94 @@ +/* + * 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.DeleteRequest; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * Argument class of {@link MinioAsyncClient#deleteObjects} and {@link MinioClient#deleteObjects}. + */ +public class DeleteObjectsArgs extends BucketArgs { + private boolean quiet; + private boolean bypassGovernanceMode; + private List objects = new LinkedList<>(); + + public boolean quiet() { + return quiet; + } + + public boolean bypassGovernanceMode() { + return bypassGovernanceMode; + } + + public List objects() { + return objects; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link DeleteObjectsArgs}. */ + public static final class Builder extends BucketArgs.Builder { + @Override + protected void validate(DeleteObjectsArgs args) { + super.validate(args); + Utils.validateNotNull(args.objects, "objects"); + if (args.objects.size() > 1000) { + throw new IllegalArgumentException("list of objects must not be more than 1000"); + } + } + + public Builder quiet(boolean flag) { + operations.add(args -> args.quiet = flag); + return this; + } + + public Builder bypassGovernanceMode(boolean flag) { + operations.add(args -> args.bypassGovernanceMode = flag); + return this; + } + + public Builder objects(List objects) { + Utils.validateNotNull(objects, "objects"); + if (objects.size() > 1000) { + throw new IllegalArgumentException("list of objects must not be more than 1000"); + } + operations.add(args -> args.objects = objects); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof DeleteObjectsArgs)) return false; + if (!super.equals(o)) return false; + DeleteObjectsArgs that = (DeleteObjectsArgs) o; + return quiet == that.quiet + && bypassGovernanceMode == that.bypassGovernanceMode + && Objects.equals(objects, that.objects); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), quiet, bypassGovernanceMode, objects); + } +} diff --git a/api/src/main/java/io/minio/Digest.java b/api/src/main/java/io/minio/Digest.java deleted file mode 100644 index 22d13134c..000000000 --- a/api/src/main/java/io/minio/Digest.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2015 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.io.BaseEncoding; -import io.minio.errors.InsufficientDataException; -import io.minio.errors.InternalException; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; -import java.util.Locale; - -/** Various global static functions used. */ -public class Digest { - // MD5 hash of zero length byte array. - public static final String ZERO_MD5_HASH = "1B2M2Y8AsgTpgAmY7PhCfg=="; - // SHA-256 hash of zero length byte array. - public static final String ZERO_SHA256_HASH = - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; - - /** Private constructor. */ - private Digest() {} - - /** Returns MD5 hash of byte array. */ - public static String md5Hash(byte[] data, int length) throws NoSuchAlgorithmException { - MessageDigest md5Digest = MessageDigest.getInstance("MD5"); - md5Digest.update(data, 0, length); - return Base64.getEncoder().encodeToString(md5Digest.digest()); - } - - /** Returns SHA-256 hash of byte array. */ - public static String sha256Hash(byte[] data, int length) throws NoSuchAlgorithmException { - MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256"); - sha256Digest.update((byte[]) data, 0, length); - return BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase(Locale.US); - } - - /** Returns SHA-256 hash of given string. */ - public static String sha256Hash(String string) throws NoSuchAlgorithmException { - byte[] data = string.getBytes(StandardCharsets.UTF_8); - return sha256Hash(data, data.length); - } - - /** - * Returns SHA-256 and MD5 hashes of given data and it's length. - * - * @param data must be {@link RandomAccessFile}, {@link BufferedInputStream} or byte array. - * @param len length of data to be read for hash calculation. - * @deprecated This method is no longer supported. - */ - @Deprecated - public static String[] sha256Md5Hashes(Object data, int len) - throws NoSuchAlgorithmException, IOException, InsufficientDataException, InternalException { - MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256"); - MessageDigest md5Digest = MessageDigest.getInstance("MD5"); - - if (data instanceof BufferedInputStream || data instanceof RandomAccessFile) { - updateDigests(data, len, sha256Digest, md5Digest); - } else if (data instanceof byte[]) { - sha256Digest.update((byte[]) data, 0, len); - md5Digest.update((byte[]) data, 0, len); - } else { - throw new InternalException( - "Unknown data source to calculate SHA-256 hash. This should not happen, " - + "please report this issue at https://github.com/minio/minio-java/issues", - null); - } - - return new String[] { - BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase(Locale.US), - BaseEncoding.base64().encode(md5Digest.digest()) - }; - } - - /** Updated MessageDigest with bytes read from file and stream. */ - private static int updateDigests( - Object inputStream, int len, MessageDigest sha256Digest, MessageDigest md5Digest) - throws IOException, InsufficientDataException { - RandomAccessFile file = null; - BufferedInputStream stream = null; - if (inputStream instanceof RandomAccessFile) { - file = (RandomAccessFile) inputStream; - } else if (inputStream instanceof BufferedInputStream) { - stream = (BufferedInputStream) inputStream; - } - - // hold current position of file/stream to reset back to this position. - long pos = 0; - if (file != null) { - pos = file.getFilePointer(); - } else { - stream.mark(len); - } - - // 16KiB buffer for optimization - byte[] buf = new byte[16384]; - int bytesToRead = buf.length; - int bytesRead = 0; - int totalBytesRead = 0; - while (totalBytesRead < len) { - if ((len - totalBytesRead) < bytesToRead) { - bytesToRead = len - totalBytesRead; - } - - if (file != null) { - bytesRead = file.read(buf, 0, bytesToRead); - } else { - bytesRead = stream.read(buf, 0, bytesToRead); - } - - if (bytesRead < 0) { - // reached EOF - throw new InsufficientDataException( - "Insufficient data. bytes read " + totalBytesRead + " expected " + len); - } - - if (bytesRead > 0) { - if (sha256Digest != null) { - sha256Digest.update(buf, 0, bytesRead); - } - - if (md5Digest != null) { - md5Digest.update(buf, 0, bytesRead); - } - - totalBytesRead += bytesRead; - } - } - - // reset back to saved position. - if (file != null) { - file.seek(pos); - } else { - stream.reset(); - } - - return totalBytesRead; - } -} diff --git a/api/src/main/java/io/minio/DownloadObjectArgs.java b/api/src/main/java/io/minio/DownloadObjectArgs.java index eb9fa6daf..e2d066567 100644 --- a/api/src/main/java/io/minio/DownloadObjectArgs.java +++ b/api/src/main/java/io/minio/DownloadObjectArgs.java @@ -40,7 +40,7 @@ public static Builder builder() { /** Argument class of {@link DownloadObjectArgs}. */ public static final class Builder extends ObjectReadArgs.Builder { private void validateFilename(String filename) { - validateNotEmptyString(filename, "filename"); + Utils.validateNotEmptyString(filename, "filename"); } public Builder filename(String filename) { diff --git a/api/src/main/java/io/minio/messages/ParquetInputSerialization.java b/api/src/main/java/io/minio/GetBucketLocationArgs.java similarity index 60% rename from api/src/main/java/io/minio/messages/ParquetInputSerialization.java rename to api/src/main/java/io/minio/GetBucketLocationArgs.java index d40e85960..5d7184e52 100644 --- a/api/src/main/java/io/minio/messages/ParquetInputSerialization.java +++ b/api/src/main/java/io/minio/GetBucketLocationArgs.java @@ -1,5 +1,5 @@ /* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 MinIO, Inc. + * 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. @@ -14,13 +14,17 @@ * limitations under the License. */ -package io.minio.messages; - -import org.simpleframework.xml.Root; +package io.minio; /** - * Helper class to denote Parquet input serialization request XML as per {@link - * SelectObjectContentRequest}. + * Argument class of {@link MinioAsyncClient#getBucketLocation} and {@link + * MinioClient#getBucketLocation}. */ -@Root(name = "Parquet") -public class ParquetInputSerialization {} +public class GetBucketLocationArgs extends BucketArgs { + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link GetBucketLocationArgs}. */ + public static final class Builder extends BucketArgs.Builder {} +} diff --git a/api/src/main/java/io/minio/GetObjectAttributesArgs.java b/api/src/main/java/io/minio/GetObjectAttributesArgs.java index 19bd9bbba..143275af1 100644 --- a/api/src/main/java/io/minio/GetObjectAttributesArgs.java +++ b/api/src/main/java/io/minio/GetObjectAttributesArgs.java @@ -51,7 +51,7 @@ public static final class Builder @Override protected void validate(GetObjectAttributesArgs args) { super.validate(args); - validateNotNull(args.objectAttributes, "object attributes"); + Utils.validateNotNull(args.objectAttributes, "object attributes"); } public Builder objectAttributes(String[] objectAttributes) { diff --git a/api/src/main/java/io/minio/GetPresignedObjectUrlArgs.java b/api/src/main/java/io/minio/GetPresignedObjectUrlArgs.java index a6e3d2a23..ed418ecdd 100644 --- a/api/src/main/java/io/minio/GetPresignedObjectUrlArgs.java +++ b/api/src/main/java/io/minio/GetPresignedObjectUrlArgs.java @@ -16,7 +16,6 @@ package io.minio; -import io.minio.http.Method; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -28,10 +27,10 @@ public class GetPresignedObjectUrlArgs extends ObjectVersionArgs { // default expiration for a presigned URL is 7 days in seconds public static final int DEFAULT_EXPIRY_TIME = (int) TimeUnit.DAYS.toSeconds(7); - private Method method; + private Http.Method method; private int expiry = DEFAULT_EXPIRY_TIME; - public Method method() { + public Http.Method method() { return method; } @@ -46,8 +45,8 @@ public static Builder builder() { /** Argument builder of {@link GetPresignedObjectUrlArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder { - private void validateMethod(Method method) { - validateNotNull(method, "method"); + private void validateMethod(Http.Method method) { + Utils.validateNotNull(method, "method"); } private void validateExpiry(int expiry) { @@ -60,7 +59,7 @@ private void validateExpiry(int expiry) { } /* method HTTP {@link Method} to generate presigned URL. */ - public Builder method(Method method) { + public Builder method(Http.Method method) { validateMethod(method); operations.add(args -> args.method = method); return this; diff --git a/api/src/main/java/io/minio/ServerSideEncryptionS3.java b/api/src/main/java/io/minio/HeadObjectArgs.java similarity index 52% rename from api/src/main/java/io/minio/ServerSideEncryptionS3.java rename to api/src/main/java/io/minio/HeadObjectArgs.java index 4a2615ee7..3ca62f0ec 100644 --- a/api/src/main/java/io/minio/ServerSideEncryptionS3.java +++ b/api/src/main/java/io/minio/HeadObjectArgs.java @@ -1,5 +1,5 @@ /* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2020 MinIO, Inc. + * 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. @@ -16,31 +16,26 @@ package io.minio; -import java.util.HashMap; -import java.util.Map; +/** Argument class of {@link MinioAsyncClient#headObject} and {@link MinioClient#headObject}. */ +public class HeadObjectArgs extends HeadObjectBaseArgs { + protected HeadObjectArgs() {} -/** S3 type of Server-side encryption. */ -public class ServerSideEncryptionS3 extends ServerSideEncryption { - private static final Map headers; - - static { - Map map = new HashMap<>(); - map.put("X-Amz-Server-Side-Encryption", "AES256"); - headers = Utils.unmodifiableMap(map); + public HeadObjectArgs(SourceObject args) { + super(args); } - @Override - public final Map headers() { - return headers; + public HeadObjectArgs(DownloadObjectArgs args) { + super(args); } - @Override - public final boolean tlsRequired() { - return false; + public HeadObjectArgs(HeadObjectBaseArgs args) { + super(args); } - @Override - public String toString() { - return "SSE-S3"; + public static Builder builder() { + return new Builder(); } + + /** Argument builder of {@link HeadObjectArgs}. */ + public static final class Builder extends HeadObjectBaseArgs.Builder {} } diff --git a/api/src/main/java/io/minio/messages/QueueConfiguration.java b/api/src/main/java/io/minio/HeadObjectBaseArgs.java similarity index 50% rename from api/src/main/java/io/minio/messages/QueueConfiguration.java rename to api/src/main/java/io/minio/HeadObjectBaseArgs.java index 2123b6505..caf9d66e0 100644 --- a/api/src/main/java/io/minio/messages/QueueConfiguration.java +++ b/api/src/main/java/io/minio/HeadObjectBaseArgs.java @@ -1,5 +1,5 @@ /* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2017 MinIO, Inc. + * 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. @@ -14,28 +14,25 @@ * limitations under the License. */ -package io.minio.messages; +package io.minio; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; +/** Base argument class holds HEAD object. */ +public abstract class HeadObjectBaseArgs extends ObjectConditionalReadArgs { + protected HeadObjectBaseArgs() {} -/** Helper class to denote Queue configuration of {@link NotificationConfiguration}. */ -@Root(name = "QueueConfiguration", strict = false) -public class QueueConfiguration extends NotificationCommonConfiguration { - @Element(name = "Queue") - private String queue; - - public QueueConfiguration() { - super(); + protected HeadObjectBaseArgs(SourceObject args) { + super(args); } - /** Returns queue. */ - public String queue() { - return queue; + protected HeadObjectBaseArgs(DownloadObjectArgs args) { + super(args); } - /** Sets queue. */ - public void setQueue(String queue) { - this.queue = queue; + protected HeadObjectBaseArgs(HeadObjectBaseArgs args) { + super(args); } + + /** Base argument builder class for {@link HeadObjectBaseArgs}. */ + public abstract static class Builder, A extends HeadObjectBaseArgs> + extends ObjectConditionalReadArgs.Builder {} } diff --git a/api/src/main/java/io/minio/HeadObjectResponse.java b/api/src/main/java/io/minio/HeadObjectResponse.java new file mode 100644 index 000000000..784e99707 --- /dev/null +++ b/api/src/main/java/io/minio/HeadObjectResponse.java @@ -0,0 +1,125 @@ +/* + * 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.LegalHold; +import io.minio.messages.RetentionMode; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** Response of {@link S3Base#headObjectAsync}. */ +public class HeadObjectResponse extends GenericResponse { + private String etag; + private long size; + private ZonedDateTime lastModified; + private RetentionMode retentionMode; + private ZonedDateTime retentionRetainUntilDate; + private LegalHold legalHold; + private boolean deleteMarker; + private Http.Headers userMetadata; + + public HeadObjectResponse(okhttp3.Headers headers, String bucket, String region, String object) { + super(headers, bucket, region, object); + String value; + + value = headers.get("ETag"); + this.etag = (value != null ? value.replaceAll("\"", "") : ""); + + value = headers.get(Http.Headers.CONTENT_LENGTH); + this.size = (value != null ? Long.parseLong(value) : -1); + + this.lastModified = + ZonedDateTime.parse(headers.get("Last-Modified"), Time.HTTP_HEADER_DATE_FORMAT); + + value = headers.get("x-amz-object-lock-mode"); + this.retentionMode = (value != null ? RetentionMode.valueOf(value) : null); + + value = headers.get("x-amz-object-lock-retain-until-date"); + this.retentionRetainUntilDate = + value == null ? null : Time.S3Time.fromString(value).toZonedDateTime(); + + this.legalHold = new LegalHold("ON".equals(headers.get("x-amz-object-lock-legal-hold"))); + + this.deleteMarker = Boolean.parseBoolean(headers.get("x-amz-delete-marker")); + + Http.Headers userMetadata = new Http.Headers(); + for (Map.Entry> entry : headers.toMultimap().entrySet()) { + String lowerName = entry.getKey().toLowerCase(Locale.US); + if (lowerName.startsWith("x-amz-meta-")) { + userMetadata.putAll( + lowerName.substring("x-amz-meta-".length(), lowerName.length()), entry.getValue()); + } + } + this.userMetadata = userMetadata; + } + + public String etag() { + return etag; + } + + public long size() { + return size; + } + + public ZonedDateTime lastModified() { + return lastModified; + } + + public RetentionMode retentionMode() { + return retentionMode; + } + + public ZonedDateTime retentionRetainUntilDate() { + return retentionRetainUntilDate; + } + + public LegalHold legalHold() { + return legalHold; + } + + public boolean deleteMarker() { + return deleteMarker; + } + + public String versionId() { + return this.headers().get("x-amz-version-id"); + } + + public String contentType() { + return this.headers().get(Http.Headers.CONTENT_TYPE); + } + + public Http.Headers userMetadata() { + return userMetadata; + } + + @Override + public String toString() { + return "ObjectHead{" + + "bucket=" + + bucket() + + ", object=" + + object() + + ", last-modified=" + + lastModified + + ", size=" + + size + + "}"; + } +} diff --git a/api/src/main/java/io/minio/Http.java b/api/src/main/java/io/minio/Http.java new file mode 100644 index 000000000..a43f3bd79 --- /dev/null +++ b/api/src/main/java/io/minio/Http.java @@ -0,0 +1,1443 @@ +/* + * 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 com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import io.minio.credentials.Credentials; +import io.minio.errors.MinioException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.time.ZonedDateTime; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okio.BufferedSink; +import okio.Okio; + +/** HTTP utilities. */ +public class Http { + public static final MediaType DEFAULT_MEDIA_TYPE = MediaType.parse("application/octet-stream"); + public static final MediaType XML_MEDIA_TYPE = MediaType.parse("application/xml"); + public static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json"); + public static final String US_EAST_1 = "us-east-1"; + public static final long DEFAULT_TIMEOUT = TimeUnit.MINUTES.toMillis(5); + public static final byte[] ZERO_BODY = new byte[0]; + public static final Body EMPTY_BODY = + new Body(ZERO_BODY, 0, DEFAULT_MEDIA_TYPE, Checksum.ZERO_SHA256_HASH, Checksum.ZERO_MD5_HASH); + + public static class BaseUrl { + private okhttp3.HttpUrl url; + private String awsS3Prefix; + private String awsDomainSuffix; + private boolean awsDualstack; + private String region; + private boolean useVirtualStyle; + + public BaseUrl(String endpoint) { + setUrl(parse(endpoint)); + } + + public BaseUrl(String endpoint, int port, boolean secure) { + okhttp3.HttpUrl url = parse(endpoint); + if (port < 1 || port > 65535) { + throw new IllegalArgumentException("port must be in range of 1 to 65535"); + } + url = url.newBuilder().port(port).scheme(secure ? "https" : "http").build(); + + setUrl(url); + } + + public BaseUrl(okhttp3.HttpUrl url) { + Utils.validateNotNull(url, "url"); + Utils.validateUrl(url); + setUrl(url); + } + + public BaseUrl(URL url) { + Utils.validateNotNull(url, "url"); + setUrl(okhttp3.HttpUrl.get(url)); + } + + private void setAwsInfo(String host, boolean https) { + this.awsS3Prefix = null; + this.awsDomainSuffix = null; + this.awsDualstack = false; + + if (!Utils.HOSTNAME_REGEX.matcher(host).find()) return; + + if (Utils.AWS_ELB_ENDPOINT_REGEX.matcher(host).find()) { + String[] tokens = host.split("\\.elb\\.amazonaws\\.com", 1)[0].split("\\."); + this.region = tokens[tokens.length - 1]; + return; + } + + if (!Utils.AWS_ENDPOINT_REGEX.matcher(host).find()) return; + + if (!Utils.AWS_S3_ENDPOINT_REGEX.matcher(host).find()) { + throw new IllegalArgumentException("invalid Amazon AWS host " + host); + } + + Matcher matcher = Utils.AWS_S3_PREFIX_REGEX.matcher(host); + matcher.lookingAt(); + int end = matcher.end(); + + this.awsS3Prefix = host.substring(0, end); + if (this.awsS3Prefix.contains("s3-accesspoint") && !https) { + throw new IllegalArgumentException("use HTTPS scheme for host " + host); + } + + String[] tokens = host.substring(end).split("\\."); + awsDualstack = "dualstack".equals(tokens[0]); + if (awsDualstack) tokens = Arrays.copyOfRange(tokens, 1, tokens.length); + String regionInHost = null; + if (!tokens[0].equals("vpce") && !tokens[0].equals("amazonaws")) { + regionInHost = tokens[0]; + tokens = Arrays.copyOfRange(tokens, 1, tokens.length); + } + this.awsDomainSuffix = String.join(".", tokens); + + if (host.equals("s3-external-1.amazonaws.com")) regionInHost = "us-east-1"; + if (host.equals("s3-us-gov-west-1.amazonaws.com") + || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { + regionInHost = "us-gov-west-1"; + } + + if (regionInHost != null) this.region = regionInHost; + } + + private void setUrl(okhttp3.HttpUrl url) { + this.url = url; + this.setAwsInfo(url.host(), url.isHttps()); + this.useVirtualStyle = this.awsDomainSuffix != null || url.host().endsWith("aliyuncs.com"); + } + + private okhttp3.HttpUrl parse(String endpoint) { + Utils.validateNotEmptyString(endpoint, "endpoint"); + okhttp3.HttpUrl url = okhttp3.HttpUrl.parse(endpoint); + if (url == null) { + Utils.validateHostnameOrIPAddress(endpoint); + url = new okhttp3.HttpUrl.Builder().scheme("https").host(endpoint).build(); + } else { + Utils.validateUrl(url); + } + return url; + } + + public boolean isHttps() { + return url.isHttps(); + } + + public String awsS3Prefix() { + return awsS3Prefix; + } + + public String awsDomainSuffix() { + return awsDomainSuffix; + } + + public String region() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + /** Enables dual-stack endpoint for Amazon S3 endpoint. */ + public void enableDualStackEndpoint() { + awsDualstack = true; + } + + /** Disables dual-stack endpoint for Amazon S3 endpoint. */ + public void disableDualStackEndpoint() { + awsDualstack = false; + } + + /** Enables virtual-style endpoint. */ + public void enableVirtualStyleEndpoint() { + useVirtualStyle = true; + } + + /** Disables virtual-style endpoint. */ + public void disableVirtualStyleEndpoint() { + useVirtualStyle = false; + } + + /** Sets AWS S3 domain prefix. */ + public void setAwsS3Prefix(@Nonnull String awsS3Prefix) { + if (awsS3Prefix == null) + throw new IllegalArgumentException("null Amazon AWS S3 domain prefix"); + if (!Utils.AWS_S3_PREFIX_REGEX.matcher(awsS3Prefix).find()) { + throw new IllegalArgumentException("invalid Amazon AWS S3 domain prefix " + awsS3Prefix); + } + this.awsS3Prefix = awsS3Prefix; + } + + private String buildAwsUrl( + okhttp3.HttpUrl.Builder builder, + String bucketName, + boolean enforcePathStyle, + String region) { + String host = this.awsS3Prefix + this.awsDomainSuffix; + if (host.equals("s3-external-1.amazonaws.com") + || host.equals("s3-us-gov-west-1.amazonaws.com") + || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { + builder.host(host); + return host; + } + + host = this.awsS3Prefix; + if (this.awsS3Prefix.contains("s3-accelerate")) { + if (bucketName.contains(".")) { + throw new IllegalArgumentException( + "bucket name '" + bucketName + "' with '.' is not allowed for accelerate endpoint"); + } + if (enforcePathStyle) host = host.replaceFirst("-accelerate", ""); + } + + if (this.awsDualstack) host += "dualstack."; + if (!this.awsS3Prefix.contains("s3-accelerate")) host += region + "."; + host += this.awsDomainSuffix; + + builder.host(host); + return host; + } + + private String buildListBucketsUrl(okhttp3.HttpUrl.Builder builder, String region) { + if (this.awsDomainSuffix == null) return null; + + String host = this.awsS3Prefix + this.awsDomainSuffix; + if (host.equals("s3-external-1.amazonaws.com") + || host.equals("s3-us-gov-west-1.amazonaws.com") + || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { + builder.host(host); + return host; + } + + String s3Prefix = this.awsS3Prefix; + String domainSuffix = this.awsDomainSuffix; + if (this.awsS3Prefix.startsWith("s3.") || this.awsS3Prefix.startsWith("s3-")) { + s3Prefix = "s3."; + domainSuffix = "amazonaws.com" + (domainSuffix.endsWith(".cn") ? ".cn" : ""); + } + + host = s3Prefix + region + "." + domainSuffix; + builder.host(host); + return host; + } + + /** Build URL for given parameters. */ + public okhttp3.HttpUrl buildUrl( + Method method, + String bucketName, + String objectName, + String region, + QueryParameters queryParams) + throws MinioException { + if (bucketName == null && objectName != null) { + throw new IllegalArgumentException("null bucket name for object '" + objectName + "'"); + } + + okhttp3.HttpUrl.Builder urlBuilder = this.url.newBuilder(); + + if (queryParams != null) { + for (Map.Entry entry : queryParams.entries()) { + urlBuilder.addEncodedQueryParameter( + Utils.encode(entry.getKey()), Utils.encode(entry.getValue())); + } + } + + if (bucketName == null) { + this.buildListBucketsUrl(urlBuilder, region); + return urlBuilder.build(); + } + + boolean enforcePathStyle = ( + // use path style for make bucket to workaround "AuthorizationHeaderMalformed" error from + // s3.amazonaws.com + (method == Method.PUT && objectName == null && queryParams == null) + + // use path style for location query + || (queryParams != null && queryParams.containsKey("location")) + + // use path style where '.' in bucketName causes SSL certificate validation error + || (bucketName.contains(".") && this.url.isHttps())); + + String host = this.url.host(); + if (this.awsDomainSuffix != null) { + host = this.buildAwsUrl(urlBuilder, bucketName, enforcePathStyle, region); + } + + if (enforcePathStyle || !this.useVirtualStyle) { + urlBuilder.addEncodedPathSegment(Utils.encode(bucketName)); + } else { + urlBuilder.host(bucketName + "." + host); + } + + if (objectName != null) { + urlBuilder.addEncodedPathSegments(Utils.encodePath(objectName)); + } + + return urlBuilder.build(); + } + + @Override + public String toString() { + return url.toString(); + } + } + + public static MediaType mediaType(String value) { + if (value == null) return DEFAULT_MEDIA_TYPE; + MediaType mediaType = MediaType.parse(value); + if (mediaType == null) { + throw new IllegalArgumentException( + "invalid media/content type '" + value + "' as per RFC 2045"); + } + return mediaType; + } + + private static OkHttpClient enableJKSPKCS12Certificates( + OkHttpClient httpClient, + String trustStorePath, + String trustStorePassword, + String keyStorePath, + String keyStorePassword, + String keyStoreType) + throws MinioException { + try { + if (trustStorePath == null || trustStorePath.isEmpty()) { + throw new IllegalArgumentException("trust store path must be provided"); + } + if (trustStorePassword == null) { + throw new IllegalArgumentException("trust store password must be provided"); + } + if (keyStorePath == null || keyStorePath.isEmpty()) { + throw new IllegalArgumentException("key store path must be provided"); + } + if (keyStorePassword == null) { + throw new IllegalArgumentException("key store password must be provided"); + } + + SSLContext sslContext = SSLContext.getInstance("TLS"); + KeyStore trustStore = KeyStore.getInstance("JKS"); + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + try (FileInputStream trustInput = new FileInputStream(trustStorePath); + FileInputStream keyInput = new FileInputStream(keyStorePath); ) { + trustStore.load(trustInput, trustStorePassword.toCharArray()); + keyStore.load(keyInput, keyStorePassword.toCharArray()); + } + TrustManagerFactory trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(trustStore); + + KeyManagerFactory keyManagerFactory = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keyStore, keyStorePassword.toCharArray()); + + sslContext.init( + keyManagerFactory.getKeyManagers(), + trustManagerFactory.getTrustManagers(), + new java.security.SecureRandom()); + + return httpClient + .newBuilder() + .sslSocketFactory( + sslContext.getSocketFactory(), + (X509TrustManager) trustManagerFactory.getTrustManagers()[0]) + .build(); + } catch (GeneralSecurityException | IOException e) { + throw new MinioException(e); + } + } + + public static OkHttpClient enableJKSCertificates( + OkHttpClient httpClient, + String trustStorePath, + String trustStorePassword, + String keyStorePath, + String keyStorePassword) + throws MinioException { + return enableJKSPKCS12Certificates( + httpClient, trustStorePath, trustStorePassword, keyStorePath, keyStorePassword, "JKS"); + } + + public static OkHttpClient enablePKCS12Certificates( + OkHttpClient httpClient, + String trustStorePath, + String trustStorePassword, + String keyStorePath, + String keyStorePassword) + throws MinioException { + return enableJKSPKCS12Certificates( + httpClient, trustStorePath, trustStorePassword, keyStorePath, keyStorePassword, "PKCS12"); + } + + /** + * copied logic from + * https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java + */ + public static OkHttpClient enableExternalCertificates(OkHttpClient httpClient, String filename) + throws MinioException { + try { + Collection certificates = null; + try (FileInputStream fis = new FileInputStream(filename)) { + certificates = CertificateFactory.getInstance("X.509").generateCertificates(fis); + } + + if (certificates == null || certificates.isEmpty()) { + throw new IllegalArgumentException("expected non-empty set of trusted certificates"); + } + + char[] password = "password".toCharArray(); // Any password will work. + + // Put the certificates a key store. + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + // By convention, 'null' creates an empty key store. + keyStore.load(null, password); + + int index = 0; + for (Certificate certificate : certificates) { + String certificateAlias = Integer.toString(index++); + keyStore.setCertificateEntry(certificateAlias, certificate); + } + + // Use it to build an X509 trust manager. + KeyManagerFactory keyManagerFactory = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keyStore, password); + TrustManagerFactory trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(keyStore); + + final KeyManager[] keyManagers = keyManagerFactory.getKeyManagers(); + final TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, null); + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + return httpClient + .newBuilder() + .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0]) + .build(); + } catch (GeneralSecurityException | IOException e) { + throw new MinioException(e); + } + } + + public static OkHttpClient newDefaultClient() { + OkHttpClient httpClient = + new OkHttpClient() + .newBuilder() + .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS) + .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS) + .readTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS) + .protocols(Arrays.asList(Protocol.HTTP_1_1)) + .build(); + String filename = System.getenv("SSL_CERT_FILE"); + if (filename != null && !filename.isEmpty()) { + try { + httpClient = enableExternalCertificates(httpClient, filename); + } catch (MinioException e) { + throw new RuntimeException(e); + } + } + return httpClient; + } + + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "SIC", + justification = "Should not be used in production anyways.") + public static OkHttpClient disableCertCheck(OkHttpClient client) throws MinioException { + try { + final TrustManager[] trustAllCerts = + new TrustManager[] { + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException {} + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException {} + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[] {}; + } + } + }; + + final SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + return client + .newBuilder() + .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]) + .hostnameVerifier( + new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }) + .build(); + } catch (KeyManagementException | NoSuchAlgorithmException e) { + throw new MinioException(e); + } + } + + public static OkHttpClient setTimeout( + OkHttpClient client, long connectTimeout, long writeTimeout, long readTimeout) { + return client + .newBuilder() + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) + .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS) + .readTimeout(readTimeout, TimeUnit.MILLISECONDS) + .build(); + } + + public static class Body { + private okhttp3.RequestBody requestBody; + private RandomAccessFile file; + private ByteBuffer buffer; + private byte[] data; + private Long length; + private MediaType contentType; + private String sha256Hash; + private String md5Hash; + private boolean bodyString; + + public Body(okhttp3.RequestBody requestBody) { + this.requestBody = requestBody; + this.contentType = requestBody.contentType(); + } + + public Body( + RandomAccessFile file, + long length, + MediaType contentType, + String sha256Hash, + String md5Hash) { + if (length < 0) throw new IllegalArgumentException("valid length must be provided"); + this.file = file; + set(length, contentType, sha256Hash, md5Hash); + } + + public Body(byte[] data, int length, MediaType contentType, String sha256Hash, String md5Hash) { + if (length < 0) throw new IllegalArgumentException("valid length must be provided"); + this.data = data; + set((long) length, contentType, sha256Hash, md5Hash); + } + + public Body(Object body, MediaType contentType, String sha256Hash, String md5Hash) + throws MinioException { + if (body instanceof ByteBuffer) { + this.buffer = (ByteBuffer) body; + set(null, contentType, sha256Hash, md5Hash); + return; + } + + byte[] data = null; + if (body instanceof CharSequence) { + data = ((CharSequence) body).toString().getBytes(StandardCharsets.UTF_8); + } else { + // For any other object, do XML marshalling. + data = Xml.marshal(body).getBytes(StandardCharsets.UTF_8); + contentType = XML_MEDIA_TYPE; + } + sha256Hash = Checksum.hexString(Checksum.SHA256.sum(data)); + md5Hash = Checksum.base64String(Checksum.MD5.sum(data)); + + this.bodyString = true; + this.data = data; + set((long) data.length, contentType, sha256Hash, md5Hash); + } + + private void set(Long length, MediaType contentType, String sha256Hash, String md5Hash) { + this.length = length; + this.contentType = contentType == null ? DEFAULT_MEDIA_TYPE : contentType; + this.sha256Hash = sha256Hash; + this.md5Hash = md5Hash; + } + + public MediaType contentType() { + return contentType; + } + + public String sha256Hash() { + return sha256Hash; + } + + public String md5Hash() { + return md5Hash; + } + + public boolean isHttpRequestBody() { + return requestBody != null; + } + + public Headers headers() { + Headers headers = new Headers(Headers.CONTENT_TYPE, contentType.toString()); + if (sha256Hash != null) headers.put(Headers.X_AMZ_CONTENT_SHA256, sha256Hash); + if (md5Hash != null) headers.put(Headers.CONTENT_MD5, md5Hash); + return headers; + } + + public RequestBody toRequestBody() throws MinioException { + if (requestBody != null) return new RequestBody(requestBody); + if (file != null) return new RequestBody(file, length, contentType); + if (buffer != null) return new RequestBody(buffer, contentType); + return new RequestBody(data, length.intValue(), contentType); + } + + @Override + public String toString() { + return bodyString ? new String(data, StandardCharsets.UTF_8) : "<<>>"; + } + } + + /** RequestBody that wraps a single data object. */ + public static class RequestBody extends okhttp3.RequestBody { + private okhttp3.RequestBody body; + private ByteBuffer buffer; + private RandomAccessFile file; + private long position; + private byte[] bytes; + private long length; + private MediaType contentType; + + public RequestBody( + @Nonnull final byte[] bytes, final int length, @Nonnull final MediaType contentType) { + this.bytes = Utils.validateNotNull(bytes, "data bytes"); + if (length < 0) throw new IllegalArgumentException("length must not be negative value"); + this.length = length; + this.contentType = Utils.validateNotNull(contentType, "content type"); + } + + public RequestBody( + @Nonnull final RandomAccessFile file, + final long length, + @Nonnull final MediaType contentType) + throws MinioException { + this.file = Utils.validateNotNull(file, "randome access file"); + if (length < 0) throw new IllegalArgumentException("length must not be negative value"); + this.length = length; + this.contentType = Utils.validateNotNull(contentType, "content type"); + try { + this.position = file.getFilePointer(); + } catch (IOException e) { + throw new MinioException(e); + } + } + + public RequestBody(@Nonnull final ByteBuffer buffer, @Nonnull final MediaType contentType) { + this.buffer = Utils.validateNotNull(buffer, "buffer"); + this.length = buffer.length(); + this.contentType = Utils.validateNotNull(contentType, "content type"); + } + + public RequestBody(@Nonnull final okhttp3.RequestBody body) throws MinioException { + try { + this.body = Utils.validateNotNull(body, "body"); + if (body.contentLength() < 0) { + throw new IllegalArgumentException("length must not be negative value"); + } + this.length = body.contentLength(); + this.contentType = Utils.validateNotNull(body.contentType(), "content type"); + } catch (IOException e) { + throw new MinioException(e); + } + } + + @Override + public MediaType contentType() { + return contentType; + } + + @Override + public long contentLength() { + return length; + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + if (body != null) { + body.writeTo(sink); + } else if (buffer != null) { + sink.write(Okio.source(buffer.inputStream()), length); + } else if (file != null) { + file.seek(position); + sink.write(Okio.source(Channels.newInputStream(file.getChannel())), length); + } else { + sink.write(bytes, 0, (int) length); + } + } + } + + /** HTTP methods. */ + public static enum Method { + GET, + HEAD, + POST, + PUT, + DELETE; + } + + public static class Headers implements Iterable> { + private static final long serialVersionUID = -8099023918647559669L; + + public static final String ACCEPT_ENCODING = "Accept-Encoding"; + public static final String AUTHORIZATION = "Authorization"; + public static final String CONTENT_ENCODING = "Content-Encoding"; + public static final String CONTENT_LENGTH = "Content-Length"; + public static final String CONTENT_MD5 = "Content-Md5"; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String HOST = "Host"; + public static final String USER_AGENT = "User-Agent"; + public static final String X_AMZ_CHECKSUM_SHA256 = "x-amz-checksum-sha256"; + public static final String X_AMZ_CONTENT_SHA256 = "X-Amz-Content-Sha256"; + public static final String X_AMZ_COPY_SOURCE_RANGE = "x-amz-copy-source-range"; + public static final String X_AMZ_DATE = "X-Amz-Date"; + public static final String X_AMZ_SDK_CHECKSUM_ALGORITHM = "X-Amz-Sdk-Checksum-Algorithm"; + public static final String X_AMZ_SECURITY_TOKEN = "X-Amz-Security-Token"; + + private static final Set NON_EMPTY_HEADERS = + ImmutableSet.of( + ACCEPT_ENCODING, + AUTHORIZATION, + CONTENT_ENCODING, + CONTENT_LENGTH, + CONTENT_MD5, + CONTENT_TYPE, + HOST, + USER_AGENT, + X_AMZ_CHECKSUM_SHA256, + X_AMZ_CONTENT_SHA256, + X_AMZ_DATE, + X_AMZ_SDK_CHECKSUM_ALGORITHM, + X_AMZ_SECURITY_TOKEN); + private final Map> headers = new HashMap<>(); + + // Normalize header names to Title-Case + private String normalize(String name) { + String[] parts = name.toLowerCase(Locale.US).split("-", -1); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < parts.length; i++) { + String part = parts[i]; + if (!part.isEmpty()) { + sb.append(Character.toUpperCase(part.charAt(0))); + if (part.length() > 1) { + sb.append(part.substring(1)); + } + } + if (i < parts.length - 1) { + sb.append("-"); + } + } + return sb.toString(); + } + + public Headers() {} + + public Headers(Headers source) { + if (source != null) putAll(source); + } + + public Headers(Map source) { + if (source != null) putAll(source); + } + + public Headers(Multimap source) { + if (source != null) putAll(source); + } + + public Headers(String... keysAndValues) { + if (keysAndValues.length % 2 != 0) { + throw new IllegalArgumentException("Expected alternating keys and values"); + } + for (int i = 0; i < keysAndValues.length; i += 2) { + set(keysAndValues[i], keysAndValues[i + 1]); + } + } + + public static Headers merge(Headers... headersList) { + Headers headers = new Headers(); + for (Headers h : headersList) headers.putAll(h); + return headers; + } + + private String validateName(String name) { + if (!Utils.validateNotNull(name, "name").trim().equals(name)) { + throw new IllegalArgumentException("leading/trailing spaces are not allowed in name"); + } + if (name.isEmpty()) throw new IllegalArgumentException("name must not be empty"); + return normalize(name); + } + + private String validateValue(String name, String value) { + Utils.validateNotNull(value, "value"); + if (NON_EMPTY_HEADERS.contains(name) && value.isEmpty()) { + throw new IllegalArgumentException("value must not be empty for name " + name); + } + if (CONTENT_TYPE.equals(name) && MediaType.parse(value) == null) { + throw new IllegalArgumentException("invalid content type '" + value + "' as per RFC 2045"); + } + return value; + } + + public void add(@Nonnull String name, @Nonnull String value) { + name = validateName(name); + value = validateValue(name, value); + headers.computeIfAbsent(name, k -> new HashSet<>()).add(value); + } + + public void set(@Nonnull String name, @Nonnull String value) { + name = validateName(name); + value = validateValue(name, value); + headers.put(name, new HashSet<>(Collections.singletonList(value))); + } + + public String getFirst(String name) { + if (name == null) return null; + Set values = get(name); + return values.isEmpty() ? null : values.iterator().next(); + } + + public boolean contains(String name) { + if (name == null) return false; + return headers.containsKey(normalize(name)); + } + + public boolean namePrefixAny(String prefix) { + if (prefix == null) return false; + final String finalPrefix = normalize(prefix); + return names().stream().anyMatch(name -> name.startsWith(finalPrefix)); + } + + public Set names() { + return Collections.unmodifiableSet(headers.keySet()); + } + + @Override + public Iterator> iterator() { + return entrySet().iterator(); + } + + @Override + public String toString() { + return headers.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Headers)) return false; + Headers headers = (Headers) o; + return Objects.equals(this.headers, headers.headers); + } + + @Override + public int hashCode() { + return Objects.hash(headers); + } + + public okhttp3.Headers toHttpHeaders() { + okhttp3.Headers.Builder builder = new okhttp3.Headers.Builder(); + if (containsKey(CONTENT_ENCODING)) { + builder.add( + CONTENT_ENCODING, + get(CONTENT_ENCODING).stream() + .distinct() + .filter(encoding -> !encoding.isEmpty()) + .collect(Collectors.joining(","))); + } + + for (Map.Entry entry : entries()) { + if (!entry.getKey().equals(CONTENT_ENCODING)) { + builder.addUnsafeNonAscii(entry.getKey(), entry.getValue()); + } + } + + return builder.build(); + } + + public void clear() { + headers.clear(); + } + + public boolean containsKey(String name) { + if (name == null) return false; + return headers.containsKey(normalize(name)); + } + + public Set> entrySet() { + Set> result = new LinkedHashSet<>(); + for (Map.Entry> entry : headers.entrySet()) { + for (String value : entry.getValue()) { + result.add(new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), value)); + } + } + return result; + } + + public Set> entries() { + return entrySet(); + } + + public Set get(String name) { + if (name == null) name = ""; + return headers.getOrDefault(normalize(name), Collections.emptySet()); + } + + public Set keySet() { + return names(); + } + + public boolean put(@Nonnull String name, @Nonnull String value) { + add(name, value); + return true; + } + + public Headers putAll(Map source) { + if (source == null) return this; + for (Map.Entry entry : source.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + public Headers putAll(Multimap source) { + if (source == null) return this; + for (Map.Entry entry : source.entries()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + public Headers putAll(Headers source) { + if (source == null) return this; + for (Map.Entry entry : source.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + public Headers putAll(@Nonnull String name, @Nonnull Set values) { + Utils.validateNotNull(values, "value"); + for (String value : values) put(name, value); + return this; + } + + public Headers putAll(@Nonnull String name, @Nonnull List values) { + Utils.validateNotNull(values, "value"); + for (String value : values) put(name, value); + return this; + } + + public Set remove(String name) { + if (name == null) name = ""; + return headers.remove(normalize(name)); + } + + public boolean remove(String name, String value) { + if (name == null) return false; + name = normalize(name); + return headers.containsKey(name) ? headers.get(name).remove(value) : false; + } + + public boolean removeAll(String name) { + return remove(name) != null; + } + + public int size() { + return headers.size(); + } + } + + public static class QueryParameters implements Iterable> { + private static final long serialVersionUID = 5193347714796984439L; + + private final Map> parameters = new HashMap<>(); + + public QueryParameters() {} + + public QueryParameters(QueryParameters source) { + if (source != null) putAll(source); + } + + public QueryParameters(Map source) { + if (source != null) putAll(source); + } + + public QueryParameters(Multimap source) { + if (source != null) putAll(source); + } + + public QueryParameters(String... keysAndValues) { + if (keysAndValues.length % 2 != 0) { + throw new IllegalArgumentException("Expected alternating keys and values"); + } + for (int i = 0; i < keysAndValues.length; i += 2) { + set(keysAndValues[i], keysAndValues[i + 1]); + } + } + + public static QueryParameters merge(QueryParameters... queryParamsList) { + QueryParameters queryParams = new QueryParameters(); + for (QueryParameters q : queryParamsList) queryParams.putAll(q); + return queryParams; + } + + public void add(@Nonnull String key, @Nonnull String value) { + Utils.validateNotEmptyString(key, "key"); + if (value == null) value = ""; + parameters.computeIfAbsent(key, k -> new ArrayList<>()).add(value); + } + + public void set(@Nonnull String key, @Nonnull String value) { + Utils.validateNotEmptyString(key, "key"); + if (value == null) value = ""; + parameters.put(key, new ArrayList<>(Collections.singletonList(value))); + } + + public String getFirst(String key) { + if (key == null) return null; + List values = get(key); + return values.isEmpty() ? null : values.get(0); + } + + public boolean contains(String key) { + if (key == null) return false; + return parameters.containsKey(key); + } + + public boolean keyPrefixAny(String prefix) { + if (prefix == null) return false; + return keys().stream().anyMatch(key -> key.startsWith(prefix)); + } + + public Set keys() { + return Collections.unmodifiableSet(parameters.keySet()); + } + + @Override + public Iterator> iterator() { + return entrySet().iterator(); + } + + @Override + public String toString() { + return parameters.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof QueryParameters)) return false; + QueryParameters queryParams = (QueryParameters) o; + return Objects.equals(this.parameters, queryParams.parameters); + } + + @Override + public int hashCode() { + return Objects.hash(parameters); + } + + public void clear() { + parameters.clear(); + } + + public boolean containsKey(String key) { + if (key == null) return false; + return parameters.containsKey(key); + } + + public Set> entrySet() { + Set> result = new LinkedHashSet<>(); + for (Map.Entry> entry : parameters.entrySet()) { + for (String value : entry.getValue()) { + result.add(new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), value)); + } + } + return result; + } + + public Set> entries() { + return entrySet(); + } + + public List get(String key) { + if (key == null) key = ""; + return parameters.getOrDefault(key, Collections.emptyList()); + } + + public Set keySet() { + return keys(); + } + + public boolean put(@Nonnull String key, @Nonnull String value) { + add(key, value); + return true; + } + + public QueryParameters putAll(Map source) { + if (source == null) return this; + for (Map.Entry entry : source.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + public QueryParameters putAll(Multimap source) { + if (source == null) return this; + for (Map.Entry entry : source.entries()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + public QueryParameters putAll(QueryParameters source) { + if (source == null) return this; + for (Map.Entry entry : source.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + public QueryParameters putAll(@Nonnull String key, @Nonnull List values) { + Utils.validateNotNull(values, "value"); + for (String value : values) put(key, value); + return this; + } + + public List remove(String key) { + if (key == null) key = ""; + return parameters.remove(key); + } + + public boolean remove(String key, String value) { + if (key == null) return false; + return parameters.containsKey(key) ? parameters.get(key).remove(value) : false; + } + + public boolean removeAll(String key) { + return remove(key) != null; + } + + public int size() { + return parameters.size(); + } + } + + public static class Request { + private okhttp3.Request httpRequest; + private String httpTraces; + + public Request(okhttp3.Request httpRequest, String httpTraces) { + this.httpRequest = httpRequest; + this.httpTraces = httpTraces; + } + + public okhttp3.Request httpRequest() { + return httpRequest; + } + + public String httpTraces() { + return httpTraces; + } + } + + public static class S3Request { + private String userAgent; + private Method method; + private BaseArgs args; + private Headers headers; + private QueryParameters queryParams; + private Body body; + + private String bucket; + private String region; + private String object; + + private S3Request(Builder builder) { + this.userAgent = builder.userAgent; + this.method = builder.method; + this.args = builder.args; + this.headers = builder.headers; + this.queryParams = builder.queryParams; + this.body = builder.body; + + if (args != null) { + this.headers = Headers.merge(args.extraHeaders(), builder.headers); + this.queryParams = QueryParameters.merge(args.extraQueryParams(), builder.queryParams); + + if (args instanceof BucketArgs) { + this.bucket = ((BucketArgs) args).bucket(); + this.region = ((BucketArgs) args).region(); + } + if (args instanceof ObjectArgs) this.object = ((ObjectArgs) args).object(); + } + } + + public String userAgent() { + return userAgent; + } + + public Method method() { + return method; + } + + public BaseArgs args() { + return args; + } + + public Headers headers() { + return headers; + } + + public QueryParameters queryParams() { + return queryParams; + } + + public String bucket() { + return bucket; + } + + public String region() { + return region; + } + + public String object() { + return object; + } + + private Request toRequest( + BaseUrl baseUrl, String region, Credentials credentials, Integer expiry) + throws MinioException { + if (region == null) region = this.region; + if (region == null) region = US_EAST_1; + + okhttp3.HttpUrl url = baseUrl.buildUrl(method, bucket, object, region, queryParams); + + Body body = this.body; + if (body == null) { + body = + headers.containsKey(Headers.CONTENT_TYPE) + ? new Body( + ZERO_BODY, + 0, + MediaType.parse(headers.getFirst(Headers.CONTENT_TYPE)), + Checksum.ZERO_SHA256_HASH, + Checksum.ZERO_MD5_HASH) + : EMPTY_BODY; + } + + String sha256Hash = null; + if (!body.isHttpRequestBody()) { + if (credentials == null) { + if (body.md5Hash() == null) { + throw new IllegalArgumentException("MD5 hash must be provided to request body"); + } + } else if (!url.isHttps()) { + if (body.sha256Hash() == null) { + throw new IllegalArgumentException("SHA256 hash must be provided to request body"); + } + } else if (body.sha256Hash() == null) { + sha256Hash = Checksum.UNSIGNED_PAYLOAD; + } + } + + okhttp3.RequestBody requestBody = body.toRequestBody(); + + Headers headers = Headers.merge(this.headers, body.headers()); + if (sha256Hash != null) headers.put(Headers.X_AMZ_CONTENT_SHA256, sha256Hash); + if (credentials != null) { + String sessionToken = credentials.sessionToken(); + if (sessionToken != null) headers.put(Headers.X_AMZ_SECURITY_TOKEN, sessionToken); + headers.put(Headers.X_AMZ_DATE, ZonedDateTime.now().format(Time.AMZ_DATE_FORMAT)); + } + // Disable default okhttp gzip compression + headers.put(Headers.ACCEPT_ENCODING, "identity"); + headers.put(Headers.USER_AGENT, userAgent); + headers.put(Headers.HOST, Utils.getHostHeader(url)); + if (method == Method.PUT || method == Method.POST) { + headers.put(Headers.CONTENT_TYPE, body.contentType().toString()); + try { + headers.put(Headers.CONTENT_LENGTH, String.valueOf(requestBody.contentLength())); + } catch (IOException e) { + throw new MinioException(e); + } + } else { + headers.remove(Headers.CONTENT_TYPE); + } + + if (expiry != null) { + headers.remove(Headers.CONTENT_LENGTH); + headers.remove(Headers.CONTENT_TYPE); + } + + okhttp3.Request request = + new okhttp3.Request.Builder() + .url(url) + .headers(headers.toHttpHeaders()) + .method( + method.toString(), + (method == Method.PUT || method == Method.POST) ? requestBody : null) + .build(); + if (!body.isHttpRequestBody()) { + if (credentials != null) { + if (expiry == null) { + request = + Signer.signV4S3( + request, + region, + credentials.accessKey(), + credentials.secretKey(), + sha256Hash != null ? sha256Hash : body.sha256Hash()); + } else { + okhttp3.HttpUrl signedUrl = + Signer.presignV4( + request, region, credentials.accessKey(), credentials.secretKey(), expiry); + request = request.newBuilder().url(signedUrl).build(); + } + } + } + + StringBuilder traceBuilder = new StringBuilder(); + traceBuilder.append("---------START-HTTP---------\n"); + String encodedPath = request.url().encodedPath(); + String encodedQuery = request.url().encodedQuery(); + if (encodedQuery != null) encodedPath += "?" + encodedQuery; + traceBuilder.append(request.method()).append(" ").append(encodedPath).append(" HTTP/1.1\n"); + traceBuilder + .append( + request + .headers() + .toString() + .replaceAll("Signature=([0-9a-f]+)", "Signature=*REDACTED*") + .replaceAll("Credential=([^/]+)", "Credential=*REDACTED*")) + .append("\n\n"); + String value = body.toString(); + if (method == Method.PUT || method == Method.POST) traceBuilder.append(value); + if (!value.endsWith("\n")) traceBuilder.append("\n"); + + return new Request(request, traceBuilder.toString()); + } + + public Request toRequest(BaseUrl baseUrl, String region, Credentials credentials) + throws MinioException { + return toRequest(baseUrl, region, credentials, null); + } + + public Request toPresignedRequest( + BaseUrl baseUrl, String region, Credentials credentials, int expiry) throws MinioException { + return toRequest(baseUrl, region, credentials, expiry); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String userAgent; + private Method method; + private BaseArgs args; + private Headers headers; + private QueryParameters queryParams; + private Body body; + + public Builder userAgent(String userAgent) { + this.userAgent = Utils.validateNotNull(userAgent, "user agent"); + return this; + } + + public Builder method(Method method) { + this.method = Utils.validateNotNull(method, "HTTP method"); + return this; + } + + public Builder args(BaseArgs args) { + this.args = args; + return this; + } + + public Builder headers(Headers headers) { + this.headers = headers; + return this; + } + + public Builder queryParams(QueryParameters queryParams) { + this.queryParams = queryParams; + return this; + } + + public Builder body(Body body) { + this.body = body; + return this; + } + + public S3Request build() { + if (userAgent == null) throw new IllegalArgumentException("user agent must be provided"); + if (method == null) throw new IllegalArgumentException("method must be provided"); + return new S3Request(this); + } + } + } +} diff --git a/api/src/main/java/io/minio/HttpRequestBody.java b/api/src/main/java/io/minio/HttpRequestBody.java deleted file mode 100644 index cbc59706c..000000000 --- a/api/src/main/java/io/minio/HttpRequestBody.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2015 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.io.IOException; -import okhttp3.MediaType; -import okhttp3.RequestBody; -import okio.BufferedSink; - -/** RequestBody that wraps a single data object. */ -class HttpRequestBody extends RequestBody { - private PartSource partSource; - private byte[] bytes; - private int length; - private String contentType; - - HttpRequestBody(final PartSource partSource, final String contentType) { - this.partSource = partSource; - this.contentType = contentType; - } - - HttpRequestBody(final byte[] bytes, final int length, final String contentType) { - this.bytes = bytes; - this.length = length; - this.contentType = contentType; - } - - @Override - public MediaType contentType() { - MediaType mediaType = null; - if (contentType != null) mediaType = MediaType.parse(contentType); - return (mediaType == null) ? MediaType.parse("application/octet-stream") : mediaType; - } - - @Override - public long contentLength() { - return (partSource != null) ? partSource.size() : length; - } - - @Override - public void writeTo(BufferedSink sink) throws IOException { - if (partSource != null) { - sink.write(partSource.source(), partSource.size()); - } else { - sink.write(bytes, 0, length); - } - } -} diff --git a/api/src/main/java/io/minio/ListBucketsArgs.java b/api/src/main/java/io/minio/ListBucketsArgs.java index 539e4192f..0651dcef2 100644 --- a/api/src/main/java/io/minio/ListBucketsArgs.java +++ b/api/src/main/java/io/minio/ListBucketsArgs.java @@ -51,7 +51,7 @@ public static final class Builder extends BaseArgs.Builder args.bucketRegion = region); return this; } @@ -66,13 +66,13 @@ public Builder maxBuckets(int maxBuckets) { } public Builder prefix(String prefix) { - validateNullOrNotEmptyString(prefix, "prefix"); + Utils.validateNullOrNotEmptyString(prefix, "prefix"); operations.add(args -> args.prefix = prefix); return this; } public Builder continuationToken(String continuationToken) { - validateNullOrNotEmptyString(continuationToken, "continuation token"); + Utils.validateNullOrNotEmptyString(continuationToken, "continuation token"); operations.add(args -> args.continuationToken = continuationToken); return this; } diff --git a/api/src/main/java/io/minio/ListMultipartUploadsArgs.java b/api/src/main/java/io/minio/ListMultipartUploadsArgs.java new file mode 100644 index 000000000..33561782b --- /dev/null +++ b/api/src/main/java/io/minio/ListMultipartUploadsArgs.java @@ -0,0 +1,117 @@ +/* + * 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 java.util.Objects; + +/** + * Argument class of {@link MinioAsyncClient#listMultipartUploads} and {@link + * MinioClient#listMultipartUploads}. + */ +public class ListMultipartUploadsArgs extends BucketArgs { + private String delimiter; + private String encodingType; + private Integer maxUploads; + private String prefix; + private String keyMarker; + private String uploadIdMarker; + + public String delimiter() { + return delimiter; + } + + public String encodingType() { + return encodingType; + } + + public Integer maxUploads() { + return maxUploads; + } + + public String prefix() { + return prefix; + } + + public String keyMarker() { + return keyMarker; + } + + public String uploadIdMarker() { + return uploadIdMarker; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link ListMultipartUploadsArgs}. */ + public static final class Builder extends BucketArgs.Builder { + public Builder delimiter(String delimiter) { + operations.add(args -> args.delimiter = delimiter); + return this; + } + + public Builder encodingType(String encodingType) { + operations.add(args -> args.encodingType = encodingType); + return this; + } + + public Builder maxUploads(Integer maxUploads) { + if (maxUploads != null && maxUploads < 1) { + throw new IllegalArgumentException("valid max keys must be provided"); + } + + operations.add(args -> args.maxUploads = maxUploads); + return this; + } + + public Builder prefix(String prefix) { + operations.add(args -> args.prefix = prefix); + return this; + } + + public Builder keyMarker(String keyMarker) { + operations.add(args -> args.keyMarker = keyMarker); + return this; + } + + public Builder uploadIdMarker(String uploadIdMarker) { + operations.add(args -> args.uploadIdMarker = uploadIdMarker); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ListMultipartUploadsArgs)) return false; + if (!super.equals(o)) return false; + ListMultipartUploadsArgs that = (ListMultipartUploadsArgs) o; + return Objects.equals(delimiter, that.delimiter) + && Objects.equals(encodingType, that.encodingType) + && Objects.equals(maxUploads, that.maxUploads) + && Objects.equals(prefix, that.prefix) + && Objects.equals(keyMarker, that.keyMarker) + && Objects.equals(uploadIdMarker, that.uploadIdMarker); + } + + @Override + public int hashCode() { + return Objects.hash( + super.hashCode(), delimiter, encodingType, maxUploads, prefix, keyMarker, uploadIdMarker); + } +} diff --git a/api/src/main/java/io/minio/ListObjectVersionsArgs.java b/api/src/main/java/io/minio/ListObjectVersionsArgs.java new file mode 100644 index 000000000..2c6579ae9 --- /dev/null +++ b/api/src/main/java/io/minio/ListObjectVersionsArgs.java @@ -0,0 +1,132 @@ +/* + * 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 java.util.Objects; + +/** + * Argument class of {@link MinioAsyncClient#ListObjectVersions} and {@link + * MinioClient#ListObjectVersions}. + */ +public class ListObjectVersionsArgs extends BucketArgs { + private String delimiter; + private String encodingType; + private Integer maxKeys; + private String prefix; + private String keyMarker; + private String versionIdMarker; + + protected ListObjectVersionsArgs() {} + + public ListObjectVersionsArgs(ListObjectsArgs args) { + this.extraHeaders = args.extraHeaders(); + this.extraQueryParams = args.extraQueryParams(); + this.bucketName = args.bucket(); + this.region = args.region(); + this.delimiter = args.delimiter(); + this.encodingType = args.useUrlEncodingType() ? "url" : null; + this.maxKeys = args.maxKeys(); + this.prefix = args.prefix(); + this.keyMarker = args.keyMarker(); + this.versionIdMarker = args.versionIdMarker(); + } + + public String delimiter() { + return delimiter; + } + + public String encodingType() { + return encodingType; + } + + public int maxKeys() { + return maxKeys; + } + + public String prefix() { + return prefix; + } + + public String keyMarker() { + return keyMarker; + } + + public String versionIdMarker() { + return versionIdMarker; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link ListObjectVersionsArgs}. */ + public static final class Builder extends BucketArgs.Builder { + public Builder delimiter(String delimiter) { + operations.add(args -> args.delimiter = delimiter); + return this; + } + + public Builder encodingType(String encodingType) { + operations.add(args -> args.encodingType = encodingType); + return this; + } + + public Builder maxKeys(Integer maxKeys) { + if (maxKeys != null && maxKeys < 1) { + throw new IllegalArgumentException("valid max keys must be provided"); + } + + operations.add(args -> args.maxKeys = maxKeys); + return this; + } + + public Builder prefix(String prefix) { + operations.add(args -> args.prefix = prefix); + return this; + } + + public Builder keyMarker(String keyMarker) { + operations.add(args -> args.keyMarker = keyMarker); + return this; + } + + public Builder versionIdMarker(String versionIdMarker) { + operations.add(args -> args.versionIdMarker = versionIdMarker); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ListObjectVersionsArgs)) return false; + if (!super.equals(o)) return false; + ListObjectVersionsArgs that = (ListObjectVersionsArgs) o; + return Objects.equals(delimiter, that.delimiter) + && Objects.equals(encodingType, that.encodingType) + && Objects.equals(maxKeys, that.maxKeys) + && Objects.equals(prefix, that.prefix) + && Objects.equals(keyMarker, that.keyMarker) + && Objects.equals(versionIdMarker, that.versionIdMarker); + } + + @Override + public int hashCode() { + return Objects.hash( + super.hashCode(), delimiter, encodingType, maxKeys, prefix, keyMarker, versionIdMarker); + } +} diff --git a/api/src/main/java/io/minio/ListObjectsArgs.java b/api/src/main/java/io/minio/ListObjectsArgs.java index 40231b6c3..1f4fda6c9 100644 --- a/api/src/main/java/io/minio/ListObjectsArgs.java +++ b/api/src/main/java/io/minio/ListObjectsArgs.java @@ -128,7 +128,7 @@ public Builder useUrlEncodingType(boolean flag) { } public Builder keyMarker(String keyMarker) { - validateNullOrNotEmptyString(keyMarker, "key marker"); + Utils.validateNullOrNotEmptyString(keyMarker, "key marker"); operations.add(args -> args.keyMarker = keyMarker); return this; } @@ -158,7 +158,7 @@ public Builder prefix(String prefix) { } public Builder continuationToken(String continuationToken) { - validateNullOrNotEmptyString(continuationToken, "continuation token"); + Utils.validateNullOrNotEmptyString(continuationToken, "continuation token"); operations.add(args -> args.continuationToken = continuationToken); return this; } @@ -169,7 +169,7 @@ public Builder fetchOwner(boolean fetchOwner) { } public Builder versionIdMarker(String versionIdMarker) { - validateNullOrNotEmptyString(versionIdMarker, "version ID marker"); + Utils.validateNullOrNotEmptyString(versionIdMarker, "version ID marker"); operations.add(args -> args.versionIdMarker = versionIdMarker); return this; } diff --git a/api/src/main/java/io/minio/ListObjectsV1Args.java b/api/src/main/java/io/minio/ListObjectsV1Args.java new file mode 100644 index 000000000..8daedb0cf --- /dev/null +++ b/api/src/main/java/io/minio/ListObjectsV1Args.java @@ -0,0 +1,118 @@ +/* + * 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 java.util.Objects; + +/** + * Argument class of {@link MinioAsyncClient#listObjectsV1} and {@link MinioClient#listObjectsV1}. + */ +public class ListObjectsV1Args extends BucketArgs { + private String delimiter; + private String encodingType; + private Integer maxKeys; + private String prefix; + private String marker; + + protected ListObjectsV1Args() {} + + public ListObjectsV1Args(ListObjectsArgs args) { + this.extraHeaders = args.extraHeaders(); + this.extraQueryParams = args.extraQueryParams(); + this.bucketName = args.bucket(); + this.region = args.region(); + this.delimiter = args.delimiter(); + this.encodingType = args.useUrlEncodingType() ? "url" : null; + this.maxKeys = args.maxKeys(); + this.prefix = args.prefix(); + this.marker = args.keyMarker(); + } + + public String delimiter() { + return delimiter; + } + + public String encodingType() { + return encodingType; + } + + public int maxKeys() { + return maxKeys; + } + + public String prefix() { + return prefix; + } + + public String marker() { + return marker; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link ListObjectsV1Args}. */ + public static final class Builder extends BucketArgs.Builder { + public Builder delimiter(String delimiter) { + operations.add(args -> args.delimiter = delimiter); + return this; + } + + public Builder encodingType(String encodingType) { + operations.add(args -> args.encodingType = encodingType); + return this; + } + + public Builder maxKeys(Integer maxKeys) { + if (maxKeys != null && maxKeys < 1) { + throw new IllegalArgumentException("valid max keys must be provided"); + } + + operations.add(args -> args.maxKeys = maxKeys); + return this; + } + + public Builder prefix(String prefix) { + operations.add(args -> args.prefix = prefix); + return this; + } + + public Builder marker(String marker) { + operations.add(args -> args.marker = marker); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ListObjectsV1Args)) return false; + if (!super.equals(o)) return false; + ListObjectsV1Args that = (ListObjectsV1Args) o; + return Objects.equals(delimiter, that.delimiter) + && Objects.equals(encodingType, that.encodingType) + && Objects.equals(maxKeys, that.maxKeys) + && Objects.equals(prefix, that.prefix) + && Objects.equals(marker, that.marker); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), delimiter, encodingType, maxKeys, prefix, marker); + } +} diff --git a/api/src/main/java/io/minio/ListObjectsV2Args.java b/api/src/main/java/io/minio/ListObjectsV2Args.java new file mode 100644 index 000000000..2c2560fd8 --- /dev/null +++ b/api/src/main/java/io/minio/ListObjectsV2Args.java @@ -0,0 +1,163 @@ +/* + * 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 java.util.Objects; + +/** + * Argument class of {@link MinioAsyncClient#listObjectsV2} and {@link MinioClient#listObjectsV2}. + */ +public class ListObjectsV2Args extends BucketArgs { + private String delimiter; + private String encodingType; + private Integer maxKeys; + private String prefix; + private String startAfter; + private String continuationToken; + private boolean fetchOwner; + private boolean includeUserMetadata; + + protected ListObjectsV2Args() {} + + public ListObjectsV2Args(ListObjectsArgs args) { + this.extraHeaders = args.extraHeaders(); + this.extraQueryParams = args.extraQueryParams(); + this.bucketName = args.bucket(); + this.region = args.region(); + this.delimiter = args.delimiter(); + this.encodingType = args.useUrlEncodingType() ? "url" : null; + this.maxKeys = args.maxKeys(); + this.prefix = args.prefix(); + this.startAfter = args.startAfter(); + this.continuationToken = args.continuationToken(); + this.fetchOwner = args.fetchOwner(); + this.includeUserMetadata = args.includeUserMetadata(); + } + + public String delimiter() { + return delimiter; + } + + public String encodingType() { + return encodingType; + } + + public int maxKeys() { + return maxKeys; + } + + public String prefix() { + return prefix; + } + + public String startAfter() { + return startAfter; + } + + public String continuationToken() { + return continuationToken; + } + + public boolean fetchOwner() { + return fetchOwner; + } + + public boolean includeUserMetadata() { + return includeUserMetadata; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link ListObjectsV2Args}. */ + public static final class Builder extends BucketArgs.Builder { + public Builder delimiter(String delimiter) { + operations.add(args -> args.delimiter = delimiter); + return this; + } + + public Builder encodingType(String encodingType) { + operations.add(args -> args.encodingType = encodingType); + return this; + } + + public Builder maxKeys(Integer maxKeys) { + if (maxKeys != null && maxKeys < 1) { + throw new IllegalArgumentException("valid max keys must be provided"); + } + + operations.add(args -> args.maxKeys = maxKeys); + return this; + } + + public Builder prefix(String prefix) { + operations.add(args -> args.prefix = prefix); + return this; + } + + public Builder startAfter(String startAfter) { + operations.add(args -> args.startAfter = startAfter); + return this; + } + + public Builder continuationToken(String continuationToken) { + operations.add(args -> args.continuationToken = continuationToken); + return this; + } + + public Builder fetchOwner(boolean fetchOwner) { + operations.add(args -> args.fetchOwner = fetchOwner); + return this; + } + + public Builder includeUserMetadata(boolean includeUserMetadata) { + operations.add(args -> args.includeUserMetadata = includeUserMetadata); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ListObjectsV2Args)) return false; + if (!super.equals(o)) return false; + ListObjectsV2Args that = (ListObjectsV2Args) o; + return Objects.equals(delimiter, that.delimiter) + && Objects.equals(encodingType, that.encodingType) + && Objects.equals(maxKeys, that.maxKeys) + && Objects.equals(prefix, that.prefix) + && Objects.equals(startAfter, that.startAfter) + && Objects.equals(continuationToken, that.continuationToken) + && fetchOwner == that.fetchOwner + && includeUserMetadata == that.includeUserMetadata; + } + + @Override + public int hashCode() { + return Objects.hash( + super.hashCode(), + delimiter, + encodingType, + maxKeys, + prefix, + startAfter, + continuationToken, + fetchOwner, + includeUserMetadata); + } +} diff --git a/api/src/main/java/io/minio/ListPartsArgs.java b/api/src/main/java/io/minio/ListPartsArgs.java new file mode 100644 index 000000000..1fcbe9581 --- /dev/null +++ b/api/src/main/java/io/minio/ListPartsArgs.java @@ -0,0 +1,84 @@ +/* + * 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 java.util.Objects; + +/** Argument class of {@link MinioAsyncClient#listParts} and {@link MinioClient#listParts}. */ +public class ListPartsArgs extends ObjectArgs { + private String uploadId; + private Integer maxParts; + private Integer partNumberMarker; + + public String uploadId() { + return uploadId; + } + + public Integer maxParts() { + return maxParts; + } + + public Integer partNumberMarker() { + return partNumberMarker; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link ListPartsArgs}. */ + public static final class Builder extends BucketArgs.Builder { + public Builder uploadId(String uploadId) { + Utils.validateNotEmptyString(uploadId, "upload ID"); + operations.add(args -> args.uploadId = uploadId); + return this; + } + + public Builder maxParts(Integer maxParts) { + if (maxParts != null && maxParts < 1) { + throw new IllegalArgumentException("valid max parts must be provided"); + } + + operations.add(args -> args.maxParts = maxParts); + return this; + } + + public Builder partNumberMarker(Integer partNumberMarker) { + if (partNumberMarker != null && (partNumberMarker < 1 || partNumberMarker > 10000)) { + throw new IllegalArgumentException("valid part number marker must be provided"); + } + operations.add(args -> args.partNumberMarker = partNumberMarker); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ListPartsArgs)) return false; + if (!super.equals(o)) return false; + ListPartsArgs that = (ListPartsArgs) o; + return Objects.equals(uploadId, that.uploadId) + && Objects.equals(maxParts, that.maxParts) + && Objects.equals(partNumberMarker, that.partNumberMarker); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), uploadId, maxParts, partNumberMarker); + } +} diff --git a/api/src/main/java/io/minio/ListenBucketNotificationArgs.java b/api/src/main/java/io/minio/ListenBucketNotificationArgs.java index a1dc359bc..3240828ed 100644 --- a/api/src/main/java/io/minio/ListenBucketNotificationArgs.java +++ b/api/src/main/java/io/minio/ListenBucketNotificationArgs.java @@ -48,7 +48,7 @@ public static Builder builder() { public static final class Builder extends BucketArgs.Builder { private void validateEvents(String[] events) { - validateNotNull(events, "events"); + Utils.validateNotNull(events, "events"); } protected void validate(ListenBucketNotificationArgs args) { diff --git a/api/src/main/java/io/minio/MakeBucketArgs.java b/api/src/main/java/io/minio/MakeBucketArgs.java index 81b9e0ac3..d33ac9487 100644 --- a/api/src/main/java/io/minio/MakeBucketArgs.java +++ b/api/src/main/java/io/minio/MakeBucketArgs.java @@ -1,5 +1,5 @@ /* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2020 MinIO, Inc. + * 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. @@ -16,39 +16,12 @@ package io.minio; -import java.util.Objects; - /** Argument class of {@link MinioAsyncClient#makeBucket} and {@link MinioClient#makeBucket}. */ -public class MakeBucketArgs extends BucketArgs { - private boolean objectLock; - - public boolean objectLock() { - return objectLock; - } - +public class MakeBucketArgs extends CreateBucketBaseArgs { public static Builder builder() { return new Builder(); } /** Argument builder of {@link MakeBucketArgs}. */ - public static final class Builder extends BucketArgs.Builder { - public Builder objectLock(boolean objectLock) { - operations.add(args -> args.objectLock = objectLock); - return this; - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof MakeBucketArgs)) return false; - if (!super.equals(o)) return false; - MakeBucketArgs that = (MakeBucketArgs) o; - return objectLock == that.objectLock; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), objectLock); - } + public static final class Builder extends CreateBucketBaseArgs.Builder {} } diff --git a/api/src/main/java/io/minio/MinioAsyncClient.java b/api/src/main/java/io/minio/MinioAsyncClient.java index f72e52622..d052dc8ff 100644 --- a/api/src/main/java/io/minio/MinioAsyncClient.java +++ b/api/src/main/java/io/minio/MinioAsyncClient.java @@ -18,9 +18,13 @@ package io.minio; import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; import com.google.common.io.ByteStreams; import io.minio.credentials.Credentials; import io.minio.credentials.Provider; @@ -28,24 +32,21 @@ import io.minio.errors.BucketPolicyTooLargeException; import io.minio.errors.ErrorResponseException; import io.minio.errors.InsufficientDataException; -import io.minio.errors.InternalException; -import io.minio.errors.InvalidResponseException; -import io.minio.errors.ServerException; +import io.minio.errors.MinioException; import io.minio.errors.XmlParserException; -import io.minio.http.HttpUtils; -import io.minio.http.Method; import io.minio.messages.AccessControlPolicy; -import io.minio.messages.Bucket; import io.minio.messages.CORSConfiguration; -import io.minio.messages.CopyObjectResult; -import io.minio.messages.CreateBucketConfiguration; -import io.minio.messages.DeleteError; -import io.minio.messages.DeleteObject; +import io.minio.messages.DeleteRequest; +import io.minio.messages.DeleteResult; import io.minio.messages.GetObjectAttributesOutput; import io.minio.messages.Item; import io.minio.messages.LegalHold; import io.minio.messages.LifecycleConfiguration; import io.minio.messages.ListAllMyBucketsResult; +import io.minio.messages.ListBucketResultV1; +import io.minio.messages.ListBucketResultV2; +import io.minio.messages.ListObjectsResult; +import io.minio.messages.ListVersionsResult; import io.minio.messages.NotificationConfiguration; import io.minio.messages.NotificationRecords; import io.minio.messages.ObjectLockConfiguration; @@ -60,6 +61,7 @@ import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.math.BigInteger; @@ -70,10 +72,8 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; @@ -81,14 +81,16 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Scanner; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; -import java.util.regex.Matcher; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; -import okhttp3.Request; import okhttp3.Response; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; @@ -139,27 +141,101 @@ * .build(); * } */ -public class MinioAsyncClient extends S3Base { +public class MinioAsyncClient extends BaseS3Client { + /** Argument builder of {@link MinioAsyncClient}. */ + public static final class Builder { + private Http.BaseUrl baseUrl = null; + private String region; + private Provider provider; + private OkHttpClient httpClient; + private boolean closeHttpClient; + + public Builder baseUrl(Http.BaseUrl baseUrl) { + if (baseUrl.region() == null) { + baseUrl.setRegion(region); + } + region = null; + this.baseUrl = baseUrl; + return this; + } + + public Builder endpoint(String endpoint) { + return this.baseUrl(new Http.BaseUrl(endpoint)); + } + + public Builder endpoint(String endpoint, int port, boolean secure) { + return this.baseUrl(new Http.BaseUrl(endpoint, port, secure)); + } + + public Builder endpoint(URL url) { + return this.baseUrl(new Http.BaseUrl(url)); + } + + public Builder endpoint(HttpUrl url) { + return this.baseUrl(new Http.BaseUrl(url)); + } + + public Builder region(String region) { + if (region != null && !Utils.REGION_REGEX.matcher(region).find()) { + throw new IllegalArgumentException("invalid region " + region); + } + if (baseUrl != null) { + baseUrl.setRegion(region); + } else { + this.region = region; + } + return this; + } + + public Builder credentials(String accessKey, String secretKey) { + provider = new StaticProvider(accessKey, secretKey, null); + return this; + } + + public Builder credentialsProvider(Provider provider) { + this.provider = provider; + return this; + } + + public Builder httpClient(OkHttpClient httpClient) { + Utils.validateNotNull(httpClient, "http client"); + this.httpClient = httpClient; + return this; + } + + public Builder httpClient(OkHttpClient httpClient, boolean close) { + Utils.validateNotNull(httpClient, "http client"); + this.httpClient = httpClient; + this.closeHttpClient = close; + return this; + } + + public MinioAsyncClient build() { + Utils.validateNotNull(baseUrl, "endpoint"); + + if (baseUrl.awsDomainSuffix() != null + && baseUrl.awsDomainSuffix().endsWith(".cn") + && !baseUrl.awsS3Prefix().endsWith("s3-accelerate.") + && baseUrl.region() == null) { + throw new IllegalArgumentException("Region missing in Amazon S3 China endpoint " + baseUrl); + } + + if (httpClient == null) { + closeHttpClient = true; + httpClient = Http.newDefaultClient(); + } + + return new MinioAsyncClient(baseUrl, provider, httpClient, closeHttpClient); + } + } + + public static Builder builder() { + return new Builder(); + } + private MinioAsyncClient( - HttpUrl baseUrl, - String awsS3Prefix, - String awsDomainSuffix, - boolean awsDualstack, - boolean useVirtualStyle, - String region, - Provider provider, - OkHttpClient httpClient, - boolean closeHttpClient) { - super( - baseUrl, - awsS3Prefix, - awsDomainSuffix, - awsDualstack, - useVirtualStyle, - region, - provider, - httpClient, - closeHttpClient); + Http.BaseUrl baseUrl, Provider provider, OkHttpClient httpClient, boolean closeHttpClient) { + super(baseUrl, provider, httpClient, closeHttpClient); } protected MinioAsyncClient(MinioAsyncClient client) { @@ -206,18 +282,13 @@ protected MinioAsyncClient(MinioAsyncClient client) { * * @param args {@link StatObjectArgs} object. * @return {@link CompletableFuture}<{@link StatObjectResponse}> 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. + * @throws MinioException thrown to indicate SDK exception. * @see StatObjectResponse */ public CompletableFuture statObject(StatObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return super.statObjectAsync(args); + throws MinioException { + return headObject(new HeadObjectArgs(args)) + .thenApply(response -> new StatObjectResponse(response)); } /** @@ -236,23 +307,18 @@ public CompletableFuture statObject(StatObjectArgs args) * * @param args Object of {@link GetObjectArgs} * @return {@link CompletableFuture}<{@link GetObjectResponse}> 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. + * @throws MinioException thrown to indicate SDK exception. * @see GetObjectResponse */ - public CompletableFuture getObject(GetObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture getObject(GetObjectArgs args) throws MinioException { checkArgs(args); - args.validateSsec(this.baseUrl); + args.validateSsec(this.baseUrl.isHttps()); return executeGetAsync( args, - args.getHeaders(), - (args.versionId() != null) ? newMultimap("versionId", args.versionId()) : null) + args.makeHeaders(), + (args.versionId() != null) + ? new Http.QueryParameters("versionId", args.versionId()) + : null) .thenApply( response -> { return new GetObjectResponse( @@ -267,23 +333,23 @@ public CompletableFuture getObject(GetObjectArgs args) private void downloadObject( String filename, boolean overwrite, - StatObjectResponse statObjectResponse, + HeadObjectResponse headObjectResponse, GetObjectResponse getObjectResponse) - throws IOException { + throws MinioException { OutputStream os = null; try { Path filePath = Paths.get(filename); String tempFilename = - filename + "." + S3Escaper.encode(statObjectResponse.etag()) + ".part.minio"; + filename + "." + Utils.encode(headObjectResponse.etag()) + ".part.minio"; Path tempFilePath = Paths.get(tempFilename); if (Files.exists(tempFilePath)) Files.delete(tempFilePath); os = Files.newOutputStream(tempFilePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE); long bytesWritten = ByteStreams.copy(getObjectResponse, os); - if (bytesWritten != statObjectResponse.size()) { + if (bytesWritten != headObjectResponse.size()) { throw new IOException( tempFilename + ": unexpected data written. expected = " - + statObjectResponse.size() + + headObjectResponse.size() + ", written = " + bytesWritten); } @@ -293,9 +359,15 @@ private void downloadObject( } else { Files.move(tempFilePath, filePath); } + } catch (IOException e) { + throw new MinioException(e); } finally { - getObjectResponse.close(); - if (os != null) os.close(); + try { + getObjectResponse.close(); + if (os != null) os.close(); + } catch (IOException e) { + throw new MinioException(e); + } } } @@ -314,30 +386,23 @@ private void downloadObject( * * @param args Object of {@link DownloadObjectArgs} * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture downloadObject(DownloadObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture downloadObject(DownloadObjectArgs args) throws MinioException { String filename = args.filename(); Path filePath = Paths.get(filename); if (!args.overwrite() && Files.exists(filePath)) { throw new IllegalArgumentException("Destination file " + filename + " already exists"); } - return statObjectAsync(new StatObjectArgs(args)) + return headObject(new HeadObjectArgs(args)) .thenCombine( getObject(new GetObjectArgs(args)), - (statObjectResponse, getObjectResponse) -> { + (headObjectResponse, getObjectResponse) -> { try { - downloadObject(filename, args.overwrite(), statObjectResponse, getObjectResponse); + downloadObject(filename, args.overwrite(), headObjectResponse, getObjectResponse); return null; - } catch (IOException e) { + } catch (MinioException e) { throw new CompletionException(e); } }) @@ -355,7 +420,7 @@ public CompletableFuture downloadObject(DownloadObjectArgs args) * .bucket("my-bucketname") * .object("my-objectname") * .source( - * CopySource.builder() + * SourceObject.builder() * .bucket("my-source-bucketname") * .object("my-objectname") * .build()) @@ -368,7 +433,7 @@ public CompletableFuture downloadObject(DownloadObjectArgs args) * .bucket("my-bucketname") * .object("my-objectname") * .source( - * CopySource.builder() + * SourceObject.builder() * .bucket("my-source-bucketname") * .object("my-source-objectname") * .build()) @@ -381,7 +446,7 @@ public CompletableFuture downloadObject(DownloadObjectArgs args) * .bucket("my-bucketname") * .object("my-objectname") * .source( - * CopySource.builder() + * SourceObject.builder() * .bucket("my-source-bucketname") * .object("my-objectname") * .build()) @@ -395,7 +460,7 @@ public CompletableFuture downloadObject(DownloadObjectArgs args) * .bucket("my-bucketname") * .object("my-objectname") * .source( - * CopySource.builder() + * SourceObject.builder() * .bucket("my-source-bucketname") * .object("my-objectname") * .build()) @@ -409,7 +474,7 @@ public CompletableFuture downloadObject(DownloadObjectArgs args) * .bucket("my-bucketname") * .object("my-objectname") * .source( - * CopySource.builder() + * SourceObject.builder() * .bucket("my-source-bucketname") * .object("my-objectname") * .build()) @@ -423,7 +488,7 @@ public CompletableFuture downloadObject(DownloadObjectArgs args) * .bucket("my-bucketname") * .object("my-objectname") * .source( - * CopySource.builder() + * SourceObject.builder() * .bucket("my-source-bucketname") * .object("my-source-objectname") * .ssec(ssec) // Replace with actual key. @@ -437,7 +502,7 @@ public CompletableFuture downloadObject(DownloadObjectArgs args) * .bucket("my-bucketname") * .object("my-objectname") * .source( - * CopySource.builder() + * SourceObject.builder() * .bucket("my-source-bucketname") * .object("my-objectname") * .matchETag(etag) // Replace with actual etag. @@ -448,133 +513,244 @@ public CompletableFuture downloadObject(DownloadObjectArgs args) * * @param args {@link CopyObjectArgs} object. * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture copyObject(CopyObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - args.validateSse(this.baseUrl); + args.validateSse(this.baseUrl.isHttps()); - return CompletableFuture.supplyAsync( - () -> args.source().offset() != null && args.source().length() != null) + CompletableFuture future = + args.source().objectSize() == null + ? headObject(new HeadObjectArgs(args.source())) + : CompletableFuture.completedFuture((HeadObjectResponse) null); + return future + .thenApply( + response -> + response == null + ? args + : new CopyObjectArgs( + args, new SourceObject(args.source(), response.size(), response.etag()))) .thenCompose( - condition -> { - if (condition) { - try { - return statObjectAsync(new StatObjectArgs((ObjectReadArgs) args.source())); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); + copyArgs -> { + long size = copyArgs.source().objectSize(); + try { + if (size < ObjectWriteArgs.MAX_PART_SIZE + && copyArgs.source().offset() == null + && copyArgs.source().length() == null) { + return super.copyObject(copyArgs); } - } - return CompletableFuture.completedFuture(null); - }) - .thenApply(stat -> (stat == null) ? (long) -1 : stat.size()) - .thenCompose( - size -> { - if (args.source().offset() != null - || args.source().length() != null - || size > ObjectWriteArgs.MAX_PART_SIZE) { - if (args.metadataDirective() != null - && args.metadataDirective() == Directive.COPY) { - throw new IllegalArgumentException( - "COPY metadata directive is not applicable to source object size greater than" - + " 5 GiB"); + + if (size > ObjectWriteArgs.MAX_PART_SIZE) { + if (copyArgs.metadataDirective() == Directive.COPY) { + throw new IllegalArgumentException( + "COPY metadata directive is not applicable to source object size greater" + + " than 5 GiB"); + } } - if (args.taggingDirective() != null && args.taggingDirective() == Directive.COPY) { + if (copyArgs.taggingDirective() == Directive.COPY) { throw new IllegalArgumentException( "COPY tagging directive is not applicable to source object size greater than" + " 5 GiB"); } - try { - return composeObject(new ComposeObjectArgs(args)); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - } - return CompletableFuture.completedFuture(null); - }) - .thenCompose( - objectWriteResponse -> { - if (objectWriteResponse != null) { - return CompletableFuture.completedFuture(objectWriteResponse); + return composeObject(new ComposeObjectArgs(copyArgs)); + } catch (MinioException e) { + throw new CompletionException(e); } + }); + } - Multimap headers = args.genHeaders(); + protected CompletableFuture calculatePartCount(List sources) + throws MinioException { + long[] objectSize = {0}; + + CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> 0); + + int sourceSize = sources.size(); + for (int i = 0; i < sourceSize; i++) { + final int index = i; + final boolean interimPart = sourceSize != 1 && sourceSize != (i + 1); + + CompletableFuture future = + sources.get(index).objectSize() == null + ? headObject(new HeadObjectArgs(sources.get(index))) + : CompletableFuture.completedFuture((HeadObjectResponse) null); + completableFuture = + completableFuture.thenCombine( + future, + (partCount, response) -> { + SourceObject source = sources.get(index); + if (response != null) { + source = new SourceObject(source, response.size(), response.etag()); + sources.set(index, source); + } - if (args.metadataDirective() != null) { - headers.put("x-amz-metadata-directive", args.metadataDirective().name()); - } + long size = source.objectSize(); + if (source.length() != null) { + size = source.length(); + } else if (source.offset() != null) { + size -= source.offset(); + } - if (args.taggingDirective() != null) { - headers.put("x-amz-tagging-directive", args.taggingDirective().name()); - } + if (size < ObjectWriteArgs.MIN_MULTIPART_SIZE && interimPart) { + throw new IllegalArgumentException( + "compose source " + + source.bucket() + + "/" + + source.object() + + ": size " + + size + + " must be greater than " + + ObjectWriteArgs.MIN_MULTIPART_SIZE); + } + + objectSize[0] += size; + if (objectSize[0] > ObjectWriteArgs.MAX_OBJECT_SIZE) { + throw new IllegalArgumentException( + "destination object size must be less than " + + ObjectWriteArgs.MAX_OBJECT_SIZE); + } + + if (size > ObjectWriteArgs.MAX_PART_SIZE) { + long count = size / ObjectWriteArgs.MAX_PART_SIZE; + long lastPartSize = size - (count * ObjectWriteArgs.MAX_PART_SIZE); + if (lastPartSize > 0) { + count++; + } else { + lastPartSize = ObjectWriteArgs.MAX_PART_SIZE; + } + + if (lastPartSize < ObjectWriteArgs.MIN_MULTIPART_SIZE && interimPart) { + throw new IllegalArgumentException( + "compose source " + + source.bucket() + + "/" + + source.object() + + ": " + + "for multipart split upload of " + + size + + ", last part size is less than " + + ObjectWriteArgs.MIN_MULTIPART_SIZE); + } + partCount += (int) count; + } else { + partCount++; + } + + if (partCount > ObjectWriteArgs.MAX_MULTIPART_COUNT) { + throw new IllegalArgumentException( + "Compose sources create more than allowed multipart count " + + ObjectWriteArgs.MAX_MULTIPART_COUNT); + } + return partCount; + }); + } + + return completableFuture; + } + + private CompletableFuture uploadParts( + ComposeObjectArgs args, int partCount, String uploadId) throws MinioException { + Http.Headers ssecHeaders = + (args.sse() != null && args.sse() instanceof ServerSideEncryption.CustomerKey) + ? args.sse().headers() + : null; + + int partNumber = 0; + CompletableFuture future = CompletableFuture.supplyAsync(() -> new Part[partCount]); + for (SourceObject source : args.sources()) { + long size = source.objectSize(); + if (source.length() != null) { + size = source.length(); + } else if (source.offset() != null) { + size -= source.offset(); + } + + long offset = source.offset() == null ? 0 : source.offset(); + final Http.Headers headers = Http.Headers.merge(source.headers(), ssecHeaders); + + if (size <= ObjectWriteArgs.MAX_PART_SIZE) { + partNumber++; + if (source.length() != null) { + headers.put( + Http.Headers.X_AMZ_COPY_SOURCE_RANGE, + "bytes=" + offset + "-" + (offset + source.length() - 1)); + } else if (source.offset() != null) { + headers.put( + Http.Headers.X_AMZ_COPY_SOURCE_RANGE, "bytes=" + offset + "-" + (offset + size - 1)); + } + + final int finalPartNumber = partNumber; + future = + future.thenCombine( + uploadPartCopy(new UploadPartCopyArgs(args, uploadId, finalPartNumber, headers)), + (parts, response) -> { + parts[response.partNumber() - 1] = response.part(); + return parts; + }); + continue; + } + + while (size > 0) { + partNumber++; + + long length = Math.min(size, ObjectWriteArgs.MAX_PART_SIZE); + long endBytes = offset + length - 1; + + Http.Headers finalHeaders = + new Http.Headers( + Http.Headers.X_AMZ_COPY_SOURCE_RANGE, "bytes=" + offset + "-" + endBytes); + finalHeaders.putAll(headers); + + final int finalPartNumber = partNumber; + future = + future.thenCombine( + uploadPartCopy(new UploadPartCopyArgs(args, uploadId, finalPartNumber, headers)), + (parts, response) -> { + parts[response.partNumber() - 1] = response.part(); + return parts; + }); + offset += length; + size -= length; + } + } - headers.putAll(args.source().genCopyHeaders()); + return future; + } + private CompletableFuture composeObject( + ComposeObjectArgs args, int partCount) throws MinioException { + String[] uploadId = {null}; + return createMultipartUpload(new CreateMultipartUploadArgs(args)) + .thenCompose( + response -> { + uploadId[0] = response.result().uploadId(); try { - return executePutAsync(args, headers, null, null, 0) - .thenApply( - response -> { - try { - CopyObjectResult result = - Xml.unmarshal(CopyObjectResult.class, response.body().charStream()); - return new ObjectWriteResponse( - response.headers(), - args.bucket(), - args.region(), - args.object(), - result.etag(), - response.header("x-amz-version-id"), - result); - } catch (XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { + return uploadParts(args, partCount, uploadId[0]); + } catch (MinioException e) { throw new CompletionException(e); } - }); - } - - private CompletableFuture uploadPartCopy( - String bucketName, - String region, - String objectName, - String uploadId, - int partNumber, - Multimap headers, - Part[] parts) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return uploadPartCopyAsync(bucketName, region, objectName, uploadId, partNumber, headers, null) - .thenApply( - uploadPartCopyResponse -> { - parts[partNumber - 1] = new Part(partNumber, uploadPartCopyResponse.result().etag()); - return parts; + }) + .thenCompose( + parts -> { + try { + return completeMultipartUpload( + new CompleteMultipartUploadArgs(args, uploadId[0], parts)); + } catch (MinioException e) { + throw new CompletionException(e); + } + }) + .exceptionally( + e -> { + if (uploadId[0] != null) { + try { + abortMultipartUpload(new AbortMultipartUploadArgs(args, uploadId[0])).get(); + } catch (MinioException | InterruptedException | ExecutionException ex) { + e.addSuppressed(ex); + } + } + throw new CompletionException(e); }); } @@ -582,14 +758,14 @@ private CompletableFuture uploadPartCopy( * Creates an object by combining data from different source objects using server-side copy. * *
Example:{@code
-   * List sourceObjectList = new ArrayList();
+   * List sourceObjectList = new ArrayList();
    *
    * sourceObjectList.add(
-   *    ComposeSource.builder().bucket("my-job-bucket").object("my-objectname-part-one").build());
+   *    SourceObject.builder().bucket("my-job-bucket").object("my-objectname-part-one").build());
    * sourceObjectList.add(
-   *    ComposeSource.builder().bucket("my-job-bucket").object("my-objectname-part-two").build());
+   *    SourceObject.builder().bucket("my-job-bucket").object("my-objectname-part-two").build());
    * sourceObjectList.add(
-   *    ComposeSource.builder().bucket("my-job-bucket").object("my-objectname-part-three").build());
+   *    SourceObject.builder().bucket("my-job-bucket").object("my-objectname-part-three").build());
    *
    * // Create my-bucketname/my-objectname by combining source object list.
    * CompletableFuture future = minioAsyncClient.composeObject(
@@ -625,246 +801,26 @@ private CompletableFuture uploadPartCopy(
    *
    * @param args {@link ComposeObjectArgs} object.
    * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> 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.
+   * @throws MinioException thrown to indicate SDK exception.
    */
   public CompletableFuture composeObject(ComposeObjectArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+      throws MinioException {
     checkArgs(args);
-    args.validateSse(this.baseUrl);
-    List sources = args.sources();
-    int[] partCount = {0};
-    String[] uploadIdCopy = {null};
+    args.validateSse(this.baseUrl.isHttps());
 
-    return calculatePartCountAsync(sources)
-        .thenApply(
-            count -> {
-              partCount[0] = count;
-              return (count == 1
-                  && args.sources().get(0).offset() == null
-                  && args.sources().get(0).length() == null);
-            })
+    return calculatePartCount(args.sources())
         .thenCompose(
-            copyObjectFlag -> {
-              if (copyObjectFlag) {
-                try {
+            partCount -> {
+              try {
+                if (partCount == 1
+                    && args.sources().get(0).offset() == null
+                    && args.sources().get(0).length() == null) {
                   return copyObject(new CopyObjectArgs(args));
-                } catch (InsufficientDataException
-                    | InternalException
-                    | InvalidKeyException
-                    | IOException
-                    | NoSuchAlgorithmException
-                    | XmlParserException e) {
-                  throw new CompletionException(e);
                 }
+                return composeObject(args, partCount);
+              } catch (MinioException e) {
+                throw new CompletionException(e);
               }
-              return CompletableFuture.completedFuture(null);
-            })
-        .thenCompose(
-            objectWriteResponse -> {
-              if (objectWriteResponse != null) {
-                return CompletableFuture.completedFuture(objectWriteResponse);
-              }
-
-              CompletableFuture completableFuture =
-                  CompletableFuture.supplyAsync(
-                          () -> {
-                            Multimap headers = newMultimap(args.extraHeaders());
-                            headers.putAll(args.genHeaders());
-                            return headers;
-                          })
-                      .thenCompose(
-                          headers -> {
-                            try {
-                              return createMultipartUploadAsync(
-                                  args.bucket(),
-                                  args.region(),
-                                  args.object(),
-                                  headers,
-                                  args.extraQueryParams());
-                            } catch (InsufficientDataException
-                                | InternalException
-                                | InvalidKeyException
-                                | IOException
-                                | NoSuchAlgorithmException
-                                | XmlParserException e) {
-                              throw new CompletionException(e);
-                            }
-                          })
-                      .thenApply(
-                          createMultipartUploadResponse -> {
-                            String uploadId = createMultipartUploadResponse.result().uploadId();
-                            uploadIdCopy[0] = uploadId;
-                            return uploadId;
-                          })
-                      .thenCompose(
-                          uploadId -> {
-                            Multimap ssecHeaders = HashMultimap.create();
-                            if (args.sse() != null
-                                && args.sse() instanceof ServerSideEncryptionCustomerKey) {
-                              ssecHeaders.putAll(newMultimap(args.sse().headers()));
-                            }
-
-                            int partNumber = 0;
-                            CompletableFuture future =
-                                CompletableFuture.supplyAsync(
-                                    () -> {
-                                      return new Part[partCount[0]];
-                                    });
-                            for (ComposeSource src : sources) {
-                              long size = 0;
-                              try {
-                                size = src.objectSize();
-                              } catch (InternalException e) {
-                                throw new CompletionException(e);
-                              }
-                              if (src.length() != null) {
-                                size = src.length();
-                              } else if (src.offset() != null) {
-                                size -= src.offset();
-                              }
-                              long offset = 0;
-                              if (src.offset() != null) offset = src.offset();
-
-                              final Multimap headers;
-                              try {
-                                headers = newMultimap(src.headers());
-                              } catch (InternalException e) {
-                                throw new CompletionException(e);
-                              }
-                              headers.putAll(ssecHeaders);
-
-                              if (size <= ObjectWriteArgs.MAX_PART_SIZE) {
-                                partNumber++;
-                                if (src.length() != null) {
-                                  headers.put(
-                                      "x-amz-copy-source-range",
-                                      "bytes=" + offset + "-" + (offset + src.length() - 1));
-                                } else if (src.offset() != null) {
-                                  headers.put(
-                                      "x-amz-copy-source-range",
-                                      "bytes=" + offset + "-" + (offset + size - 1));
-                                }
-
-                                final int partNum = partNumber;
-                                future =
-                                    future.thenCompose(
-                                        parts -> {
-                                          try {
-                                            return uploadPartCopy(
-                                                args.bucket(),
-                                                args.region(),
-                                                args.object(),
-                                                uploadId,
-                                                partNum,
-                                                headers,
-                                                parts);
-                                          } catch (InsufficientDataException
-                                              | InternalException
-                                              | InvalidKeyException
-                                              | IOException
-                                              | NoSuchAlgorithmException
-                                              | XmlParserException e) {
-                                            throw new CompletionException(e);
-                                          }
-                                        });
-                                continue;
-                              }
-
-                              while (size > 0) {
-                                partNumber++;
-
-                                long length = size;
-                                if (length > ObjectWriteArgs.MAX_PART_SIZE) {
-                                  length = ObjectWriteArgs.MAX_PART_SIZE;
-                                }
-                                long endBytes = offset + length - 1;
-
-                                Multimap headersCopy = newMultimap(headers);
-                                headersCopy.put(
-                                    "x-amz-copy-source-range", "bytes=" + offset + "-" + endBytes);
-
-                                final int partNum = partNumber;
-                                future =
-                                    future.thenCompose(
-                                        parts -> {
-                                          try {
-                                            return uploadPartCopy(
-                                                args.bucket(),
-                                                args.region(),
-                                                args.object(),
-                                                uploadId,
-                                                partNum,
-                                                headersCopy,
-                                                parts);
-                                          } catch (InsufficientDataException
-                                              | InternalException
-                                              | InvalidKeyException
-                                              | IOException
-                                              | NoSuchAlgorithmException
-                                              | XmlParserException e) {
-                                            throw new CompletionException(e);
-                                          }
-                                        });
-                                offset += length;
-                                size -= length;
-                              }
-                            }
-
-                            return future;
-                          })
-                      .thenCompose(
-                          parts -> {
-                            try {
-                              return completeMultipartUploadAsync(
-                                  args.bucket(),
-                                  args.region(),
-                                  args.object(),
-                                  uploadIdCopy[0],
-                                  parts,
-                                  null,
-                                  null);
-                            } catch (InsufficientDataException
-                                | InternalException
-                                | InvalidKeyException
-                                | IOException
-                                | NoSuchAlgorithmException
-                                | XmlParserException e) {
-                              throw new CompletionException(e);
-                            }
-                          });
-
-              completableFuture.exceptionally(
-                  e -> {
-                    if (uploadIdCopy[0] != null) {
-                      try {
-                        abortMultipartUploadAsync(
-                                args.bucket(),
-                                args.region(),
-                                args.object(),
-                                uploadIdCopy[0],
-                                null,
-                                null)
-                            .get();
-                      } catch (InsufficientDataException
-                          | InternalException
-                          | InvalidKeyException
-                          | IOException
-                          | NoSuchAlgorithmException
-                          | XmlParserException
-                          | InterruptedException
-                          | ExecutionException ex) {
-                        throw new CompletionException(ex);
-                      }
-                    }
-                    throw new CompletionException(e);
-                  });
-              return completableFuture;
             });
   }
 
@@ -877,7 +833,7 @@ public CompletableFuture composeObject(ComposeObjectArgs ar
    * String url =
    *    minioAsyncClient.getPresignedObjectUrl(
    *        GetPresignedObjectUrlArgs.builder()
-   *            .method(Method.DELETE)
+   *            .method(Http.Method.DELETE)
    *            .bucket("my-bucketname")
    *            .object("my-objectname")
    *            .expiry(24 * 60 * 60)
@@ -892,7 +848,7 @@ public CompletableFuture composeObject(ComposeObjectArgs ar
    * String url =
    *    minioAsyncClient.getPresignedObjectUrl(
    *        GetPresignedObjectUrlArgs.builder()
-   *            .method(Method.PUT)
+   *            .method(Http.Method.PUT)
    *            .bucket("my-bucketname")
    *            .object("my-objectname")
    *            .expiry(1, TimeUnit.DAYS)
@@ -905,7 +861,7 @@ public CompletableFuture composeObject(ComposeObjectArgs ar
    * String url =
    *    minioAsyncClient.getPresignedObjectUrl(
    *        GetPresignedObjectUrlArgs.builder()
-   *            .method(Method.GET)
+   *            .method(Http.Method.GET)
    *            .bucket("my-bucketname")
    *            .object("my-objectname")
    *            .expiry(2, TimeUnit.HOURS)
@@ -915,56 +871,38 @@ public CompletableFuture composeObject(ComposeObjectArgs ar
    *
    * @param args {@link GetPresignedObjectUrlArgs} object.
    * @return String - URL string.
-   * @throws ErrorResponseException thrown to indicate S3 service returned an error response.
-   * @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 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.
-   * @throws ServerException
+   * @throws MinioException thrown to indicate SDK exception.
    */
-  public String getPresignedObjectUrl(GetPresignedObjectUrlArgs args)
-      throws ErrorResponseException, InsufficientDataException, InternalException,
-          InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException,
-          XmlParserException, ServerException {
+  public String getPresignedObjectUrl(GetPresignedObjectUrlArgs args) throws MinioException {
     checkArgs(args);
 
-    byte[] body =
-        (args.method() == Method.PUT || args.method() == Method.POST) ? HttpUtils.EMPTY_BODY : null;
-
-    Multimap queryParams = newMultimap(args.extraQueryParams());
-    if (args.versionId() != null) queryParams.put("versionId", args.versionId());
-
     String region = null;
     try {
-      region = getRegionAsync(args.bucket(), args.region()).get();
+      region = getRegion(args.bucket(), args.region()).get();
     } catch (InterruptedException e) {
       throw new RuntimeException(e);
     } catch (ExecutionException e) {
       throwEncapsulatedException(e);
     }
 
-    if (provider == null) {
-      HttpUrl url = buildUrl(args.method(), args.bucket(), args.object(), region, queryParams);
-      return url.toString();
+    Http.QueryParameters queryParams = new Http.QueryParameters();
+    if (args.versionId() == null) queryParams.put("versionId", args.versionId());
+
+    Credentials credentials = provider == null ? null : provider.fetch();
+    if (credentials != null && credentials.sessionToken() != null) {
+      queryParams.put(Http.Headers.X_AMZ_SECURITY_TOKEN, credentials.sessionToken());
     }
 
-    Credentials creds = provider.fetch();
-    if (creds.sessionToken() != null) queryParams.put("X-Amz-Security-Token", creds.sessionToken());
-    HttpUrl url = buildUrl(args.method(), args.bucket(), args.object(), region, queryParams);
-    Request request =
-        createRequest(
-            url,
-            args.method(),
-            args.extraHeaders() == null ? null : httpHeaders(args.extraHeaders()),
-            body,
-            0,
-            creds);
-    url = Signer.presignV4(request, region, creds.accessKey(), creds.secretKey(), args.expiry());
-    return url.toString();
+    return Http.S3Request.builder()
+        .userAgent(userAgent)
+        .method(args.method())
+        .args(args)
+        .queryParams(queryParams)
+        .build()
+        .toPresignedRequest(baseUrl, region, credentials, args.expiry())
+        .httpRequest()
+        .url()
+        .toString();
   }
 
   /**
@@ -1014,21 +952,10 @@ public String getPresignedObjectUrl(GetPresignedObjectUrlArgs args)
    *
    * @param policy Post policy of an object.
    * @return {@code Map} - Contains form-data to upload an object using POST method.
-   * @throws ErrorResponseException thrown to indicate S3 service returned an error response.
-   * @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 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.
+   * @throws MinioException thrown to indicate SDK exception.
    * @see PostPolicy
    */
-  public Map getPresignedPostFormData(PostPolicy policy)
-      throws ErrorResponseException, InsufficientDataException, InternalException,
-          InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException,
-          ServerException, XmlParserException {
+  public Map getPresignedPostFormData(PostPolicy policy) throws MinioException {
     if (provider == null) {
       throw new IllegalArgumentException(
           "Anonymous access does not require presigned post form-data");
@@ -1036,7 +963,7 @@ public Map getPresignedPostFormData(PostPolicy policy)
 
     String region = null;
     try {
-      region = getRegionAsync(policy.bucket(), null).get();
+      region = getRegion(policy.bucket(), null).get();
     } catch (InterruptedException e) {
       throw new RuntimeException(e);
     } catch (ExecutionException e) {
@@ -1073,26 +1000,18 @@ public Map getPresignedPostFormData(PostPolicy policy)
    *
    * @param args {@link RemoveObjectArgs} object.
    * @return {@link CompletableFuture}<{@link Void}> object.
-   * @throws ErrorResponseException thrown to indicate S3 service returned an error response.
-   * @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 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.
+   * @throws MinioException thrown to indicate SDK exception.
    */
-  public CompletableFuture removeObject(RemoveObjectArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture removeObject(RemoveObjectArgs args) throws MinioException {
     checkArgs(args);
     return executeDeleteAsync(
             args,
             args.bypassGovernanceMode()
-                ? newMultimap("x-amz-bypass-governance-retention", "true")
+                ? new Http.Headers("x-amz-bypass-governance-retention", "true")
                 : null,
-            (args.versionId() != null) ? newMultimap("versionId", args.versionId()) : null)
+            (args.versionId() != null)
+                ? new Http.QueryParameters("versionId", args.versionId())
+                : null)
         .thenAccept(response -> response.close());
   }
 
@@ -1105,35 +1024,36 @@ public CompletableFuture removeObject(RemoveObjectArgs args)
    * objects.add(new DeleteObject("my-objectname1"));
    * objects.add(new DeleteObject("my-objectname2"));
    * objects.add(new DeleteObject("my-objectname3"));
-   * Iterable> results =
+   * Iterable> results =
    *     minioAsyncClient.removeObjects(
    *         RemoveObjectsArgs.builder().bucket("my-bucketname").objects(objects).build());
-   * for (Result result : results) {
-   *   DeleteError error = result.get();
+   * for (Result result : results) {
+   *   DeleteResult.Error error = result.get();
    *   System.out.println(
    *       "Error in deleting object " + error.objectName() + "; " + error.message());
    * }
    * }
* * @param args {@link RemoveObjectsArgs} object. - * @return {@code Iterable>} - Lazy iterator contains object removal status. + * @return {@code Iterable>} - Lazy iterator contains object removal + * status. */ - public Iterable> removeObjects(RemoveObjectsArgs args) { + public Iterable> removeObjects(RemoveObjectsArgs args) { checkArgs(args); - return new Iterable>() { + return new Iterable>() { @Override - public Iterator> iterator() { - return new Iterator>() { - private Result error = null; - private Iterator errorIterator = null; + public Iterator> iterator() { + return new Iterator>() { + private Result error = null; + private Iterator errorIterator = null; private boolean completed = false; - private Iterator objectIter = args.objects().iterator(); + private Iterator objectIter = args.objects().iterator(); private void setError() { error = null; while (errorIterator.hasNext()) { - DeleteError deleteError = errorIterator.next(); + DeleteResult.Error deleteError = errorIterator.next(); if (!"NoSuchVersion".equals(deleteError.code())) { error = new Result<>(deleteError); break; @@ -1147,7 +1067,7 @@ private synchronized void populate() { } try { - List objectList = new LinkedList<>(); + List objectList = new LinkedList<>(); while (objectIter.hasNext() && objectList.size() < 1000) { objectList.add(objectIter.next()); } @@ -1157,34 +1077,28 @@ private synchronized void populate() { DeleteObjectsResponse response = null; try { response = - deleteObjectsAsync( - args.bucket(), - args.region(), - objectList, - true, - args.bypassGovernanceMode(), - args.extraHeaders(), - args.extraQueryParams()) + deleteObjects( + DeleteObjectsArgs.builder() + .extraHeaders(args.extraHeaders()) + .extraQueryParams(args.extraQueryParams()) + .bucket(args.bucket()) + .region(args.region()) + .objects(objectList) + .quiet(true) + .bypassGovernanceMode(args.bypassGovernanceMode()) + .build()) .get(); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throwEncapsulatedException(e); } - if (!response.result().errorList().isEmpty()) { - errorIterator = response.result().errorList().iterator(); + if (!response.result().errors().isEmpty()) { + errorIterator = response.result().errors().iterator(); setError(); completed = true; } - } catch (ErrorResponseException - | InsufficientDataException - | InternalException - | InvalidKeyException - | InvalidResponseException - | IOException - | NoSuchAlgorithmException - | ServerException - | XmlParserException e) { + } catch (MinioException e) { error = new Result<>(e); completed = true; } @@ -1205,11 +1119,11 @@ public boolean hasNext() { } @Override - public Result next() { + public Result next() { if (!hasNext()) throw new NoSuchElementException(); if (this.error != null) { - Result error = this.error; + Result error = this.error; this.error = null; return error; } @@ -1251,18 +1165,15 @@ public void remove() { * * @param args {@link RestoreObjectArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture restoreObject(RestoreObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture restoreObject(RestoreObjectArgs args) throws MinioException { checkArgs(args); - return executePostAsync(args, null, newMultimap("restore", ""), args.request()) + return executePostAsync( + args, + null, + new Http.QueryParameters("restore", ""), + new Http.Body(args.request(), null, null, null)) .thenAccept(response -> response.close()); } @@ -1307,39 +1218,33 @@ public CompletableFuture restoreObject(RestoreObjectArgs args) * * @param args Instance of {@link ListObjectsArgs} built using the builder * @return {@code Iterable>} - Lazy iterator contains object information. - * @throws XmlParserException upon parsing response xml */ public Iterable> listObjects(ListObjectsArgs args) { if (args.includeVersions() || args.versionIdMarker() != null) { - return listObjectVersions(args); + return objectVersionLister(new ListObjectVersionsArgs(args)); } if (args.useApiVersion1()) { - return listObjectsV1(args); + return objectV1Lister(new ListObjectsV1Args(args)); } - return listObjectsV2(args); + return objectV2Lister(new ListObjectsV2Args(args)); } /** * Lists bucket information of all buckets. * *
Example:{@code
-   * CompletableFuture> future = minioAsyncClient.listBuckets();
+   * CompletableFuture> future = minioAsyncClient.listBuckets();
    * }
* - * @return {@link CompletableFuture}<{@link List}<{@link Bucket}>> 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 CompletableFuture}<{@link List}<{@link + * ListAllMyBucketsResult.Bucket}>> object. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture> listBuckets() - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return listBucketsAsync(null, null, null, null, null, null) + public CompletableFuture> listBuckets() + throws MinioException { + return listBucketsApi(ListBucketsArgs.builder().build()) .thenApply( response -> { return response.result().buckets(); @@ -1350,41 +1255,45 @@ public CompletableFuture> listBuckets() * Lists bucket information of all buckets. * *
Example:{@code
-   * Iterable> results = minioAsyncClient.listBuckets(ListBucketsArgs.builder().build());
-   * for (Result result : results) {
+   * Iterable> results = minioAsyncClient.listBuckets(ListBucketsArgs.builder().build());
+   * for (Result result : results) {
    *   Bucket bucket = result.get();
    *   System.out.println(String.format("Bucket: %s, Region: %s, CreationDate: %s", bucket.name(), bucket.bucketRegion(), bucket.creationDate()));
    * }
    * }
* - * @return {@link Iterable}<{@link List}<{@link Bucket}>> object. + * @return {@link Iterable}<{@link List}<{@link ListAllMyBucketsResult.Bucket}>> + * object. */ - public Iterable> listBuckets(ListBucketsArgs args) { - return new Iterable>() { + public Iterable> listBuckets(ListBucketsArgs args) { + return new Iterable>() { @Override - public Iterator> iterator() { - return new Iterator>() { + public Iterator> iterator() { + return new Iterator>() { private ListAllMyBucketsResult result = null; - private Result error = null; - private Iterator iterator = null; + private Result error = null; + private Iterator iterator = null; private boolean completed = false; private synchronized void populate() { if (completed) return; try { - this.iterator = new LinkedList().iterator(); + this.iterator = new LinkedList().iterator(); try { ListBucketsResponse response = - listBucketsAsync( - args.bucketRegion(), - args.maxBuckets(), - args.prefix(), - (result == null) - ? args.continuationToken() - : result.continuationToken(), - args.extraHeaders(), - args.extraQueryParams()) + listBucketsApi( + ListBucketsArgs.builder() + .extraHeaders(args.extraHeaders()) + .extraQueryParams(args.extraQueryParams()) + .bucketRegion(args.bucketRegion()) + .maxBuckets(args.maxBuckets()) + .prefix(args.prefix()) + .continuationToken( + result == null + ? args.continuationToken() + : result.continuationToken()) + .build()) .get(); this.result = response.result(); } catch (InterruptedException e) { @@ -1393,15 +1302,7 @@ private synchronized void populate() { throwEncapsulatedException(e); } this.iterator = this.result.buckets().iterator(); - } catch (ErrorResponseException - | InsufficientDataException - | InternalException - | InvalidKeyException - | InvalidResponseException - | IOException - | NoSuchAlgorithmException - | ServerException - | XmlParserException e) { + } catch (MinioException e) { this.error = new Result<>(e); completed = true; } @@ -1430,7 +1331,7 @@ public boolean hasNext() { } @Override - public Result next() { + public Result next() { if (this.completed) throw new NoSuchElementException(); if (this.error == null && this.iterator == null) { populate(); @@ -1448,7 +1349,7 @@ public Result next() { return this.error; } - Bucket item = null; + ListAllMyBucketsResult.Bucket item = null; if (this.iterator.hasNext()) { item = this.iterator.next(); } @@ -1480,16 +1381,9 @@ public void remove() { * * @param args {@link BucketExistsArgs} object. * @return {@link CompletableFuture}<{@link Boolean}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture bucketExists(BucketExistsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture bucketExists(BucketExistsArgs args) throws MinioException { return executeHeadAsync(args, null, null) .exceptionally( e -> { @@ -1547,52 +1441,12 @@ public CompletableFuture bucketExists(BucketExistsArgs args) * } * * @param args Object with bucket name, region and lock functionality - * @return {@link CompletableFuture}<{@link Void}> 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 CompletableFuture}<{@link GenericResponse}> object. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture makeBucket(MakeBucketArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture makeBucket(MakeBucketArgs args) throws MinioException { checkArgs(args); - - String region = args.region(); - if (this.region != null && !this.region.isEmpty()) { - // Error out if region does not match with region passed via constructor. - if (region != null && !region.equals(this.region)) { - throw new IllegalArgumentException( - "region must be " + this.region + ", but passed " + region); - } - - region = this.region; - } - - if (region == null) { - region = US_EAST_1; - } - - Multimap headers = - args.objectLock() ? newMultimap("x-amz-bucket-object-lock-enabled", "true") : null; - final String location = region; - - return executeAsync( - Method.PUT, - args.bucket(), - null, - location, - httpHeaders(merge(args.extraHeaders(), headers)), - args.extraQueryParams(), - location.equals(US_EAST_1) ? null : new CreateBucketConfiguration(location), - 0) - .thenAccept( - response -> { - regionCache.put(args.bucket(), location); - response.close(); - }); + return createBucket(new CreateBucketArgs(args)); } /** @@ -1605,18 +1459,16 @@ public CompletableFuture makeBucket(MakeBucketArgs args) * * @param args {@link SetBucketVersioningArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture setBucketVersioning(SetBucketVersioningArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executePutAsync(args, null, newMultimap("versioning", ""), args.config(), 0) + return executePutAsync( + args, + null, + new Http.QueryParameters("versioning", ""), + new Http.Body(args.config(), null, null, null)) .thenAccept(response -> response.close()); } @@ -1631,19 +1483,12 @@ public CompletableFuture setBucketVersioning(SetBucketVersioningArgs args) * * @param args {@link GetBucketVersioningArgs} object. * @return {@link CompletableFuture}<{@link VersioningConfiguration}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getBucketVersioning( - GetBucketVersioningArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + GetBucketVersioningArgs args) throws MinioException { checkArgs(args); - return executeGetAsync(args, null, newMultimap("versioning", "")) + return executeGetAsync(args, null, new Http.QueryParameters("versioning", "")) .thenApply( response -> { try { @@ -1668,18 +1513,16 @@ public CompletableFuture getBucketVersioning( * * @param args {@link SetObjectLockConfigurationArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture setObjectLockConfiguration(SetObjectLockConfigurationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executePutAsync(args, null, newMultimap("object-lock", ""), args.config(), 0) + return executePutAsync( + args, + null, + new Http.QueryParameters("object-lock", ""), + new Http.Body(args.config(), null, null, null)) .thenAccept(response -> response.close()); } @@ -1693,20 +1536,16 @@ public CompletableFuture setObjectLockConfiguration(SetObjectLockConfigura * * @param args {@link DeleteObjectLockConfigurationArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture deleteObjectLockConfiguration( - DeleteObjectLockConfigurationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + DeleteObjectLockConfigurationArgs args) throws MinioException { checkArgs(args); return executePutAsync( - args, null, newMultimap("object-lock", ""), new ObjectLockConfiguration(), 0) + args, + null, + new Http.QueryParameters("object-lock", ""), + new Http.Body(new ObjectLockConfiguration(), null, null, null)) .thenAccept(response -> response.close()); } @@ -1721,19 +1560,12 @@ args, null, newMultimap("object-lock", ""), new ObjectLockConfiguration(), 0) * * @param args {@link GetObjectLockConfigurationArgs} object. * @return {@link CompletableFuture}<{@link ObjectLockConfiguration}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getObjectLockConfiguration( - GetObjectLockConfigurationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + GetObjectLockConfigurationArgs args) throws MinioException { checkArgs(args); - return executeGetAsync(args, null, newMultimap("object-lock", "")) + return executeGetAsync(args, null, new Http.QueryParameters("object-lock", "")) .thenApply( response -> { try { @@ -1763,27 +1595,20 @@ public CompletableFuture getObjectLockConfiguration( * * @param args {@link SetObjectRetentionArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture setObjectRetention(SetObjectRetentionArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("retention", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("retention", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executePutAsync( args, args.bypassGovernanceMode() - ? newMultimap("x-amz-bypass-governance-retention", "True") + ? new Http.Headers("x-amz-bypass-governance-retention", "True") : null, queryParams, - args.config(), - 0) + new Http.Body(args.config(), null, null, null)) .thenAccept(response -> response.close()); } @@ -1801,18 +1626,12 @@ public CompletableFuture setObjectRetention(SetObjectRetentionArgs args) * * @param args {@link GetObjectRetentionArgs} object. * @return {@link CompletableFuture}<{@link Retention}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getObjectRetention(GetObjectRetentionArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("retention", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("retention", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executeGetAsync(args, null, queryParams) .exceptionally( @@ -1864,20 +1683,15 @@ public CompletableFuture getObjectRetention(GetObjectRetentionArgs ar * * @param args {@link EnableObjectLegalHoldArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture enableObjectLegalHold(EnableObjectLegalHoldArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("legal-hold", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("legal-hold", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); - return executePutAsync(args, null, queryParams, new LegalHold(true), 0) + return executePutAsync( + args, null, queryParams, new Http.Body(new LegalHold(true), null, null, null)) .thenAccept(response -> response.close()); } @@ -1895,20 +1709,15 @@ public CompletableFuture enableObjectLegalHold(EnableObjectLegalHoldArgs a * * @param args {@link DisableObjectLegalHoldArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture disableObjectLegalHold(DisableObjectLegalHoldArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("legal-hold", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("legal-hold", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); - return executePutAsync(args, null, queryParams, new LegalHold(false), 0) + return executePutAsync( + args, null, queryParams, new Http.Body(new LegalHold(false), null, null, null)) .thenAccept(response -> response.close()); } @@ -1927,18 +1736,12 @@ public CompletableFuture disableObjectLegalHold(DisableObjectLegalHoldArgs * * @param args {@link IsObjectLegalHoldEnabledArgs} object. * @return {@link CompletableFuture}<{@link Boolean}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture isObjectLegalHoldEnabled(IsObjectLegalHoldEnabledArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("legal-hold", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("legal-hold", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executeGetAsync(args, null, queryParams) .exceptionally( @@ -1987,21 +1790,225 @@ public CompletableFuture isObjectLegalHoldEnabled(IsObjectLegalHoldEnab * * @param args {@link RemoveBucketArgs} bucket. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture removeBucket(RemoveBucketArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture removeBucket(RemoveBucketArgs args) throws MinioException { checkArgs(args); return executeDeleteAsync(args, null, null) .thenAccept(response -> regionCache.remove(args.bucket())); } + private CompletableFuture> uploadParts( + PutObjectBaseArgs args, + String uploadId, + PartReader partReader, + boolean computeSha256, + boolean sha256Algorithm, + ByteBuffer buffer, + int parallelUploads, + long partSize) + throws MinioException { + ByteBufferPool pool = new ByteBufferPool(parallelUploads, partSize); + ExecutorService executorService = Executors.newFixedThreadPool(parallelUploads); + List> partFutures = new ArrayList<>(); + + while (true) { + if (buffer == null) { + buffer = pool.get(); + partReader.read(buffer); + } + + final int partNumber = partReader.partNumber(); + final Http.Headers checksumHeaders = + Checksum.makeHeaders(partReader.hashers(), computeSha256, sha256Algorithm); + + final ByteBuffer finalBuffer = buffer; + CompletableFuture partFuture = + CompletableFuture.supplyAsync( + () -> { + try { + return uploadPart( + new UploadPartArgs( + args, uploadId, partNumber, finalBuffer, checksumHeaders)) + .thenApply( + response -> { + try { + pool.put(finalBuffer); + } catch (IOException e) { + throw new CompletionException(new MinioException(e)); + } + return response.part(); + }) + .join(); + } catch (MinioException e) { + throw new CompletionException(e); + } + }, + executorService); + partFutures.add(partFuture); + + buffer = null; + + if (partReader.partCount() == partReader.partNumber()) break; + } + + // Combine all part futures into a single future + return CompletableFuture.allOf(partFutures.toArray(new CompletableFuture[0])) + .thenApply( + v -> { + List parts = new ArrayList<>(); + for (CompletableFuture future : partFutures) { + parts.add(future.join()); // Safe to use join() here since all futures are complete + } + return parts; + }); + } + + private CompletableFuture putObject( + PutObjectBaseArgs args, Object fileStreamData, MediaType contentType, boolean computeSha256) + throws MinioException { + RandomAccessFile file = null; + InputStream stream = null; + byte[] data = null; + if (fileStreamData instanceof RandomAccessFile) file = (RandomAccessFile) fileStreamData; + if (fileStreamData instanceof InputStream) stream = (InputStream) fileStreamData; + if (fileStreamData instanceof byte[]) data = (byte[]) fileStreamData; + + Checksum.Algorithm algorithm = args.checksum(); + boolean sha256Algorithm = algorithm == Checksum.Algorithm.SHA256; + List algorithmList = new ArrayList<>(); + algorithmList.add(algorithm == null ? Checksum.Algorithm.CRC32C : algorithm); + if (computeSha256 && !sha256Algorithm) algorithmList.add(Checksum.Algorithm.SHA256); + Checksum.Algorithm[] algorithms = algorithmList.toArray(new Checksum.Algorithm[0]); + + PartReader partReader = null; + ByteBuffer buffer = null; + int partCount = args.partCount(); + + if (stream != null) { + partReader = + new PartReader(stream, args.objectSize(), args.partSize(), args.partCount(), algorithms); + buffer = new ByteBuffer(partReader.partCount() == 1 ? args.objectSize() : args.partSize()); + partReader.read(buffer); + partCount = partReader.partCount(); + } + + if (partCount == 1) { + if (stream != null) { + return putObject( + new PutObjectAPIArgs( + args, + buffer, + contentType, + Checksum.makeHeaders(partReader.hashers(), computeSha256, sha256Algorithm))); + } + + if (args.objectSize() == null) { + throw new MinioException("object size is null; this should not happen"); + } + long length = args.objectSize(); + + Map hashers = Checksum.newHasherMap(algorithms); + + if (file != null) { + try { + long position = file.getFilePointer(); + Checksum.update(hashers, file, length); + file.seek(position); + } catch (IOException e) { + throw new MinioException(e); + } + return putObject( + new PutObjectAPIArgs( + args, + file, + length, + contentType, + Checksum.makeHeaders(hashers, computeSha256, sha256Algorithm))); + } + + Checksum.update(hashers, data, (int) length); + return putObject( + new PutObjectAPIArgs( + args, + data, + (int) length, + contentType, + Checksum.makeHeaders(hashers, computeSha256, sha256Algorithm))); + } + + if (args.checksum() != null && !args.checksum().compositeSupport()) { + throw new IllegalArgumentException( + "unsupported checksum " + args.checksum() + " for multipart upload"); + } + + if (file != null) { + partReader = + new PartReader(file, args.objectSize(), args.partSize(), args.partCount(), algorithms); + buffer = new ByteBuffer(args.partSize()); + partReader.read(buffer); + } + + int parallelUploads = args.parallelUploads(); + if (parallelUploads <= 0) parallelUploads = 1; + if (partReader.partCount() > 0 && parallelUploads > partReader.partCount()) { + parallelUploads = partReader.partCount(); + } + + final PartReader finalPartReader = partReader; + final ByteBuffer finalBuffer = buffer; + final int finalParallelUploads = parallelUploads; + return createMultipartUpload(new CreateMultipartUploadArgs(args, contentType)) + .exceptionally( + e -> { + throw new CompletionException(e); + }) + .thenCompose( + response -> { + try { + return uploadParts( + args, + response.result().uploadId(), + finalPartReader, + computeSha256, + sha256Algorithm, + finalBuffer, + finalParallelUploads, + args.partSize()) + .exceptionally( + e -> { + try { + abortMultipartUpload( + new AbortMultipartUploadArgs(args, response.result().uploadId())); + } catch (MinioException ex) { + // FIXME: log and ignore this error + } + throw new CompletionException(e); + }) + .thenCompose( + partList -> { + try { + return completeMultipartUpload( + new CompleteMultipartUploadArgs( + args, + response.result().uploadId(), + partList.toArray(new Part[0]))); + } catch (MinioException e) { + try { + abortMultipartUpload( + new AbortMultipartUploadArgs(args, response.result().uploadId())); + } catch (MinioException ex) { + // FIXME: log and ignore this error + } + throw new CompletionException(e); + } + }); + } catch (MinioException e) { + throw new CompletionException(e); + } + }); + } + /** * Uploads data from a stream to an object. * @@ -2048,25 +2055,21 @@ public CompletableFuture removeBucket(RemoveBucketArgs args) * * @param args {@link PutObjectArgs} object. * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture putObject(PutObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - args.validateSse(this.baseUrl); - return putObjectAsync( - args, - args.stream(), - args.objectSize(), - args.partSize(), - args.partCount(), - args.contentType()); + args.validateSse(this.baseUrl.isHttps()); + try { + return putObject( + args, + args.stream() != null ? args.stream() : args.data(), + args.contentType(), + !this.baseUrl.isHttps()); + } catch (IOException e) { + throw new MinioException(e); + } } /** @@ -2090,50 +2093,47 @@ public CompletableFuture putObject(PutObjectArgs args) * * @param args {@link UploadObjectArgs} object. * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture uploadObject(UploadObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - args.validateSse(this.baseUrl); - final RandomAccessFile file = new RandomAccessFile(args.filename(), "r"); - return putObjectAsync( - args, file, args.objectSize(), args.partSize(), args.partCount(), args.contentType()) - .exceptionally( - e -> { - try { - file.close(); - } catch (IOException ex) { - throw new CompletionException(ex); - } + args.validateSse(this.baseUrl.isHttps()); + try { + final RandomAccessFile file = new RandomAccessFile(args.filename(), "r"); + return putObject(args, file, args.contentType(), !this.baseUrl.isHttps()) + .exceptionally( + e -> { + try { + file.close(); + } catch (IOException ex) { + throw new CompletionException(new MinioException(ex)); + } - Throwable ex = e.getCause(); + Throwable ex = e.getCause(); - if (ex instanceof CompletionException) { - ex = ((CompletionException) ex).getCause(); - } + if (ex instanceof CompletionException) { + ex = ((CompletionException) ex).getCause(); + } - if (ex instanceof ExecutionException) { - ex = ((ExecutionException) ex).getCause(); - } + if (ex instanceof ExecutionException) { + ex = ((ExecutionException) ex).getCause(); + } - throw new CompletionException(ex); - }) - .thenApply( - objectWriteResponse -> { - try { - file.close(); - } catch (IOException e) { - throw new CompletionException(e); - } - return objectWriteResponse; - }); + throw new CompletionException(ex); + }) + .thenApply( + objectWriteResponse -> { + try { + file.close(); + } catch (IOException e) { + throw new CompletionException(new MinioException(e)); + } + return objectWriteResponse; + }); + } catch (IOException e) { + throw new MinioException(e); + } } /** @@ -2147,18 +2147,11 @@ public CompletableFuture uploadObject(UploadObjectArgs args * * @param args {@link GetBucketPolicyArgs} object. * @return {@link CompletableFuture}<{@link String}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture getBucketPolicy(GetBucketPolicyArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture getBucketPolicy(GetBucketPolicyArgs args) throws MinioException { checkArgs(args); - return executeGetAsync(args, null, newMultimap("policy", "")) + return executeGetAsync(args, null, new Http.QueryParameters("policy", "")) .exceptionally( e -> { Throwable ex = e.getCause(); @@ -2212,7 +2205,7 @@ public CompletableFuture getBucketPolicy(GetBucketPolicyArgs args) return new String(buf, 0, bytesRead, StandardCharsets.UTF_8); } catch (IOException e) { - throw new CompletionException(e); + throw new CompletionException(new MinioException(e)); } finally { response.close(); } @@ -2251,23 +2244,15 @@ public CompletableFuture getBucketPolicy(GetBucketPolicyArgs args) * * @param args {@link SetBucketPolicyArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture setBucketPolicy(SetBucketPolicyArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture setBucketPolicy(SetBucketPolicyArgs args) throws MinioException { checkArgs(args); return executePutAsync( args, - newMultimap("Content-Type", "application/json"), - newMultimap("policy", ""), - args.config(), - 0) + null, + new Http.QueryParameters("policy", ""), + new Http.Body(args.config(), Http.JSON_MEDIA_TYPE, null, null)) .thenAccept(response -> response.close()); } @@ -2282,18 +2267,12 @@ public CompletableFuture setBucketPolicy(SetBucketPolicyArgs args) * * @param args {@link DeleteBucketPolicyArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture deleteBucketPolicy(DeleteBucketPolicyArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executeDeleteAsync(args, null, newMultimap("policy", "")) + return executeDeleteAsync(args, null, new Http.QueryParameters("policy", "")) .exceptionally( e -> { Throwable ex = e.getCause(); @@ -2344,18 +2323,16 @@ public CompletableFuture deleteBucketPolicy(DeleteBucketPolicyArgs args) * * @param args {@link SetBucketLifecycleArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture setBucketLifecycle(SetBucketLifecycleArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executePutAsync(args, null, newMultimap("lifecycle", ""), args.config(), 0) + return executePutAsync( + args, + null, + new Http.QueryParameters("lifecycle", ""), + new Http.Body(args.config(), null, null, null)) .thenAccept(response -> response.close()); } @@ -2369,18 +2346,12 @@ public CompletableFuture setBucketLifecycle(SetBucketLifecycleArgs args) * * @param args {@link DeleteBucketLifecycleArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture deleteBucketLifecycle(DeleteBucketLifecycleArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executeDeleteAsync(args, null, newMultimap("lifecycle", "")) + return executeDeleteAsync(args, null, new Http.QueryParameters("lifecycle", "")) .thenAccept(response -> response.close()); } @@ -2396,18 +2367,12 @@ public CompletableFuture deleteBucketLifecycle(DeleteBucketLifecycleArgs a * @param args {@link GetBucketLifecycleArgs} object. * @return {@link LifecycleConfiguration} object. * @return {@link CompletableFuture}<{@link LifecycleConfiguration}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getBucketLifecycle(GetBucketLifecycleArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executeGetAsync(args, null, newMultimap("lifecycle", "")) + return executeGetAsync(args, null, new Http.QueryParameters("lifecycle", "")) .exceptionally( e -> { Throwable ex = e.getCause(); @@ -2454,19 +2419,12 @@ public CompletableFuture getBucketLifecycle(GetBucketLif * * @param args {@link GetBucketNotificationArgs} object. * @return {@link CompletableFuture}<{@link NotificationConfiguration}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getBucketNotification( - GetBucketNotificationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + GetBucketNotificationArgs args) throws MinioException { checkArgs(args); - return executeGetAsync(args, null, newMultimap("notification", "")) + return executeGetAsync(args, null, new Http.QueryParameters("notification", "")) .thenApply( response -> { try { @@ -2505,18 +2463,16 @@ public CompletableFuture getBucketNotification( * * @param args {@link SetBucketNotificationArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture setBucketNotification(SetBucketNotificationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executePutAsync(args, null, newMultimap("notification", ""), args.config(), 0) + return executePutAsync( + args, + null, + new Http.QueryParameters("notification", ""), + new Http.Body(args.config(), null, null, null)) .thenAccept(response -> response.close()); } @@ -2530,19 +2486,16 @@ public CompletableFuture setBucketNotification(SetBucketNotificationArgs a * * @param args {@link DeleteBucketNotificationArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture deleteBucketNotification(DeleteBucketNotificationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); return executePutAsync( - args, null, newMultimap("notification", ""), new NotificationConfiguration(), 0) + args, + null, + new Http.QueryParameters("notification", ""), + new Http.Body(new NotificationConfiguration(null, null, null, null), null, null, null)) .thenAccept(response -> response.close()); } @@ -2557,19 +2510,12 @@ args, null, newMultimap("notification", ""), new NotificationConfiguration(), 0) * * @param args {@link GetBucketReplicationArgs} object. * @return {@link CompletableFuture}<{@link ReplicationConfiguration}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getBucketReplication( - GetBucketReplicationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + GetBucketReplicationArgs args) throws MinioException { checkArgs(args); - return executeGetAsync(args, null, newMultimap("replication", "")) + return executeGetAsync(args, null, new Http.QueryParameters("replication", "")) .exceptionally( e -> { Throwable ex = e.getCause(); @@ -2638,25 +2584,18 @@ public CompletableFuture getBucketReplication( * * @param args {@link SetBucketReplicationArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture setBucketReplication(SetBucketReplicationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); return executePutAsync( args, (args.objectLockToken() != null) - ? newMultimap("x-amz-bucket-object-lock-token", args.objectLockToken()) + ? new Http.Headers("x-amz-bucket-object-lock-token", args.objectLockToken()) : null, - newMultimap("replication", ""), - args.config(), - 0) + new Http.QueryParameters("replication", ""), + new Http.Body(args.config(), null, null, null)) .thenAccept(response -> response.close()); } @@ -2670,18 +2609,12 @@ public CompletableFuture setBucketReplication(SetBucketReplicationArgs arg * * @param args {@link DeleteBucketReplicationArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture deleteBucketReplication(DeleteBucketReplicationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executeDeleteAsync(args, null, newMultimap("replication", "")) + return executeDeleteAsync(args, null, new Http.QueryParameters("replication", "")) .thenAccept(response -> response.close()); } @@ -2714,25 +2647,14 @@ public CompletableFuture deleteBucketReplication(DeleteBucketReplicationAr * @param args {@link ListenBucketNotificationArgs} object. * @return {@code CloseableIterator>} - Lazy closable iterator * contains event records. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public CloseableIterator> listenBucketNotification( - ListenBucketNotificationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + ListenBucketNotificationArgs args) throws MinioException { checkArgs(args); - Multimap queryParams = - newMultimap("prefix", args.prefix(), "suffix", args.suffix()); + Http.QueryParameters queryParams = + new Http.QueryParameters("prefix", args.prefix(), "suffix", args.suffix()); for (String event : args.events()) { queryParams.put("events", event); } @@ -2784,36 +2706,30 @@ public CloseableIterator> listenBucketNotification( * * @param args instance of {@link SelectObjectContentArgs} * @return {@link SelectResponseStream} - Contains filtered records and progress. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public SelectResponseStream selectObjectContent(SelectObjectContentArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { checkArgs(args); - args.validateSsec(this.baseUrl); + args.validateSsec(this.baseUrl.isHttps()); Response response = null; try { response = executePostAsync( args, - (args.ssec() != null) ? newMultimap(args.ssec().headers()) : null, - newMultimap("select", "", "select-type", "2"), - new SelectObjectContentRequest( - args.sqlExpression(), - args.requestProgress(), - args.inputSerialization(), - args.outputSerialization(), - args.scanStartRange(), - args.scanEndRange())) + args.ssec() == null ? null : args.ssec().headers(), + new Http.QueryParameters("select", "", "select-type", "2"), + new Http.Body( + new SelectObjectContentRequest( + args.sqlExpression(), + args.requestProgress(), + args.inputSerialization(), + args.outputSerialization(), + args.scanStartRange(), + args.scanEndRange()), + null, + null, + null)) .get(); } catch (InterruptedException e) { throw new RuntimeException(e); @@ -2833,18 +2749,16 @@ public SelectResponseStream selectObjectContent(SelectObjectContentArgs args) * * @param args {@link SetBucketEncryptionArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture setBucketEncryption(SetBucketEncryptionArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executePutAsync(args, null, newMultimap("encryption", ""), args.config(), 0) + return executePutAsync( + args, + null, + new Http.QueryParameters("encryption", ""), + new Http.Body(args.config(), null, null, null)) .thenAccept(response -> response.close()); } @@ -2859,18 +2773,12 @@ public CompletableFuture setBucketEncryption(SetBucketEncryptionArgs args) * * @param args {@link GetBucketEncryptionArgs} object. * @return {@link CompletableFuture}<{@link SseConfiguration}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getBucketEncryption(GetBucketEncryptionArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executeGetAsync(args, null, newMultimap("encryption", "")) + return executeGetAsync(args, null, new Http.QueryParameters("encryption", "")) .exceptionally( e -> { Throwable ex = e.getCause(); @@ -2916,18 +2824,12 @@ public CompletableFuture getBucketEncryption(GetBucketEncrypti * * @param args {@link DeleteBucketEncryptionArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture deleteBucketEncryption(DeleteBucketEncryptionArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executeDeleteAsync(args, null, newMultimap("encryption", "")) + return executeDeleteAsync(args, null, new Http.QueryParameters("encryption", "")) .exceptionally( e -> { Throwable ex = e.getCause(); @@ -2966,18 +2868,11 @@ public CompletableFuture deleteBucketEncryption(DeleteBucketEncryptionArgs * * @param args {@link GetBucketTagsArgs} object. * @return {@link CompletableFuture}<{@link Tags}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture getBucketTags(GetBucketTagsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture getBucketTags(GetBucketTagsArgs args) throws MinioException { checkArgs(args); - return executeGetAsync(args, null, newMultimap("tagging", "")) + return executeGetAsync(args, null, new Http.QueryParameters("tagging", "")) .exceptionally( e -> { Throwable ex = e.getCause(); @@ -3023,18 +2918,15 @@ public CompletableFuture getBucketTags(GetBucketTagsArgs args) * * @param args {@link SetBucketTagsArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture setBucketTags(SetBucketTagsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture setBucketTags(SetBucketTagsArgs args) throws MinioException { checkArgs(args); - return executePutAsync(args, null, newMultimap("tagging", ""), args.tags(), 0) + return executePutAsync( + args, + null, + new Http.QueryParameters("tagging", ""), + new Http.Body(args.tags(), null, null, null)) .thenAccept(response -> response.close()); } @@ -3048,18 +2940,11 @@ public CompletableFuture setBucketTags(SetBucketTagsArgs args) * * @param args {@link DeleteBucketTagsArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture deleteBucketTags(DeleteBucketTagsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture deleteBucketTags(DeleteBucketTagsArgs args) throws MinioException { checkArgs(args); - return executeDeleteAsync(args, null, newMultimap("tagging", "")) + return executeDeleteAsync(args, null, new Http.QueryParameters("tagging", "")) .thenAccept(response -> response.close()); } @@ -3074,18 +2959,11 @@ public CompletableFuture deleteBucketTags(DeleteBucketTagsArgs args) * * @param args {@link GetObjectTagsArgs} object. * @return {@link CompletableFuture}<{@link Tags}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture getObjectTags(GetObjectTagsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture getObjectTags(GetObjectTagsArgs args) throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("tagging", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("tagging", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executeGetAsync(args, null, queryParams) .thenApply( @@ -3117,20 +2995,13 @@ public CompletableFuture getObjectTags(GetObjectTagsArgs args) * * @param args {@link SetObjectTagsArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture setObjectTags(SetObjectTagsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture setObjectTags(SetObjectTagsArgs args) throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("tagging", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("tagging", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); - return executePutAsync(args, null, queryParams, args.tags(), 0) + return executePutAsync(args, null, queryParams, new Http.Body(args.tags(), null, null, null)) .thenAccept(response -> response.close()); } @@ -3144,18 +3015,11 @@ public CompletableFuture setObjectTags(SetObjectTagsArgs args) * * @param args {@link DeleteObjectTagsArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture deleteObjectTags(DeleteObjectTagsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture deleteObjectTags(DeleteObjectTagsArgs args) throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("tagging", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("tagging", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executeDeleteAsync(args, null, queryParams).thenAccept(response -> response.close()); } @@ -3170,18 +3034,12 @@ public CompletableFuture deleteObjectTags(DeleteObjectTagsArgs args) * * @param args {@link GetBucketCorsArgs} object. * @return {@link CompletableFuture}<{@link CORSConfiguration}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getBucketCors(GetBucketCorsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - return executeGetAsync(args, null, newMultimap("cors", "")) + return executeGetAsync(args, null, new Http.QueryParameters("cors", "")) .exceptionally( e -> { Throwable ex = e.getCause(); @@ -3247,18 +3105,15 @@ public CompletableFuture getBucketCors(GetBucketCorsArgs args * * @param args {@link SetBucketCorsArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture setBucketCors(SetBucketCorsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture setBucketCors(SetBucketCorsArgs args) throws MinioException { checkArgs(args); - return executePutAsync(args, null, newMultimap("cors", ""), args.config(), 0) + return executePutAsync( + args, + null, + new Http.QueryParameters("cors", ""), + new Http.Body(args.config(), null, null, null)) .thenAccept(response -> response.close()); } @@ -3272,18 +3127,11 @@ public CompletableFuture setBucketCors(SetBucketCorsArgs args) * * @param args {@link DeleteBucketCorsArgs} object. * @return {@link CompletableFuture}<{@link Void}> 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. + * @throws MinioException thrown to indicate SDK exception. */ - public CompletableFuture deleteBucketCors(DeleteBucketCorsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture deleteBucketCors(DeleteBucketCorsArgs args) throws MinioException { checkArgs(args); - return executeDeleteAsync(args, null, newMultimap("cors", "")) + return executeDeleteAsync(args, null, new Http.QueryParameters("cors", "")) .thenAccept(response -> response.close()); } @@ -3298,18 +3146,12 @@ public CompletableFuture deleteBucketCors(DeleteBucketCorsArgs args) * * @param args {@link GetObjectAclArgs} object. * @return {@link CompletableFuture}<{@link AccessControlPolicy}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getObjectAcl(GetObjectAclArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("acl", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("acl", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executeGetAsync(args, null, queryParams) .thenApply( @@ -3342,23 +3184,16 @@ public CompletableFuture getObjectAcl(GetObjectAclArgs args * * @param args {@link GetObjectAttributesArgs} object. * @return {@link CompletableFuture}<{@link GetObjectAttributesResponse}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture getObjectAttributes( - GetObjectAttributesArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + GetObjectAttributesArgs args) throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("attributes", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("attributes", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); - Multimap headers = HashMultimap.create(); + Http.Headers headers = new Http.Headers(); if (args.maxParts() != null) headers.put("x-amz-max-parts", args.maxParts().toString()); if (args.partNumberMarker() != null) { headers.put("x-amz-part-number-marker", args.partNumberMarker().toString()); @@ -3417,17 +3252,10 @@ public CompletableFuture getObjectAttributes( * * @param args {@link UploadSnowballObjectsArgs} object. * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture uploadSnowballObjects( - UploadSnowballObjectsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + UploadSnowballObjectsArgs args) throws MinioException { checkArgs(args); return CompletableFuture.supplyAsync( @@ -3475,7 +3303,7 @@ public CompletableFuture uploadSnowballObjects( } tarOutputStream.finish(); } catch (IOException e) { - throw new CompletionException(e); + throw new CompletionException(new MinioException(e)); } finally { try { if (tarOutputStream != null) tarOutputStream.flush(); @@ -3487,34 +3315,21 @@ public CompletableFuture uploadSnowballObjects( if (bos != null) bos.close(); if (fos != null) fos.close(); } catch (IOException e) { - throw new CompletionException(e); + throw new CompletionException(new MinioException(e)); } } return baos; }) .thenCompose( baos -> { - Multimap headers = newMultimap(args.extraHeaders()); - headers.putAll(args.genHeaders()); + Http.Headers headers = args.makeHeaders(); headers.put("X-Amz-Meta-Snowball-Auto-Extract", "true"); if (args.stagingFilename() == null) { byte[] data = baos.toByteArray(); try { - return putObjectAsync( - args.bucket(), - args.region(), - args.object(), - data, - data.length, - headers, - args.extraQueryParams()); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { + return putObject(new PutObjectAPIArgs(args, data, data.length, headers)); + } catch (MinioException e) { throw new CompletionException(e); } } @@ -3525,20 +3340,10 @@ public CompletableFuture uploadSnowballObjects( "tarball size " + length + " is more than maximum allowed 5TiB"); } try (RandomAccessFile file = new RandomAccessFile(args.stagingFilename(), "r")) { - return putObjectAsync( - args.bucket(), - args.region(), - args.object(), - file, - length, - headers, - args.extraQueryParams()); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { + return putObject(new PutObjectAPIArgs(args, file, length, headers)); + } catch (IOException e) { + throw new CompletionException(new MinioException(e)); + } catch (MinioException e) { throw new CompletionException(e); } }); @@ -3566,22 +3371,34 @@ public CompletableFuture uploadSnowballObjects( * * @param args {@link PutObjectFanOutArgs} object. * @return {@link CompletableFuture}<{@link PutObjectFanOutResponse}> object. - * @throws ErrorResponseException thrown to indicate presigned POST data failure. - * @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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture putObjectFanOut(PutObjectFanOutArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - args.validateSse(this.baseUrl); + args.validateSse(this.baseUrl.isHttps()); return CompletableFuture.supplyAsync( () -> { + byte[] buf16k = new byte[16384]; // 16KiB buffer for optimization. + ByteBuffer buffer = new ByteBuffer(args.size()); + long bytesWritten = 0; + while (bytesWritten != args.size()) { + try { + int length = args.stream().read(buf16k); + if (length < 0) { + throw new InsufficientDataException( + "insufficient data; expected=" + args.size() + ", got=" + bytesWritten); + } + buffer.write(buf16k, 0, length); + bytesWritten += length; + } catch (IOException e) { + throw new CompletionException(new MinioException(e)); + } catch (MinioException e) { + throw new CompletionException(e); + } + } + // Build POST object data String objectName = "pan-out-" @@ -3617,33 +3434,20 @@ public CompletableFuture putObjectFanOut(PutObjectFanOu multipartBuilder.addFormDataPart( "file", "fanout-content", - new HttpRequestBody(new PartSource(args.stream(), args.size()), null)); + new Http.RequestBody(buffer, Http.DEFAULT_MEDIA_TYPE)); return multipartBuilder.build(); } catch (JsonProcessingException e) { - throw new CompletionException(e); - } catch (ErrorResponseException - | InsufficientDataException - | InternalException - | InvalidKeyException - | InvalidResponseException - | IOException - | NoSuchAlgorithmException - | ServerException - | XmlParserException e) { + throw new CompletionException(new MinioException(e)); + } catch (MinioException e) { throw new CompletionException(e); } }) .thenCompose( body -> { try { - return executePostAsync(args, null, null, body); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { + return executePostAsync(args, null, null, new Http.Body(body)); + } catch (MinioException e) { throw new CompletionException(e); } }) @@ -3660,7 +3464,7 @@ public CompletableFuture putObjectFanOut(PutObjectFanOu return new PutObjectFanOutResponse( response.headers(), args.bucket(), args.region(), results); } catch (IOException e) { - throw new CompletionException(e); + throw new CompletionException(new MinioException(e)); } finally { response.close(); } @@ -3672,196 +3476,389 @@ public CompletableFuture putObjectFanOut(PutObjectFanOu * * @param args {@link PromptObjectArgs} object. * @return {@link CompletableFuture}<{@link PromptObjectResponse}> 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. + * @throws MinioException thrown to indicate SDK exception. */ public CompletableFuture promptObject(PromptObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + throws MinioException { checkArgs(args); - Multimap queryParams = newMultimap("lambdaArn", args.lambdaArn()); - Multimap headers = - merge(newMultimap(args.headers()), newMultimap("Content-Type", "application/json")); + Http.QueryParameters queryParams = new Http.QueryParameters("lambdaArn", args.lambdaArn()); + Http.Headers headers = + Http.Headers.merge( + new Http.Headers(args.headers()), + new Http.Headers(Http.Headers.CONTENT_TYPE, Http.JSON_MEDIA_TYPE.toString())); Map promptArgs = args.promptArgs(); if (promptArgs == null) promptArgs = new HashMap<>(); promptArgs.put("prompt", args.prompt()); - byte[] data = objectMapper.writeValueAsString(promptArgs).getBytes(StandardCharsets.UTF_8); - - return executePostAsync(args, headers, queryParams, data) - .thenApply( - response -> { - return new PromptObjectResponse( - response.headers(), - args.bucket(), - args.region(), - args.object(), - response.body().byteStream()); - }); + try { + byte[] data = objectMapper.writeValueAsString(promptArgs).getBytes(StandardCharsets.UTF_8); + return executePostAsync( + args, headers, queryParams, new Http.Body(data, data.length, null, null, null)) + .thenApply( + response -> { + return new PromptObjectResponse( + response.headers(), + args.bucket(), + args.region(), + args.object(), + response.body().byteStream()); + }); + } catch (JsonProcessingException e) { + throw new MinioException(e); + } } - public static Builder builder() { - return new Builder(); + ///////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////// Higher level ListObjects implementation /////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + + /** Throws encapsulated exception wrapped by {@link ExecutionException}. */ + public void throwEncapsulatedException(ExecutionException e) throws MinioException { + if (e == null) return; + + Throwable ex = e.getCause(); + + if (ex instanceof CompletionException) ex = ((CompletionException) ex).getCause(); + if (ex instanceof ExecutionException) ex = ((ExecutionException) ex).getCause(); + try { + throw ex; + } catch (MinioException exc) { + throw exc; + } catch (Throwable exc) { + throw new RuntimeException(exc.getCause() == null ? exc : exc.getCause()); + } } - /** Argument builder of {@link MinioClient}. */ - public static final class Builder { - private HttpUrl baseUrl; - private String awsS3Prefix; - private String awsDomainSuffix; - private boolean awsDualstack; - private boolean useVirtualStyle; + private abstract class ObjectIterator implements Iterator> { + protected Result error; + protected Iterator itemIterator; + protected Iterator deleteMarkerIterator; + protected Iterator prefixIterator; + protected boolean completed = false; + protected ListObjectsResult listObjectsResult; + protected String lastObjectName; - private String region; - private Provider provider; - private OkHttpClient httpClient; - private boolean closeHttpClient; + protected abstract void populateResult() throws MinioException; + + protected synchronized void populate() { + try { + populateResult(); + } catch (MinioException e) { + this.error = new Result<>(e); + } + + if (this.listObjectsResult != null) { + this.itemIterator = this.listObjectsResult.contents().iterator(); + this.deleteMarkerIterator = this.listObjectsResult.deleteMarkers().iterator(); + this.prefixIterator = this.listObjectsResult.commonPrefixes().iterator(); + } else { + this.itemIterator = new LinkedList().iterator(); + this.deleteMarkerIterator = new LinkedList().iterator(); + this.prefixIterator = new LinkedList().iterator(); + } + } - private void setAwsInfo(String host, boolean https) { - this.awsS3Prefix = null; - this.awsDomainSuffix = null; - this.awsDualstack = false; + @Override + public boolean hasNext() { + if (this.completed) return false; - if (!HttpUtils.HOSTNAME_REGEX.matcher(host).find()) return; + if (this.error == null + && this.itemIterator == null + && this.deleteMarkerIterator == null + && this.prefixIterator == null) { + populate(); + } - if (HttpUtils.AWS_ELB_ENDPOINT_REGEX.matcher(host).find()) { - String[] tokens = host.split("\\.elb\\.amazonaws\\.com", 1)[0].split("\\."); - this.region = tokens[tokens.length - 1]; - return; + if (this.error == null + && !this.itemIterator.hasNext() + && !this.deleteMarkerIterator.hasNext() + && !this.prefixIterator.hasNext() + && this.listObjectsResult.isTruncated()) { + populate(); } - if (!HttpUtils.AWS_ENDPOINT_REGEX.matcher(host).find()) return; + if (this.error != null) return true; + if (this.itemIterator.hasNext()) return true; + if (this.deleteMarkerIterator.hasNext()) return true; + if (this.prefixIterator.hasNext()) return true; + + this.completed = true; + return false; + } - if (!HttpUtils.AWS_S3_ENDPOINT_REGEX.matcher(host).find()) { - throw new IllegalArgumentException("invalid Amazon AWS host " + host); + @Override + public Result next() { + if (this.completed) throw new NoSuchElementException(); + if (this.error == null + && this.itemIterator == null + && this.deleteMarkerIterator == null + && this.prefixIterator == null) { + populate(); } - Matcher matcher = HttpUtils.AWS_S3_PREFIX_REGEX.matcher(host); - matcher.lookingAt(); - int end = matcher.end(); + if (this.error == null + && !this.itemIterator.hasNext() + && !this.deleteMarkerIterator.hasNext() + && !this.prefixIterator.hasNext() + && this.listObjectsResult.isTruncated()) { + populate(); + } - this.awsS3Prefix = host.substring(0, end); - if (this.awsS3Prefix.contains("s3-accesspoint") && !https) { - throw new IllegalArgumentException("use HTTPS scheme for host " + host); + if (this.error != null) { + this.completed = true; + return this.error; } - String[] tokens = host.substring(end).split("\\."); - awsDualstack = "dualstack".equals(tokens[0]); - if (awsDualstack) tokens = Arrays.copyOfRange(tokens, 1, tokens.length); - String regionInHost = null; - if (!tokens[0].equals("vpce") && !tokens[0].equals("amazonaws")) { - regionInHost = tokens[0]; - tokens = Arrays.copyOfRange(tokens, 1, tokens.length); + Item item = null; + if (this.itemIterator.hasNext()) { + item = this.itemIterator.next(); + item.setEncodingType(this.listObjectsResult.encodingType()); + this.lastObjectName = item.objectName(); + } else if (this.deleteMarkerIterator.hasNext()) { + item = this.deleteMarkerIterator.next(); + } else if (this.prefixIterator.hasNext()) { + item = this.prefixIterator.next().toItem(); } - this.awsDomainSuffix = String.join(".", tokens); - if (host.equals("s3-external-1.amazonaws.com")) regionInHost = "us-east-1"; - if (host.equals("s3-us-gov-west-1.amazonaws.com") - || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { - regionInHost = "us-gov-west-1"; + if (item != null) { + item.setEncodingType(this.listObjectsResult.encodingType()); + return new Result<>(item); } - if (regionInHost != null) this.region = regionInHost; + this.completed = true; + throw new NoSuchElementException(); } - private void setBaseUrl(HttpUrl url) { - this.baseUrl = url; - this.setAwsInfo(url.host(), url.isHttps()); - this.useVirtualStyle = this.awsDomainSuffix != null || url.host().endsWith("aliyuncs.com"); + @Override + public void remove() { + throw new UnsupportedOperationException(); } + } - public Builder endpoint(String endpoint) { - setBaseUrl(HttpUtils.getBaseUrl(endpoint)); - return this; - } + /** Execute list objects v1. */ + protected Iterable> objectV1Lister(ListObjectsV1Args args) { + return new Iterable>() { + @Override + public Iterator> iterator() { + return new ObjectIterator() { + private ListBucketResultV1 result = null; - public Builder endpoint(String endpoint, int port, boolean secure) { - HttpUrl url = HttpUtils.getBaseUrl(endpoint); - if (port < 1 || port > 65535) { - throw new IllegalArgumentException("port must be in range of 1 to 65535"); + @Override + protected void populateResult() throws MinioException { + this.listObjectsResult = null; + this.itemIterator = null; + this.prefixIterator = null; + + String nextMarker = (result == null) ? args.marker() : result.nextMarker(); + if (nextMarker == null) nextMarker = this.lastObjectName; + + try { + ListObjectsV1Response response = + listObjectsV1( + ListObjectsV1Args.builder() + .extraHeaders(args.extraHeaders()) + .extraQueryParams(args.extraQueryParams()) + .bucket(args.bucket()) + .region(args.region()) + .delimiter(args.delimiter()) + .encodingType(args.encodingType()) + .maxKeys(args.maxKeys()) + .prefix(args.prefix()) + .marker(nextMarker) + .build()) + .get(); + result = response.result(); + this.listObjectsResult = response.result(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throwEncapsulatedException(e); + } + } + }; } - url = url.newBuilder().port(port).scheme(secure ? "https" : "http").build(); + }; + } - setBaseUrl(url); - return this; - } + /** Execute list objects v2. */ + protected Iterable> objectV2Lister(ListObjectsV2Args args) { + return new Iterable>() { + @Override + public Iterator> iterator() { + return new ObjectIterator() { + private ListBucketResultV2 result = null; - public Builder endpoint(URL url) { - HttpUtils.validateNotNull(url, "url"); - return endpoint(HttpUrl.get(url)); - } + @Override + protected void populateResult() throws MinioException { + this.listObjectsResult = null; + this.itemIterator = null; + this.prefixIterator = null; - public Builder endpoint(HttpUrl url) { - HttpUtils.validateNotNull(url, "url"); - HttpUtils.validateUrl(url); - setBaseUrl(url); - return this; - } + try { + ListObjectsV2Response response = + listObjectsV2( + ListObjectsV2Args.builder() + .extraHeaders(args.extraHeaders()) + .extraQueryParams(args.extraQueryParams()) + .bucket(args.bucket()) + .region(args.region()) + .delimiter(args.delimiter()) + .encodingType(args.encodingType()) + .maxKeys(args.maxKeys()) + .prefix(args.prefix()) + .startAfter(args.startAfter()) + .continuationToken( + result == null + ? args.continuationToken() + : result.nextContinuationToken()) + .fetchOwner(args.fetchOwner()) + .includeUserMetadata(args.includeUserMetadata()) + .build()) + .get(); + result = response.result(); + this.listObjectsResult = response.result(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throwEncapsulatedException(e); + } + } + }; + } + }; + } - public Builder region(String region) { - if (region != null && !HttpUtils.REGION_REGEX.matcher(region).find()) { - throw new IllegalArgumentException("invalid region " + region); + /** Execute list object versions. */ + protected Iterable> objectVersionLister(ListObjectVersionsArgs args) { + return new Iterable>() { + @Override + public Iterator> iterator() { + return new ObjectIterator() { + private ListVersionsResult result = null; + + @Override + protected void populateResult() throws MinioException { + this.listObjectsResult = null; + this.itemIterator = null; + this.prefixIterator = null; + + try { + ListObjectVersionsResponse response = + listObjectVersions( + ListObjectVersionsArgs.builder() + .extraHeaders(args.extraHeaders()) + .extraQueryParams(args.extraQueryParams()) + .bucket(args.bucket()) + .region(args.region()) + .delimiter(args.delimiter()) + .encodingType(args.encodingType()) + .maxKeys(args.maxKeys()) + .prefix(args.prefix()) + .keyMarker(result == null ? args.keyMarker() : result.nextKeyMarker()) + .versionIdMarker( + result == null + ? args.versionIdMarker() + : result.nextVersionIdMarker()) + .build()) + .get(); + result = response.result(); + this.listObjectsResult = response.result(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throwEncapsulatedException(e); + } + } + }; } - this.region = region; - return this; - } + }; + } - public Builder credentials(String accessKey, String secretKey) { - this.provider = new StaticProvider(accessKey, secretKey, null); - return this; - } + ///////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////// ListenBucketNotification API implementation ///////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// - public Builder credentialsProvider(Provider provider) { - this.provider = provider; - return this; + /** Notification result records representation. */ + protected static class NotificationResultRecords { + Response response = null; + Scanner scanner = null; + ObjectMapper mapper = null; + + public NotificationResultRecords(Response response) { + this.response = response; + this.scanner = new Scanner(response.body().charStream()).useDelimiter("\n"); + this.mapper = + JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) + .build(); } - public Builder httpClient(OkHttpClient httpClient) { - HttpUtils.validateNotNull(httpClient, "http client"); - this.httpClient = httpClient; - return this; - } + /** returns closeable iterator of result of notification records. */ + public CloseableIterator> closeableIterator() { + return new CloseableIterator>() { + String recordsString = null; + NotificationRecords records = null; + boolean isClosed = false; - public Builder httpClient(OkHttpClient httpClient, boolean close) { - HttpUtils.validateNotNull(httpClient, "http client"); - this.httpClient = httpClient; - this.closeHttpClient = close; - return this; - } + @Override + public void close() throws IOException { + if (!isClosed) { + try { + response.body().close(); + scanner.close(); + } finally { + isClosed = true; + } + } + } - public MinioAsyncClient build() { - HttpUtils.validateNotNull(this.baseUrl, "endpoint"); - - if (this.awsDomainSuffix != null - && this.awsDomainSuffix.endsWith(".cn") - && !this.awsS3Prefix.endsWith("s3-accelerate.") - && this.region == null) { - throw new IllegalArgumentException( - "Region missing in Amazon S3 China endpoint " + this.baseUrl); - } + public boolean populate() { + if (isClosed) return false; + if (recordsString != null) return true; - if (this.httpClient == null) { - this.closeHttpClient = true; - this.httpClient = - HttpUtils.newDefaultHttpClient( - DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); - } + while (scanner.hasNext()) { + recordsString = scanner.next().trim(); + if (!recordsString.equals("")) break; + } - return new MinioAsyncClient( - baseUrl, - awsS3Prefix, - awsDomainSuffix, - awsDualstack, - useVirtualStyle, - region, - provider, - httpClient, - closeHttpClient); + if (recordsString == null || recordsString.equals("")) { + try { + close(); + } catch (IOException e) { + isClosed = true; + } + return false; + } + return true; + } + + @Override + public boolean hasNext() { + return populate(); + } + + @Override + public Result next() { + if (isClosed) throw new NoSuchElementException(); + if ((recordsString == null || recordsString.equals("")) && !populate()) { + throw new NoSuchElementException(); + } + + try { + records = mapper.readValue(recordsString, NotificationRecords.class); + return new Result<>(records); + } catch (JsonMappingException | JsonParseException e) { + return new Result<>(new MinioException(e)); + } catch (IOException e) { + return new Result<>(new MinioException(e)); + } finally { + recordsString = null; + records = null; + } + } + }; } } } diff --git a/api/src/main/java/io/minio/MinioClient.java b/api/src/main/java/io/minio/MinioClient.java index 16e00f7c8..4d2ac06ef 100644 --- a/api/src/main/java/io/minio/MinioClient.java +++ b/api/src/main/java/io/minio/MinioClient.java @@ -17,21 +17,14 @@ package io.minio; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.minio.credentials.Provider; -import io.minio.errors.BucketPolicyTooLargeException; -import io.minio.errors.ErrorResponseException; -import io.minio.errors.InsufficientDataException; -import io.minio.errors.InternalException; -import io.minio.errors.InvalidResponseException; -import io.minio.errors.ServerException; -import io.minio.errors.XmlParserException; +import io.minio.errors.MinioException; import io.minio.messages.AccessControlPolicy; -import io.minio.messages.Bucket; import io.minio.messages.CORSConfiguration; -import io.minio.messages.DeleteError; +import io.minio.messages.DeleteResult; import io.minio.messages.Item; import io.minio.messages.LifecycleConfiguration; +import io.minio.messages.ListAllMyBucketsResult; import io.minio.messages.NotificationConfiguration; import io.minio.messages.NotificationRecords; import io.minio.messages.ObjectLockConfiguration; @@ -40,13 +33,8 @@ import io.minio.messages.SseConfiguration; import io.minio.messages.Tags; import io.minio.messages.VersioningConfiguration; -import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.net.URL; -import java.security.InvalidKeyException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -148,21 +136,10 @@ protected MinioClient(MinioClient client) { * * @param args {@link StatObjectArgs} object. * @return {@link StatObjectResponse} - Populated object information and metadata. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. * @see StatObjectResponse */ - public StatObjectResponse statObject(StatObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + public StatObjectResponse statObject(StatObjectArgs args) throws MinioException { try { return asyncClient.statObject(args).get(); } catch (InterruptedException e) { @@ -174,11 +151,11 @@ public StatObjectResponse statObject(StatObjectArgs args) } /** - * Gets data from offset to length of a SSE-C encrypted object. Returned {@link InputStream} must - * be closed after use to release network resources. + * Gets data from offset to length of a SSE-C encrypted object. Returned {@link GetObjectResponse} + * must be closed after use to release network resources. * *
Example:{@code
-   * try (InputStream stream =
+   * try (GetObjectResponse response =
    *     minioClient.getObject(
    *   GetObjectArgs.builder()
    *     .bucket("my-bucketname")
@@ -188,25 +165,15 @@ public StatObjectResponse statObject(StatObjectArgs args)
    *     .ssec(ssec)
    *     .build()
    * ) {
-   *   // Read data from stream
+   *   // Read data from response
+   *   // which is InputStream interface compatible
    * }
    * }
* * @param args Object of {@link GetObjectArgs} - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 GetObjectResponse getObject(GetObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public GetObjectResponse getObject(GetObjectArgs args) throws MinioException { try { return asyncClient.getObject(args).get(); } catch (InterruptedException e) { @@ -231,20 +198,9 @@ public GetObjectResponse getObject(GetObjectArgs args) * } * * @param args Object of {@link DownloadObjectArgs} - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 downloadObject(DownloadObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void downloadObject(DownloadObjectArgs args) throws MinioException { try { asyncClient.downloadObject(args).get(); } catch (InterruptedException e) { @@ -357,20 +313,9 @@ public void downloadObject(DownloadObjectArgs args) * } * * @param args {@link CopyObjectArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 ObjectWriteResponse copyObject(CopyObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public ObjectWriteResponse copyObject(CopyObjectArgs args) throws MinioException { try { return asyncClient.copyObject(args).get(); } catch (InterruptedException e) { @@ -428,20 +373,9 @@ public ObjectWriteResponse copyObject(CopyObjectArgs args) * * @param args {@link ComposeObjectArgs} object. * @return {@link ObjectWriteResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 ObjectWriteResponse composeObject(ComposeObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public ObjectWriteResponse composeObject(ComposeObjectArgs args) throws MinioException { try { return asyncClient.composeObject(args).get(); } catch (InterruptedException e) { @@ -461,7 +395,7 @@ public ObjectWriteResponse composeObject(ComposeObjectArgs args) * String url = * minioClient.getPresignedObjectUrl( * GetPresignedObjectUrlArgs.builder() - * .method(Method.DELETE) + * .method(Http.Method.DELETE) * .bucket("my-bucketname") * .object("my-objectname") * .expiry(24 * 60 * 60) @@ -476,7 +410,7 @@ public ObjectWriteResponse composeObject(ComposeObjectArgs args) * String url = * minioClient.getPresignedObjectUrl( * GetPresignedObjectUrlArgs.builder() - * .method(Method.PUT) + * .method(Http.Method.PUT) * .bucket("my-bucketname") * .object("my-objectname") * .expiry(1, TimeUnit.DAYS) @@ -489,7 +423,7 @@ public ObjectWriteResponse composeObject(ComposeObjectArgs args) * String url = * minioClient.getPresignedObjectUrl( * GetPresignedObjectUrlArgs.builder() - * .method(Method.GET) + * .method(Http.Method.GET) * .bucket("my-bucketname") * .object("my-objectname") * .expiry(2, TimeUnit.HOURS) @@ -499,21 +433,9 @@ public ObjectWriteResponse composeObject(ComposeObjectArgs args) * * @param args {@link GetPresignedObjectUrlArgs} object. * @return String - URL string. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. - * @throws ServerException - */ - public String getPresignedObjectUrl(GetPresignedObjectUrlArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - XmlParserException, ServerException { + * @throws MinioException thrown to indicate SDK exception. + */ + public String getPresignedObjectUrl(GetPresignedObjectUrlArgs args) throws MinioException { return asyncClient.getPresignedObjectUrl(args); } @@ -564,21 +486,10 @@ public String getPresignedObjectUrl(GetPresignedObjectUrlArgs args) * * @param policy Post policy of an object. * @return {@code Map} - Contains form-data to upload an object using POST method. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. * @see PostPolicy */ - public Map getPresignedPostFormData(PostPolicy policy) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + public Map getPresignedPostFormData(PostPolicy policy) throws MinioException { return asyncClient.getPresignedPostFormData(policy); } @@ -609,20 +520,9 @@ public Map getPresignedPostFormData(PostPolicy policy) * } * * @param args {@link RemoveObjectArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 removeObject(RemoveObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void removeObject(RemoveObjectArgs args) throws MinioException { try { asyncClient.removeObject(args).get(); } catch (InterruptedException e) { @@ -641,20 +541,21 @@ public void removeObject(RemoveObjectArgs args) * objects.add(new DeleteObject("my-objectname1")); * objects.add(new DeleteObject("my-objectname2")); * objects.add(new DeleteObject("my-objectname3")); - * Iterable> results = + * Iterable> results = * minioClient.removeObjects( * RemoveObjectsArgs.builder().bucket("my-bucketname").objects(objects).build()); - * for (Result result : results) { - * DeleteError error = result.get(); + * for (Result result : results) { + * DeleteResult.Error error = result.get(); * System.out.println( * "Error in deleting object " + error.objectName() + "; " + error.message()); * } * } * * @param args {@link RemoveObjectsArgs} object. - * @return {@code Iterable>} - Lazy iterator contains object removal status. + * @return {@code Iterable>} - Lazy iterator contains object removal + * status. */ - public Iterable> removeObjects(RemoveObjectsArgs args) { + public Iterable> removeObjects(RemoveObjectsArgs args) { return asyncClient.removeObjects(args); } @@ -681,20 +582,9 @@ public Iterable> removeObjects(RemoveObjectsArgs args) { * } * * @param args {@link RestoreObjectArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 restoreObject(RestoreObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void restoreObject(RestoreObjectArgs args) throws MinioException { try { asyncClient.restoreObject(args).get(); } catch (InterruptedException e) { @@ -745,7 +635,6 @@ public void restoreObject(RestoreObjectArgs args) * * @param args Instance of {@link ListObjectsArgs} built using the builder * @return {@code Iterable>} - Lazy iterator contains object information. - * @throws XmlParserException upon parsing response xml */ public Iterable> listObjects(ListObjectsArgs args) { return asyncClient.listObjects(args); @@ -755,27 +644,16 @@ public Iterable> listObjects(ListObjectsArgs args) { * Lists bucket information of all buckets. * *
Example:{@code
-   * List bucketList = minioClient.listBuckets();
+   * List bucketList = minioClient.listBuckets();
    * for (Bucket bucket : bucketList) {
    *   System.out.println(bucket.creationDate() + ", " + bucket.name());
    * }
    * }
* - * @return {@code List} - List of bucket information. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 List listBuckets() - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @return {@code List} - List of bucket information. + * @throws MinioException thrown to indicate SDK exception. + */ + public List listBuckets() throws MinioException { try { return asyncClient.listBuckets().get(); } catch (InterruptedException e) { @@ -790,16 +668,17 @@ public List listBuckets() * Lists bucket information of all buckets. * *
Example:{@code
-   * Iterable> results = minioClient.listBuckets(ListBucketsArgs.builder().build());
-   * for (Result result : results) {
+   * Iterable> results = minioClient.listBuckets(ListBucketsArgs.builder().build());
+   * for (Result result : results) {
    *   Bucket bucket = result.get();
    *   System.out.println(String.format("Bucket: %s, Region: %s, CreationDate: %s", bucket.name(), bucket.bucketRegion(), bucket.creationDate()));
    * }
    * }
* - * @return {@link Iterable}<{@link List}<{@link Bucket}>> object. + * @return {@link Iterable}<{@link List}<{@link ListAllMyBucketsResult.Bucket}>> + * object. */ - public Iterable> listBuckets(ListBucketsArgs args) { + public Iterable> listBuckets(ListBucketsArgs args) { return asyncClient.listBuckets(args); } @@ -818,20 +697,9 @@ public Iterable> listBuckets(ListBucketsArgs args) { * * @param args {@link BucketExistsArgs} object. * @return boolean - True if the bucket exists. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 boolean bucketExists(BucketExistsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public boolean bucketExists(BucketExistsArgs args) throws MinioException { try { return asyncClient.bucketExists(args).get(); } catch (InterruptedException e) { @@ -869,20 +737,9 @@ public boolean bucketExists(BucketExistsArgs args) * } * * @param args Object with bucket name, region and lock functionality - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 makeBucket(MakeBucketArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void makeBucket(MakeBucketArgs args) throws MinioException { try { asyncClient.makeBucket(args).get(); } catch (InterruptedException e) { @@ -901,20 +758,9 @@ public void makeBucket(MakeBucketArgs args) * } * * @param args {@link SetBucketVersioningArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setBucketVersioning(SetBucketVersioningArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setBucketVersioning(SetBucketVersioningArgs args) throws MinioException { try { asyncClient.setBucketVersioning(args).get(); } catch (InterruptedException e) { @@ -935,20 +781,10 @@ public void setBucketVersioning(SetBucketVersioningArgs args) * * @param args {@link GetBucketVersioningArgs} object. * @return {@link VersioningConfiguration} - Versioning configuration. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public VersioningConfiguration getBucketVersioning(GetBucketVersioningArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { try { return asyncClient.getBucketVersioning(args).get(); } catch (InterruptedException e) { @@ -970,20 +806,10 @@ public VersioningConfiguration getBucketVersioning(GetBucketVersioningArgs args) * } * * @param args {@link SetObjectLockConfigurationArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public void setObjectLockConfiguration(SetObjectLockConfigurationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { try { asyncClient.setObjectLockConfiguration(args).get(); } catch (InterruptedException e) { @@ -1002,20 +828,10 @@ public void setObjectLockConfiguration(SetObjectLockConfigurationArgs args) * } * * @param args {@link DeleteObjectLockConfigurationArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public void deleteObjectLockConfiguration(DeleteObjectLockConfigurationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { try { asyncClient.deleteObjectLockConfiguration(args).get(); } catch (InterruptedException e) { @@ -1039,20 +855,10 @@ public void deleteObjectLockConfiguration(DeleteObjectLockConfigurationArgs args * * @param args {@link GetObjectLockConfigurationArgs} object. * @return {@link ObjectLockConfiguration} - Default retention configuration. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public ObjectLockConfiguration getObjectLockConfiguration(GetObjectLockConfigurationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { try { return asyncClient.getObjectLockConfiguration(args).get(); } catch (InterruptedException e) { @@ -1079,20 +885,9 @@ public ObjectLockConfiguration getObjectLockConfiguration(GetObjectLockConfigura * } * * @param args {@link SetObjectRetentionArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setObjectRetention(SetObjectRetentionArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setObjectRetention(SetObjectRetentionArgs args) throws MinioException { try { asyncClient.setObjectRetention(args).get(); } catch (InterruptedException e) { @@ -1118,20 +913,9 @@ public void setObjectRetention(SetObjectRetentionArgs args) * * @param args {@link GetObjectRetentionArgs} object. * @return {@link Retention} - Object retention configuration. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 Retention getObjectRetention(GetObjectRetentionArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public Retention getObjectRetention(GetObjectRetentionArgs args) throws MinioException { try { return asyncClient.getObjectRetention(args).get(); } catch (InterruptedException e) { @@ -1155,20 +939,9 @@ public Retention getObjectRetention(GetObjectRetentionArgs args) * } * * @param args {@link EnableObjectLegalHoldArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 enableObjectLegalHold(EnableObjectLegalHoldArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void enableObjectLegalHold(EnableObjectLegalHoldArgs args) throws MinioException { try { asyncClient.enableObjectLegalHold(args).get(); } catch (InterruptedException e) { @@ -1191,20 +964,9 @@ public void enableObjectLegalHold(EnableObjectLegalHoldArgs args) * } * * @param args {@link DisableObjectLegalHoldArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 disableObjectLegalHold(DisableObjectLegalHoldArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void disableObjectLegalHold(DisableObjectLegalHoldArgs args) throws MinioException { try { asyncClient.disableObjectLegalHold(args).get(); } catch (InterruptedException e) { @@ -1235,20 +997,9 @@ public void disableObjectLegalHold(DisableObjectLegalHoldArgs args) * args {@link IsObjectLegalHoldEnabledArgs} object. * * @return boolean - True if legal hold is enabled. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 boolean isObjectLegalHoldEnabled(IsObjectLegalHoldEnabledArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public boolean isObjectLegalHoldEnabled(IsObjectLegalHoldEnabledArgs args) throws MinioException { try { return asyncClient.isObjectLegalHoldEnabled(args).get(); } catch (InterruptedException e) { @@ -1267,20 +1018,9 @@ public boolean isObjectLegalHoldEnabled(IsObjectLegalHoldEnabledArgs args) * } * * @param args {@link RemoveBucketArgs} bucket. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 removeBucket(RemoveBucketArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void removeBucket(RemoveBucketArgs args) throws MinioException { try { asyncClient.removeBucket(args).get(); } catch (InterruptedException e) { @@ -1336,20 +1076,9 @@ public void removeBucket(RemoveBucketArgs args) * * @param args {@link PutObjectArgs} object. * @return {@link ObjectWriteResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 ObjectWriteResponse putObject(PutObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public ObjectWriteResponse putObject(PutObjectArgs args) throws MinioException { try { return asyncClient.putObject(args).get(); } catch (InterruptedException e) { @@ -1381,20 +1110,9 @@ public ObjectWriteResponse putObject(PutObjectArgs args) * * @param args {@link UploadObjectArgs} object. * @return {@link ObjectWriteResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 ObjectWriteResponse uploadObject(UploadObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public ObjectWriteResponse uploadObject(UploadObjectArgs args) throws MinioException { try { return asyncClient.uploadObject(args).get(); } catch (InterruptedException e) { @@ -1415,21 +1133,9 @@ public ObjectWriteResponse uploadObject(UploadObjectArgs args) * * @param args {@link GetBucketPolicyArgs} object. * @return String - Bucket policy configuration as JSON string. - * @throws BucketPolicyTooLargeException thrown to indicate returned bucket policy is too large. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 String getBucketPolicy(GetBucketPolicyArgs args) - throws BucketPolicyTooLargeException, ErrorResponseException, InsufficientDataException, - InternalException, InvalidKeyException, InvalidResponseException, IOException, - NoSuchAlgorithmException, ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public String getBucketPolicy(GetBucketPolicyArgs args) throws MinioException { try { return asyncClient.getBucketPolicy(args).get(); } catch (InterruptedException e) { @@ -1471,20 +1177,9 @@ public String getBucketPolicy(GetBucketPolicyArgs args) * } * * @param args {@link SetBucketPolicyArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setBucketPolicy(SetBucketPolicyArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setBucketPolicy(SetBucketPolicyArgs args) throws MinioException { try { asyncClient.setBucketPolicy(args).get(); } catch (InterruptedException e) { @@ -1502,20 +1197,9 @@ public void setBucketPolicy(SetBucketPolicyArgs args) * } * * @param args {@link DeleteBucketPolicyArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 deleteBucketPolicy(DeleteBucketPolicyArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void deleteBucketPolicy(DeleteBucketPolicyArgs args) throws MinioException { try { asyncClient.deleteBucketPolicy(args).get(); } catch (InterruptedException e) { @@ -1546,20 +1230,9 @@ public void deleteBucketPolicy(DeleteBucketPolicyArgs args) * } * * @param args {@link SetBucketLifecycleArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setBucketLifecycle(SetBucketLifecycleArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setBucketLifecycle(SetBucketLifecycleArgs args) throws MinioException { try { asyncClient.setBucketLifecycle(args).get(); } catch (InterruptedException e) { @@ -1577,20 +1250,9 @@ public void setBucketLifecycle(SetBucketLifecycleArgs args) * } * * @param args {@link DeleteBucketLifecycleArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 deleteBucketLifecycle(DeleteBucketLifecycleArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void deleteBucketLifecycle(DeleteBucketLifecycleArgs args) throws MinioException { try { asyncClient.deleteBucketLifecycle(args).get(); } catch (InterruptedException e) { @@ -1612,20 +1274,10 @@ public void deleteBucketLifecycle(DeleteBucketLifecycleArgs args) * @param args {@link GetBucketLifecycleArgs} object. * @return {@link LifecycleConfiguration} object. * @return String - Life cycle configuration as XML string. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public LifecycleConfiguration getBucketLifecycle(GetBucketLifecycleArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { try { return asyncClient.getBucketLifecycle(args).get(); } catch (InterruptedException e) { @@ -1647,20 +1299,10 @@ public LifecycleConfiguration getBucketLifecycle(GetBucketLifecycleArgs args) * * @param args {@link GetBucketNotificationArgs} object. * @return {@link NotificationConfiguration} - Notification configuration. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public NotificationConfiguration getBucketNotification(GetBucketNotificationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { try { return asyncClient.getBucketNotification(args).get(); } catch (InterruptedException e) { @@ -1696,20 +1338,9 @@ public NotificationConfiguration getBucketNotification(GetBucketNotificationArgs * } * * @param args {@link SetBucketNotificationArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setBucketNotification(SetBucketNotificationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setBucketNotification(SetBucketNotificationArgs args) throws MinioException { try { asyncClient.setBucketNotification(args).get(); } catch (InterruptedException e) { @@ -1728,20 +1359,9 @@ public void setBucketNotification(SetBucketNotificationArgs args) * } * * @param args {@link DeleteBucketNotificationArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 deleteBucketNotification(DeleteBucketNotificationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void deleteBucketNotification(DeleteBucketNotificationArgs args) throws MinioException { try { asyncClient.deleteBucketNotification(args).get(); } catch (InterruptedException e) { @@ -1762,20 +1382,10 @@ public void deleteBucketNotification(DeleteBucketNotificationArgs args) * * @param args {@link GetBucketReplicationArgs} object. * @return {@link ReplicationConfiguration} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public ReplicationConfiguration getBucketReplication(GetBucketReplicationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { try { return asyncClient.getBucketReplication(args).get(); } catch (InterruptedException e) { @@ -1818,20 +1428,9 @@ public ReplicationConfiguration getBucketReplication(GetBucketReplicationArgs ar * } * * @param args {@link SetBucketReplicationArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setBucketReplication(SetBucketReplicationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setBucketReplication(SetBucketReplicationArgs args) throws MinioException { try { asyncClient.setBucketReplication(args).get(); } catch (InterruptedException e) { @@ -1850,20 +1449,9 @@ public void setBucketReplication(SetBucketReplicationArgs args) * } * * @param args {@link DeleteBucketReplicationArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 deleteBucketReplication(DeleteBucketReplicationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void deleteBucketReplication(DeleteBucketReplicationArgs args) throws MinioException { try { asyncClient.deleteBucketReplication(args).get(); } catch (InterruptedException e) { @@ -1902,21 +1490,10 @@ public void deleteBucketReplication(DeleteBucketReplicationArgs args) * @param args {@link ListenBucketNotificationArgs} object. * @return {@code CloseableIterator>} - Lazy closable iterator * contains event records. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public CloseableIterator> listenBucketNotification( - ListenBucketNotificationArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + ListenBucketNotificationArgs args) throws MinioException { return asyncClient.listenBucketNotification(args); } @@ -1955,20 +1532,10 @@ public CloseableIterator> listenBucketNotification( * * @param args instance of {@link SelectObjectContentArgs} * @return {@link SelectResponseStream} - Contains filtered records and progress. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public SelectResponseStream selectObjectContent(SelectObjectContentArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { return asyncClient.selectObjectContent(args); } @@ -1981,20 +1548,9 @@ public SelectResponseStream selectObjectContent(SelectObjectContentArgs args) * } * * @param args {@link SetBucketEncryptionArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setBucketEncryption(SetBucketEncryptionArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setBucketEncryption(SetBucketEncryptionArgs args) throws MinioException { try { asyncClient.setBucketEncryption(args).get(); } catch (InterruptedException e) { @@ -2015,20 +1571,9 @@ public void setBucketEncryption(SetBucketEncryptionArgs args) * * @param args {@link GetBucketEncryptionArgs} object. * @return {@link SseConfiguration} - Server-side encryption configuration. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 SseConfiguration getBucketEncryption(GetBucketEncryptionArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public SseConfiguration getBucketEncryption(GetBucketEncryptionArgs args) throws MinioException { try { return asyncClient.getBucketEncryption(args).get(); } catch (InterruptedException e) { @@ -2048,20 +1593,9 @@ public SseConfiguration getBucketEncryption(GetBucketEncryptionArgs args) * } * * @param args {@link DeleteBucketEncryptionArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 deleteBucketEncryption(DeleteBucketEncryptionArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void deleteBucketEncryption(DeleteBucketEncryptionArgs args) throws MinioException { try { asyncClient.deleteBucketEncryption(args).get(); } catch (InterruptedException e) { @@ -2081,20 +1615,9 @@ public void deleteBucketEncryption(DeleteBucketEncryptionArgs args) * * @param args {@link GetBucketTagsArgs} object. * @return {@link Tags} - Tags. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 Tags getBucketTags(GetBucketTagsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public Tags getBucketTags(GetBucketTagsArgs args) throws MinioException { try { return asyncClient.getBucketTags(args).get(); } catch (InterruptedException e) { @@ -2117,20 +1640,9 @@ public Tags getBucketTags(GetBucketTagsArgs args) * } * * @param args {@link SetBucketTagsArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setBucketTags(SetBucketTagsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setBucketTags(SetBucketTagsArgs args) throws MinioException { try { asyncClient.setBucketTags(args).get(); } catch (InterruptedException e) { @@ -2148,20 +1660,9 @@ public void setBucketTags(SetBucketTagsArgs args) * } * * @param args {@link DeleteBucketTagsArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 deleteBucketTags(DeleteBucketTagsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void deleteBucketTags(DeleteBucketTagsArgs args) throws MinioException { try { asyncClient.deleteBucketTags(args).get(); } catch (InterruptedException e) { @@ -2182,20 +1683,9 @@ public void deleteBucketTags(DeleteBucketTagsArgs args) * * @param args {@link GetObjectTagsArgs} object. * @return {@link Tags} - Tags. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 Tags getObjectTags(GetObjectTagsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public Tags getObjectTags(GetObjectTagsArgs args) throws MinioException { try { return asyncClient.getObjectTags(args).get(); } catch (InterruptedException e) { @@ -2222,20 +1712,9 @@ public Tags getObjectTags(GetObjectTagsArgs args) * } * * @param args {@link SetObjectTagsArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setObjectTags(SetObjectTagsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setObjectTags(SetObjectTagsArgs args) throws MinioException { try { asyncClient.setObjectTags(args).get(); } catch (InterruptedException e) { @@ -2254,20 +1733,9 @@ public void setObjectTags(SetObjectTagsArgs args) * } * * @param args {@link DeleteObjectTagsArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 deleteObjectTags(DeleteObjectTagsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void deleteObjectTags(DeleteObjectTagsArgs args) throws MinioException { try { asyncClient.deleteObjectTags(args).get(); } catch (InterruptedException e) { @@ -2287,20 +1755,9 @@ public void deleteObjectTags(DeleteObjectTagsArgs args) * * @param args {@link GetBucketCorsArgs} object. * @return {@link CORSConfiguration} - CORSConfiguration. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 CORSConfiguration getBucketCors(GetBucketCorsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public CORSConfiguration getBucketCors(GetBucketCorsArgs args) throws MinioException { try { return asyncClient.getBucketCors(args).get(); } catch (InterruptedException e) { @@ -2343,20 +1800,9 @@ public CORSConfiguration getBucketCors(GetBucketCorsArgs args) * } * * @param args {@link SetBucketCorsArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 setBucketCors(SetBucketCorsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void setBucketCors(SetBucketCorsArgs args) throws MinioException { try { asyncClient.setBucketCors(args).get(); } catch (InterruptedException e) { @@ -2374,20 +1820,9 @@ public void setBucketCors(SetBucketCorsArgs args) * } * * @param args {@link DeleteBucketCorsArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 deleteBucketCors(DeleteBucketCorsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public void deleteBucketCors(DeleteBucketCorsArgs args) throws MinioException { try { asyncClient.deleteBucketCors(args).get(); } catch (InterruptedException e) { @@ -2408,20 +1843,9 @@ public void deleteBucketCors(DeleteBucketCorsArgs args) * * @param args {@link GetObjectAclArgs} object. * @return {@link AccessControlPolicy} - Access control policy object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 AccessControlPolicy getObjectAcl(GetObjectAclArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public AccessControlPolicy getObjectAcl(GetObjectAclArgs args) throws MinioException { try { return asyncClient.getObjectAcl(args).get(); } catch (InterruptedException e) { @@ -2450,20 +1874,10 @@ public AccessControlPolicy getObjectAcl(GetObjectAclArgs args) * * @param args {@link GetObjectAttributesArgs} object. * @return {@link GetObjectAttributesResponse} - Response object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public GetObjectAttributesResponse getObjectAttributes(GetObjectAttributesArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { try { return asyncClient.getObjectAttributes(args).get(); } catch (InterruptedException e) { @@ -2498,20 +1912,10 @@ public GetObjectAttributesResponse getObjectAttributes(GetObjectAttributesArgs a * } * * @param args {@link UploadSnowballObjectsArgs} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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. + * @throws MinioException thrown to indicate SDK exception. */ public ObjectWriteResponse uploadSnowballObjects(UploadSnowballObjectsArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + throws MinioException { try { return asyncClient.uploadSnowballObjects(args).get(); } catch (InterruptedException e) { @@ -2544,20 +1948,9 @@ public ObjectWriteResponse uploadSnowballObjects(UploadSnowballObjectsArgs args) * * @param args {@link PutObjectFanOutArgs} object. * @return {@link PutObjectFanOutResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 PutObjectFanOutResponse putObjectFanOut(PutObjectFanOutArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public PutObjectFanOutResponse putObjectFanOut(PutObjectFanOutArgs args) throws MinioException { try { return asyncClient.putObjectFanOut(args).get(); } catch (InterruptedException e) { @@ -2573,20 +1966,9 @@ public PutObjectFanOutResponse putObjectFanOut(PutObjectFanOutArgs args) * * @param args {@link PromptObjectArgs} object. * @return {@link PromptObjectResponse} object. - * @throws ErrorResponseException thrown to indicate S3 service returned an error response. - * @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 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 PromptObjectResponse promptObject(PromptObjectArgs args) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { + * @throws MinioException thrown to indicate SDK exception. + */ + public PromptObjectResponse promptObject(PromptObjectArgs args) throws MinioException { try { return asyncClient.promptObject(args).get(); } catch (InterruptedException e) { @@ -2621,11 +2003,12 @@ public void setTimeout(long connectTimeout, long writeTimeout, long readTimeout) * minioClient.ignoreCertCheck(); * } * - * @throws KeyManagementException thrown to indicate key management error. - * @throws NoSuchAlgorithmException thrown to indicate missing of SSL library. + * @throws MinioException thrown to indicate SDK exception. */ - @SuppressFBWarnings(value = "SIC", justification = "Should not be used in production anyways.") - public void ignoreCertCheck() throws KeyManagementException, NoSuchAlgorithmException { + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "SIC", + justification = "Should not be used in production anyways.") + public void ignoreCertCheck() throws MinioException { asyncClient.ignoreCertCheck(); } @@ -2654,9 +2037,8 @@ public void traceOn(OutputStream traceStream) { * Disables HTTP call tracing previously enabled. * * @see #traceOn - * @throws IOException upon connection error */ - public void traceOff() throws IOException { + public void traceOff() { asyncClient.traceOff(); } diff --git a/api/src/main/java/io/minio/MinioProperties.java b/api/src/main/java/io/minio/MinioProperties.java deleted file mode 100644 index 95673050a..000000000 --- a/api/src/main/java/io/minio/MinioProperties.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2015 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.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; -import java.util.concurrent.atomic.AtomicReference; -import java.util.jar.Manifest; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** Identifies and stores version information of minio-java package at run time. */ -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "MS_EXPOSE_REP") -public enum MinioProperties { - INSTANCE; - - private static final Logger LOGGER = Logger.getLogger(MinioProperties.class.getName()); - - private final AtomicReference version = new AtomicReference<>(null); - - public String getVersion() { - String result = version.get(); - if (result != null) { - return result; - } - setVersion(); - return version.get(); - } - - private synchronized void setVersion() { - if (version.get() != null) { - return; - } - version.set("dev"); - ClassLoader classLoader = getClass().getClassLoader(); - if (classLoader == null) { - return; - } - - try { - Enumeration resources = classLoader.getResources("META-INF/MANIFEST.MF"); - while (resources.hasMoreElements()) { - try (InputStream is = resources.nextElement().openStream()) { - Manifest manifest = new Manifest(is); - if ("minio".equals(manifest.getMainAttributes().getValue("Implementation-Title"))) { - version.set(manifest.getMainAttributes().getValue("Implementation-Version")); - return; - } - } - } - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "IOException occurred", e); - version.set("unknown"); - } - } - - public String getDefaultUserAgent() { - return "MinIO (" - + System.getProperty("os.name") - + "; " - + System.getProperty("os.arch") - + ") minio-java/" - + getVersion(); - } -} diff --git a/api/src/main/java/io/minio/ObjectArgs.java b/api/src/main/java/io/minio/ObjectArgs.java index 6ec9c4f46..854537b83 100644 --- a/api/src/main/java/io/minio/ObjectArgs.java +++ b/api/src/main/java/io/minio/ObjectArgs.java @@ -22,6 +22,13 @@ public abstract class ObjectArgs extends BucketArgs { protected String objectName; + protected ObjectArgs() {} + + protected ObjectArgs(ObjectArgs args) { + super(args); + this.objectName = args.objectName; + } + public String object() { return objectName; } @@ -30,7 +37,7 @@ public String object() { public abstract static class Builder, A extends ObjectArgs> extends BucketArgs.Builder { protected void validateObjectName(String name) { - validateNotEmptyString(name, "object name"); + Utils.validateNotEmptyString(name, "object name"); if (skipValidation) { return; } diff --git a/api/src/main/java/io/minio/ObjectConditionalReadArgs.java b/api/src/main/java/io/minio/ObjectConditionalReadArgs.java index bdbf9cf67..88b6d2357 100644 --- a/api/src/main/java/io/minio/ObjectConditionalReadArgs.java +++ b/api/src/main/java/io/minio/ObjectConditionalReadArgs.java @@ -16,9 +16,6 @@ package io.minio; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; import java.time.ZonedDateTime; import java.util.Objects; @@ -31,6 +28,31 @@ public abstract class ObjectConditionalReadArgs extends ObjectReadArgs { protected ZonedDateTime modifiedSince; protected ZonedDateTime unmodifiedSince; + protected ObjectConditionalReadArgs() {} + + protected ObjectConditionalReadArgs(SourceObject args) { + super(args); + } + + protected ObjectConditionalReadArgs(DownloadObjectArgs args) { + super(args); + } + + protected ObjectConditionalReadArgs(ObjectConditionalReadArgs args) { + super(args); + this.offset = args.offset; + this.length = args.length; + this.matchETag = args.matchETag; + this.notMatchETag = args.notMatchETag; + this.modifiedSince = args.modifiedSince; + this.unmodifiedSince = args.unmodifiedSince; + } + + protected ObjectConditionalReadArgs(ObjectConditionalReadArgs args, String matchETag) { + this(args); + this.matchETag = args.matchETag; + } + public Long offset() { return offset; } @@ -55,7 +77,7 @@ public ZonedDateTime unmodifiedSince() { return unmodifiedSince; } - public Multimap getHeaders() { + public Http.Headers makeHeaders() { Long offset = this.offset; Long length = this.length; if (length != null && offset == null) { @@ -70,7 +92,7 @@ public Multimap getHeaders() { } } - Multimap headers = HashMultimap.create(); + Http.Headers headers = new Http.Headers(ssec == null ? null : ssec.headers()); if (range != null) headers.put("Range", range); if (matchETag != null) headers.put("if-match", matchETag); @@ -84,22 +106,18 @@ public Multimap getHeaders() { headers.put("if-unmodified-since", unmodifiedSince.format(Time.HTTP_HEADER_DATE_FORMAT)); } - if (ssec != null) headers.putAll(Multimaps.forMap(ssec.headers())); - return headers; } - public Multimap genCopyHeaders() { - Multimap headers = HashMultimap.create(); - - String copySource = S3Escaper.encodePath("/" + bucketName + "/" + objectName); + public Http.Headers makeCopyHeaders() { + String copySource = Utils.encodePath("/" + bucketName + "/" + objectName); if (versionId != null) { - copySource += "?versionId=" + S3Escaper.encode(versionId); + copySource += "?versionId=" + Utils.encode(versionId); } - headers.put("x-amz-copy-source", copySource); + Http.Headers headers = new Http.Headers("x-amz-copy-source", copySource); - if (ssec != null) headers.putAll(Multimaps.forMap(ssec.copySourceHeaders())); + if (ssec != null) headers.putAll(ssec.copySourceHeaders()); if (matchETag != null) headers.put("x-amz-copy-source-if-match", matchETag); if (notMatchETag != null) headers.put("x-amz-copy-source-if-none-match", notMatchETag); @@ -147,13 +165,13 @@ public B length(Long length) { } public B matchETag(String etag) { - validateNullOrNotEmptyString(etag, "etag"); + Utils.validateNullOrNotEmptyString(etag, "etag"); operations.add(args -> args.matchETag = etag); return (B) this; } public B notMatchETag(String etag) { - validateNullOrNotEmptyString(etag, "etag"); + Utils.validateNullOrNotEmptyString(etag, "etag"); operations.add(args -> args.notMatchETag = etag); return (B) this; } diff --git a/api/src/main/java/io/minio/ObjectReadArgs.java b/api/src/main/java/io/minio/ObjectReadArgs.java index 2c36048f1..649c963f5 100644 --- a/api/src/main/java/io/minio/ObjectReadArgs.java +++ b/api/src/main/java/io/minio/ObjectReadArgs.java @@ -17,25 +17,31 @@ package io.minio; import java.util.Objects; -import okhttp3.HttpUrl; /** Base argument class for reading object. */ public abstract class ObjectReadArgs extends ObjectVersionArgs { - protected ServerSideEncryptionCustomerKey ssec; + protected ServerSideEncryption.CustomerKey ssec; - public ServerSideEncryptionCustomerKey ssec() { + protected ObjectReadArgs() {} + + protected ObjectReadArgs(ObjectReadArgs args) { + super(args); + this.ssec = args.ssec; + } + + public ServerSideEncryption.CustomerKey ssec() { return ssec; } - protected void validateSsec(HttpUrl url) { - checkSse(ssec, url); + protected void validateSsec(boolean isHttps) { + checkSse(ssec, isHttps); } /** Base argument builder class for {@link ObjectReadArgs}. */ public abstract static class Builder, A extends ObjectReadArgs> extends ObjectVersionArgs.Builder { @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class - public B ssec(ServerSideEncryptionCustomerKey ssec) { + public B ssec(ServerSideEncryption.CustomerKey ssec) { operations.add(args -> args.ssec = ssec); return (B) this; } diff --git a/api/src/main/java/io/minio/ObjectVersionArgs.java b/api/src/main/java/io/minio/ObjectVersionArgs.java index 528fc02cd..6189aa283 100644 --- a/api/src/main/java/io/minio/ObjectVersionArgs.java +++ b/api/src/main/java/io/minio/ObjectVersionArgs.java @@ -22,6 +22,13 @@ public abstract class ObjectVersionArgs extends ObjectArgs { protected String versionId; + protected ObjectVersionArgs() {} + + protected ObjectVersionArgs(ObjectVersionArgs args) { + super(args); + this.versionId = args.versionId; + } + public String versionId() { return versionId; } diff --git a/api/src/main/java/io/minio/ObjectWriteArgs.java b/api/src/main/java/io/minio/ObjectWriteArgs.java index b7d76613b..6f84b4b81 100644 --- a/api/src/main/java/io/minio/ObjectWriteArgs.java +++ b/api/src/main/java/io/minio/ObjectWriteArgs.java @@ -16,16 +16,14 @@ 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.io.IOException; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import okhttp3.HttpUrl; +import okhttp3.MediaType; /** Base argument class for writing object. */ public abstract class ObjectWriteArgs extends ObjectArgs { @@ -37,20 +35,30 @@ public abstract class ObjectWriteArgs extends ObjectArgs { public static final long MAX_PART_SIZE = 5L * 1024 * 1024 * 1024; public static final int MAX_MULTIPART_COUNT = 10000; - protected Multimap headers = - Multimaps.unmodifiableMultimap(HashMultimap.create()); - protected Multimap userMetadata = - Multimaps.unmodifiableMultimap(HashMultimap.create()); + protected Http.Headers headers; + protected Http.Headers userMetadata; protected ServerSideEncryption sse; protected Tags tags = new Tags(); protected Retention retention; protected boolean legalHold; - public Multimap headers() { + protected ObjectWriteArgs() {} + + protected ObjectWriteArgs(ObjectWriteArgs args) { + super(args); + this.headers = args.headers; + this.userMetadata = args.userMetadata; + this.sse = args.sse; + this.tags = args.tags; + this.retention = args.retention; + this.legalHold = args.legalHold; + } + + public Http.Headers headers() { return headers; } - public Multimap userMetadata() { + public Http.Headers userMetadata() { return userMetadata; } @@ -70,40 +78,46 @@ public boolean legalHold() { return legalHold; } - public Multimap genHeaders() { - Multimap headers = HashMultimap.create(); + public MediaType contentType() throws IOException { + return (headers != null && headers.getFirst(Http.Headers.CONTENT_TYPE) != null) + ? MediaType.parse(headers.getFirst(Http.Headers.CONTENT_TYPE)) + : null; + } - headers.putAll(this.headers); - headers.putAll(userMetadata); + public Http.Headers makeHeaders() { + return makeHeaders(null, null); + } - if (sse != null) { - headers.putAll(Multimaps.forMap(sse.headers())); - } + public Http.Headers makeHeaders(MediaType contentType) { + return makeHeaders(contentType, null); + } + + public Http.Headers makeHeaders(MediaType contentType, Http.Headers checksumHeaders) { + Http.Headers headers = + Http.Headers.merge( + this.headers, userMetadata, sse == null ? null : sse.headers(), checksumHeaders); String tagging = tags.get().entrySet().stream() - .map(e -> S3Escaper.encode(e.getKey()) + "=" + S3Escaper.encode(e.getValue())) + .map(e -> Utils.encode(e.getKey()) + "=" + Utils.encode(e.getValue())) .collect(Collectors.joining("&")); - if (!tagging.isEmpty()) { - headers.put("x-amz-tagging", tagging); - } + 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-mode", retention.mode().toString()); headers.put( "x-amz-object-lock-retain-until-date", - retention.retainUntilDate().format(Time.RESPONSE_DATE_FORMAT)); + retention.retainUntilDate().format(Time.ISO8601UTC_FORMAT)); } - if (legalHold) { - headers.put("x-amz-object-lock-legal-hold", "ON"); - } + if (legalHold) headers.put("x-amz-object-lock-legal-hold", "ON"); + if (contentType != null) headers.put(Http.Headers.CONTENT_TYPE, contentType.toString()); return headers; } - protected void validateSse(HttpUrl url) { - checkSse(sse, url); + protected void validateSse(boolean isHttps) { + checkSse(sse, isHttps); } /** Base argument builder class for {@link ObjectWriteArgs}. */ @@ -111,33 +125,30 @@ protected void validateSse(HttpUrl url) { public abstract static class Builder, A extends ObjectWriteArgs> extends ObjectArgs.Builder { public B headers(Map headers) { - final Multimap headersCopy = toMultimap(headers); - operations.add(args -> args.headers = headersCopy); - return (B) this; + return headers(new Http.Headers(headers)); } - public B headers(Multimap headers) { - final Multimap headersCopy = copyMultimap(headers); - operations.add(args -> args.headers = headersCopy); + public B headers(Http.Headers headers) { + final Http.Headers finalHeaders = new Http.Headers(headers); + operations.add(args -> args.headers = finalHeaders); return (B) this; } public B userMetadata(Map userMetadata) { - return userMetadata((userMetadata == null) ? null : Multimaps.forMap(userMetadata)); + return userMetadata(new Http.Headers(userMetadata)); } - public B userMetadata(Multimap userMetadata) { - Multimap userMetadataCopy = HashMultimap.create(); + public B userMetadata(Http.Headers userMetadata) { + Http.Headers normalizedHeaders = new Http.Headers(); if (userMetadata != null) { for (String key : userMetadata.keySet()) { - userMetadataCopy.putAll( + normalizedHeaders.putAll( (key.toLowerCase(Locale.US).startsWith("x-amz-meta-") ? "" : "x-amz-meta-") + key, userMetadata.get(key)); } } - final Multimap finalUserMetadata = - Multimaps.unmodifiableMultimap(userMetadataCopy); + final Http.Headers finalUserMetadata = normalizedHeaders; operations.add(args -> args.userMetadata = finalUserMetadata); return (B) this; } diff --git a/api/src/main/java/io/minio/PartReader.java b/api/src/main/java/io/minio/PartReader.java index 9209c4536..dc227f802 100644 --- a/api/src/main/java/io/minio/PartReader.java +++ b/api/src/main/java/io/minio/PartReader.java @@ -1,6 +1,5 @@ /* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, - * (C) 2021 MinIO, Inc. + * 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. @@ -17,157 +16,153 @@ package io.minio; -import com.google.common.io.BaseEncoding; +import io.minio.errors.MinioException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Locale; +import java.util.Map; import java.util.Objects; import javax.annotation.Nonnull; -/** PartReader reads part data from file or input stream sequentially and returns PartSource. */ class PartReader { - private static final long CHUNK_SIZE = Integer.MAX_VALUE; - - private byte[] buf16k = new byte[16384]; // 16KiB buffer for optimization. - - private RandomAccessFile file; - private InputStream stream; - - private long objectSize; - private long partSize; - private int partCount; - - private int partNumber; - private long totalDataRead; - - private ByteBufferStream[] buffers; - private byte[] oneByte = null; + byte[] buf16k = new byte[16384]; + + RandomAccessFile file; + InputStream stream; + long objectSize; + long partSize; + int partCount; + Map hashers; + + long totalBytesRead = 0; + int partNumber = 0; + byte[] oneByte = null; boolean eof; - private PartReader(long objectSize, long partSize, int partCount) { - this.objectSize = objectSize; - this.partSize = partSize; - this.partCount = partCount; - - long bufferCount = partSize / CHUNK_SIZE; - if ((partSize - (bufferCount * CHUNK_SIZE)) > 0) bufferCount++; - if (bufferCount == 0) bufferCount++; - - this.buffers = new ByteBufferStream[(int) bufferCount]; - } - - public PartReader(@Nonnull RandomAccessFile file, long objectSize, long partSize, int partCount) { - this(objectSize, partSize, partCount); + public PartReader( + @Nonnull RandomAccessFile file, + long objectSize, + long partSize, + int partCount, + Checksum.Algorithm... algorithms) + throws MinioException { this.file = Objects.requireNonNull(file, "file must not be null"); - if (this.objectSize < 0) throw new IllegalArgumentException("object size must be provided"); + if (objectSize < 0) throw new IllegalArgumentException("valid object size must be provided"); + if (partCount < 0) throw new IllegalArgumentException("part count must be provided"); + set(objectSize, partSize, partCount, algorithms); } - public PartReader(@Nonnull InputStream stream, long objectSize, long partSize, int partCount) { - this(objectSize, partSize, partCount); + public PartReader( + @Nonnull InputStream stream, + Long objectSize, + long partSize, + int partCount, + Checksum.Algorithm... algorithms) + throws MinioException { this.stream = Objects.requireNonNull(stream, "stream must not be null"); - for (int i = 0; i < this.buffers.length; i++) this.buffers[i] = new ByteBufferStream(); + if (partCount == -1) { + objectSize = -1L; + } else if (objectSize < 0) { + throw new IllegalArgumentException("object size must be provided for part count"); + } + set(objectSize, partSize, partCount, algorithms); } - private long readStreamChunk(ByteBufferStream buffer, long size, MessageDigest sha256) - throws IOException { - long totalBytesRead = 0; + private void set(Long objectSize, long partSize, int partCount, Checksum.Algorithm[] algorithms) + throws MinioException { + if (partCount == 0) partCount = -1; + this.objectSize = objectSize == null ? -1 : objectSize; + this.partSize = partSize; + this.partCount = partCount; + this.hashers = Checksum.newHasherMap(algorithms); + } - if (this.oneByte != null) { - buffer.write(this.oneByte); - sha256.update(this.oneByte); - totalBytesRead++; - this.oneByte = null; + private int readBuf16k(int length) throws MinioException { + try { + return file != null ? file.read(buf16k, 0, length) : stream.read(buf16k, 0, length); + } catch (IOException e) { + throw new MinioException(e); } + } - while (totalBytesRead < size) { - long bytesToRead = size - totalBytesRead; - if (bytesToRead > this.buf16k.length) bytesToRead = this.buf16k.length; - int bytesRead = this.stream.read(this.buf16k, 0, (int) bytesToRead); - this.eof = (bytesRead < 0); - if (this.eof) { - if (this.objectSize < 0) break; - throw new IOException("unexpected EOF"); - } - buffer.write(this.buf16k, 0, bytesRead); - sha256.update(this.buf16k, 0, bytesRead); - totalBytesRead += bytesRead; + private void readOneByte() throws MinioException { + if (eof) return; + + oneByte = new byte[] {0}; + int n = 0; + + try { + while ((n = file != null ? file.read(oneByte) : stream.read(oneByte)) == 0) ; + } catch (IOException e) { + throw new MinioException(e); } - return totalBytesRead; + if ((eof = n < 0)) oneByte = null; } - private long readStream(long size, MessageDigest sha256) throws IOException { - long count = size / CHUNK_SIZE; - long lastChunkSize = size - (count * CHUNK_SIZE); - if (lastChunkSize > 0) { - count++; - } else { - lastChunkSize = CHUNK_SIZE; - } + public void read(ByteBuffer buffer) throws MinioException { + if (buffer == null) throw new IllegalArgumentException("valid buffer must be provided"); + if (eof) throw new MinioException("EOF reached"); + if (partNumber == partCount) throw new MinioException("data fully read"); - long totalBytesRead = 0; - for (int i = 0; i < buffers.length; i++) buffers[i].reset(); - for (long i = 1; i <= count && !this.eof; i++) { - long chunkSize = (i != count) ? CHUNK_SIZE : lastChunkSize; - long bytesRead = this.readStreamChunk(buffers[(int) (i - 1)], chunkSize, sha256); - totalBytesRead += bytesRead; + long size = partSize; + if (partCount == 1) { + size = objectSize; + } else if (partNumber == partCount - 1) { + size = objectSize - totalBytesRead; + } + if (buffer.size() < size) { + throw new IllegalArgumentException( + "insufficient buffer size " + buffer.size() + " for data size " + size); } - if (!this.eof && this.objectSize < 0) { - this.oneByte = new byte[1]; - this.eof = this.stream.read(this.oneByte) < 0; + for (Map.Entry entry : hashers.entrySet()) { + entry.getValue().reset(); } - return totalBytesRead; - } + long bytesRead = 0; - private long readFile(long size, MessageDigest sha256) throws IOException { - long position = this.file.getFilePointer(); - long totalBytesRead = 0; - - while (totalBytesRead < size) { - long bytesToRead = size - totalBytesRead; - if (bytesToRead > this.buf16k.length) bytesToRead = this.buf16k.length; - int bytesRead = this.file.read(this.buf16k, 0, (int) bytesToRead); - if (bytesRead < 0) throw new IOException("unexpected EOF"); - sha256.update(this.buf16k, 0, bytesRead); - totalBytesRead += bytesRead; + if (oneByte != null) { + try { + buffer.write(oneByte); + } catch (IOException e) { + throw new MinioException(e); + } + Checksum.update(hashers, oneByte, oneByte.length); + bytesRead++; + oneByte = null; } - this.file.seek(position); - return totalBytesRead; - } + while (bytesRead < size) { + int n = readBuf16k((int) Math.min(size - bytesRead, this.buf16k.length)); + if ((eof = n < 0)) { + if (partCount < 0) break; + throw new MinioException("unexpected EOF"); + } + try { + buffer.write(this.buf16k, 0, n); + } catch (IOException e) { + throw new MinioException(e); + } + Checksum.update(hashers, this.buf16k, n); + bytesRead += n; + } - private long read(long size, MessageDigest sha256) throws IOException { - return (this.file != null) ? readFile(size, sha256) : readStream(size, sha256); + totalBytesRead += bytesRead; + partNumber++; + readOneByte(); + if (eof && partCount < 0) partCount = partNumber; } - public PartSource getPart() throws NoSuchAlgorithmException, IOException { - if (this.partNumber == this.partCount) return null; - - this.partNumber++; - - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - - long partSize = this.partSize; - if (this.partNumber == this.partCount) partSize = this.objectSize - this.totalDataRead; - long bytesRead = this.read(partSize, sha256); - this.totalDataRead += bytesRead; - if (this.objectSize < 0 && this.eof) this.partCount = this.partNumber; - - String sha256Hash = BaseEncoding.base16().encode(sha256.digest()).toLowerCase(Locale.US); - - if (this.file != null) { - return new PartSource(this.partNumber, this.file, bytesRead, null, sha256Hash); - } + public Map hashers() { + return hashers; + } - return new PartSource(this.partNumber, this.buffers, bytesRead, null, sha256Hash); + public int partNumber() { + return partNumber; } public int partCount() { - return this.partCount; + return partCount; } } diff --git a/api/src/main/java/io/minio/PartSource.java b/api/src/main/java/io/minio/PartSource.java index b18581eb5..304edbcf3 100644 --- a/api/src/main/java/io/minio/PartSource.java +++ b/api/src/main/java/io/minio/PartSource.java @@ -25,36 +25,34 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import javax.annotation.Nonnull; -import okio.Okio; -import okio.Source; /** Part source information. */ class PartSource { private int partNumber; private long size; - private String md5Hash; - private String sha256Hash; + private Map checksums; private RandomAccessFile file; private long position; private ByteBufferStream[] buffers; - private InputStream inputStream; - - private PartSource(int partNumber, long size, String md5Hash, String sha256Hash) { + private PartSource(int partNumber, long size, Map checksums) { this.partNumber = partNumber; this.size = size; - this.md5Hash = md5Hash; - this.sha256Hash = sha256Hash; + this.checksums = checksums; } public PartSource( - int partNumber, @Nonnull RandomAccessFile file, long size, String md5Hash, String sha256Hash) + int partNumber, + @Nonnull RandomAccessFile file, + long size, + Map checksums) throws IOException { - this(partNumber, size, md5Hash, sha256Hash); + this(partNumber, size, checksums); this.file = Objects.requireNonNull(file, "file must not be null"); this.position = this.file.getFilePointer(); } @@ -63,17 +61,11 @@ public PartSource( int partNumber, @Nonnull ByteBufferStream[] buffers, long size, - String md5Hash, - String sha256Hash) { - this(partNumber, size, md5Hash, sha256Hash); + Map checksums) { + this(partNumber, size, checksums); this.buffers = Objects.requireNonNull(buffers, "buffers must not be null"); } - public PartSource(@Nonnull InputStream inputStream, long size) { - this(0, size, null, null); - this.inputStream = Objects.requireNonNull(inputStream, "input stream must not be null"); - } - public int partNumber() { return this.partNumber; } @@ -82,26 +74,18 @@ public long size() { return this.size; } - public String md5Hash() { - return this.md5Hash; + public Map checksums() { + return checksums; } - public String sha256Hash() { - return this.sha256Hash; - } - - public Source source() throws IOException { + public InputStream inputStream() throws IOException { if (this.file != null) { this.file.seek(this.position); - return Okio.source(Channels.newInputStream(this.file.getChannel())); - } - - if (this.inputStream != null) { - return Okio.source(this.inputStream); + return Channels.newInputStream(this.file.getChannel()); } InputStream stream = buffers[0].inputStream(); - if (buffers.length == 1) return Okio.source(stream); + if (buffers.length == 1) return stream; List streams = new ArrayList<>(); streams.add(stream); @@ -109,7 +93,7 @@ public Source source() throws IOException { if (buffers[i].size() == 0) break; streams.add(buffers[i].inputStream()); } - if (streams.size() == 1) return Okio.source(stream); - return Okio.source(new SequenceInputStream(Collections.enumeration(streams))); + if (streams.size() == 1) return stream; + return new SequenceInputStream(Collections.enumeration(streams)); } } diff --git a/api/src/main/java/io/minio/PostPolicy.java b/api/src/main/java/io/minio/PostPolicy.java index 204d06a30..600865454 100644 --- a/api/src/main/java/io/minio/PostPolicy.java +++ b/api/src/main/java/io/minio/PostPolicy.java @@ -19,9 +19,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.minio.credentials.Credentials; +import io.minio.errors.MinioException; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Base64; @@ -29,6 +28,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.annotation.Nonnull; @@ -44,7 +44,7 @@ public class PostPolicy { "bucket", "x-amz-algorithm", "x-amz-credential", - "x-amz-date", + Http.Headers.X_AMZ_DATE.toLowerCase(Locale.US), "policy", "x-amz-signature" }); @@ -173,7 +173,7 @@ public void removeContentLengthRangeCondition() { * x-amz-credential, x-amz-security-token, x-amz-date, policy and x-amz-signature. */ public Map formData(@Nonnull Credentials creds, @Nonnull String region) - throws NoSuchAlgorithmException, InvalidKeyException { + throws MinioException { if (creds == null) { throw new IllegalArgumentException("credentials cannot be null"); } @@ -187,7 +187,7 @@ public Map formData(@Nonnull Credentials creds, @Nonnull String } Map policyMap = new HashMap<>(); - policyMap.put("expiration", expiration.format(Time.EXPIRATION_DATE_FORMAT)); + policyMap.put("expiration", expiration.format(Time.ISO8601UTC_FORMAT)); List> conditionList = new LinkedList<>(); conditionList.add(Arrays.asList(new Object[] {"eq", "$bucket", bucketName})); for (Map.Entry> condition : conditions.entrySet()) { @@ -228,9 +228,9 @@ public Map formData(@Nonnull Credentials creds, @Nonnull String formData.put("x-amz-algorithm", ALGORITHM); formData.put("x-amz-credential", credential); if (creds.sessionToken() != null) { - formData.put("x-amz-security-token", creds.sessionToken()); + formData.put(Http.Headers.X_AMZ_SECURITY_TOKEN, creds.sessionToken()); } - formData.put("x-amz-date", amzDate); + formData.put(Http.Headers.X_AMZ_DATE, amzDate); formData.put("policy", policy); formData.put("x-amz-signature", signature); return formData; diff --git a/api/src/main/java/io/minio/PromptObjectArgs.java b/api/src/main/java/io/minio/PromptObjectArgs.java index f092f4c65..cc115eb2b 100644 --- a/api/src/main/java/io/minio/PromptObjectArgs.java +++ b/api/src/main/java/io/minio/PromptObjectArgs.java @@ -51,25 +51,25 @@ public static final class Builder extends ObjectArgs.Builder args.prompt = prompt); return this; } public Builder lambdaArn(String lambdaArn) { - validateNotEmptyString(lambdaArn, "lambda ARN"); + Utils.validateNotEmptyString(lambdaArn, "lambda ARN"); operations.add(args -> args.lambdaArn = lambdaArn); return this; } public Builder promptArgs(Map promptArgs) { - validateNotNull(promptArgs, "prompt argument"); + Utils.validateNotNull(promptArgs, "prompt argument"); operations.add(args -> args.promptArgs = promptArgs); return this; } diff --git a/api/src/main/java/io/minio/PutObjectAPIArgs.java b/api/src/main/java/io/minio/PutObjectAPIArgs.java new file mode 100644 index 000000000..69dfdce47 --- /dev/null +++ b/api/src/main/java/io/minio/PutObjectAPIArgs.java @@ -0,0 +1,106 @@ +/* + * 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 java.io.RandomAccessFile; +import java.util.Objects; +import okhttp3.MediaType; + +/** Argument class of {@link MinioAsyncClient#putObjectAPI} and {@link MinioClient#putObjectAPI}. */ +public class PutObjectAPIArgs extends PutObjectAPIBaseArgs { + private MediaType contentType; + + protected PutObjectAPIArgs() {} + + public PutObjectAPIArgs( + PutObjectBaseArgs args, + ByteBuffer buffer, + MediaType contentType, + Http.Headers checksumHeaders) { + super(args, buffer, args.makeHeaders(contentType, checksumHeaders)); + this.contentType = contentType; + } + + public PutObjectAPIArgs( + PutObjectBaseArgs args, + RandomAccessFile file, + long length, + MediaType contentType, + Http.Headers checksumHeaders) { + super(args, file, length, args.makeHeaders(contentType, checksumHeaders)); + this.contentType = contentType; + } + + public PutObjectAPIArgs( + PutObjectBaseArgs args, + byte[] data, + int length, + MediaType contentType, + Http.Headers checksumHeaders) { + super(args, data, length, args.makeHeaders(contentType, checksumHeaders)); + this.contentType = contentType; + } + + public PutObjectAPIArgs( + UploadSnowballObjectsArgs args, byte[] data, int length, Http.Headers headers) { + super(args, data, length, headers); + } + + public PutObjectAPIArgs( + UploadSnowballObjectsArgs args, RandomAccessFile file, long length, Http.Headers headers) { + super(args, file, length, headers); + } + + public MediaType contentType() { + return contentType; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link PutObjectAPIArgs}. */ + public static final class Builder + extends PutObjectAPIBaseArgs.Builder { + public Builder contentType(String value) { + MediaType contentType = MediaType.parse(value); + if (value != null && contentType == null) { + throw new IllegalArgumentException("invalid content type '" + value + "' as per RFC 2045"); + } + return contentType(contentType); + } + + public Builder contentType(MediaType contentType) { + operations.add(args -> args.contentType = contentType); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PutObjectAPIArgs)) return false; + if (!super.equals(o)) return false; + PutObjectAPIArgs that = (PutObjectAPIArgs) o; + return Objects.equals(contentType, that.contentType); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), contentType); + } +} diff --git a/api/src/main/java/io/minio/PutObjectAPIBaseArgs.java b/api/src/main/java/io/minio/PutObjectAPIBaseArgs.java new file mode 100644 index 000000000..4423772c3 --- /dev/null +++ b/api/src/main/java/io/minio/PutObjectAPIBaseArgs.java @@ -0,0 +1,153 @@ +/* + * 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 java.io.RandomAccessFile; +import java.util.Arrays; +import java.util.Objects; + +/** Argument class of {@link MinioAsyncClient#putObjectAPI} and {@link MinioClient#putObjectAPI}. */ +public abstract class PutObjectAPIBaseArgs extends ObjectArgs { + protected RandomAccessFile file; + protected ByteBuffer buffer; + protected byte[] data; + protected Long length; + protected Http.Headers headers; + + protected PutObjectAPIBaseArgs() {} + + private PutObjectAPIBaseArgs(PutObjectBaseArgs args, Http.Headers headers) { + super(args); + this.headers = headers; + } + + protected PutObjectAPIBaseArgs(PutObjectBaseArgs args, ByteBuffer buffer, Http.Headers headers) { + this(args, headers); + this.buffer = buffer; + } + + protected PutObjectAPIBaseArgs( + PutObjectBaseArgs args, RandomAccessFile file, long length, Http.Headers headers) { + this(args, headers); + this.file = file; + this.length = length; + } + + protected PutObjectAPIBaseArgs( + PutObjectBaseArgs args, byte[] data, int length, Http.Headers headers) { + this(args, headers); + this.data = data; + this.length = (long) length; + } + + protected PutObjectAPIBaseArgs( + UploadSnowballObjectsArgs args, byte[] data, int length, Http.Headers headers) { + super(args); + this.data = data; + this.length = (long) length; + this.headers = headers; + } + + protected PutObjectAPIBaseArgs( + UploadSnowballObjectsArgs args, RandomAccessFile file, long length, Http.Headers headers) { + super(args); + this.file = file; + this.length = length; + this.headers = headers; + } + + public RandomAccessFile file() { + return file; + } + + public ByteBuffer buffer() { + return buffer; + } + + public byte[] data() { + return data; + } + + public Long length() { + return length; + } + + public Http.Headers headers() { + return headers; + } + + /** Base argument builder of {@link PutObjectAPIBaseArgs}. */ + @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class + public abstract static class Builder, A extends PutObjectAPIBaseArgs> + extends ObjectArgs.Builder { + protected void validate(A args) { + super.validate(args); + if (!((args.file != null) != (args.buffer != null) != (args.data != null) + && !(args.file != null && args.buffer != null && args.data != null))) { + throw new IllegalArgumentException("only one of file, buffer or data must be provided"); + } + } + + public B setData(RandomAccessFile file, ByteBuffer buffer, byte[] data, Long length) { + operations.add(args -> args.file = file); + operations.add(args -> args.buffer = buffer); + operations.add(args -> args.data = data); + operations.add(args -> args.length = length); + return (B) this; + } + + public B file(RandomAccessFile file, long length) { + Utils.validateNotNull(file, "file"); + if (length < 0) throw new IllegalArgumentException("valid length must be provided"); + return setData(file, null, null, length); + } + + public B buffer(ByteBuffer buffer) { + Utils.validateNotNull(buffer, "buffer"); + return setData(null, buffer, null, null); + } + + public B data(byte[] data, int length) { + Utils.validateNotNull(data, "data"); + if (length < 0) throw new IllegalArgumentException("valid length must be provided"); + return setData(null, null, data, (long) length); + } + + public B headers(Http.Headers headers) { + operations.add(args -> args.headers = headers); + return (B) this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PutObjectAPIBaseArgs)) return false; + if (!super.equals(o)) return false; + PutObjectAPIBaseArgs that = (PutObjectAPIBaseArgs) o; + return Objects.equals(file, that.file) + && Objects.equals(buffer, that.buffer) + && Arrays.equals(data, that.data) + && Objects.equals(length, that.length) + && Objects.equals(headers, that.headers); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), file, buffer, data, length, headers); + } +} diff --git a/api/src/main/java/io/minio/PutObjectArgs.java b/api/src/main/java/io/minio/PutObjectArgs.java index bdc2f5869..8ea90589a 100644 --- a/api/src/main/java/io/minio/PutObjectArgs.java +++ b/api/src/main/java/io/minio/PutObjectArgs.java @@ -16,26 +16,26 @@ package io.minio; -import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Objects; +import okhttp3.MediaType; /** Argument class of {@link MinioAsyncClient#putObject} and {@link MinioClient#putObject}. */ public class PutObjectArgs extends PutObjectBaseArgs { - private BufferedInputStream stream; + private InputStream stream; + private byte[] data; - public BufferedInputStream stream() { + public InputStream stream() { return stream; } - /** - * Gets content type. It returns if content type is set (or) value of "Content-Type" header (or) - * default "application/octet-stream". - */ - public String contentType() throws IOException { - String contentType = super.contentType(); - return (contentType != null) ? contentType : "application/octet-stream"; + public byte[] data() { + return data; + } + + public MediaType contentType() throws IOException { + return super.contentType(); } public static Builder builder() { @@ -47,48 +47,48 @@ public static final class Builder extends PutObjectBaseArgs.Builder args.stream = stream); + operations.add(args -> args.data = data); + operations.add(args -> args.objectSize = objectSize); + operations.add(args -> args.partSize = partSize); + operations.add(args -> args.partCount = partCount); + return this; } /** * Sets stream to upload. Two ways to provide object/part sizes. * *
    - *
  • If object size is unknown, pass -1 to objectSize and pass valid partSize. - *
  • If object size is known, pass -1 to partSize for auto detect; else pass valid partSize - * to control memory usage and no. of parts in upload. - *
  • If partSize is greater than objectSize, objectSize is used as partSize. + *
  • If object size is unknown, pass valid part size. + *
  • If object size is known, pass valid part size to control memory usage and no. of parts + * to upload. *
* *

A valid part size is between 5MiB to 5GiB (both limits inclusive). */ - public Builder stream(InputStream stream, long objectSize, long partSize) { - validateNotNull(stream, "stream"); - + public Builder stream(InputStream stream, Long objectSize, Long partSize) { + Utils.validateNotNull(stream, "stream"); long[] partinfo = getPartInfo(objectSize, partSize); - long pSize = partinfo[0]; - int pCount = (int) partinfo[1]; - - final BufferedInputStream bis = - (stream instanceof BufferedInputStream) - ? (BufferedInputStream) stream - : new BufferedInputStream(stream); - return setStream(bis, objectSize, pSize, pCount); - } - - private Builder setStream( - BufferedInputStream stream, long objectSize, long partSize, int partCount) { - operations.add(args -> args.stream = stream); - operations.add(args -> args.objectSize = objectSize); - operations.add(args -> args.partSize = partSize); - operations.add(args -> args.partCount = partCount); - return this; + return setStream(stream, null, objectSize, partinfo[0], (int) partinfo[1]); } - public Builder contentType(String contentType) { - validateContentType(contentType); - operations.add(args -> args.contentType = contentType); - return this; + public Builder data(byte[] data, int length) { + if (data != null && length < 0) { + throw new IllegalArgumentException("valid length must be provided"); + } + return setStream( + null, + data, + data == null ? null : (long) length, + (long) Math.max(MIN_MULTIPART_SIZE, data == null ? -1 : length), + 1); } } diff --git a/api/src/main/java/io/minio/PutObjectBaseArgs.java b/api/src/main/java/io/minio/PutObjectBaseArgs.java index aaec5e1bb..abd787a43 100644 --- a/api/src/main/java/io/minio/PutObjectBaseArgs.java +++ b/api/src/main/java/io/minio/PutObjectBaseArgs.java @@ -22,13 +22,14 @@ /** Base argument class for {@link PutObjectArgs} and {@link UploadObjectArgs}. */ public abstract class PutObjectBaseArgs extends ObjectWriteArgs { - protected long objectSize; + protected Long objectSize; protected long partSize; protected int partCount; - protected String contentType; - protected boolean preloadData; + protected MediaType contentType; + protected Checksum.Algorithm checksum; + protected int parallelUploads; - public long objectSize() { + public Long objectSize() { return objectSize; } @@ -40,37 +41,35 @@ public int partCount() { return partCount; } - /** Gets content type. It returns if content type is set (or) value of "Content-Type" header. */ - public String contentType() throws IOException { - if (contentType != null) { - return contentType; - } - - if (this.headers().containsKey("Content-Type")) { - return this.headers().get("Content-Type").iterator().next(); - } + public MediaType contentType() throws IOException { + return contentType != null ? contentType : super.contentType(); + } - return null; + public Checksum.Algorithm checksum() { + return checksum; } - public boolean preloadData() { - return preloadData; + public int parallelUploads() { + return parallelUploads; } /** Base argument builder class for {@link PutObjectBaseArgs}. */ @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public abstract static class Builder, A extends PutObjectBaseArgs> extends ObjectWriteArgs.Builder { - protected void validateContentType(String contentType) { - validateNotEmptyString(contentType, "content type"); - if (MediaType.parse(contentType) == null) { + protected void validate(A args) { + super.validate(args); + if (args.checksum != null + && args.partCount > 0 + && (!(args.partCount == 1 && args.checksum.fullObjectSupport() + || args.partCount > 1 && args.checksum.compositeSupport()))) { throw new IllegalArgumentException( - "invalid content type '" + contentType + "' as per RFC 2045"); + "unsupported checksum " + args.checksum + " for part count " + args.partCount); } } - private void validateSizes(long objectSize, long partSize) { - if (partSize > 0) { + protected long[] getPartInfo(Long objectSize, Long partSize) { + if (partSize != null && partSize > 0) { if (partSize < MIN_MULTIPART_SIZE) { throw new IllegalArgumentException( "part size " + partSize + " is not supported; minimum allowed 5MiB"); @@ -82,31 +81,24 @@ private void validateSizes(long objectSize, long partSize) { } } - if (objectSize >= 0) { - if (objectSize > MAX_OBJECT_SIZE) { + if (objectSize == null || objectSize < 0) { + if (partSize == null || partSize <= 0) { throw new IllegalArgumentException( - "object size " + objectSize + " is not supported; maximum allowed 5TiB"); + "valid part size must be provided for unknown object size"); } - } else if (partSize <= 0) { - throw new IllegalArgumentException( - "valid part size must be provided when object size is unknown"); + return new long[] {partSize, -1}; } - } - - protected long[] getPartInfo(long objectSize, long partSize) { - validateSizes(objectSize, partSize); - - if (objectSize < 0) return new long[] {partSize, -1}; - if (partSize <= 0) { + if (partSize == null || partSize <= 0) { // Calculate part size by multiple of MIN_MULTIPART_SIZE. double dPartSize = Math.ceil((double) objectSize / MAX_MULTIPART_COUNT); dPartSize = Math.ceil(dPartSize / MIN_MULTIPART_SIZE) * MIN_MULTIPART_SIZE; partSize = (long) dPartSize; } - if (partSize > objectSize) partSize = objectSize; - long partCount = partSize > 0 ? (long) Math.ceil((double) objectSize / partSize) : 1; + if (partSize > objectSize) return new long[] {partSize, 1}; + + long partCount = (long) Math.ceil((double) objectSize / partSize); if (partCount > MAX_MULTIPART_COUNT) { throw new IllegalArgumentException( "object size " @@ -121,16 +113,29 @@ protected long[] getPartInfo(long objectSize, long partSize) { return new long[] {partSize, partCount}; } - /** - * Sets flag to control data preload of stream/file. When this flag is enabled, entire - * part/object data is loaded into memory to enable connection retry on network failure in the - * middle of upload. - * - * @deprecated As this behavior is enabled by default and cannot be turned off. - */ - @Deprecated - public B preloadData(boolean preloadData) { - operations.add(args -> args.preloadData = preloadData); + public B contentType(String value) { + MediaType contentType = MediaType.parse(value); + if (value != null && contentType == null) { + throw new IllegalArgumentException("invalid content type '" + value + "' as per RFC 2045"); + } + return contentType(contentType); + } + + public B contentType(MediaType contentType) { + operations.add(args -> args.contentType = contentType); + return (B) this; + } + + public B checksum(Checksum.Algorithm algorithm) { + if (algorithm == Checksum.Algorithm.MD5) { + throw new IllegalArgumentException(Checksum.Algorithm.MD5 + " algorithm is not allowed"); + } + operations.add(args -> args.checksum = algorithm); + return (B) this; + } + + public B parallelUploads(int parallelUploads) { + operations.add(args -> args.parallelUploads = parallelUploads); return (B) this; } } @@ -141,16 +146,17 @@ public boolean equals(Object o) { if (!(o instanceof PutObjectBaseArgs)) return false; if (!super.equals(o)) return false; PutObjectBaseArgs that = (PutObjectBaseArgs) o; - return objectSize == that.objectSize + return Objects.equals(objectSize, that.objectSize) && partSize == that.partSize && partCount == that.partCount && Objects.equals(contentType, that.contentType) - && preloadData == that.preloadData; + && Objects.equals(checksum, that.checksum) + && parallelUploads == that.parallelUploads; } @Override public int hashCode() { return Objects.hash( - super.hashCode(), objectSize, partSize, partCount, contentType, preloadData); + super.hashCode(), objectSize, partSize, partCount, contentType, checksum, parallelUploads); } } diff --git a/api/src/main/java/io/minio/PutObjectFanOutArgs.java b/api/src/main/java/io/minio/PutObjectFanOutArgs.java index 64ad1cb60..68aeca6f3 100644 --- a/api/src/main/java/io/minio/PutObjectFanOutArgs.java +++ b/api/src/main/java/io/minio/PutObjectFanOutArgs.java @@ -25,7 +25,6 @@ import java.io.InputStream; import java.util.List; import java.util.Objects; -import okhttp3.HttpUrl; /** * Argument class of {@link MinioAsyncClient#putObjectFanOut} and {@link @@ -74,8 +73,8 @@ public String fanOutList() throws JsonProcessingException { return builder.toString(); } - public void validateSse(HttpUrl url) { - checkSse(sse, url); + public void validateSse(boolean isHttps) { + checkSse(sse, isHttps); } public static Builder builder() { @@ -87,12 +86,12 @@ public static final class Builder extends BucketArgs.Builder { @Override protected void validate(PutObjectFanOutEntry args) { - validateNotEmptyString(args.key, "key"); + Utils.validateNotEmptyString(args.key, "key"); } public Builder key(String key) { - validateNotEmptyString(key, "key"); + Utils.validateNotEmptyString(key, "key"); operations.add(args -> args.key = key); return this; } diff --git a/api/src/main/java/io/minio/PutObjectFanOutResponse.java b/api/src/main/java/io/minio/PutObjectFanOutResponse.java index e1f28be06..6b7e9efca 100644 --- a/api/src/main/java/io/minio/PutObjectFanOutResponse.java +++ b/api/src/main/java/io/minio/PutObjectFanOutResponse.java @@ -17,6 +17,7 @@ package io.minio; import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; import java.util.List; import okhttp3.Headers; @@ -48,8 +49,7 @@ public static class Result { private String versionId; @JsonProperty("lastModified") - private String lastModified; - // private ResponseDate lastModified; + private Time.S3Time lastModified; @JsonProperty("error") private String error; @@ -68,8 +68,8 @@ public String versionId() { return versionId; } - public String lastModified() { - return lastModified; + public ZonedDateTime lastModified() { + return lastModified == null ? null : lastModified.toZonedDateTime(); } public String error() { diff --git a/api/src/main/java/io/minio/Reader.java b/api/src/main/java/io/minio/Reader.java new file mode 100644 index 000000000..1378a4fe1 --- /dev/null +++ b/api/src/main/java/io/minio/Reader.java @@ -0,0 +1,116 @@ +/* + * 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 java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import javax.annotation.Nonnull; + +/** Reader reads part data from file or input stream sequentially and returns PartSource. */ +class Reader { + private byte[] buf16k = new byte[16384]; // 16KiB buffer for optimization. + + private InputStream stream; + private Long objectSize; + private long partSize; + + private long bytesRead = 0; + private Byte oneByte = null; + boolean endOfStream = false; + + public Reader(@Nonnull InputStream stream, Long objectSize, long partSize) { + this.stream = Utils.validateNotNull(stream, "stream"); + if (partSize < 0) throw new IllegalArgumentException("part size must be provided"); + this.objectSize = objectSize; + this.partSize = partSize; + } + + private long copyStream(OutputStream os, long length) throws IOException { + long bytesCopied = 0; + int bytesRead = 0; + while (bytesCopied < length + && (bytesRead = stream.read(buf16k, 0, (int) Math.min(buf16k.length, length - bytesCopied))) + != -1) { + os.write(buf16k, 0, bytesRead); + bytesCopied += bytesRead; + } + return bytesCopied; + } + + private void readUnknownSizedStream(OutputStream os) throws IOException { + long partSize = this.partSize; + + if (oneByte != null) { + os.write(oneByte); + oneByte = null; + partSize--; + } + + if (copyStream(os, partSize) == partSize) { + int b = stream.read(); + if (b != -1) { + oneByte = (byte) b; + } else { + endOfStream = true; + } + } else { + endOfStream = true; + } + } + + private void readSizedStream(OutputStream os) throws IOException { + long partSize = (long) Math.min(this.partSize, objectSize - bytesRead); + long bytesCopied = copyStream(os, partSize); + bytesRead += bytesCopied; + if (bytesCopied != partSize) { + endOfStream = true; + throw new IOException( + "Unexpected end of stream; expected=" + partSize + ", got=" + bytesCopied); + } + endOfStream = bytesRead == objectSize; + } + + public void read(OutputStream os) throws IOException { + if (objectSize == null) { + readUnknownSizedStream(os); + } else { + readSizedStream(os); + } + } + + public boolean endOfStream() { + return endOfStream; + } + + // public void check(OutputStream os, InputStream is, Long objectSize, long partSize) { + // Integer partCount = null; + // if (objectSize != null) partCount = (int) Math.max(1, (objectSize + partSize - 1) / + // partSize); + // for (int partNumber = 1; partCount == null || partNumber <= partCount; partNumber++) { + // read(os); + // if (partCount == null && endOfStream()) partCount = partNumber; + // + // if (partCount != null && partNumber == partCount) { + // doSinglePutObbject(); + // } else { + // uploadPart(); + // } + // } + // } +} diff --git a/api/src/main/java/io/minio/RemoveObjectsArgs.java b/api/src/main/java/io/minio/RemoveObjectsArgs.java index a3b678e65..b03e6b94f 100644 --- a/api/src/main/java/io/minio/RemoveObjectsArgs.java +++ b/api/src/main/java/io/minio/RemoveObjectsArgs.java @@ -16,7 +16,7 @@ package io.minio; -import io.minio.messages.DeleteObject; +import io.minio.messages.DeleteRequest; import java.util.LinkedList; import java.util.Objects; @@ -25,13 +25,13 @@ */ public class RemoveObjectsArgs extends BucketArgs { private boolean bypassGovernanceMode; - private Iterable objects = new LinkedList<>(); + private Iterable objects = new LinkedList<>(); public boolean bypassGovernanceMode() { return bypassGovernanceMode; } - public Iterable objects() { + public Iterable objects() { return objects; } @@ -46,8 +46,8 @@ public Builder bypassGovernanceMode(boolean flag) { return this; } - public Builder objects(Iterable objects) { - validateNotNull(objects, "objects"); + public Builder objects(Iterable objects) { + Utils.validateNotNull(objects, "objects"); operations.add(args -> args.objects = objects); return this; } diff --git a/api/src/main/java/io/minio/RestoreObjectArgs.java b/api/src/main/java/io/minio/RestoreObjectArgs.java index 14467c6e1..ae606b7d9 100644 --- a/api/src/main/java/io/minio/RestoreObjectArgs.java +++ b/api/src/main/java/io/minio/RestoreObjectArgs.java @@ -34,7 +34,7 @@ public static Builder builder() { /** Argument builder of {@link RestoreObjectArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder { private void validateRequest(RestoreRequest request) { - validateNotNull(request, "request"); + Utils.validateNotNull(request, "request"); } public Builder request(RestoreRequest request) { diff --git a/api/src/main/java/io/minio/S3Base.java b/api/src/main/java/io/minio/S3Base.java deleted file mode 100644 index 1595913cd..000000000 --- a/api/src/main/java/io/minio/S3Base.java +++ /dev/null @@ -1,2874 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, - * (C) 2021 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.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.minio.credentials.Credentials; -import io.minio.credentials.Provider; -import io.minio.errors.ErrorResponseException; -import io.minio.errors.InsufficientDataException; -import io.minio.errors.InternalException; -import io.minio.errors.InvalidResponseException; -import io.minio.errors.ServerException; -import io.minio.errors.XmlParserException; -import io.minio.http.HttpUtils; -import io.minio.http.Method; -import io.minio.messages.CompleteMultipartUpload; -import io.minio.messages.CompleteMultipartUploadResult; -import io.minio.messages.CopyPartResult; -import io.minio.messages.DeleteError; -import io.minio.messages.DeleteMarker; -import io.minio.messages.DeleteObject; -import io.minio.messages.DeleteRequest; -import io.minio.messages.DeleteResult; -import io.minio.messages.ErrorResponse; -import io.minio.messages.InitiateMultipartUploadResult; -import io.minio.messages.Item; -import io.minio.messages.ListAllMyBucketsResult; -import io.minio.messages.ListBucketResultV1; -import io.minio.messages.ListBucketResultV2; -import io.minio.messages.ListMultipartUploadsResult; -import io.minio.messages.ListObjectsResult; -import io.minio.messages.ListPartsResult; -import io.minio.messages.ListVersionsResult; -import io.minio.messages.LocationConstraint; -import io.minio.messages.NotificationRecords; -import io.minio.messages.Part; -import io.minio.messages.Prefix; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.RandomAccessFile; -import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.time.ZonedDateTime; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Random; -import java.util.Scanner; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.Headers; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; - -/** Core S3 API client. */ -public abstract class S3Base implements AutoCloseable { - static { - try { - RequestBody.create(new byte[] {}, null); - } catch (NoSuchMethodError ex) { - throw new RuntimeException("Unsupported OkHttp library found. Must use okhttp >= 4.11.0", ex); - } - } - - protected static final String NO_SUCH_BUCKET_MESSAGE = "Bucket does not exist"; - protected static final String NO_SUCH_BUCKET = "NoSuchBucket"; - protected static final String NO_SUCH_BUCKET_POLICY = "NoSuchBucketPolicy"; - protected static final String NO_SUCH_OBJECT_LOCK_CONFIGURATION = "NoSuchObjectLockConfiguration"; - protected static final String SERVER_SIDE_ENCRYPTION_CONFIGURATION_NOT_FOUND_ERROR = - "ServerSideEncryptionConfigurationNotFoundError"; - protected static final long DEFAULT_CONNECTION_TIMEOUT = TimeUnit.MINUTES.toMillis(5); - // maximum allowed bucket policy size is 20KiB - protected static final int MAX_BUCKET_POLICY_SIZE = 20 * 1024; - protected static final String US_EAST_1 = "us-east-1"; - protected final Map regionCache = new ConcurrentHashMap<>(); - protected static final Random random = new Random(new SecureRandom().nextLong()); - protected static final ObjectMapper objectMapper = - JsonMapper.builder() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) - .build(); - - private static final String RETRY_HEAD = "RetryHead"; - private static final String END_HTTP = "----------END-HTTP----------"; - private static final String UPLOAD_ID = "uploadId"; - private static final Set TRACE_QUERY_PARAMS = - ImmutableSet.of("retention", "legal-hold", "tagging", UPLOAD_ID, "acl", "attributes"); - private PrintWriter traceStream; - private String userAgent = MinioProperties.INSTANCE.getDefaultUserAgent(); - - protected HttpUrl baseUrl; - protected String awsS3Prefix; - protected String awsDomainSuffix; - protected boolean awsDualstack; - protected boolean useVirtualStyle; - protected String region; - protected Provider provider; - protected OkHttpClient httpClient; - protected boolean closeHttpClient; - - protected S3Base( - HttpUrl baseUrl, - String awsS3Prefix, - String awsDomainSuffix, - boolean awsDualstack, - boolean useVirtualStyle, - String region, - Provider provider, - OkHttpClient httpClient, - boolean closeHttpClient) { - this.baseUrl = baseUrl; - this.awsS3Prefix = awsS3Prefix; - this.awsDomainSuffix = awsDomainSuffix; - this.awsDualstack = awsDualstack; - this.useVirtualStyle = useVirtualStyle; - this.region = region; - this.provider = provider; - this.httpClient = httpClient; - this.closeHttpClient = closeHttpClient; - } - - protected S3Base(S3Base client) { - this.baseUrl = client.baseUrl; - this.awsS3Prefix = client.awsS3Prefix; - this.awsDomainSuffix = client.awsDomainSuffix; - this.awsDualstack = client.awsDualstack; - this.useVirtualStyle = client.useVirtualStyle; - this.region = client.region; - this.provider = client.provider; - this.httpClient = client.httpClient; - this.closeHttpClient = client.closeHttpClient; - } - - /** Check whether argument is valid or not. */ - protected void checkArgs(BaseArgs args) { - if (args == null) throw new IllegalArgumentException("null arguments"); - - if ((this.awsDomainSuffix != null) && (args instanceof BucketArgs)) { - String bucketName = ((BucketArgs) args).bucket(); - if (bucketName.startsWith("xn--") - || bucketName.endsWith("--s3alias") - || bucketName.endsWith("--ol-s3")) { - throw new IllegalArgumentException( - "bucket name '" - + bucketName - + "' must not start with 'xn--' and must not end with '--s3alias' or '--ol-s3'"); - } - } - } - - /** Merge two Multimaps. */ - protected Multimap merge( - Multimap m1, Multimap m2) { - Multimap map = HashMultimap.create(); - if (m1 != null) map.putAll(m1); - if (m2 != null) map.putAll(m2); - return map; - } - - /** Create new HashMultimap by alternating keys and values. */ - protected Multimap newMultimap(String... keysAndValues) { - if (keysAndValues.length % 2 != 0) { - throw new IllegalArgumentException("Expected alternating keys and values"); - } - - Multimap map = HashMultimap.create(); - for (int i = 0; i < keysAndValues.length; i += 2) { - map.put(keysAndValues[i], keysAndValues[i + 1]); - } - - return map; - } - - /** Create new HashMultimap with copy of Map. */ - protected Multimap newMultimap(Map map) { - return (map != null) ? Multimaps.forMap(map) : HashMultimap.create(); - } - - /** Create new HashMultimap with copy of Multimap. */ - protected Multimap newMultimap(Multimap map) { - return (map != null) ? HashMultimap.create(map) : HashMultimap.create(); - } - - /** Throws encapsulated exception wrapped by {@link ExecutionException}. */ - public void throwEncapsulatedException(ExecutionException e) - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException { - if (e == null) return; - - Throwable ex = e.getCause(); - - if (ex instanceof CompletionException) { - ex = ((CompletionException) ex).getCause(); - } - - if (ex instanceof ExecutionException) { - ex = ((ExecutionException) ex).getCause(); - } - - try { - throw ex; - } catch (IllegalArgumentException - | ErrorResponseException - | InsufficientDataException - | InternalException - | InvalidKeyException - | InvalidResponseException - | IOException - | NoSuchAlgorithmException - | ServerException - | XmlParserException exc) { - throw exc; - } catch (Throwable exc) { - throw new RuntimeException(exc.getCause() == null ? exc : exc.getCause()); - } - } - - private String[] handleRedirectResponse( - Method method, String bucketName, Response response, boolean retry) { - String code = null; - String message = null; - - if (response.code() == 301) { - code = "PermanentRedirect"; - message = "Moved Permanently"; - } else if (response.code() == 307) { - code = "Redirect"; - message = "Temporary redirect"; - } else if (response.code() == 400) { - code = "BadRequest"; - message = "Bad request"; - } - - String region = response.headers().get("x-amz-bucket-region"); - if (message != null && region != null) message += ". Use region " + region; - - if (retry - && region != null - && method.equals(Method.HEAD) - && bucketName != null - && regionCache.get(bucketName) != null) { - code = RETRY_HEAD; - message = null; - } - - return new String[] {code, message}; - } - - private String buildAwsUrl( - HttpUrl.Builder builder, String bucketName, boolean enforcePathStyle, String region) { - String host = this.awsS3Prefix + this.awsDomainSuffix; - if (host.equals("s3-external-1.amazonaws.com") - || host.equals("s3-us-gov-west-1.amazonaws.com") - || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { - builder.host(host); - return host; - } - - host = this.awsS3Prefix; - if (this.awsS3Prefix.contains("s3-accelerate")) { - if (bucketName.contains(".")) { - throw new IllegalArgumentException( - "bucket name '" + bucketName + "' with '.' is not allowed for accelerate endpoint"); - } - if (enforcePathStyle) host = host.replaceFirst("-accelerate", ""); - } - - if (this.awsDualstack) host += "dualstack."; - if (!this.awsS3Prefix.contains("s3-accelerate")) host += region + "."; - host += this.awsDomainSuffix; - - builder.host(host); - return host; - } - - private String buildListBucketsUrl(HttpUrl.Builder builder, String region) { - if (this.awsDomainSuffix == null) return null; - - String host = this.awsS3Prefix + this.awsDomainSuffix; - if (host.equals("s3-external-1.amazonaws.com") - || host.equals("s3-us-gov-west-1.amazonaws.com") - || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { - builder.host(host); - return host; - } - - String s3Prefix = this.awsS3Prefix; - String domainSuffix = this.awsDomainSuffix; - if (this.awsS3Prefix.startsWith("s3.") || this.awsS3Prefix.startsWith("s3-")) { - s3Prefix = "s3."; - domainSuffix = "amazonaws.com" + (domainSuffix.endsWith(".cn") ? ".cn" : ""); - } - - host = s3Prefix + region + "." + domainSuffix; - builder.host(host); - return host; - } - - /** Build URL for given parameters. */ - protected HttpUrl buildUrl( - Method method, - String bucketName, - String objectName, - String region, - Multimap queryParamMap) - throws NoSuchAlgorithmException { - if (bucketName == null && objectName != null) { - throw new IllegalArgumentException("null bucket name for object '" + objectName + "'"); - } - - HttpUrl.Builder urlBuilder = this.baseUrl.newBuilder(); - - if (queryParamMap != null) { - for (Map.Entry entry : queryParamMap.entries()) { - urlBuilder.addEncodedQueryParameter( - S3Escaper.encode(entry.getKey()), S3Escaper.encode(entry.getValue())); - } - } - - if (bucketName == null) { - this.buildListBucketsUrl(urlBuilder, region); - return urlBuilder.build(); - } - - boolean enforcePathStyle = ( - // use path style for make bucket to workaround "AuthorizationHeaderMalformed" error from - // s3.amazonaws.com - (method == Method.PUT && objectName == null && queryParamMap == null) - - // use path style for location query - || (queryParamMap != null && queryParamMap.containsKey("location")) - - // use path style where '.' in bucketName causes SSL certificate validation error - || (bucketName.contains(".") && this.baseUrl.isHttps())); - - String host = this.baseUrl.host(); - if (this.awsDomainSuffix != null) { - host = this.buildAwsUrl(urlBuilder, bucketName, enforcePathStyle, region); - } - - if (enforcePathStyle || !this.useVirtualStyle) { - urlBuilder.addEncodedPathSegment(S3Escaper.encode(bucketName)); - } else { - urlBuilder.host(bucketName + "." + host); - } - - if (objectName != null) { - urlBuilder.addEncodedPathSegments(S3Escaper.encodePath(objectName)); - } - - return urlBuilder.build(); - } - - /** Convert Multimap to Headers. */ - protected Headers httpHeaders(Multimap headerMap) { - Headers.Builder builder = new Headers.Builder(); - if (headerMap == null) return builder.build(); - - if (headerMap.containsKey("Content-Encoding")) { - builder.add( - "Content-Encoding", - headerMap.get("Content-Encoding").stream() - .distinct() - .filter(encoding -> !encoding.isEmpty()) - .collect(Collectors.joining(","))); - } - - for (Map.Entry entry : headerMap.entries()) { - if (!entry.getKey().equals("Content-Encoding")) { - builder.addUnsafeNonAscii(entry.getKey(), entry.getValue()); - } - } - - return builder.build(); - } - - /** Create HTTP request for given paramaters. */ - protected Request createRequest( - HttpUrl url, Method method, Headers headers, Object body, int length, Credentials creds) - throws InsufficientDataException, InternalException, IOException, NoSuchAlgorithmException { - Request.Builder requestBuilder = new Request.Builder(); - requestBuilder.url(url); - - if (headers != null) requestBuilder.headers(headers); - requestBuilder.header("Host", HttpUtils.getHostHeader(url)); - // Disable default gzip compression by okhttp library. - requestBuilder.header("Accept-Encoding", "identity"); - requestBuilder.header("User-Agent", this.userAgent); - - if (body != null && body instanceof RequestBody) { - return requestBuilder.method(method.toString(), (RequestBody) body).build(); - } - - String md5Hash = Digest.ZERO_MD5_HASH; - if (body != null) { - md5Hash = (body instanceof byte[]) ? Digest.md5Hash((byte[]) body, length) : null; - } - - String sha256Hash = null; - if (creds != null) { - sha256Hash = Digest.ZERO_SHA256_HASH; - if (!url.isHttps()) { - if (body != null) { - if (body instanceof PartSource) { - sha256Hash = ((PartSource) body).sha256Hash(); - } else if (body instanceof byte[]) { - sha256Hash = Digest.sha256Hash((byte[]) body, length); - } - } - } else { - // Fix issue #415: No need to compute sha256 if endpoint scheme is HTTPS. - sha256Hash = "UNSIGNED-PAYLOAD"; - if (body != null && body instanceof PartSource) { - sha256Hash = ((PartSource) body).sha256Hash(); - } - } - } - - if (md5Hash != null) requestBuilder.header("Content-MD5", md5Hash); - if (sha256Hash != null) requestBuilder.header("x-amz-content-sha256", sha256Hash); - - if (creds != null && creds.sessionToken() != null) { - requestBuilder.header("X-Amz-Security-Token", creds.sessionToken()); - } - - ZonedDateTime date = ZonedDateTime.now(); - requestBuilder.header("x-amz-date", date.format(Time.AMZ_DATE_FORMAT)); - - RequestBody requestBody = null; - if (body != null) { - String contentType = (headers != null) ? headers.get("Content-Type") : null; - if (contentType != null && MediaType.parse(contentType) == null) { - throw new IllegalArgumentException( - "invalid content type '" + contentType + "' as per RFC 2045"); - } - - if (body instanceof PartSource) { - requestBody = new HttpRequestBody((PartSource) body, contentType); - } else { - requestBody = new HttpRequestBody((byte[]) body, length, contentType); - } - } - - requestBuilder.method(method.toString(), requestBody); - return requestBuilder.build(); - } - - private StringBuilder newTraceBuilder(Request request, String body) { - StringBuilder traceBuilder = new StringBuilder(); - traceBuilder.append("---------START-HTTP---------\n"); - String encodedPath = request.url().encodedPath(); - String encodedQuery = request.url().encodedQuery(); - if (encodedQuery != null) encodedPath += "?" + encodedQuery; - traceBuilder.append(request.method()).append(" ").append(encodedPath).append(" HTTP/1.1\n"); - traceBuilder.append( - request - .headers() - .toString() - .replaceAll("Signature=([0-9a-f]+)", "Signature=*REDACTED*") - .replaceAll("Credential=([^/]+)", "Credential=*REDACTED*")); - if (body != null) traceBuilder.append("\n").append(body); - return traceBuilder; - } - - /** Execute HTTP request asynchronously for given parameters. */ - protected CompletableFuture executeAsync( - Method method, - String bucketName, - String objectName, - String region, - Headers headers, - Multimap queryParamMap, - Object body, - int length) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - boolean traceRequestBody = false; - if (body != null - && !(body instanceof PartSource || body instanceof byte[] || body instanceof RequestBody)) { - byte[] bytes; - if (body instanceof CharSequence) { - bytes = body.toString().getBytes(StandardCharsets.UTF_8); - } else { - bytes = Xml.marshal(body).getBytes(StandardCharsets.UTF_8); - } - - body = bytes; - length = bytes.length; - traceRequestBody = true; - } - - if (body == null && (method == Method.PUT || method == Method.POST)) { - body = HttpUtils.EMPTY_BODY; - } - - HttpUrl url = buildUrl(method, bucketName, objectName, region, queryParamMap); - Credentials creds = (provider == null) ? null : provider.fetch(); - Request req = createRequest(url, method, headers, body, length, creds); - if (!(body != null && body instanceof RequestBody) && creds != null) { - req = - Signer.signV4S3( - req, - region, - creds.accessKey(), - creds.secretKey(), - req.header("x-amz-content-sha256")); - } - final Request request = req; - - StringBuilder traceBuilder = - newTraceBuilder( - request, traceRequestBody ? new String((byte[]) body, StandardCharsets.UTF_8) : null); - PrintWriter traceStream = this.traceStream; - if (traceStream != null) traceStream.println(traceBuilder.toString()); - traceBuilder.append("\n"); - - OkHttpClient httpClient = this.httpClient; - if (!(body instanceof byte[]) && (method == Method.PUT || method == Method.POST)) { - // Issue #924: disable connection retry for PUT and POST methods for other than byte array. - httpClient = this.httpClient.newBuilder().retryOnConnectionFailure(false).build(); - } - - CompletableFuture completableFuture = new CompletableFuture<>(); - httpClient - .newCall(request) - .enqueue( - new Callback() { - @Override - public void onFailure(final Call call, IOException e) { - completableFuture.completeExceptionally(e); - } - - @Override - public void onResponse(Call call, final Response response) throws IOException { - try { - onResponse(response); - } catch (Exception e) { - completableFuture.completeExceptionally(e); - } - } - - private void onResponse(final Response response) throws IOException { - String trace = - response.protocol().toString().toUpperCase(Locale.US) - + " " - + response.code() - + "\n" - + response.headers(); - traceBuilder.append(trace).append("\n"); - if (traceStream != null) traceStream.println(trace); - - if (response.isSuccessful()) { - if (traceStream != null) { - // Trace response body only if the request is not - // GetObject/ListenBucketNotification - // S3 API. - Set keys = queryParamMap.keySet(); - if ((method != Method.GET - || objectName == null - || !Collections.disjoint(keys, TRACE_QUERY_PARAMS)) - && !(keys.contains("events") - && (keys.contains("prefix") || keys.contains("suffix")))) { - ResponseBody responseBody = response.peekBody(1024 * 1024); - traceStream.println(responseBody.string()); - } - traceStream.println(END_HTTP); - } - - completableFuture.complete(response); - return; - } - - String errorXml = null; - try (ResponseBody responseBody = response.body()) { - errorXml = responseBody.string(); - } - - if (!("".equals(errorXml) && method.equals(Method.HEAD))) { - traceBuilder.append(errorXml).append("\n"); - if (traceStream != null) traceStream.println(errorXml); - } - - traceBuilder.append(END_HTTP).append("\n"); - if (traceStream != null) traceStream.println(END_HTTP); - - // Error in case of Non-XML response from server for non-HEAD requests. - String contentType = response.headers().get("content-type"); - if (!method.equals(Method.HEAD) - && (contentType == null - || !Arrays.asList(contentType.split(";")).contains("application/xml"))) { - if (response.code() == 304 && response.body().contentLength() == 0) { - completableFuture.completeExceptionally( - new ServerException( - "server failed with HTTP status code " + response.code(), - response.code(), - traceBuilder.toString())); - } - - completableFuture.completeExceptionally( - new InvalidResponseException( - response.code(), - contentType, - errorXml.substring( - 0, errorXml.length() > 1024 ? 1024 : errorXml.length()), - traceBuilder.toString())); - return; - } - - ErrorResponse errorResponse = null; - if (!"".equals(errorXml)) { - try { - errorResponse = Xml.unmarshal(ErrorResponse.class, errorXml); - } catch (XmlParserException e) { - completableFuture.completeExceptionally(e); - return; - } - } else if (!method.equals(Method.HEAD)) { - completableFuture.completeExceptionally( - new InvalidResponseException( - response.code(), contentType, errorXml, traceBuilder.toString())); - return; - } - - if (errorResponse == null) { - String code = null; - String message = null; - switch (response.code()) { - case 301: - case 307: - case 400: - String[] result = handleRedirectResponse(method, bucketName, response, true); - code = result[0]; - message = result[1]; - break; - case 404: - if (objectName != null) { - code = "NoSuchKey"; - message = "Object does not exist"; - } else if (bucketName != null) { - code = NO_SUCH_BUCKET; - message = NO_SUCH_BUCKET_MESSAGE; - } else { - code = "ResourceNotFound"; - message = "Request resource not found"; - } - break; - case 501: - case 405: - code = "MethodNotAllowed"; - message = "The specified method is not allowed against this resource"; - break; - case 409: - if (bucketName != null) { - code = NO_SUCH_BUCKET; - message = NO_SUCH_BUCKET_MESSAGE; - } else { - code = "ResourceConflict"; - message = "Request resource conflicts"; - } - break; - case 403: - code = "AccessDenied"; - message = "Access denied"; - break; - case 412: - code = "PreconditionFailed"; - message = "At least one of the preconditions you specified did not hold"; - break; - case 416: - code = "InvalidRange"; - message = "The requested range cannot be satisfied"; - break; - default: - completableFuture.completeExceptionally( - new ServerException( - "server failed with HTTP status code " + response.code(), - response.code(), - traceBuilder.toString())); - return; - } - - errorResponse = - new ErrorResponse( - code, - message, - bucketName, - objectName, - request.url().encodedPath(), - response.header("x-amz-request-id"), - response.header("x-amz-id-2")); - } - - // invalidate region cache if needed - if (errorResponse.code().equals(NO_SUCH_BUCKET) - || errorResponse.code().equals(RETRY_HEAD)) { - regionCache.remove(bucketName); - } - - ErrorResponseException e = - new ErrorResponseException(errorResponse, response, traceBuilder.toString()); - completableFuture.completeExceptionally(e); - } - }); - return completableFuture; - } - - /** Execute HTTP request asynchronously for given args and parameters. */ - protected CompletableFuture executeAsync( - Method method, - BaseArgs args, - Multimap headers, - Multimap queryParams, - Object body, - int length) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - final Multimap extraHeaders; - final Multimap extraQueryParams; - final String bucketName; - final String region; - final String objectName; - - if (args != null) { - extraHeaders = args.extraHeaders(); - extraQueryParams = args.extraQueryParams(); - - if (args instanceof BucketArgs) { - bucketName = ((BucketArgs) args).bucket(); - region = ((BucketArgs) args).region(); - } else { - bucketName = null; - region = null; - } - - if (args instanceof ObjectArgs) { - objectName = ((ObjectArgs) args).object(); - } else { - objectName = null; - } - } else { - extraHeaders = null; - extraQueryParams = null; - bucketName = null; - region = null; - objectName = null; - } - - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - method, - bucketName, - objectName, - location, - httpHeaders(merge(extraHeaders, headers)), - merge(extraQueryParams, queryParams), - body, - length); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }); - } - - /** Returns region of given bucket either from region cache or set in constructor. */ - protected CompletableFuture getRegionAsync(String bucketName, String region) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - if (region != null) { - // Error out if region does not match with region passed via constructor. - if (this.region != null && !this.region.equals(region)) { - throw new IllegalArgumentException( - "region must be " + this.region + ", but passed " + region); - } - return CompletableFuture.completedFuture(region); - } - - if (this.region != null && !this.region.equals("")) { - return CompletableFuture.completedFuture(this.region); - } - if (bucketName == null || this.provider == null) { - return CompletableFuture.completedFuture(US_EAST_1); - } - region = regionCache.get(bucketName); - if (region != null) return CompletableFuture.completedFuture(region); - - // Execute GetBucketLocation REST API to get region of the bucket. - CompletableFuture future = - executeAsync( - Method.GET, bucketName, null, US_EAST_1, null, newMultimap("location", null), null, 0); - return future.thenApply( - response -> { - String location; - try (ResponseBody body = response.body()) { - LocationConstraint lc = Xml.unmarshal(LocationConstraint.class, body.charStream()); - if (lc.location() == null || lc.location().equals("")) { - location = US_EAST_1; - } else if (lc.location().equals("EU") && this.awsDomainSuffix != null) { - location = "eu-west-1"; // eu-west-1 is also referred as 'EU'. - } else { - location = lc.location(); - } - } catch (XmlParserException e) { - throw new CompletionException(e); - } - - regionCache.put(bucketName, location); - return location; - }); - } - - /** Execute asynchronously GET HTTP request for given parameters. */ - protected CompletableFuture executeGetAsync( - BaseArgs args, Multimap headers, Multimap queryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return executeAsync(Method.GET, args, headers, queryParams, null, 0); - } - - /** Execute asynchronously HEAD HTTP request for given parameters. */ - protected CompletableFuture executeHeadAsync( - BaseArgs args, Multimap headers, Multimap queryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return executeAsync(Method.HEAD, args, headers, queryParams, null, 0) - .exceptionally( - e -> { - if (e instanceof ErrorResponseException) { - ErrorResponseException ex = (ErrorResponseException) e; - if (ex.errorResponse().code().equals(RETRY_HEAD)) { - return null; - } - } - throw new CompletionException(e); - }) - .thenCompose( - response -> { - if (response != null) { - return CompletableFuture.completedFuture(response); - } - - try { - return executeAsync(Method.HEAD, args, headers, queryParams, null, 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }); - } - - /** Execute asynchronously DELETE HTTP request for given parameters. */ - protected CompletableFuture executeDeleteAsync( - BaseArgs args, Multimap headers, Multimap queryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return executeAsync(Method.DELETE, args, headers, queryParams, null, 0) - .thenApply( - response -> { - if (response != null) response.body().close(); - return response; - }); - } - - /** Execute asynchronously POST HTTP request for given parameters. */ - protected CompletableFuture executePostAsync( - BaseArgs args, - Multimap headers, - Multimap queryParams, - Object data) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return executeAsync(Method.POST, args, headers, queryParams, data, 0); - } - - /** Execute asynchronously PUT HTTP request for given parameters. */ - protected CompletableFuture executePutAsync( - BaseArgs args, - Multimap headers, - Multimap queryParams, - Object data, - int length) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return executeAsync(Method.PUT, args, headers, queryParams, data, length); - } - - protected CompletableFuture calculatePartCountAsync(List sources) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - long[] objectSize = {0}; - int index = 0; - - CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> 0); - for (ComposeSource src : sources) { - index++; - final int i = index; - completableFuture = - completableFuture.thenCombine( - statObjectAsync(new StatObjectArgs((ObjectReadArgs) src)), - (partCount, statObjectResponse) -> { - src.buildHeaders(statObjectResponse.size(), statObjectResponse.etag()); - - long size = statObjectResponse.size(); - if (src.length() != null) { - size = src.length(); - } else if (src.offset() != null) { - size -= src.offset(); - } - - if (size < ObjectWriteArgs.MIN_MULTIPART_SIZE - && sources.size() != 1 - && i != sources.size()) { - throw new IllegalArgumentException( - "source " - + src.bucket() - + "/" - + src.object() - + ": size " - + size - + " must be greater than " - + ObjectWriteArgs.MIN_MULTIPART_SIZE); - } - - objectSize[0] += size; - if (objectSize[0] > ObjectWriteArgs.MAX_OBJECT_SIZE) { - throw new IllegalArgumentException( - "destination object size must be less than " - + ObjectWriteArgs.MAX_OBJECT_SIZE); - } - - if (size > ObjectWriteArgs.MAX_PART_SIZE) { - long count = size / ObjectWriteArgs.MAX_PART_SIZE; - long lastPartSize = size - (count * ObjectWriteArgs.MAX_PART_SIZE); - if (lastPartSize > 0) { - count++; - } else { - lastPartSize = ObjectWriteArgs.MAX_PART_SIZE; - } - - if (lastPartSize < ObjectWriteArgs.MIN_MULTIPART_SIZE - && sources.size() != 1 - && i != sources.size()) { - throw new IllegalArgumentException( - "source " - + src.bucket() - + "/" - + src.object() - + ": " - + "for multipart split upload of " - + size - + ", last part size is less than " - + ObjectWriteArgs.MIN_MULTIPART_SIZE); - } - partCount += (int) count; - } else { - partCount++; - } - - if (partCount > ObjectWriteArgs.MAX_MULTIPART_COUNT) { - throw new IllegalArgumentException( - "Compose sources create more than allowed multipart count " - + ObjectWriteArgs.MAX_MULTIPART_COUNT); - } - return partCount; - }); - } - - return completableFuture; - } - - private abstract class ObjectIterator implements Iterator> { - protected Result error; - protected Iterator itemIterator; - protected Iterator deleteMarkerIterator; - protected Iterator prefixIterator; - protected boolean completed = false; - protected ListObjectsResult listObjectsResult; - protected String lastObjectName; - - protected abstract void populateResult() - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, - ServerException, XmlParserException; - - protected synchronized void populate() { - try { - populateResult(); - } catch (ErrorResponseException - | InsufficientDataException - | InternalException - | InvalidKeyException - | InvalidResponseException - | IOException - | NoSuchAlgorithmException - | ServerException - | XmlParserException e) { - this.error = new Result<>(e); - } - - if (this.listObjectsResult != null) { - this.itemIterator = this.listObjectsResult.contents().iterator(); - this.deleteMarkerIterator = this.listObjectsResult.deleteMarkers().iterator(); - this.prefixIterator = this.listObjectsResult.commonPrefixes().iterator(); - } else { - this.itemIterator = new LinkedList().iterator(); - this.deleteMarkerIterator = new LinkedList().iterator(); - this.prefixIterator = new LinkedList().iterator(); - } - } - - @Override - public boolean hasNext() { - if (this.completed) return false; - - if (this.error == null - && this.itemIterator == null - && this.deleteMarkerIterator == null - && this.prefixIterator == null) { - populate(); - } - - if (this.error == null - && !this.itemIterator.hasNext() - && !this.deleteMarkerIterator.hasNext() - && !this.prefixIterator.hasNext() - && this.listObjectsResult.isTruncated()) { - populate(); - } - - if (this.error != null) return true; - if (this.itemIterator.hasNext()) return true; - if (this.deleteMarkerIterator.hasNext()) return true; - if (this.prefixIterator.hasNext()) return true; - - this.completed = true; - return false; - } - - @Override - public Result next() { - if (this.completed) throw new NoSuchElementException(); - if (this.error == null - && this.itemIterator == null - && this.deleteMarkerIterator == null - && this.prefixIterator == null) { - populate(); - } - - if (this.error == null - && !this.itemIterator.hasNext() - && !this.deleteMarkerIterator.hasNext() - && !this.prefixIterator.hasNext() - && this.listObjectsResult.isTruncated()) { - populate(); - } - - if (this.error != null) { - this.completed = true; - return this.error; - } - - Item item = null; - if (this.itemIterator.hasNext()) { - item = this.itemIterator.next(); - item.setEncodingType(this.listObjectsResult.encodingType()); - this.lastObjectName = item.objectName(); - } else if (this.deleteMarkerIterator.hasNext()) { - item = this.deleteMarkerIterator.next(); - } else if (this.prefixIterator.hasNext()) { - item = this.prefixIterator.next().toItem(); - } - - if (item != null) { - item.setEncodingType(this.listObjectsResult.encodingType()); - return new Result<>(item); - } - - this.completed = true; - throw new NoSuchElementException(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } - - /** Execute list objects v2. */ - protected Iterable> listObjectsV2(ListObjectsArgs args) { - return new Iterable>() { - @Override - public Iterator> iterator() { - return new ObjectIterator() { - private ListBucketResultV2 result = null; - - @Override - protected void populateResult() - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, - NoSuchAlgorithmException, ServerException, XmlParserException { - this.listObjectsResult = null; - this.itemIterator = null; - this.prefixIterator = null; - - try { - ListObjectsV2Response response = - listObjectsV2Async( - args.bucket(), - args.region(), - args.delimiter(), - args.useUrlEncodingType() ? "url" : null, - args.startAfter(), - args.maxKeys(), - args.prefix(), - (result == null) - ? args.continuationToken() - : result.nextContinuationToken(), - args.fetchOwner(), - args.includeUserMetadata(), - args.extraHeaders(), - args.extraQueryParams()) - .get(); - result = response.result(); - this.listObjectsResult = response.result(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - } - } - }; - } - }; - } - - /** Execute list objects v1. */ - protected Iterable> listObjectsV1(ListObjectsArgs args) { - return new Iterable>() { - @Override - public Iterator> iterator() { - return new ObjectIterator() { - private ListBucketResultV1 result = null; - - @Override - protected void populateResult() - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, - NoSuchAlgorithmException, ServerException, XmlParserException { - this.listObjectsResult = null; - this.itemIterator = null; - this.prefixIterator = null; - - String nextMarker = (result == null) ? args.marker() : result.nextMarker(); - if (nextMarker == null) nextMarker = this.lastObjectName; - - try { - ListObjectsV1Response response = - listObjectsV1Async( - args.bucket(), - args.region(), - args.delimiter(), - args.useUrlEncodingType() ? "url" : null, - nextMarker, - args.maxKeys(), - args.prefix(), - args.extraHeaders(), - args.extraQueryParams()) - .get(); - result = response.result(); - this.listObjectsResult = response.result(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - } - } - }; - } - }; - } - - /** Execute list object versions. */ - protected Iterable> listObjectVersions(ListObjectsArgs args) { - return new Iterable>() { - @Override - public Iterator> iterator() { - return new ObjectIterator() { - private ListVersionsResult result = null; - - @Override - protected void populateResult() - throws ErrorResponseException, InsufficientDataException, InternalException, - InvalidKeyException, InvalidResponseException, IOException, - NoSuchAlgorithmException, ServerException, XmlParserException { - this.listObjectsResult = null; - this.itemIterator = null; - this.prefixIterator = null; - - try { - ListObjectVersionsResponse response = - listObjectVersionsAsync( - args.bucket(), - args.region(), - args.delimiter(), - args.useUrlEncodingType() ? "url" : null, - (result == null) ? args.keyMarker() : result.nextKeyMarker(), - args.maxKeys(), - args.prefix(), - (result == null) ? args.versionIdMarker() : result.nextVersionIdMarker(), - args.extraHeaders(), - args.extraQueryParams()) - .get(); - result = response.result(); - this.listObjectsResult = response.result(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - } - } - }; - } - }; - } - - protected PartReader newPartReader(Object data, long objectSize, long partSize, int partCount) { - if (data instanceof RandomAccessFile) { - return new PartReader((RandomAccessFile) data, objectSize, partSize, partCount); - } - - if (data instanceof InputStream) { - return new PartReader((InputStream) data, objectSize, partSize, partCount); - } - - return null; - } - - /** Notification result records representation. */ - protected static class NotificationResultRecords { - Response response = null; - Scanner scanner = null; - ObjectMapper mapper = null; - - public NotificationResultRecords(Response response) { - this.response = response; - this.scanner = new Scanner(response.body().charStream()).useDelimiter("\n"); - this.mapper = - JsonMapper.builder() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) - .build(); - } - - /** returns closeable iterator of result of notification records. */ - public CloseableIterator> closeableIterator() { - return new CloseableIterator>() { - String recordsString = null; - NotificationRecords records = null; - boolean isClosed = false; - - @Override - public void close() throws IOException { - if (!isClosed) { - try { - response.body().close(); - scanner.close(); - } finally { - isClosed = true; - } - } - } - - public boolean populate() { - if (isClosed) return false; - if (recordsString != null) return true; - - while (scanner.hasNext()) { - recordsString = scanner.next().trim(); - if (!recordsString.equals("")) break; - } - - if (recordsString == null || recordsString.equals("")) { - try { - close(); - } catch (IOException e) { - isClosed = true; - } - return false; - } - return true; - } - - @Override - public boolean hasNext() { - return populate(); - } - - @Override - public Result next() { - if (isClosed) throw new NoSuchElementException(); - if ((recordsString == null || recordsString.equals("")) && !populate()) { - throw new NoSuchElementException(); - } - - try { - records = mapper.readValue(recordsString, NotificationRecords.class); - return new Result<>(records); - } catch (JsonMappingException e) { - return new Result<>(e); - } catch (JsonParseException e) { - return new Result<>(e); - } catch (IOException e) { - return new Result<>(e); - } finally { - recordsString = null; - records = null; - } - } - }; - } - } - - private Multimap getCommonListObjectsQueryParams( - String delimiter, String encodingType, Integer maxKeys, String prefix) { - Multimap queryParams = - newMultimap( - "delimiter", - (delimiter == null) ? "" : delimiter, - "max-keys", - Integer.toString(maxKeys > 0 ? maxKeys : 1000), - "prefix", - (prefix == null) ? "" : prefix); - if (encodingType != null) queryParams.put("encoding-type", encodingType); - return queryParams; - } - - /** - * Sets HTTP connect, write and read timeouts. A value of 0 means no timeout, otherwise values - * must be between 1 and Integer.MAX_VALUE when converted to milliseconds. - * - *

Example:{@code
-   * minioClient.setTimeout(TimeUnit.SECONDS.toMillis(10), TimeUnit.SECONDS.toMillis(10),
-   *     TimeUnit.SECONDS.toMillis(30));
-   * }
- * - * @param connectTimeout HTTP connect timeout in milliseconds. - * @param writeTimeout HTTP write timeout in milliseconds. - * @param readTimeout HTTP read timeout in milliseconds. - */ - public void setTimeout(long connectTimeout, long writeTimeout, long readTimeout) { - this.httpClient = - HttpUtils.setTimeout(this.httpClient, connectTimeout, writeTimeout, readTimeout); - } - - /** - * Ignores check on server certificate for HTTPS connection. - * - *
Example:{@code
-   * minioClient.ignoreCertCheck();
-   * }
- * - * @throws KeyManagementException thrown to indicate key management error. - * @throws NoSuchAlgorithmException thrown to indicate missing of SSL library. - */ - @SuppressFBWarnings(value = "SIC", justification = "Should not be used in production anyways.") - public void ignoreCertCheck() throws KeyManagementException, NoSuchAlgorithmException { - this.httpClient = HttpUtils.disableCertCheck(this.httpClient); - } - - /** - * Sets application's name/version to user agent. For more information about user agent refer #rfc2616. - * - * @param name Your application name. - * @param version Your application version. - */ - public void setAppInfo(String name, String version) { - if (name == null || version == null) return; - this.userAgent = - MinioProperties.INSTANCE.getDefaultUserAgent() + " " + name.trim() + "/" + version.trim(); - } - - /** - * Enables HTTP call tracing and written to traceStream. - * - * @param traceStream {@link OutputStream} for writing HTTP call tracing. - * @see #traceOff - */ - public void traceOn(OutputStream traceStream) { - if (traceStream == null) throw new IllegalArgumentException("trace stream must be provided"); - this.traceStream = - new PrintWriter(new OutputStreamWriter(traceStream, StandardCharsets.UTF_8), true); - } - - /** - * Disables HTTP call tracing previously enabled. - * - * @see #traceOn - * @throws IOException upon connection error - */ - public void traceOff() throws IOException { - this.traceStream = null; - } - - /** Enables dual-stack endpoint for Amazon S3 endpoint. */ - public void enableDualStackEndpoint() { - this.awsDualstack = true; - } - - /** Disables dual-stack endpoint for Amazon S3 endpoint. */ - public void disableDualStackEndpoint() { - this.awsDualstack = false; - } - - /** Enables virtual-style endpoint. */ - public void enableVirtualStyleEndpoint() { - this.useVirtualStyle = true; - } - - /** Disables virtual-style endpoint. */ - public void disableVirtualStyleEndpoint() { - this.useVirtualStyle = false; - } - - /** Sets AWS S3 domain prefix. */ - public void setAwsS3Prefix(@Nonnull String awsS3Prefix) { - if (awsS3Prefix == null) throw new IllegalArgumentException("null Amazon AWS S3 domain prefix"); - if (!HttpUtils.AWS_S3_PREFIX_REGEX.matcher(awsS3Prefix).find()) { - throw new IllegalArgumentException("invalid Amazon AWS S3 domain prefix " + awsS3Prefix); - } - this.awsS3Prefix = awsS3Prefix; - } - - /** Execute stat object a.k.a head object S3 API asynchronously. */ - protected CompletableFuture statObjectAsync(StatObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - checkArgs(args); - args.validateSsec(baseUrl); - return executeHeadAsync( - args, - args.getHeaders(), - (args.versionId() != null) ? newMultimap("versionId", args.versionId()) : null) - .thenApply( - response -> - new StatObjectResponse( - response.headers(), args.bucket(), args.region(), args.object())); - } - - /** - * Do AbortMultipartUpload - * S3 API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket. - * @param objectName Object name in the bucket. - * @param uploadId Upload ID. - * @param extraHeaders Extra headers (Optional). - * @param extraQueryParams Extra query parameters (Optional). - * @return {@link CompletableFuture}<{@link AbortMultipartUploadResponse}> 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. - */ - public CompletableFuture abortMultipartUploadAsync( - String bucketName, - String region, - String objectName, - String uploadId, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.DELETE, - bucketName, - objectName, - location, - httpHeaders(extraHeaders), - merge(extraQueryParams, newMultimap(UPLOAD_ID, uploadId)), - null, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - return new AbortMultipartUploadResponse( - response.headers(), bucketName, region, objectName, uploadId); - } finally { - response.close(); - } - }); - } - - /** - * Do CompleteMultipartUpload - * S3 API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket. - * @param objectName Object name in the bucket. - * @param uploadId Upload ID. - * @param parts List of parts. - * @param extraHeaders Extra headers (Optional). - * @param extraQueryParams Extra query parameters (Optional). - * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> 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. - */ - public CompletableFuture completeMultipartUploadAsync( - String bucketName, - String region, - String objectName, - String uploadId, - Part[] parts, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - Multimap queryParams = newMultimap(extraQueryParams); - queryParams.put(UPLOAD_ID, uploadId); - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.POST, - bucketName, - objectName, - location, - httpHeaders(extraHeaders), - queryParams, - new CompleteMultipartUpload(parts), - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - String bodyContent = response.body().string(); - bodyContent = bodyContent.trim(); - if (!bodyContent.isEmpty()) { - try { - if (Xml.validate(ErrorResponse.class, bodyContent)) { - ErrorResponse errorResponse = Xml.unmarshal(ErrorResponse.class, bodyContent); - throw new CompletionException( - new ErrorResponseException(errorResponse, response, null)); - } - } catch (XmlParserException e) { - // As it is not message, fallback to parse CompleteMultipartUploadOutput - // XML. - } - - try { - CompleteMultipartUploadResult result = - Xml.unmarshal(CompleteMultipartUploadResult.class, bodyContent); - return new ObjectWriteResponse( - response.headers(), - result.bucket(), - result.location(), - result.object(), - result.etag(), - response.header("x-amz-version-id"), - result); - } catch (XmlParserException e) { - // As this CompleteMultipartUpload REST call succeeded, just log it. - Logger.getLogger(S3Base.class.getName()) - .warning( - "S3 service returned unknown XML for CompleteMultipartUpload REST API. " - + bodyContent); - } - } - - return new ObjectWriteResponse( - response.headers(), - bucketName, - region, - objectName, - null, - response.header("x-amz-version-id")); - } catch (IOException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - /** - * Do CreateMultipartUpload - * S3 API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region name of buckets in S3 service. - * @param objectName Object name in the bucket. - * @param headers Request headers. - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link CreateMultipartUploadResponse}> 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. - */ - public CompletableFuture createMultipartUploadAsync( - String bucketName, - String region, - String objectName, - Multimap headers, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - Multimap queryParams = newMultimap(extraQueryParams); - queryParams.put("uploads", ""); - - Multimap headersCopy = newMultimap(headers); - // set content type if not set already - if (!headersCopy.containsKey("Content-Type")) { - headersCopy.put("Content-Type", "application/octet-stream"); - } - - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.POST, - bucketName, - objectName, - location, - httpHeaders(headersCopy), - queryParams, - null, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - InitiateMultipartUploadResult result = - Xml.unmarshal( - InitiateMultipartUploadResult.class, response.body().charStream()); - return new CreateMultipartUploadResponse( - response.headers(), bucketName, region, objectName, result); - } catch (XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - /** - * Do DeleteObjects S3 - * API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param objectList List of object names. - * @param quiet Quiet flag. - * @param bypassGovernanceMode Bypass Governance retention mode. - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link DeleteObjectsResponse}> 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. - */ - protected CompletableFuture deleteObjectsAsync( - String bucketName, - String region, - List objectList, - boolean quiet, - boolean bypassGovernanceMode, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - if (objectList == null) objectList = new LinkedList<>(); - - if (objectList.size() > 1000) { - throw new IllegalArgumentException("list of objects must not be more than 1000"); - } - - Multimap headers = - merge( - extraHeaders, - bypassGovernanceMode ? newMultimap("x-amz-bypass-governance-retention", "true") : null); - - final List objects = objectList; - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.POST, - bucketName, - null, - location, - httpHeaders(headers), - merge(extraQueryParams, newMultimap("delete", "")), - new DeleteRequest(objects, quiet), - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - String bodyContent = response.body().string(); - try { - if (Xml.validate(DeleteError.class, bodyContent)) { - DeleteError error = Xml.unmarshal(DeleteError.class, bodyContent); - DeleteResult result = new DeleteResult(error); - return new DeleteObjectsResponse( - response.headers(), bucketName, region, result); - } - } catch (XmlParserException e) { - // Ignore this exception as it is not message, - // but parse it as message below. - } - - DeleteResult result = Xml.unmarshal(DeleteResult.class, bodyContent); - return new DeleteObjectsResponse(response.headers(), bucketName, region, result); - } catch (IOException | XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - /** - * Do ListObjects - * version 2 S3 API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param delimiter Delimiter (Optional). - * @param encodingType Encoding type (Optional). - * @param startAfter Fetch listing after this key (Optional). - * @param maxKeys Maximum object information to fetch (Optional). - * @param prefix Prefix (Optional). - * @param continuationToken Continuation token (Optional). - * @param fetchOwner Flag to fetch owner information (Optional). - * @param includeUserMetadata MinIO extension flag to include user metadata (Optional). - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link ListObjectsV2Response}> 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. - */ - protected CompletableFuture listObjectsV2Async( - String bucketName, - String region, - String delimiter, - String encodingType, - String startAfter, - Integer maxKeys, - String prefix, - String continuationToken, - boolean fetchOwner, - boolean includeUserMetadata, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - Multimap queryParams = - merge( - extraQueryParams, - getCommonListObjectsQueryParams(delimiter, encodingType, maxKeys, prefix)); - queryParams.put("list-type", "2"); - if (continuationToken != null) queryParams.put("continuation-token", continuationToken); - if (fetchOwner) queryParams.put("fetch-owner", "true"); - if (startAfter != null) queryParams.put("start-after", startAfter); - if (includeUserMetadata) queryParams.put("metadata", "true"); - - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.GET, - bucketName, - null, - location, - httpHeaders(extraHeaders), - queryParams, - null, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - ListBucketResultV2 result = - Xml.unmarshal(ListBucketResultV2.class, response.body().charStream()); - return new ListObjectsV2Response(response.headers(), bucketName, region, result); - } catch (XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - /** - * Do ListObjects - * version 1 S3 API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param delimiter Delimiter (Optional). - * @param encodingType Encoding type (Optional). - * @param marker Marker (Optional). - * @param maxKeys Maximum object information to fetch (Optional). - * @param prefix Prefix (Optional). - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link ListObjectsV1Response}> 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. - */ - protected CompletableFuture listObjectsV1Async( - String bucketName, - String region, - String delimiter, - String encodingType, - String marker, - Integer maxKeys, - String prefix, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - Multimap queryParams = - merge( - extraQueryParams, - getCommonListObjectsQueryParams(delimiter, encodingType, maxKeys, prefix)); - if (marker != null) queryParams.put("marker", marker); - - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.GET, - bucketName, - null, - location, - httpHeaders(extraHeaders), - queryParams, - null, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - ListBucketResultV1 result = - Xml.unmarshal(ListBucketResultV1.class, response.body().charStream()); - return new ListObjectsV1Response(response.headers(), bucketName, region, result); - } catch (XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - /** - * Do ListObjectVersions - * API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param delimiter Delimiter (Optional). - * @param encodingType Encoding type (Optional). - * @param keyMarker Key marker (Optional). - * @param maxKeys Maximum object information to fetch (Optional). - * @param prefix Prefix (Optional). - * @param versionIdMarker Version ID marker (Optional). - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link ListObjectVersionsResponse}> 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. - */ - protected CompletableFuture listObjectVersionsAsync( - String bucketName, - String region, - String delimiter, - String encodingType, - String keyMarker, - Integer maxKeys, - String prefix, - String versionIdMarker, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - Multimap queryParams = - merge( - extraQueryParams, - getCommonListObjectsQueryParams(delimiter, encodingType, maxKeys, prefix)); - if (keyMarker != null) queryParams.put("key-marker", keyMarker); - if (versionIdMarker != null) queryParams.put("version-id-marker", versionIdMarker); - queryParams.put("versions", ""); - - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.GET, - bucketName, - null, - location, - httpHeaders(extraHeaders), - queryParams, - null, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - ListVersionsResult result = - Xml.unmarshal(ListVersionsResult.class, response.body().charStream()); - return new ListObjectVersionsResponse( - response.headers(), bucketName, region, result); - } catch (XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - private Part[] uploadParts( - PutObjectBaseArgs args, String uploadId, PartReader partReader, PartSource firstPartSource) - throws InterruptedException, ExecutionException, InsufficientDataException, InternalException, - InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { - Part[] parts = new Part[ObjectWriteArgs.MAX_MULTIPART_COUNT]; - int partNumber = 0; - PartSource partSource = firstPartSource; - while (true) { - partNumber++; - - Multimap ssecHeaders = null; - // set encryption headers in the case of SSE-C. - if (args.sse() != null && args.sse() instanceof ServerSideEncryptionCustomerKey) { - ssecHeaders = Multimaps.forMap(args.sse().headers()); - } - - UploadPartResponse response = - uploadPartAsync( - args.bucket(), - args.region(), - args.object(), - partSource, - partNumber, - uploadId, - ssecHeaders, - null) - .get(); - parts[partNumber - 1] = new Part(partNumber, response.etag()); - - partSource = partReader.getPart(); - if (partSource == null) break; - } - - return parts; - } - - private CompletableFuture putMultipartObjectAsync( - PutObjectBaseArgs args, - Multimap headers, - PartReader partReader, - PartSource firstPartSource) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return CompletableFuture.supplyAsync( - () -> { - String uploadId = null; - ObjectWriteResponse response = null; - try { - CreateMultipartUploadResponse createMultipartUploadResponse = - createMultipartUploadAsync( - args.bucket(), - args.region(), - args.object(), - headers, - args.extraQueryParams()) - .get(); - uploadId = createMultipartUploadResponse.result().uploadId(); - Part[] parts = uploadParts(args, uploadId, partReader, firstPartSource); - response = - completeMultipartUploadAsync( - args.bucket(), args.region(), args.object(), uploadId, parts, null, null) - .get(); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException - | InterruptedException - | ExecutionException e) { - Throwable throwable = e; - if (throwable instanceof ExecutionException) { - throwable = ((ExecutionException) throwable).getCause(); - } - if (throwable instanceof CompletionException) { - throwable = ((CompletionException) throwable).getCause(); - } - if (uploadId == null) { - throw new CompletionException(throwable); - } - try { - abortMultipartUploadAsync( - args.bucket(), args.region(), args.object(), uploadId, null, null) - .get(); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException - | InterruptedException - | ExecutionException ex) { - throwable = ex; - if (throwable instanceof ExecutionException) { - throwable = ((ExecutionException) throwable).getCause(); - } - if (throwable instanceof CompletionException) { - throwable = ((CompletionException) throwable).getCause(); - } - } - throw new CompletionException(throwable); - } - return response; - }); - } - - /** - * Execute put object asynchronously from object data from {@link RandomAccessFile} or {@link - * InputStream}. - * - * @param args {@link PutObjectBaseArgs}. - * @param data {@link RandomAccessFile} or {@link InputStream}. - * @param objectSize object size. - * @param partSize part size for multipart upload. - * @param partCount Number of parts for multipart upload. - * @param contentType content-type of object. - * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> 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. - */ - protected CompletableFuture putObjectAsync( - PutObjectBaseArgs args, - Object data, - long objectSize, - long partSize, - int partCount, - String contentType) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - PartReader partReader = newPartReader(data, objectSize, partSize, partCount); - if (partReader == null) { - throw new IllegalArgumentException("data must be RandomAccessFile or InputStream"); - } - - Multimap headers = newMultimap(args.extraHeaders()); - headers.putAll(args.genHeaders()); - if (!headers.containsKey("Content-Type")) headers.put("Content-Type", contentType); - - return CompletableFuture.supplyAsync( - () -> { - try { - return partReader.getPart(); - } catch (NoSuchAlgorithmException | IOException e) { - throw new CompletionException(e); - } - }) - .thenCompose( - partSource -> { - try { - if (partReader.partCount() == 1) { - return putObjectAsync( - args.bucket(), - args.region(), - args.object(), - partSource, - headers, - args.extraQueryParams()); - } else { - return putMultipartObjectAsync(args, headers, partReader, partSource); - } - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }); - } - - /** - * Do PutObject S3 - * API for {@link PartSource} asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param objectName Object name in the bucket. - * @param partSource PartSource object. - * @param headers Additional headers. - * @param extraQueryParams Additional query parameters if any. - * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> 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. - */ - private CompletableFuture putObjectAsync( - String bucketName, - String region, - String objectName, - PartSource partSource, - Multimap headers, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.PUT, - bucketName, - objectName, - location, - httpHeaders(headers), - extraQueryParams, - partSource, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - return new ObjectWriteResponse( - response.headers(), - bucketName, - region, - objectName, - response.header("ETag").replaceAll("\"", ""), - response.header("x-amz-version-id")); - } finally { - response.close(); - } - }); - } - - /** - * Do PutObject S3 - * API asynchronously. - * - * @param bucketName Name of the bucket. - * @param objectName Object name in the bucket. - * @param data Object data must be InputStream, RandomAccessFile, byte[] or String. - * @param length Length of object data. - * @param headers Additional headers. - * @param extraQueryParams Additional query parameters if any. - * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> 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. - */ - protected CompletableFuture putObjectAsync( - String bucketName, - String region, - String objectName, - Object data, - long length, - Multimap headers, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - if (!(data instanceof InputStream - || data instanceof RandomAccessFile - || data instanceof byte[] - || data instanceof CharSequence)) { - throw new IllegalArgumentException( - "data must be InputStream, RandomAccessFile, byte[] or String"); - } - - PartReader partReader = newPartReader(data, length, length, 1); - - if (partReader != null) { - return putObjectAsync( - bucketName, region, objectName, partReader.getPart(), headers, extraQueryParams); - } - - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.PUT, - bucketName, - objectName, - location, - httpHeaders(headers), - extraQueryParams, - data, - (int) length); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - return new ObjectWriteResponse( - response.headers(), - bucketName, - region, - objectName, - response.header("ETag").replaceAll("\"", ""), - response.header("x-amz-version-id")); - } finally { - response.close(); - } - }); - } - - /** - * Do ListMultipartUploads - * S3 API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param delimiter Delimiter (Optional). - * @param encodingType Encoding type (Optional). - * @param keyMarker Key marker (Optional). - * @param maxUploads Maximum upload information to fetch (Optional). - * @param prefix Prefix (Optional). - * @param uploadIdMarker Upload ID marker (Optional). - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link ListMultipartUploadsResponse}> 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. - */ - public CompletableFuture listMultipartUploadsAsync( - String bucketName, - String region, - String delimiter, - String encodingType, - String keyMarker, - Integer maxUploads, - String prefix, - String uploadIdMarker, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - Multimap queryParams = - merge( - extraQueryParams, - newMultimap( - "uploads", - "", - "delimiter", - (delimiter != null) ? delimiter : "", - "max-uploads", - (maxUploads != null) ? maxUploads.toString() : "1000", - "prefix", - (prefix != null) ? prefix : "")); - if (encodingType != null) queryParams.put("encoding-type", encodingType); - if (keyMarker != null) queryParams.put("key-marker", keyMarker); - if (uploadIdMarker != null) queryParams.put("upload-id-marker", uploadIdMarker); - - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.GET, - bucketName, - null, - location, - httpHeaders(extraHeaders), - queryParams, - null, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - ListMultipartUploadsResult result = - Xml.unmarshal(ListMultipartUploadsResult.class, response.body().charStream()); - return new ListMultipartUploadsResponse( - response.headers(), bucketName, region, result); - } catch (XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - /** - * Do ListParts S3 - * API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Name of the bucket (Optional). - * @param objectName Object name in the bucket. - * @param maxParts Maximum parts information to fetch (Optional). - * @param partNumberMarker Part number marker (Optional). - * @param uploadId Upload ID. - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link ListPartsResponse}> 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. - */ - public CompletableFuture listPartsAsync( - String bucketName, - String region, - String objectName, - Integer maxParts, - Integer partNumberMarker, - String uploadId, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - Multimap queryParams = - merge( - extraQueryParams, - newMultimap( - UPLOAD_ID, - uploadId, - "max-parts", - (maxParts != null) ? maxParts.toString() : "1000")); - if (partNumberMarker != null) { - queryParams.put("part-number-marker", partNumberMarker.toString()); - } - - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.GET, - bucketName, - objectName, - location, - httpHeaders(extraHeaders), - queryParams, - null, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - ListPartsResult result = - Xml.unmarshal(ListPartsResult.class, response.body().charStream()); - return new ListPartsResponse( - response.headers(), bucketName, region, objectName, result); - } catch (XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - /** - * Do UploadPart S3 - * API for PartSource asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param objectName Object name in the bucket. - * @param partSource PartSource Object. - * @param partNumber Part number. - * @param uploadId Upload ID. - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link UploadPartResponse}> 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. - */ - public CompletableFuture uploadPartAsync( - String bucketName, - String region, - String objectName, - PartSource partSource, - int partNumber, - String uploadId, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.PUT, - bucketName, - objectName, - location, - httpHeaders(extraHeaders), - merge( - extraQueryParams, - newMultimap( - "partNumber", Integer.toString(partNumber), UPLOAD_ID, uploadId)), - partSource, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - return new UploadPartResponse( - response.headers(), - bucketName, - region, - objectName, - uploadId, - partNumber, - response.header("ETag").replaceAll("\"", "")); - } finally { - response.close(); - } - }); - } - - /** - * Do UploadPart S3 - * API asynchronously. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param objectName Object name in the bucket. - * @param data Object data must be InputStream, RandomAccessFile, byte[] or String. - * @param length Length of object data. - * @param uploadId Upload ID. - * @param partNumber Part number. - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link UploadPartResponse}> 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. - */ - public CompletableFuture uploadPartAsync( - String bucketName, - String region, - String objectName, - Object data, - long length, - String uploadId, - int partNumber, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - if (!(data instanceof InputStream - || data instanceof RandomAccessFile - || data instanceof byte[] - || data instanceof CharSequence)) { - throw new IllegalArgumentException( - "data must be InputStream, RandomAccessFile, byte[] or String"); - } - - PartReader partReader = newPartReader(data, length, length, 1); - - if (partReader != null) { - return uploadPartAsync( - bucketName, - region, - objectName, - partReader.getPart(), - partNumber, - uploadId, - extraHeaders, - extraQueryParams); - } - - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.PUT, - bucketName, - objectName, - location, - httpHeaders(extraHeaders), - merge( - extraQueryParams, - newMultimap( - "partNumber", Integer.toString(partNumber), UPLOAD_ID, uploadId)), - data, - (int) length); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - return new UploadPartResponse( - response.headers(), - bucketName, - region, - objectName, - uploadId, - partNumber, - response.header("ETag").replaceAll("\"", "")); - } finally { - response.close(); - } - }); - } - - /** - * Do UploadPartCopy - * S3 API. - * - * @param bucketName Name of the bucket. - * @param region Region of the bucket (Optional). - * @param objectName Object name in the bucket. - * @param uploadId Upload ID. - * @param partNumber Part number. - * @param headers Request headers with source object definitions. - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link UploadPartCopyResponse}> 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. - */ - public CompletableFuture uploadPartCopyAsync( - String bucketName, - String region, - String objectName, - String uploadId, - int partNumber, - Multimap headers, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return getRegionAsync(bucketName, region) - .thenCompose( - location -> { - try { - return executeAsync( - Method.PUT, - bucketName, - objectName, - location, - httpHeaders(headers), - merge( - extraQueryParams, - newMultimap( - "partNumber", Integer.toString(partNumber), "uploadId", uploadId)), - null, - 0); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); - } - }) - .thenApply( - response -> { - try { - CopyPartResult result = - Xml.unmarshal(CopyPartResult.class, response.body().charStream()); - return new UploadPartCopyResponse( - response.headers(), - bucketName, - region, - objectName, - uploadId, - partNumber, - result); - } catch (XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - /** - * Do ListBuckets - * S3 API. - * - * @param bucketRegion Fetch buckets from the region (Optional). - * @param maxBuckets Maximum buckets to be fetched (Optional). - * @param prefix Bucket name prefix (Optional). - * @param continuationToken continuation token (Optional). - * @param extraHeaders Extra headers for request (Optional). - * @param extraQueryParams Extra query parameters for request (Optional). - * @return {@link CompletableFuture}<{@link ListBucketsResponse}> 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. - */ - protected CompletableFuture listBucketsAsync( - String bucketRegion, - Integer maxBuckets, - String prefix, - String continuationToken, - Multimap extraHeaders, - Multimap extraQueryParams) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - Multimap queryParams = newMultimap(extraQueryParams); - if (bucketRegion != null) queryParams.put("bucket-region", bucketRegion); - if (maxBuckets != null) - queryParams.put("max-buckets", Integer.toString(maxBuckets > 0 ? maxBuckets : 10000)); - if (prefix != null) queryParams.put("prefix", prefix); - if (continuationToken != null) queryParams.put("continuation-token", continuationToken); - return executeGetAsync(null, extraHeaders, queryParams) - .thenApply( - response -> { - try { - ListAllMyBucketsResult result = - Xml.unmarshal(ListAllMyBucketsResult.class, response.body().charStream()); - return new ListBucketsResponse(response.headers(), result); - } catch (XmlParserException e) { - throw new CompletionException(e); - } finally { - response.close(); - } - }); - } - - @Override - public void close() throws Exception { - if (closeHttpClient) { - httpClient.dispatcher().executorService().shutdown(); - httpClient.connectionPool().evictAll(); - } - } -} diff --git a/api/src/main/java/io/minio/S3Escaper.java b/api/src/main/java/io/minio/S3Escaper.java deleted file mode 100644 index 028dd83b1..000000000 --- a/api/src/main/java/io/minio/S3Escaper.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2016 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.escape.Escaper; -import com.google.common.net.UrlEscapers; - -public class S3Escaper { - private static final Escaper ESCAPER = UrlEscapers.urlPathSegmentEscaper(); - - /** Returns S3 encoded string. */ - public static String encode(String str) { - if (str == null) { - return ""; - } - - StringBuilder builder = new StringBuilder(); - for (char ch : ESCAPER.escape(str).toCharArray()) { - switch (ch) { - case '!': - builder.append("%21"); - break; - case '$': - builder.append("%24"); - break; - case '&': - builder.append("%26"); - break; - case '\'': - builder.append("%27"); - break; - case '(': - builder.append("%28"); - break; - case ')': - builder.append("%29"); - break; - case '*': - builder.append("%2A"); - break; - case '+': - builder.append("%2B"); - break; - case ',': - builder.append("%2C"); - break; - case '/': - builder.append("%2F"); - break; - case ':': - builder.append("%3A"); - break; - case ';': - builder.append("%3B"); - break; - case '=': - builder.append("%3D"); - break; - case '@': - builder.append("%40"); - break; - case '[': - builder.append("%5B"); - break; - case ']': - builder.append("%5D"); - break; - default: - builder.append(ch); - } - } - return builder.toString(); - } - - /** Returns S3 encoded string of given path where multiple '/' are trimmed. */ - public static String encodePath(String path) { - final StringBuilder encodedPath = new StringBuilder(); - for (String pathSegment : path.split("/")) { - if (!pathSegment.isEmpty()) { - if (encodedPath.length() > 0) { - encodedPath.append("/"); - } - encodedPath.append(S3Escaper.encode(pathSegment)); - } - } - - if (path.startsWith("/")) { - encodedPath.insert(0, "/"); - } - if (path.endsWith("/")) { - encodedPath.append("/"); - } - - return encodedPath.toString(); - } -} diff --git a/api/src/main/java/io/minio/SelectObjectContentArgs.java b/api/src/main/java/io/minio/SelectObjectContentArgs.java index 684f429a4..b756d8811 100644 --- a/api/src/main/java/io/minio/SelectObjectContentArgs.java +++ b/api/src/main/java/io/minio/SelectObjectContentArgs.java @@ -65,7 +65,7 @@ public static Builder builder() { public static final class Builder extends ObjectReadArgs.Builder { private void validateSqlExpression(String se) { - validateNotEmptyString(se, "sqlExpression"); + Utils.validateNotEmptyString(se, "sqlExpression"); } public Builder sqlExpression(String sqlExpression) { @@ -75,7 +75,7 @@ public Builder sqlExpression(String sqlExpression) { } private void validateInputSerialization(InputSerialization is) { - validateNotNull(is, "inputSerialization"); + Utils.validateNotNull(is, "inputSerialization"); } public Builder inputSerialization(InputSerialization inputSerialization) { @@ -85,7 +85,7 @@ public Builder inputSerialization(InputSerialization inputSerialization) { } private void validateOutputSerialization(OutputSerialization os) { - validateNotNull(os, "outputSerialization"); + Utils.validateNotNull(os, "outputSerialization"); } public Builder outputSerialization(OutputSerialization outputSerialization) { diff --git a/api/src/main/java/io/minio/ServerSideEncryption.java b/api/src/main/java/io/minio/ServerSideEncryption.java index c4e90cce0..191dc8a01 100644 --- a/api/src/main/java/io/minio/ServerSideEncryption.java +++ b/api/src/main/java/io/minio/ServerSideEncryption.java @@ -16,19 +16,152 @@ package io.minio; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.io.BaseEncoding; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; import java.util.Map; +import javax.crypto.SecretKey; +import javax.security.auth.DestroyFailedException; -/** Base class of server-side encryption. */ +/** Server-side encryption support. */ public abstract class ServerSideEncryption { - private static final Map emptyHeaders = Utils.unmodifiableMap(null); + private static final Http.Headers emptyHeaders = new Http.Headers(); - public abstract Map headers(); + public abstract Http.Headers headers(); public boolean tlsRequired() { return true; } - public Map copySourceHeaders() { + public Http.Headers copySourceHeaders() { return emptyHeaders; } + + /** S3 type of Server-side encryption. */ + public static class S3 extends ServerSideEncryption { + private static final Http.Headers headers = + new Http.Headers("X-Amz-Server-Side-Encryption", "AES256"); + + @Override + public final Http.Headers headers() { + return headers; + } + + @Override + public final boolean tlsRequired() { + return false; + } + + @Override + public String toString() { + return "SSE-S3"; + } + } + + /** KMS type of Server-side encryption. */ + public static class KMS extends ServerSideEncryption { + private static final ObjectMapper objectMapper = new ObjectMapper(); + private final Http.Headers headers; + + public KMS(String keyId, Map context) throws JsonProcessingException { + if (keyId == null) { + throw new IllegalArgumentException("Key ID cannot be null"); + } + + this.headers = + new Http.Headers( + "X-Amz-Server-Side-Encryption", + "aws:kms", + "X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id", + keyId); + if (context != null) { + this.headers.put( + "X-Amz-Server-Side-Encryption-Context", + Base64.getEncoder() + .encodeToString( + objectMapper.writeValueAsString(context).getBytes(StandardCharsets.UTF_8))); + } + } + + @Override + public final Http.Headers headers() { + return headers; + } + + @Override + public String toString() { + return "SSE-KMS"; + } + } + + /** Customer-key type of Server-side encryption. */ + public static class CustomerKey extends ServerSideEncryption { + private boolean isDestroyed = false; + private final SecretKey secretKey; + private final Http.Headers headers; + private final Http.Headers copySourceHeaders; + + public CustomerKey(SecretKey key) throws InvalidKeyException, NoSuchAlgorithmException { + if (key == null || !key.getAlgorithm().equals("AES") || key.getEncoded().length != 32) { + throw new IllegalArgumentException("Secret key must be 256 bit AES key"); + } + + if (key.isDestroyed()) { + throw new IllegalArgumentException("Secret key already destroyed"); + } + + this.secretKey = key; + + byte[] keyBytes = key.getEncoded(); + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(keyBytes); + String customerKey = BaseEncoding.base64().encode(keyBytes); + String customerKeyMd5 = BaseEncoding.base64().encode(md5.digest()); + + this.headers = + new Http.Headers( + "X-Amz-Server-Side-Encryption-Customer-Algorithm", "AES256", + "X-Amz-Server-Side-Encryption-Customer-Key", customerKey, + "X-Amz-Server-Side-Encryption-Customer-Key-Md5", customerKeyMd5); + + this.copySourceHeaders = + new Http.Headers( + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm", "AES256", + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key", customerKey, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5", customerKeyMd5); + } + + @Override + public final Http.Headers headers() { + if (isDestroyed) { + throw new IllegalStateException("Secret key was destroyed"); + } + + return headers; + } + + @Override + public final Http.Headers copySourceHeaders() { + if (isDestroyed) { + throw new IllegalStateException("Secret key was destroyed"); + } + + return copySourceHeaders; + } + + public final void destroy() throws DestroyFailedException { + secretKey.destroy(); + isDestroyed = true; + } + + @Override + public String toString() { + return "SSE-C"; + } + } } diff --git a/api/src/main/java/io/minio/ServerSideEncryptionCustomerKey.java b/api/src/main/java/io/minio/ServerSideEncryptionCustomerKey.java deleted file mode 100644 index 74f883c6d..000000000 --- a/api/src/main/java/io/minio/ServerSideEncryptionCustomerKey.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.io.BaseEncoding; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.HashMap; -import java.util.Map; -import javax.crypto.SecretKey; -import javax.security.auth.DestroyFailedException; - -/** Customer-key type of Server-side encryption. */ -public class ServerSideEncryptionCustomerKey extends ServerSideEncryption { - private boolean isDestroyed = false; - private final SecretKey secretKey; - private final Map headers; - private final Map copySourceHeaders; - - public ServerSideEncryptionCustomerKey(SecretKey key) - throws InvalidKeyException, NoSuchAlgorithmException { - if (key == null || !key.getAlgorithm().equals("AES") || key.getEncoded().length != 32) { - throw new IllegalArgumentException("Secret key must be 256 bit AES key"); - } - - if (key.isDestroyed()) { - throw new IllegalArgumentException("Secret key already destroyed"); - } - - this.secretKey = key; - - byte[] keyBytes = key.getEncoded(); - MessageDigest md5 = MessageDigest.getInstance("MD5"); - md5.update(keyBytes); - String customerKey = BaseEncoding.base64().encode(keyBytes); - String customerKeyMd5 = BaseEncoding.base64().encode(md5.digest()); - - Map map = new HashMap<>(); - map.put("X-Amz-Server-Side-Encryption-Customer-Algorithm", "AES256"); - map.put("X-Amz-Server-Side-Encryption-Customer-Key", customerKey); - map.put("X-Amz-Server-Side-Encryption-Customer-Key-Md5", customerKeyMd5); - this.headers = Utils.unmodifiableMap(map); - - map = new HashMap<>(); - map.put("X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm", "AES256"); - map.put("X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key", customerKey); - map.put("X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5", customerKeyMd5); - this.copySourceHeaders = Utils.unmodifiableMap(map); - } - - @Override - public final Map headers() { - if (isDestroyed) { - throw new IllegalStateException("Secret key was destroyed"); - } - - return headers; - } - - @Override - public final Map copySourceHeaders() { - if (isDestroyed) { - throw new IllegalStateException("Secret key was destroyed"); - } - - return copySourceHeaders; - } - - public final void destroy() throws DestroyFailedException { - secretKey.destroy(); - isDestroyed = true; - } - - @Override - public String toString() { - return "SSE-C"; - } -} diff --git a/api/src/main/java/io/minio/ServerSideEncryptionKms.java b/api/src/main/java/io/minio/ServerSideEncryptionKms.java deleted file mode 100644 index f3c898287..000000000 --- a/api/src/main/java/io/minio/ServerSideEncryptionKms.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; - -/** KMS type of Server-side encryption. */ -public class ServerSideEncryptionKms extends ServerSideEncryption { - private static final ObjectMapper objectMapper = new ObjectMapper(); - private final Map headers; - - public ServerSideEncryptionKms(String keyId, Map context) - throws JsonProcessingException { - if (keyId == null) { - throw new IllegalArgumentException("Key ID cannot be null"); - } - - Map headers = new HashMap<>(); - headers.put("X-Amz-Server-Side-Encryption", "aws:kms"); - headers.put("X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id", keyId); - if (context != null) { - headers.put( - "X-Amz-Server-Side-Encryption-Context", - Base64.getEncoder() - .encodeToString( - objectMapper.writeValueAsString(context).getBytes(StandardCharsets.UTF_8))); - } - - this.headers = Utils.unmodifiableMap(headers); - } - - @Override - public final Map headers() { - return headers; - } - - @Override - public String toString() { - return "SSE-KMS"; - } -} diff --git a/api/src/main/java/io/minio/SetBucketCorsArgs.java b/api/src/main/java/io/minio/SetBucketCorsArgs.java index 11d6b59ee..7a297d25e 100644 --- a/api/src/main/java/io/minio/SetBucketCorsArgs.java +++ b/api/src/main/java/io/minio/SetBucketCorsArgs.java @@ -36,7 +36,7 @@ public static Builder builder() { /** Argument builder of {@link SetBucketCorsArgs}. */ public static final class Builder extends BucketArgs.Builder { private void validateCors(CORSConfiguration config) { - validateNotNull(config, "CORS configuration"); + Utils.validateNotNull(config, "CORS configuration"); } protected void validate(SetBucketCorsArgs args) { diff --git a/api/src/main/java/io/minio/SetBucketEncryptionArgs.java b/api/src/main/java/io/minio/SetBucketEncryptionArgs.java index 3afa3d2c5..d2284bc61 100644 --- a/api/src/main/java/io/minio/SetBucketEncryptionArgs.java +++ b/api/src/main/java/io/minio/SetBucketEncryptionArgs.java @@ -37,7 +37,7 @@ public static Builder builder() { /** Argument builder of {@link SetBucketEncryptionArgs}. */ public static final class Builder extends BucketArgs.Builder { private void validateConfig(SseConfiguration config) { - validateNotNull(config, "encryption configuration"); + Utils.validateNotNull(config, "encryption configuration"); } protected void validate(SetBucketEncryptionArgs args) { diff --git a/api/src/main/java/io/minio/SetBucketLifecycleArgs.java b/api/src/main/java/io/minio/SetBucketLifecycleArgs.java index ac1894888..a22e89ed9 100644 --- a/api/src/main/java/io/minio/SetBucketLifecycleArgs.java +++ b/api/src/main/java/io/minio/SetBucketLifecycleArgs.java @@ -37,7 +37,7 @@ public static Builder builder() { /** Argument builder of {@link SetBucketLifecycleArgs}. */ public static final class Builder extends BucketArgs.Builder { private void validateConfig(LifecycleConfiguration config) { - validateNotNull(config, "lifecycle configuration"); + Utils.validateNotNull(config, "lifecycle configuration"); } protected void validate(SetBucketLifecycleArgs args) { diff --git a/api/src/main/java/io/minio/SetBucketNotificationArgs.java b/api/src/main/java/io/minio/SetBucketNotificationArgs.java index 189a338a9..80d583801 100644 --- a/api/src/main/java/io/minio/SetBucketNotificationArgs.java +++ b/api/src/main/java/io/minio/SetBucketNotificationArgs.java @@ -37,7 +37,7 @@ public static Builder builder() { /** Argument builder of {@link SetBucketNotificationArgs}. */ public static final class Builder extends BucketArgs.Builder { private void validateConfig(NotificationConfiguration config) { - validateNotNull(config, "notification configuration"); + Utils.validateNotNull(config, "notification configuration"); } protected void validate(SetBucketNotificationArgs args) { diff --git a/api/src/main/java/io/minio/SetBucketPolicyArgs.java b/api/src/main/java/io/minio/SetBucketPolicyArgs.java index c9f454ac1..a02ee7b80 100644 --- a/api/src/main/java/io/minio/SetBucketPolicyArgs.java +++ b/api/src/main/java/io/minio/SetBucketPolicyArgs.java @@ -36,7 +36,7 @@ public static Builder builder() { /** Argument builder of {@link SetBucketPolicyArgs}. */ public static final class Builder extends BucketArgs.Builder { private void validateConfig(String config) { - validateNotNull(config, "policy configuration"); + Utils.validateNotNull(config, "policy configuration"); } @Override diff --git a/api/src/main/java/io/minio/SetBucketReplicationArgs.java b/api/src/main/java/io/minio/SetBucketReplicationArgs.java index 5bafc4f61..eb5cbd133 100644 --- a/api/src/main/java/io/minio/SetBucketReplicationArgs.java +++ b/api/src/main/java/io/minio/SetBucketReplicationArgs.java @@ -42,11 +42,11 @@ public static Builder builder() { /** Argument builder of {@link SetBucketReplicationArgs}. */ public static final class Builder extends BucketArgs.Builder { private void validateConfig(ReplicationConfiguration config) { - validateNotNull(config, "replication configuration"); + Utils.validateNotNull(config, "replication configuration"); } private void validateObjectLockToken(String token) { - validateNullOrNotEmptyString(token, "object lock token"); + Utils.validateNullOrNotEmptyString(token, "object lock token"); } @Override diff --git a/api/src/main/java/io/minio/SetBucketTagsArgs.java b/api/src/main/java/io/minio/SetBucketTagsArgs.java index 3dacccc7c..ce6d54b29 100644 --- a/api/src/main/java/io/minio/SetBucketTagsArgs.java +++ b/api/src/main/java/io/minio/SetBucketTagsArgs.java @@ -37,7 +37,7 @@ public static Builder builder() { /** Argument builder of {@link SetBucketTagsArgs}. */ public static final class Builder extends BucketArgs.Builder { private void validateTags(Tags tags) { - validateNotNull(tags, "tags"); + Utils.validateNotNull(tags, "tags"); } protected void validate(SetBucketTagsArgs args) { @@ -46,7 +46,7 @@ protected void validate(SetBucketTagsArgs args) { } public Builder tags(Map map) { - validateNotNull(map, "map for tags"); + Utils.validateNotNull(map, "map for tags"); operations.add(args -> args.tags = Tags.newBucketTags(map)); return this; } diff --git a/api/src/main/java/io/minio/SetBucketVersioningArgs.java b/api/src/main/java/io/minio/SetBucketVersioningArgs.java index 2f848c994..02beb5a39 100644 --- a/api/src/main/java/io/minio/SetBucketVersioningArgs.java +++ b/api/src/main/java/io/minio/SetBucketVersioningArgs.java @@ -37,7 +37,7 @@ public static Builder builder() { /** Argument builder of {@link SetBucketVersioningArgs}. */ public static final class Builder extends BucketArgs.Builder { private void validateConfig(VersioningConfiguration config) { - validateNotNull(config, "versioning configuration"); + Utils.validateNotNull(config, "versioning configuration"); } protected void validate(SetBucketVersioningArgs args) { diff --git a/api/src/main/java/io/minio/SetObjectLockConfigurationArgs.java b/api/src/main/java/io/minio/SetObjectLockConfigurationArgs.java index 6cef98dff..5fbe5d535 100644 --- a/api/src/main/java/io/minio/SetObjectLockConfigurationArgs.java +++ b/api/src/main/java/io/minio/SetObjectLockConfigurationArgs.java @@ -38,7 +38,7 @@ public static Builder builder() { public static final class Builder extends BucketArgs.Builder { private void validateConfig(ObjectLockConfiguration config) { - validateNotNull(config, "object-lock configuration"); + Utils.validateNotNull(config, "object-lock configuration"); } @Override diff --git a/api/src/main/java/io/minio/SetObjectRetentionArgs.java b/api/src/main/java/io/minio/SetObjectRetentionArgs.java index 00b363392..492fb4056 100644 --- a/api/src/main/java/io/minio/SetObjectRetentionArgs.java +++ b/api/src/main/java/io/minio/SetObjectRetentionArgs.java @@ -43,7 +43,7 @@ public static Builder builder() { public static final class Builder extends ObjectVersionArgs.Builder { private void validateConfig(Retention config) { - validateNotNull(config, "retention configuration"); + Utils.validateNotNull(config, "retention configuration"); } protected void validate(SetObjectRetentionArgs args) { diff --git a/api/src/main/java/io/minio/SetObjectTagsArgs.java b/api/src/main/java/io/minio/SetObjectTagsArgs.java index c832d94e1..edcd72acb 100644 --- a/api/src/main/java/io/minio/SetObjectTagsArgs.java +++ b/api/src/main/java/io/minio/SetObjectTagsArgs.java @@ -37,7 +37,7 @@ public static Builder builder() { /** Argument builder of {@link SetObjectTagsArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder { private void validateTags(Tags tags) { - validateNotNull(tags, "tags"); + Utils.validateNotNull(tags, "tags"); } protected void validate(SetObjectTagsArgs args) { @@ -46,7 +46,7 @@ protected void validate(SetObjectTagsArgs args) { } public Builder tags(Map map) { - validateNotNull(map, "map for tags"); + Utils.validateNotNull(map, "map for tags"); operations.add(args -> args.tags = Tags.newObjectTags(map)); return this; } diff --git a/api/src/main/java/io/minio/Signer.java b/api/src/main/java/io/minio/Signer.java index c45f11f1a..c2ad64f95 100644 --- a/api/src/main/java/io/minio/Signer.java +++ b/api/src/main/java/io/minio/Signer.java @@ -21,6 +21,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.io.BaseEncoding; +import io.minio.errors.MinioException; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -54,16 +55,19 @@ public class Signer { // calculation. // private static final Set IGNORED_HEADERS = - ImmutableSet.of("accept-encoding", "authorization", "user-agent"); + ImmutableSet.of( + Http.Headers.ACCEPT_ENCODING.toLowerCase(Locale.US), + Http.Headers.AUTHORIZATION.toLowerCase(Locale.US), + Http.Headers.USER_AGENT.toLowerCase(Locale.US)); private static final Set PRESIGN_IGNORED_HEADERS = ImmutableSet.of( - "accept-encoding", - "authorization", - "user-agent", - "content-md5", - "x-amz-content-sha256", - "x-amz-date", - "x-amz-security-token"); + Http.Headers.ACCEPT_ENCODING.toLowerCase(Locale.US), + Http.Headers.AUTHORIZATION.toLowerCase(Locale.US), + Http.Headers.USER_AGENT.toLowerCase(Locale.US), + Http.Headers.CONTENT_MD5.toLowerCase(Locale.US), + Http.Headers.X_AMZ_CONTENT_SHA256.toLowerCase(Locale.US), + Http.Headers.X_AMZ_DATE.toLowerCase(Locale.US), + Http.Headers.X_AMZ_SECURITY_TOKEN.toLowerCase(Locale.US)); private Request request; private String contentSha256; @@ -175,7 +179,7 @@ private void setCanonicalQueryString() { Joiner.on("&").withKeyValueSeparator("=").join(signedQueryParams.entries()); } - private void setCanonicalRequest() throws NoSuchAlgorithmException { + private void setCanonicalRequest() throws MinioException { setCanonicalHeaders(IGNORED_HEADERS); this.url = this.request.url(); setCanonicalQueryString(); @@ -200,7 +204,7 @@ private void setCanonicalRequest() throws NoSuchAlgorithmException { + "\n" + this.contentSha256; - this.canonicalRequestHash = Digest.sha256Hash(this.canonicalRequest); + this.canonicalRequestHash = Checksum.hexString(Checksum.SHA256.sum(this.canonicalRequest)); } private void setStringToSign() { @@ -214,7 +218,7 @@ private void setStringToSign() { + this.canonicalRequestHash; } - private void setChunkStringToSign() throws NoSuchAlgorithmException { + private void setChunkStringToSign() throws MinioException { this.stringToSign = "AWS4-HMAC-SHA256-PAYLOAD" + "\n" @@ -224,13 +228,12 @@ private void setChunkStringToSign() throws NoSuchAlgorithmException { + "\n" + this.prevSignature + "\n" - + Digest.sha256Hash("") + + Checksum.ZERO_SHA256_HASH + "\n" + this.contentSha256; } - private void setSigningKey(String serviceName) - throws NoSuchAlgorithmException, InvalidKeyException { + private void setSigningKey(String serviceName) throws MinioException { String aws4SecretKey = "AWS4" + this.secretKey; byte[] dateKey = @@ -247,7 +250,7 @@ private void setSigningKey(String serviceName) sumHmac(dateRegionServiceKey, "aws4_request".getBytes(StandardCharsets.UTF_8)); } - private void setSignature() throws NoSuchAlgorithmException, InvalidKeyException { + private void setSignature() throws MinioException { byte[] digest = sumHmac(this.signingKey, this.stringToSign.getBytes(StandardCharsets.UTF_8)); this.signature = BaseEncoding.base16().encode(digest).toLowerCase(Locale.US); } @@ -267,7 +270,7 @@ private void setAuthorization() { /** Returns chunk signature calculated using given arguments. */ public static String getChunkSignature( String chunkSha256, ZonedDateTime date, String region, String secretKey, String prevSignature) - throws NoSuchAlgorithmException, InvalidKeyException { + throws MinioException { Signer signer = new Signer(null, chunkSha256, date, region, null, secretKey, prevSignature); signer.setScope("s3"); signer.setChunkStringToSign(); @@ -285,8 +288,9 @@ private static Request signV4( String accessKey, String secretKey, String contentSha256) - throws NoSuchAlgorithmException, InvalidKeyException { - ZonedDateTime date = ZonedDateTime.parse(request.header("x-amz-date"), Time.AMZ_DATE_FORMAT); + throws MinioException { + ZonedDateTime date = + ZonedDateTime.parse(request.header(Http.Headers.X_AMZ_DATE), Time.AMZ_DATE_FORMAT); Signer signer = new Signer(request, contentSha256, date, region, accessKey, secretKey, null); signer.setScope(serviceName); @@ -296,37 +300,38 @@ private static Request signV4( signer.setSignature(); signer.setAuthorization(); - return request.newBuilder().header("Authorization", signer.authorization).build(); + return request.newBuilder().header(Http.Headers.AUTHORIZATION, signer.authorization).build(); } /** Returns signed request of given request for S3 service. */ public static Request signV4S3( Request request, String region, String accessKey, String secretKey, String contentSha256) - throws NoSuchAlgorithmException, InvalidKeyException { + throws MinioException { return signV4("s3", request, region, accessKey, secretKey, contentSha256); } /** Returns signed request of given request for STS service. */ public static Request signV4Sts( Request request, String region, String accessKey, String secretKey, String contentSha256) - throws NoSuchAlgorithmException, InvalidKeyException { + throws MinioException { return signV4("sts", request, region, accessKey, secretKey, contentSha256); } - private void setPresignCanonicalRequest(int expires) throws NoSuchAlgorithmException { + private void setPresignCanonicalRequest(int expires) throws MinioException { setCanonicalHeaders(PRESIGN_IGNORED_HEADERS); HttpUrl.Builder urlBuilder = this.request.url().newBuilder(); urlBuilder.addEncodedQueryParameter( - S3Escaper.encode("X-Amz-Algorithm"), S3Escaper.encode("AWS4-HMAC-SHA256")); + Utils.encode("X-Amz-Algorithm"), Utils.encode("AWS4-HMAC-SHA256")); urlBuilder.addEncodedQueryParameter( - S3Escaper.encode("X-Amz-Credential"), S3Escaper.encode(this.accessKey + "/" + this.scope)); + Utils.encode("X-Amz-Credential"), Utils.encode(this.accessKey + "/" + this.scope)); urlBuilder.addEncodedQueryParameter( - S3Escaper.encode("X-Amz-Date"), S3Escaper.encode(this.date.format(Time.AMZ_DATE_FORMAT))); + Utils.encode(Http.Headers.X_AMZ_DATE), + Utils.encode(this.date.format(Time.AMZ_DATE_FORMAT))); urlBuilder.addEncodedQueryParameter( - S3Escaper.encode("X-Amz-Expires"), S3Escaper.encode(Integer.toString(expires))); + Utils.encode("X-Amz-Expires"), Utils.encode(Integer.toString(expires))); urlBuilder.addEncodedQueryParameter( - S3Escaper.encode("X-Amz-SignedHeaders"), S3Escaper.encode(this.signedHeaders)); + Utils.encode("X-Amz-SignedHeaders"), Utils.encode(this.signedHeaders)); this.url = urlBuilder.build(); setCanonicalQueryString(); @@ -344,7 +349,7 @@ private void setPresignCanonicalRequest(int expires) throws NoSuchAlgorithmExcep + "\n" + this.contentSha256; - this.canonicalRequestHash = Digest.sha256Hash(this.canonicalRequest); + this.canonicalRequestHash = Checksum.hexString(Checksum.SHA256.sum(this.canonicalRequest)); } /** @@ -353,11 +358,12 @@ private void setPresignCanonicalRequest(int expires) throws NoSuchAlgorithmExcep */ public static HttpUrl presignV4( Request request, String region, String accessKey, String secretKey, int expires) - throws NoSuchAlgorithmException, InvalidKeyException { - String contentSha256 = "UNSIGNED-PAYLOAD"; - ZonedDateTime date = ZonedDateTime.parse(request.header("x-amz-date"), Time.AMZ_DATE_FORMAT); + throws MinioException { + ZonedDateTime date = + ZonedDateTime.parse(request.header(Http.Headers.X_AMZ_DATE), Time.AMZ_DATE_FORMAT); - Signer signer = new Signer(request, contentSha256, date, region, accessKey, secretKey, null); + Signer signer = + new Signer(request, Checksum.UNSIGNED_PAYLOAD, date, region, accessKey, secretKey, null); signer.setScope("s3"); signer.setPresignCanonicalRequest(expires); signer.setStringToSign(); @@ -367,8 +373,7 @@ public static HttpUrl presignV4( return signer .url .newBuilder() - .addEncodedQueryParameter( - S3Escaper.encode("X-Amz-Signature"), S3Escaper.encode(signer.signature)) + .addEncodedQueryParameter(Utils.encode("X-Amz-Signature"), Utils.encode(signer.signature)) .build(); } @@ -385,23 +390,23 @@ public static String credential(String accessKey, ZonedDateTime date, String reg /** Returns pre-signed post policy string for given stringToSign, secret key, date and region. */ public static String postPresignV4( String stringToSign, String secretKey, ZonedDateTime date, String region) - throws NoSuchAlgorithmException, InvalidKeyException { + throws MinioException { Signer signer = new Signer(null, null, date, region, null, secretKey, null); signer.stringToSign = stringToSign; signer.setSigningKey("s3"); signer.setSignature(); - return signer.signature; } /** Returns HMacSHA256 digest of given key and data. */ - public static byte[] sumHmac(byte[] key, byte[] data) - throws NoSuchAlgorithmException, InvalidKeyException { - Mac mac = Mac.getInstance("HmacSHA256"); - - mac.init(new SecretKeySpec(key, "HmacSHA256")); - mac.update(data); - - return mac.doFinal(); + public static byte[] sumHmac(byte[] key, byte[] data) throws MinioException { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(key, "HmacSHA256")); + mac.update(data); + return mac.doFinal(); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new MinioException(e); + } } } diff --git a/api/src/main/java/io/minio/SourceObject.java b/api/src/main/java/io/minio/SourceObject.java new file mode 100644 index 000000000..d07d2a052 --- /dev/null +++ b/api/src/main/java/io/minio/SourceObject.java @@ -0,0 +1,101 @@ +/* + * 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.errors.InternalException; +import java.util.Objects; + +/** A source object defintion for {@link ComposeObjectArgs} and {@link CopyObjectArgs}. */ +public class SourceObject extends ObjectConditionalReadArgs { + private Long objectSize = null; + + protected SourceObject() {} + + public SourceObject(SourceObject args, long objectSize, String etag) { + super(args, etag); + validateSize(objectSize); + this.objectSize = objectSize; + } + + private void throwException(long objectsize, long arg, String argName) { + StringBuilder builder = + new StringBuilder().append("source ").append(bucketName).append("/").append(objectName); + + if (versionId != null) { + builder.append("?versionId=").append(versionId); + } + + builder + .append(": ") + .append(argName) + .append(" ") + .append(arg) + .append(" is beyond object size ") + .append(objectSize); + + throw new IllegalArgumentException(builder.toString()); + } + + private void validateSize(long objectSize) { + if (offset != null && offset >= objectSize) { + throwException(objectSize, offset, "offset"); + } + + if (length != null) { + if (length > objectSize) { + throwException(objectSize, length, "length"); + } + + if (offset + length > objectSize) { + throwException(objectSize, offset + length, "compose size"); + } + } + } + + public Long objectSize() { + return objectSize; + } + + public Http.Headers headers() throws InternalException { + if (this.objectSize == null) { + throw new InternalException("SourceObject must be created with object size", null); + } + return makeCopyHeaders(); + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link SourceObject}. */ + public static final class Builder + extends ObjectConditionalReadArgs.Builder {} + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SourceObject)) return false; + if (!super.equals(o)) return false; + SourceObject that = (SourceObject) o; + return Objects.equals(objectSize, that.objectSize); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), objectSize); + } +} diff --git a/api/src/main/java/io/minio/StatObjectArgs.java b/api/src/main/java/io/minio/StatObjectArgs.java index 522b4c540..e93b0ec4c 100644 --- a/api/src/main/java/io/minio/StatObjectArgs.java +++ b/api/src/main/java/io/minio/StatObjectArgs.java @@ -1,5 +1,5 @@ /* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2020 MinIO, Inc. + * 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. @@ -17,24 +17,13 @@ package io.minio; /** Argument class of {@link MinioAsyncClient#statObject} and {@link MinioClient#statObject}. */ -public class StatObjectArgs extends ObjectConditionalReadArgs { +public class StatObjectArgs extends HeadObjectBaseArgs { protected StatObjectArgs() {} - public StatObjectArgs(ObjectReadArgs args) { - this.extraHeaders = args.extraHeaders; - this.extraQueryParams = args.extraQueryParams; - this.bucketName = args.bucketName; - this.region = args.region; - this.objectName = args.objectName; - this.versionId = args.versionId; - this.ssec = args.ssec; - } - public static Builder builder() { return new Builder(); } /** Argument builder of {@link StatObjectArgs}. */ - public static final class Builder - extends ObjectConditionalReadArgs.Builder {} + public static final class Builder extends HeadObjectBaseArgs.Builder {} } diff --git a/api/src/main/java/io/minio/StatObjectResponse.java b/api/src/main/java/io/minio/StatObjectResponse.java index 85cf8fe1e..a266d3b4d 100644 --- a/api/src/main/java/io/minio/StatObjectResponse.java +++ b/api/src/main/java/io/minio/StatObjectResponse.java @@ -1,5 +1,5 @@ /* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2020 MinIO, Inc. + * 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. @@ -16,113 +16,9 @@ package io.minio; -import io.minio.messages.LegalHold; -import io.minio.messages.ResponseDate; -import io.minio.messages.RetentionMode; -import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import okhttp3.Headers; - -/** Response of {@link S3Base#statObjectAsync}. */ -public class StatObjectResponse extends GenericResponse { - private String etag; - private long size; - private ZonedDateTime lastModified; - private RetentionMode retentionMode; - private ZonedDateTime retentionRetainUntilDate; - private LegalHold legalHold; - private boolean deleteMarker; - private Map userMetadata; - - public StatObjectResponse(Headers headers, String bucket, String region, String object) { - super(headers, bucket, region, object); - String value; - - value = headers.get("ETag"); - this.etag = (value != null ? value.replaceAll("\"", "") : ""); - - value = headers.get("Content-Length"); - this.size = (value != null ? Long.parseLong(value) : -1); - - this.lastModified = - ZonedDateTime.parse(headers.get("Last-Modified"), Time.HTTP_HEADER_DATE_FORMAT); - - value = headers.get("x-amz-object-lock-mode"); - this.retentionMode = (value != null ? RetentionMode.valueOf(value) : null); - - value = headers.get("x-amz-object-lock-retain-until-date"); - this.retentionRetainUntilDate = - (value != null ? ResponseDate.fromString(value).zonedDateTime() : null); - - this.legalHold = new LegalHold("ON".equals(headers.get("x-amz-object-lock-legal-hold"))); - - this.deleteMarker = Boolean.parseBoolean(headers.get("x-amz-delete-marker")); - - Map userMetadata = new HashMap<>(); - for (String key : headers.names()) { - if (key.toLowerCase(Locale.US).startsWith("x-amz-meta-")) { - userMetadata.put( - key.toLowerCase(Locale.US).substring("x-amz-meta-".length(), key.length()), - headers.get(key)); - } - } - - this.userMetadata = Utils.unmodifiableMap(userMetadata); - } - - public String etag() { - return etag; - } - - public long size() { - return size; - } - - public ZonedDateTime lastModified() { - return lastModified; - } - - public RetentionMode retentionMode() { - return retentionMode; - } - - public ZonedDateTime retentionRetainUntilDate() { - return retentionRetainUntilDate; - } - - public LegalHold legalHold() { - return legalHold; - } - - public boolean deleteMarker() { - return deleteMarker; - } - - public String versionId() { - return this.headers().get("x-amz-version-id"); - } - - public String contentType() { - return this.headers().get("Content-Type"); - } - - public Map userMetadata() { - return userMetadata; - } - - @Override - public String toString() { - return "ObjectStat{" - + "bucket=" - + bucket() - + ", object=" - + object() - + ", last-modified=" - + lastModified - + ", size=" - + size - + "}"; +/** Response of {@link MinioAsyncClient#statObject}. */ +public class StatObjectResponse extends HeadObjectResponse { + public StatObjectResponse(HeadObjectResponse response) { + super(response.headers(), response.bucket(), response.region(), response.object()); } } diff --git a/api/src/main/java/io/minio/Time.java b/api/src/main/java/io/minio/Time.java index 01b788f68..06cfc75bf 100644 --- a/api/src/main/java/io/minio/Time.java +++ b/api/src/main/java/io/minio/Time.java @@ -17,9 +17,18 @@ package io.minio; +import com.fasterxml.jackson.annotation.JsonCreator; import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; import java.util.Locale; +import org.simpleframework.xml.Root; +import org.simpleframework.xml.convert.Convert; +import org.simpleframework.xml.convert.Converter; +import org.simpleframework.xml.stream.InputNode; +import org.simpleframework.xml.stream.OutputNode; /** Time formatters for S3 APIs. */ public class Time { @@ -28,7 +37,7 @@ public class Time { public static final DateTimeFormatter AMZ_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'", Locale.US).withZone(UTC); - public static final DateTimeFormatter RESPONSE_DATE_FORMAT = + public static final DateTimeFormatter ISO8601UTC_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH':'mm':'ss'.'SSS'Z'", Locale.US).withZone(UTC); // Formatted string is convertible to LocalDate only, not to LocalDateTime or ZonedDateTime. @@ -40,7 +49,54 @@ public class Time { public static final DateTimeFormatter HTTP_HEADER_DATE_FORMAT = DateTimeFormatter.ofPattern("EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'", Locale.US).withZone(UTC); - public static final DateTimeFormatter EXPIRATION_DATE_FORMAT = RESPONSE_DATE_FORMAT; - private Time() {} + + /** Wrapped {@link ZonedDateTime} to handle ISO8601UTC format. */ + @Root + @Convert(S3Time.S3TimeConverter.class) + public static class S3Time { + // ISO8601UTC format handles 0 or more digits of fraction-of-second + private static final DateTimeFormatter FORMAT = + new DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd'T'HH':'mm':'ss") + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) + .appendPattern("'Z'") + .toFormatter(Locale.US) + .withZone(UTC); + + private ZonedDateTime value; + + public S3Time() {} + + public S3Time(ZonedDateTime value) { + this.value = value; + } + + public ZonedDateTime toZonedDateTime() { + return value; + } + + @Override + public String toString() { + return value == null ? null : value.format(ISO8601UTC_FORMAT); + } + + @JsonCreator + public static S3Time fromString(String value) { + return new S3Time(ZonedDateTime.parse(value, FORMAT)); + } + + /** XML converter class. */ + public static class S3TimeConverter implements Converter { + @Override + public S3Time read(InputNode node) throws Exception { + return S3Time.fromString(node.getValue()); + } + + @Override + public void write(OutputNode node, S3Time time) { + node.setValue(time.toString()); + } + } + } } diff --git a/api/src/main/java/io/minio/UploadObjectArgs.java b/api/src/main/java/io/minio/UploadObjectArgs.java index 68775b184..d490ca247 100644 --- a/api/src/main/java/io/minio/UploadObjectArgs.java +++ b/api/src/main/java/io/minio/UploadObjectArgs.java @@ -20,6 +20,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Objects; +import okhttp3.MediaType; /** Argument class of {@link MinioAsyncClient#uploadObject} and {@link MinioClient#uploadObject}. */ public class UploadObjectArgs extends PutObjectBaseArgs { @@ -29,20 +30,11 @@ public String filename() { return filename; } - /** - * Gets content type. It returns if content type is set (or) value of "Content-Type" header (or) - * probed content type of file (or) default "application/octet-stream". - */ - public String contentType() throws IOException { - String contentType = super.contentType(); - if (contentType != null) { - return contentType; - } - - contentType = Files.probeContentType(Paths.get(filename)); - return (contentType != null && !contentType.isEmpty()) - ? contentType - : "application/octet-stream"; + public MediaType contentType() throws IOException { + MediaType contentType = super.contentType(); + if (contentType != null) return contentType; + String type = Files.probeContentType(Paths.get(filename)); + return type != null ? MediaType.parse(type) : null; } public static Builder builder() { @@ -58,7 +50,7 @@ protected void validate(UploadObjectArgs args) { } private void validateFilename(String filename) { - validateNotEmptyString(filename, "filename"); + Utils.validateNotEmptyString(filename, "filename"); if (!Files.isRegularFile(Paths.get(filename))) { throw new IllegalArgumentException(filename + " not a regular file"); } @@ -82,12 +74,6 @@ public Builder filename(String filename, long partSize) throws IOException { public Builder filename(String filename) throws IOException { return this.filename(filename, 0); } - - public Builder contentType(String contentType) { - validateContentType(contentType); - operations.add(args -> args.contentType = contentType); - return this; - } } @Override diff --git a/api/src/main/java/io/minio/UploadPartArgs.java b/api/src/main/java/io/minio/UploadPartArgs.java new file mode 100644 index 000000000..30dd30fb9 --- /dev/null +++ b/api/src/main/java/io/minio/UploadPartArgs.java @@ -0,0 +1,92 @@ +/* + * 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 java.util.Objects; + +/** Argument class of {@link MinioAsyncClient#uploadPart} and {@link MinioClient#uploadPart}. */ +public class UploadPartArgs extends PutObjectAPIBaseArgs { + private String uploadId; + private int partNumber; + + protected UploadPartArgs() {} + + public UploadPartArgs( + PutObjectBaseArgs args, + String uploadId, + int partNumber, + ByteBuffer buffer, + Http.Headers checksumHeaders) { + super(args, buffer, checksumHeaders); + this.uploadId = uploadId; + this.partNumber = partNumber; + this.buffer = buffer; + if (args.sse() != null && args.sse() instanceof ServerSideEncryption.CustomerKey) { + this.headers.putAll(args.sse().headers()); + } + } + + public String uploadId() { + return uploadId; + } + + public int partNumber() { + return partNumber; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link UploadPartArgs}. */ + public static final class Builder extends PutObjectAPIBaseArgs.Builder { + @Override + protected void validate(UploadPartArgs args) { + super.validate(args); + Utils.validateNotEmptyString(args.uploadId, "upload ID"); + if (args.partNumber <= 0) { + throw new IllegalArgumentException("valid part number must be provided"); + } + } + + public Builder uploadId(String uploadId) { + Utils.validateNotEmptyString(uploadId, "upload ID"); + operations.add(args -> args.uploadId = uploadId); + return this; + } + + public Builder partNumber(int partNumber) { + if (partNumber <= 0) throw new IllegalArgumentException("valid part number must be provided"); + operations.add(args -> args.partNumber = partNumber); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UploadPartArgs)) return false; + if (!super.equals(o)) return false; + UploadPartArgs that = (UploadPartArgs) o; + return Objects.equals(uploadId, that.uploadId) && partNumber == that.partNumber; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), uploadId, partNumber); + } +} diff --git a/api/src/main/java/io/minio/UploadPartCopyArgs.java b/api/src/main/java/io/minio/UploadPartCopyArgs.java new file mode 100644 index 000000000..044179a30 --- /dev/null +++ b/api/src/main/java/io/minio/UploadPartCopyArgs.java @@ -0,0 +1,101 @@ +/* + * 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 java.util.Objects; + +/** + * Argument class of {@link MinioAsyncClient#uploadPartCopy} and {@link MinioClient#uploadPartCopy}. + */ +public class UploadPartCopyArgs extends ObjectArgs { + private String uploadId; + private int partNumber; + private Http.Headers headers; + + private UploadPartCopyArgs() {} + + public UploadPartCopyArgs( + ComposeObjectArgs args, String uploadId, int partNumber, Http.Headers headers) { + super(args); + this.uploadId = uploadId; + this.partNumber = partNumber; + this.headers = headers; + } + + public String uploadId() { + return uploadId; + } + + public int partNumber() { + return partNumber; + } + + public Http.Headers headers() { + return headers; + } + + public static Builder builder() { + return new Builder(); + } + + /** Argument builder of {@link UploadPartCopyArgs}. */ + public static final class Builder extends ObjectArgs.Builder { + @Override + protected void validate(UploadPartCopyArgs args) { + super.validate(args); + Utils.validateNotEmptyString(args.uploadId, "upload ID"); + if (args.partNumber <= 0) { + throw new IllegalArgumentException("valid part number must be provided"); + } + Utils.validateNotNull(args.headers, "headers"); + } + + public Builder uploadId(String uploadId) { + Utils.validateNotEmptyString(uploadId, "upload ID"); + operations.add(args -> args.uploadId = uploadId); + return this; + } + + public Builder partNumber(int partNumber) { + if (partNumber <= 0) throw new IllegalArgumentException("valid part number must be provided"); + operations.add(args -> args.partNumber = partNumber); + return this; + } + + public Builder headers(Http.Headers headers) { + Utils.validateNotNull(headers, "headers"); + operations.add(args -> args.headers = headers); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UploadPartCopyArgs)) return false; + if (!super.equals(o)) return false; + UploadPartCopyArgs that = (UploadPartCopyArgs) o; + return Objects.equals(uploadId, that.uploadId) + && Objects.equals(partNumber, that.partNumber) + && Objects.equals(headers, that.headers); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), uploadId, partNumber, headers); + } +} diff --git a/api/src/main/java/io/minio/UploadPartCopyResponse.java b/api/src/main/java/io/minio/UploadPartCopyResponse.java index 6f1651ade..348dc930a 100644 --- a/api/src/main/java/io/minio/UploadPartCopyResponse.java +++ b/api/src/main/java/io/minio/UploadPartCopyResponse.java @@ -17,6 +17,7 @@ package io.minio; import io.minio.messages.CopyPartResult; +import io.minio.messages.Part; import okhttp3.Headers; /** Response class of {@link S3Base#uploadPartCopyAsync}. */ @@ -50,4 +51,8 @@ public int partNumber() { public CopyPartResult result() { return result; } + + public Part part() { + return new Part(result, partNumber); + } } diff --git a/api/src/main/java/io/minio/UploadPartResponse.java b/api/src/main/java/io/minio/UploadPartResponse.java index 7d42661d2..028bd2fcc 100644 --- a/api/src/main/java/io/minio/UploadPartResponse.java +++ b/api/src/main/java/io/minio/UploadPartResponse.java @@ -16,13 +16,13 @@ package io.minio; +import io.minio.messages.Part; import okhttp3.Headers; /** Response class of {@link S3Base#uploadPartAsync}. */ public class UploadPartResponse extends GenericResponse { private String uploadId; - private int partNumber; - private String etag; + private Part part; public UploadPartResponse( Headers headers, @@ -34,19 +34,22 @@ public UploadPartResponse( String etag) { super(headers, bucket, region, object); this.uploadId = uploadId; - this.partNumber = partNumber; - this.etag = etag; + this.part = + new Part( + partNumber, + etag, + headers.get("x-amz-checksum-crc32"), + headers.get("x-amz-checksum-crc32c"), + headers.get("x-amz-checksum-crc64nvme"), + headers.get("x-amz-checksum-sha1"), + headers.get("x-amz-checksum-sha256")); } public String uploadId() { return uploadId; } - public int partNumber() { - return partNumber; - } - - public String etag() { - return etag; + public Part part() { + return part; } } diff --git a/api/src/main/java/io/minio/UploadSnowballObjectsArgs.java b/api/src/main/java/io/minio/UploadSnowballObjectsArgs.java index 25b13751d..136fb204d 100644 --- a/api/src/main/java/io/minio/UploadSnowballObjectsArgs.java +++ b/api/src/main/java/io/minio/UploadSnowballObjectsArgs.java @@ -51,7 +51,7 @@ public static Builder builder() { public static final class Builder extends ObjectWriteArgs.Builder { private void validateObjects(Iterable objects) { - validateNotNull(objects, "objects"); + Utils.validateNotNull(objects, "objects"); } @Override diff --git a/api/src/main/java/io/minio/Utils.java b/api/src/main/java/io/minio/Utils.java index 1fe65c140..dd8f54ddb 100644 --- a/api/src/main/java/io/minio/Utils.java +++ b/api/src/main/java/io/minio/Utils.java @@ -16,19 +16,140 @@ package io.minio; +import com.google.common.escape.Escaper; +import com.google.common.net.UrlEscapers; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.net.URL; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import okhttp3.HttpUrl; /** Collection of utility functions. */ public class Utils { + private static final Escaper ESCAPER = UrlEscapers.urlPathSegmentEscaper(); + public static final String UTF_8 = StandardCharsets.UTF_8.toString(); + public static final String AWS_S3_PREFIX = + "^(((bucket\\.|accesspoint\\.)" + + "vpce(-(?!_)[a-z_\\d]+(? T validateNotNull(T arg, String argName) { + return Objects.requireNonNull(arg, argName + " must not be null"); + } + + public static void validateNotEmptyString(String arg, String argName) { + validateNotNull(arg, argName); + if (arg.isEmpty()) { + throw new IllegalArgumentException(argName + " must be a non-empty string."); + } + } + + public static void validateNullOrNotEmptyString(String arg, String argName) { + if (arg != null && arg.isEmpty()) { + throw new IllegalArgumentException(argName + " must be a non-empty string."); + } + } + + public static boolean isValidIPv4OrIPv6(String value) { + return InetAddressValidator.getInstance().isValid(value); + } + + public static boolean isValidIPv6(String value) { + return InetAddressValidator.getInstance().isValidInet6Address(value); + } + + public static boolean isValidIPv4(String value) { + return InetAddressValidator.getInstance().isValidInet4Address(value); + } + + public static void validateHostnameOrIPAddress(String endpoint) { + if (isValidIPv4OrIPv6(endpoint)) return; + + if (!HOSTNAME_REGEX.matcher(endpoint).find()) { + throw new IllegalArgumentException("invalid hostname " + endpoint); + } + } + + public static void validateUrl(HttpUrl url) { + if (!url.encodedPath().equals("/")) { + throw new IllegalArgumentException("no path allowed in endpoint " + url); + } + } + + public static HttpUrl getBaseUrl(String endpoint) { + validateNotEmptyString(endpoint, "endpoint"); + HttpUrl url = HttpUrl.parse(endpoint); + if (url == null) { + validateHostnameOrIPAddress(endpoint); + url = new HttpUrl.Builder().scheme("https").host(endpoint).build(); + } else { + validateUrl(url); + } + + return url; + } + + public static String getHostHeader(HttpUrl url) { + String host = url.host(); + if (isValidIPv6(host)) host = "[" + host + "]"; + + // ignore port when port and service matches i.e HTTP -> 80, HTTPS -> 443 + if ((url.scheme().equals("http") && url.port() == 80) + || (url.scheme().equals("https") && url.port() == 443)) { + return host; + } + + return host + ":" + url.port(); + } + public static String urlDecode(String value, String type) { if (!"url".equals(type)) return value; try { @@ -46,4 +167,587 @@ public static List unmodifiableList(List value) { public static Map unmodifiableMap(Map value) { return Collections.unmodifiableMap(value == null ? new HashMap() : value); } + + public static String stringify(Object value) { + if (value == null) return ""; + + if (value.getClass().isArray()) { + StringBuilder result = new StringBuilder("["); + + int length = Array.getLength(value); + + if (value.getClass().getComponentType().isPrimitive()) { + for (int i = 0; i < length; i++) { + if (i > 0) result.append(", "); + result.append(Array.get(value, i)); + } + } else { + for (int i = 0; i < length; i++) { + if (i > 0) result.append(", "); + Object element = Array.get(value, i); + result.append(stringify(element)); + } + } + + result.append("]"); + return result.toString(); + } + + if (value instanceof CharSequence) { + return "'" + value.toString() + "'"; + } + + return value.toString(); + } + + /** Returns S3 encoded string. */ + public static String encode(String str) { + if (str == null) { + return ""; + } + + StringBuilder builder = new StringBuilder(); + for (char ch : ESCAPER.escape(str).toCharArray()) { + switch (ch) { + case '!': + builder.append("%21"); + break; + case '$': + builder.append("%24"); + break; + case '&': + builder.append("%26"); + break; + case '\'': + builder.append("%27"); + break; + case '(': + builder.append("%28"); + break; + case ')': + builder.append("%29"); + break; + case '*': + builder.append("%2A"); + break; + case '+': + builder.append("%2B"); + break; + case ',': + builder.append("%2C"); + break; + case '/': + builder.append("%2F"); + break; + case ':': + builder.append("%3A"); + break; + case ';': + builder.append("%3B"); + break; + case '=': + builder.append("%3D"); + break; + case '@': + builder.append("%40"); + break; + case '[': + builder.append("%5B"); + break; + case ']': + builder.append("%5D"); + break; + default: + builder.append(ch); + } + } + return builder.toString(); + } + + /** Returns S3 encoded string of given path where multiple '/' are trimmed. */ + public static String encodePath(String path) { + final StringBuilder encodedPath = new StringBuilder(); + for (String pathSegment : path.split("/")) { + if (!pathSegment.isEmpty()) { + if (encodedPath.length() > 0) { + encodedPath.append("/"); + } + encodedPath.append(Utils.encode(pathSegment)); + } + } + + if (path.startsWith("/")) encodedPath.insert(0, "/"); + if (path.endsWith("/")) encodedPath.append("/"); + + return encodedPath.toString(); + } + + public static String getDefaultUserAgent() { + return String.format( + "MinIO (%s; %s) minio-java/%s", + System.getProperty("os.name"), + System.getProperty("os.arch"), + MinioProperties.INSTANCE.getVersion()); + } + + /** Identifies and stores version information of minio-java package at run time. */ + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "MS_EXPOSE_REP") + public static enum MinioProperties { + INSTANCE; + + private static final Logger LOGGER = Logger.getLogger(MinioProperties.class.getName()); + + private final AtomicReference version = new AtomicReference<>(null); + + public String getVersion() { + String result = version.get(); + if (result != null) { + return result; + } + setVersion(); + return version.get(); + } + + private synchronized void setVersion() { + if (version.get() != null) { + return; + } + version.set("dev"); + ClassLoader classLoader = getClass().getClassLoader(); + if (classLoader == null) { + return; + } + + try { + Enumeration resources = classLoader.getResources("META-INF/MANIFEST.MF"); + while (resources.hasMoreElements()) { + try (InputStream is = resources.nextElement().openStream()) { + Manifest manifest = new Manifest(is); + if ("minio".equals(manifest.getMainAttributes().getValue("Implementation-Title"))) { + version.set(manifest.getMainAttributes().getValue("Implementation-Version")); + return; + } + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "IOException occurred", e); + version.set("unknown"); + } + } + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + + /** + * Regular Expression validation (using JDK 1.4+ regex support). + * + *

Construct the validator either for a single regular expression or a set (array) of regular + * expressions. By default validation is case sensitive but constructors are provided to + * allow case in-sensitive validation. For example to create a validator which does case + * in-sensitive validation for a set of regular expressions: + * + *

+   * 
+   * String[] regexs = new String[] {...};
+   * RegexValidator validator = new RegexValidator(regexs, false);
+   * 
+   * 
+ * + *

+ * + *

    + *
  • Validate true or false: + *
  • + *
      + *
    • boolean valid = validator.isValid(value); + *
    + *
  • Validate returning an aggregated String of the matched groups: + *
  • + *
      + *
    • String result = validator.validate(value); + *
    + *
  • Validate returning the matched groups: + *
  • + *
      + *
    • String[] result = validator.match(value); + *
    + *
+ * + *

Note that patterns are matched against the entire input. + * + *

+ * + *

Cached instances pre-compile and re-use {@link Pattern}(s) - which according to the {@link + * Pattern} API are safe to use in a multi-threaded environment. + * + * @version $Revision$ + * @since Validator 1.4 + */ + public static class RegexValidator implements Serializable { + + private static final long serialVersionUID = -8832409930574867162L; + + private final Pattern[] patterns; + + /** + * Construct a case sensitive validator for a single regular expression. + * + * @param regex The regular expression this validator will validate against + */ + public RegexValidator(String regex) { + this(regex, true); + } + + /** + * Construct a validator for a single regular expression with the specified case sensitivity. + * + * @param regex The regular expression this validator will validate against + * @param caseSensitive when true matching is case sensitive, otherwise + * matching is case in-sensitive + */ + public RegexValidator(String regex, boolean caseSensitive) { + this(new String[] {regex}, caseSensitive); + } + + /** + * Construct a case sensitive validator that matches any one of the set of regular + * expressions. + * + * @param regexs The set of regular expressions this validator will validate against + */ + public RegexValidator(String[] regexs) { + this(regexs, true); + } + + /** + * Construct a validator that matches any one of the set of regular expressions with the + * specified case sensitivity. + * + * @param regexs The set of regular expressions this validator will validate against + * @param caseSensitive when true matching is case sensitive, otherwise + * matching is case in-sensitive + */ + public RegexValidator(String[] regexs, boolean caseSensitive) { + if (regexs == null || regexs.length == 0) { + throw new IllegalArgumentException("Regular expressions are missing"); + } + patterns = new Pattern[regexs.length]; + int flags = (caseSensitive ? 0 : Pattern.CASE_INSENSITIVE); + for (int i = 0; i < regexs.length; i++) { + if (regexs[i] == null || regexs[i].length() == 0) { + throw new IllegalArgumentException("Regular expression[" + i + "] is missing"); + } + patterns[i] = Pattern.compile(regexs[i], flags); + } + } + + /** + * Validate a value against the set of regular expressions. + * + * @param value The value to validate. + * @return true if the value is valid otherwise false. + */ + public boolean isValid(String value) { + if (value == null) { + return false; + } + for (int i = 0; i < patterns.length; i++) { + if (patterns[i].matcher(value).matches()) { + return true; + } + } + return false; + } + + /** + * Validate a value against the set of regular expressions returning the array of matched + * groups. + * + * @param value The value to validate. + * @return String array of the groups matched if valid or null if invalid + */ + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "PZLA", + justification = "Null is checked, not empty array. API is clear as well.") + public String[] match(String value) { + if (value == null) { + return null; + } + for (int i = 0; i < patterns.length; i++) { + Matcher matcher = patterns[i].matcher(value); + if (matcher.matches()) { + int count = matcher.groupCount(); + String[] groups = new String[count]; + for (int j = 0; j < count; j++) { + groups[j] = matcher.group(j + 1); + } + return groups; + } + } + return null; + } + + /** + * Validate a value against the set of regular expressions returning a String value of the + * aggregated groups. + * + * @param value The value to validate. + * @return Aggregated String value comprised of the groups matched if valid or null + * if invalid + */ + public String validate(String value) { + if (value == null) { + return null; + } + for (int i = 0; i < patterns.length; i++) { + Matcher matcher = patterns[i].matcher(value); + if (matcher.matches()) { + int count = matcher.groupCount(); + if (count == 1) { + return matcher.group(1); + } + StringBuilder buffer = new StringBuilder(); + for (int j = 0; j < count; j++) { + String component = matcher.group(j + 1); + if (component != null) { + buffer.append(component); + } + } + return buffer.toString(); + } + } + return null; + } + + /** + * Provide a String representation of this validator. + * + * @return A String representation of this validator + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append("RegexValidator{"); + for (int i = 0; i < patterns.length; i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(patterns[i].pattern()); + } + buffer.append("}"); + return buffer.toString(); + } + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + + /** + * InetAddress validation and conversion routines (java.net.InetAddress). + * + *

+ * + *

+ * + *

This class provides methods to validate a candidate IP address. + * + *

+ * + *

This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} + * method. + * + * @version $Revision$ + * @since Validator 1.4 + */ + public static class InetAddressValidator { + + private static final int IPV4_MAX_OCTET_VALUE = 255; + + private static final int MAX_UNSIGNED_SHORT = 0xffff; + + private static final int BASE_16 = 16; + + private static final long serialVersionUID = -919201640201914789L; + + private static final String IPV4_REGEX = "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"; + + // Max number of hex groups (separated by :) in an IPV6 address + private static final int IPV6_MAX_HEX_GROUPS = 8; + + // Max hex digits in each IPv6 group + private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4; + + /** Singleton instance of this class. */ + private static final InetAddressValidator VALIDATOR = new InetAddressValidator(); + + /** IPv4 RegexValidator. */ + private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX); + + private InetAddressValidator() {} + + /** + * Returns the singleton instance of this validator. + * + * @return the singleton instance of this validator + */ + public static InetAddressValidator getInstance() { + return VALIDATOR; + } + + /** + * Checks if the specified string is a valid IP address. + * + * @param inetAddress the string to validate + * @return true if the string validates as an IP address + */ + public boolean isValid(String inetAddress) { + return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress); + } + + /** + * Validates an IPv4 address. Returns true if valid. + * + * @param inet4Address the IPv4 address to validate + * @return true if the argument contains a valid IPv4 address + */ + public boolean isValidInet4Address(String inet4Address) { + // verify that address conforms to generic IPv4 format + String[] groups = ipv4Validator.match(inet4Address); + + if (groups == null) { + return false; + } + + // verify that address subgroups are legal + for (String ipSegment : groups) { + if (ipSegment == null || ipSegment.length() == 0) { + return false; + } + + int iIpSegment = 0; + + try { + iIpSegment = Integer.parseInt(ipSegment); + } catch (NumberFormatException e) { + return false; + } + + if (iIpSegment > IPV4_MAX_OCTET_VALUE) { + return false; + } + + if (ipSegment.length() > 1 && ipSegment.startsWith("0")) { + return false; + } + } + + return true; + } + + /** + * Validates an IPv6 address. Returns true if valid. + * + * @param inet6Address the IPv6 address to validate + * @return true if the argument contains a valid IPv6 address + * @since 1.4.1 + */ + public boolean isValidInet6Address(String inet6Address) { + boolean containsCompressedZeroes = inet6Address.contains("::"); + if (containsCompressedZeroes + && inet6Address.indexOf("::") != inet6Address.lastIndexOf("::")) { + return false; + } + if (inet6Address.startsWith(":") && !inet6Address.startsWith("::") + || inet6Address.endsWith(":") && !inet6Address.endsWith("::")) { + return false; + } + String[] octets = inet6Address.split(":"); + if (containsCompressedZeroes) { + List octetList = new ArrayList(Arrays.asList(octets)); + if (inet6Address.endsWith("::")) { + // String.split() drops ending empty segments + octetList.add(""); + } else if (inet6Address.startsWith("::") && !octetList.isEmpty()) { + octetList.remove(0); + } + octets = octetList.toArray(new String[octetList.size()]); + } + if (octets.length > IPV6_MAX_HEX_GROUPS) { + return false; + } + int validOctets = 0; + int emptyOctets = 0; + for (int index = 0; index < octets.length; index++) { + String octet = octets[index]; + if (octet.length() == 0) { + emptyOctets++; + if (emptyOctets > 1) { + return false; + } + } else { + emptyOctets = 0; + if (octet.contains(".")) { // contains is Java 1.5+ + if (!inet6Address.endsWith(octet)) { + return false; + } + if (index > octets.length - 1 || index > 6) { // CHECKSTYLE IGNORE MagicNumber + // IPV4 occupies last two octets + return false; + } + if (!isValidInet4Address(octet)) { + return false; + } + validOctets += 2; + continue; + } + if (octet.length() > IPV6_MAX_HEX_DIGITS_PER_GROUP) { + return false; + } + int octetInt = 0; + try { + octetInt = Integer.valueOf(octet, BASE_16).intValue(); + } catch (NumberFormatException e) { + return false; + } + if (octetInt < 0 || octetInt > MAX_UNSIGNED_SHORT) { + return false; + } + } + validOctets++; + } + if (validOctets < IPV6_MAX_HEX_GROUPS && !containsCompressedZeroes) { + return false; + } + return true; + } + } } diff --git a/api/src/main/java/io/minio/credentials/AssumeRoleProvider.java b/api/src/main/java/io/minio/credentials/AssumeRoleProvider.java index b94496994..508a2085a 100644 --- a/api/src/main/java/io/minio/credentials/AssumeRoleProvider.java +++ b/api/src/main/java/io/minio/credentials/AssumeRoleProvider.java @@ -16,11 +16,11 @@ package io.minio.credentials; -import io.minio.Digest; +import io.minio.Checksum; +import io.minio.Http; import io.minio.Signer; import io.minio.Time; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import io.minio.errors.MinioException; import java.security.ProviderException; import java.time.ZonedDateTime; import java.util.Objects; @@ -59,7 +59,7 @@ public AssumeRoleProvider( @Nullable String roleSessionName, @Nullable String externalId, @Nullable OkHttpClient customHttpClient) - throws NoSuchAlgorithmException { + throws MinioException { super(customHttpClient); stsEndpoint = Objects.requireNonNull(stsEndpoint, "STS endpoint cannot be empty"); HttpUrl url = Objects.requireNonNull(HttpUrl.parse(stsEndpoint), "Invalid STS endpoint"); @@ -95,11 +95,11 @@ public AssumeRoleProvider( } String data = urlBuilder.build().encodedQuery(); - this.contentSha256 = Digest.sha256Hash(data); + this.contentSha256 = Checksum.hexString(Checksum.SHA256.sum(data)); this.request = new Request.Builder() .url(url) - .header("Host", host) + .header(Http.Headers.HOST, host) .method( "POST", RequestBody.create(data, MediaType.parse("application/x-www-form-urlencoded"))) @@ -112,13 +112,13 @@ protected Request getRequest() { return Signer.signV4Sts( this.request .newBuilder() - .header("x-amz-date", ZonedDateTime.now().format(Time.AMZ_DATE_FORMAT)) + .header(Http.Headers.X_AMZ_DATE, ZonedDateTime.now().format(Time.AMZ_DATE_FORMAT)) .build(), region, accessKey, secretKey, contentSha256); - } catch (NoSuchAlgorithmException | InvalidKeyException e) { + } catch (MinioException e) { throw new ProviderException("Signature calculation failed", e); } } diff --git a/api/src/main/java/io/minio/credentials/Credentials.java b/api/src/main/java/io/minio/credentials/Credentials.java index fb28da816..00d20bd48 100644 --- a/api/src/main/java/io/minio/credentials/Credentials.java +++ b/api/src/main/java/io/minio/credentials/Credentials.java @@ -18,7 +18,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import io.minio.messages.ResponseDate; +import io.minio.Time; import java.time.Duration; import java.time.ZonedDateTime; import java.util.Objects; @@ -45,13 +45,13 @@ public class Credentials { @Element(name = "Expiration") @JsonProperty("expiration") - private final ResponseDate expiration; + private final Time.S3Time expiration; public Credentials( @Nonnull @Element(name = "AccessKeyId") @JsonProperty("accessKey") String accessKey, @Nonnull @Element(name = "SecretAccessKey") @JsonProperty("secretKey") String secretKey, @Nullable @Element(name = "SessionToken") @JsonProperty("sessionToken") String sessionToken, - @Nullable @Element(name = "Expiration") @JsonProperty("expiration") ResponseDate expiration) { + @Nullable @Element(name = "Expiration") @JsonProperty("expiration") Time.S3Time expiration) { this.accessKey = Objects.requireNonNull(accessKey, "AccessKey must not be null"); this.secretKey = Objects.requireNonNull(secretKey, "SecretKey must not be null"); if (accessKey.isEmpty() || secretKey.isEmpty()) { @@ -73,11 +73,15 @@ public String sessionToken() { return sessionToken; } + public ZonedDateTime expiration() { + return expiration == null ? null : expiration.toZonedDateTime(); + } + public boolean isExpired() { if (expiration == null) { return false; } - return ZonedDateTime.now().plus(Duration.ofSeconds(10)).isAfter(expiration.zonedDateTime()); + return ZonedDateTime.now().plus(Duration.ofSeconds(10)).isAfter(expiration.toZonedDateTime()); } } diff --git a/api/src/main/java/io/minio/credentials/IamAwsProvider.java b/api/src/main/java/io/minio/credentials/IamAwsProvider.java index 08abb1050..71dad5a6b 100644 --- a/api/src/main/java/io/minio/credentials/IamAwsProvider.java +++ b/api/src/main/java/io/minio/credentials/IamAwsProvider.java @@ -21,7 +21,8 @@ import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; -import io.minio.messages.ResponseDate; +import io.minio.Http; +import io.minio.Time; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; @@ -211,7 +212,7 @@ public synchronized Credentials fetch() { return credentials; } - String tokenHeader = "Authorization"; + String tokenHeader = Http.Headers.AUTHORIZATION; String token = getProperty("AWS_CONTAINER_AUTHORIZATION_TOKEN"); if (getProperty("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") != null) { if (url == null) { @@ -248,7 +249,7 @@ public static class EcsCredentials { private String sessionToken; @JsonProperty("Expiration") - private ResponseDate expiration; + private Time.S3Time expiration; @JsonProperty("Code") private String code; diff --git a/api/src/main/java/io/minio/errors/ErrorResponseException.java b/api/src/main/java/io/minio/errors/ErrorResponseException.java index d2755932d..f62d66976 100644 --- a/api/src/main/java/io/minio/errors/ErrorResponseException.java +++ b/api/src/main/java/io/minio/errors/ErrorResponseException.java @@ -16,9 +16,7 @@ package io.minio.errors; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.minio.messages.ErrorResponse; -import okhttp3.Request; import okhttp3.Response; /** Thrown to indicate that error response is received when executing Amazon S3 operation. */ @@ -28,8 +26,8 @@ public class ErrorResponseException extends MinioException { private final ErrorResponse errorResponse; - @SuppressFBWarnings( - value = "Se", + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "SE_BAD_FIELD", justification = "There's really no excuse except that nobody has complained") private final Response response; @@ -51,30 +49,19 @@ public Response response() { @Override public String toString() { - Request request = response.request(); - return "error occurred\n" - + errorResponse.toString() - + "\n" - + "request={" - + "method=" - + request.method() - + ", " - + "url=" - + request.url() - + ", " - + "headers=" - + request + return String.format( + "S3 operation failed; ErrorResponseException{errorResponse=%s, request={method=%s, url=%s," + + " headers=%s}, response={code=%s, headers=%s}}", + errorResponse.toString(), + response.request().method(), + response.request().url(), + response + .request() .headers() .toString() .replaceAll("Signature=([0-9a-f]+)", "Signature=*REDACTED*") - .replaceAll("Credential=([^/]+)", "Credential=*REDACTED*") - + "}\n" - + "response={" - + "code=" - + response.code() - + ", " - + "headers=" - + response.headers() - + "}\n"; + .replaceAll("Credential=([^/]+)", "Credential=*REDACTED*"), + response.code(), + response.headers().toString()); } } diff --git a/api/src/main/java/io/minio/errors/InternalException.java b/api/src/main/java/io/minio/errors/InternalException.java index 4ba3e4746..5081a48cd 100644 --- a/api/src/main/java/io/minio/errors/InternalException.java +++ b/api/src/main/java/io/minio/errors/InternalException.java @@ -22,6 +22,11 @@ public class InternalException extends MinioException { private static final long serialVersionUID = 138336287983212416L; + /** Constructs a new InternalException with given error message. */ + public InternalException(String message) { + super(message); + } + /** Constructs a new InternalException with given error message. */ public InternalException(String message, String httpTrace) { super(message, httpTrace); diff --git a/api/src/main/java/io/minio/errors/MinioException.java b/api/src/main/java/io/minio/errors/MinioException.java index 108db2805..1a038be9d 100644 --- a/api/src/main/java/io/minio/errors/MinioException.java +++ b/api/src/main/java/io/minio/errors/MinioException.java @@ -38,6 +38,16 @@ public MinioException(String message, String httpTrace) { this.httpTrace = httpTrace; } + /** Constructs a new MinioException with the specified detail message and cause. */ + public MinioException(String message, Throwable cause) { + super(message, cause); + } + + /** Constructs a new MinioException with the specified cause. */ + public MinioException(Throwable cause) { + super(cause); + } + public String httpTrace() { return this.httpTrace; } diff --git a/api/src/main/java/io/minio/http/HttpUtils.java b/api/src/main/java/io/minio/http/HttpUtils.java deleted file mode 100644 index b69f3d2c2..000000000 --- a/api/src/main/java/io/minio/http/HttpUtils.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.http; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.minio.org.apache.commons.validator.routines.InetAddressValidator; -import java.io.FileInputStream; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.NoSuchAlgorithmException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.Collection; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Protocol; - -/** HTTP utilities. */ -public class HttpUtils { - public static final String AWS_S3_PREFIX = - "^(((bucket\\.|accesspoint\\.)" - + "vpce(-(?!_)[a-z_\\d]+(? 80, HTTPS -> 443 - if ((url.scheme().equals("http") && url.port() == 80) - || (url.scheme().equals("https") && url.port() == 443)) { - return host; - } - - return host + ":" + url.port(); - } - - private static OkHttpClient enableJKSPKCS12Certificates( - OkHttpClient httpClient, - String trustStorePath, - String trustStorePassword, - String keyStorePath, - String keyStorePassword, - String keyStoreType) - throws GeneralSecurityException, IOException { - if (trustStorePath == null || trustStorePath.isEmpty()) { - throw new IllegalArgumentException("trust store path must be provided"); - } - if (trustStorePassword == null) { - throw new IllegalArgumentException("trust store password must be provided"); - } - if (keyStorePath == null || keyStorePath.isEmpty()) { - throw new IllegalArgumentException("key store path must be provided"); - } - if (keyStorePassword == null) { - throw new IllegalArgumentException("key store password must be provided"); - } - - SSLContext sslContext = SSLContext.getInstance("TLS"); - KeyStore trustStore = KeyStore.getInstance("JKS"); - KeyStore keyStore = KeyStore.getInstance(keyStoreType); - try (FileInputStream trustInput = new FileInputStream(trustStorePath); - FileInputStream keyInput = new FileInputStream(keyStorePath); ) { - trustStore.load(trustInput, trustStorePassword.toCharArray()); - keyStore.load(keyInput, keyStorePassword.toCharArray()); - } - TrustManagerFactory trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init(trustStore); - - KeyManagerFactory keyManagerFactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - keyManagerFactory.init(keyStore, keyStorePassword.toCharArray()); - - sslContext.init( - keyManagerFactory.getKeyManagers(), - trustManagerFactory.getTrustManagers(), - new java.security.SecureRandom()); - - return httpClient - .newBuilder() - .sslSocketFactory( - sslContext.getSocketFactory(), - (X509TrustManager) trustManagerFactory.getTrustManagers()[0]) - .build(); - } - - public static OkHttpClient enableJKSCertificates( - OkHttpClient httpClient, - String trustStorePath, - String trustStorePassword, - String keyStorePath, - String keyStorePassword) - throws GeneralSecurityException, IOException { - return enableJKSPKCS12Certificates( - httpClient, trustStorePath, trustStorePassword, keyStorePath, keyStorePassword, "JKS"); - } - - public static OkHttpClient enablePKCS12Certificates( - OkHttpClient httpClient, - String trustStorePath, - String trustStorePassword, - String keyStorePath, - String keyStorePassword) - throws GeneralSecurityException, IOException { - return enableJKSPKCS12Certificates( - httpClient, trustStorePath, trustStorePassword, keyStorePath, keyStorePassword, "PKCS12"); - } - - /** - * copied logic from - * https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java - */ - public static OkHttpClient enableExternalCertificates(OkHttpClient httpClient, String filename) - throws GeneralSecurityException, IOException { - Collection certificates = null; - try (FileInputStream fis = new FileInputStream(filename)) { - certificates = CertificateFactory.getInstance("X.509").generateCertificates(fis); - } - - if (certificates == null || certificates.isEmpty()) { - throw new IllegalArgumentException("expected non-empty set of trusted certificates"); - } - - char[] password = "password".toCharArray(); // Any password will work. - - // Put the certificates a key store. - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - // By convention, 'null' creates an empty key store. - keyStore.load(null, password); - - int index = 0; - for (Certificate certificate : certificates) { - String certificateAlias = Integer.toString(index++); - keyStore.setCertificateEntry(certificateAlias, certificate); - } - - // Use it to build an X509 trust manager. - KeyManagerFactory keyManagerFactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - keyManagerFactory.init(keyStore, password); - TrustManagerFactory trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init(keyStore); - - final KeyManager[] keyManagers = keyManagerFactory.getKeyManagers(); - final TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(keyManagers, trustManagers, null); - SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - - return httpClient - .newBuilder() - .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0]) - .build(); - } - - public static OkHttpClient newDefaultHttpClient( - long connectTimeout, long writeTimeout, long readTimeout) { - OkHttpClient httpClient = - new OkHttpClient() - .newBuilder() - .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) - .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS) - .readTimeout(readTimeout, TimeUnit.MILLISECONDS) - .protocols(Arrays.asList(Protocol.HTTP_1_1)) - .build(); - String filename = System.getenv("SSL_CERT_FILE"); - if (filename != null && !filename.isEmpty()) { - try { - httpClient = enableExternalCertificates(httpClient, filename); - } catch (GeneralSecurityException | IOException e) { - throw new RuntimeException(e); - } - } - return httpClient; - } - - @SuppressFBWarnings(value = "SIC", justification = "Should not be used in production anyways.") - public static OkHttpClient disableCertCheck(OkHttpClient client) - throws KeyManagementException, NoSuchAlgorithmException { - final TrustManager[] trustAllCerts = - new TrustManager[] { - new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - throws CertificateException {} - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException {} - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[] {}; - } - } - }; - - final SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); - final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - - return client - .newBuilder() - .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]) - .hostnameVerifier( - new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }) - .build(); - } - - public static OkHttpClient setTimeout( - OkHttpClient client, long connectTimeout, long writeTimeout, long readTimeout) { - return client - .newBuilder() - .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) - .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS) - .readTimeout(readTimeout, TimeUnit.MILLISECONDS) - .build(); - } -} diff --git a/api/src/main/java/io/minio/http/Method.java b/api/src/main/java/io/minio/http/Method.java deleted file mode 100644 index e5224f813..000000000 --- a/api/src/main/java/io/minio/http/Method.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2015 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.http; - -/** HTTP methods. */ -public enum Method { - GET, - HEAD, - POST, - PUT, - DELETE; -} diff --git a/api/src/main/java/io/minio/messages/AbortIncompleteMultipartUpload.java b/api/src/main/java/io/minio/messages/AbortIncompleteMultipartUpload.java deleted file mode 100644 index 8704396c2..000000000 --- a/api/src/main/java/io/minio/messages/AbortIncompleteMultipartUpload.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote abort incomplete multipart upload information for {@link LifecycleRule}. - */ -@Root(name = "AbortIncompleteMultipartUpload") -public class AbortIncompleteMultipartUpload { - @Element(name = "DaysAfterInitiation") - private int daysAfterInitiation; - - public AbortIncompleteMultipartUpload( - @Element(name = "DaysAfterInitiation") int daysAfterInitiation) { - this.daysAfterInitiation = daysAfterInitiation; - } - - public int daysAfterInitiation() { - return daysAfterInitiation; - } -} diff --git a/api/src/main/java/io/minio/messages/AccessControlList.java b/api/src/main/java/io/minio/messages/AccessControlList.java index fff7fc582..5c4eb4356 100644 --- a/api/src/main/java/io/minio/messages/AccessControlList.java +++ b/api/src/main/java/io/minio/messages/AccessControlList.java @@ -20,10 +20,21 @@ import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.simpleframework.xml.Attribute; +import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; +import org.simpleframework.xml.convert.Convert; +import org.simpleframework.xml.convert.Converter; +import org.simpleframework.xml.stream.InputNode; +import org.simpleframework.xml.stream.OutputNode; -/** Helper class to denote access control list of {@link S3OutputLocation}. */ +/** + * Object represents access control list of {@link RestoreRequest.S3} and {@link + * AccessControlPolicy}. + */ @Root(name = "AccessControlList") @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") public class AccessControlList { @@ -42,4 +53,186 @@ public AccessControlList( public List grants() { return Utils.unmodifiableList(grants); } + + @Override + public String toString() { + return String.format("AccessControlList{grants=%s}", Utils.stringify(grants)); + } + + /** Object represents grant information of {@link AccessControlList}. */ + @Root(name = "Grant") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class Grant { + @Element(name = "Grantee", required = false) + private Grantee grantee; + + @Element(name = "Permission", required = false) + private Permission permission; + + public Grant( + @Nullable @Element(name = "Grantee", required = false) Grantee grantee, + @Nullable @Element(name = "Permission", required = false) Permission permission) { + if (grantee == null && permission == null) { + throw new IllegalArgumentException("Either Grantee or Permission must be provided"); + } + this.grantee = grantee; + this.permission = permission; + } + + public Grantee grantee() { + return grantee; + } + + public Permission permission() { + return permission; + } + + public String granteeUri() { + return grantee == null ? null : grantee.uri(); + } + + public String granteeId() { + return grantee == null ? null : grantee.id(); + } + + @Override + public String toString() { + return String.format( + "Grant{grantee=%s, permission=%s}", + Utils.stringify(grantee), Utils.stringify(permission)); + } + } + + /** Object represents grantee information of {@link AccessControlList}. */ + @Root(name = "Grantee") + @Namespace(prefix = "xsi", reference = "http://www.w3.org/2001/XMLSchema-instance") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class Grantee { + @Attribute(name = "type") + private String xsiType; + + @Element(name = "DisplayName", required = false) + private String displayName; + + @Element(name = "EmailAddress", required = false) + private String emailAddress; + + @Element(name = "ID", required = false) + private String id; + + @Element(name = "Type") + private Type type; + + @Element(name = "URI", required = false) + private String uri; + + public Grantee( + @Nonnull Type type, + @Nullable String displayName, + @Nullable String emailAddress, + @Nullable String id, + @Nullable String uri) { + this.type = Objects.requireNonNull(type, "Type must not be null"); + this.displayName = displayName; + this.emailAddress = emailAddress; + this.id = id; + this.uri = uri; + } + + public Grantee( + @Nonnull @Attribute(name = "type") String xsiType, + @Nonnull @Element(name = "Type") Type type, + @Nullable @Element(name = "DisplayName", required = false) String displayName, + @Nullable @Element(name = "EmailAddress", required = false) String emailAddress, + @Nullable @Element(name = "ID", required = false) String id, + @Nullable @Element(name = "URI", required = false) String uri) { + this(type, displayName, emailAddress, id, uri); + this.xsiType = xsiType; + } + + public String displayName() { + return displayName; + } + + public String emailAddress() { + return emailAddress; + } + + public String id() { + return id; + } + + public Type type() { + return type; + } + + public String uri() { + return uri; + } + + @Override + public String toString() { + return String.format( + "Grantee{xsiType=%s, displayName=%s, emailAddress=%s, id=%s, type=%s, uri=%s}", + xsiType, + Utils.stringify(displayName), + Utils.stringify(emailAddress), + Utils.stringify(id), + Utils.stringify(type), + Utils.stringify(uri)); + } + } + + /** Grantee type of {@link AccessControlList.Grantee}. */ + @Root(name = "Type") + @Convert(Type.TypeConverter.class) + public static enum Type { + CANONICAL_USER("CanonicalUser"), + AMAZON_CUSTOMER_BY_EMAIL("AmazonCustomerByEmail"), + GROUP("Group"); + + private final String value; + + private Type(String value) { + this.value = value; + } + + public String toString() { + return this.value; + } + + /** Returns Type of given string. */ + public static Type fromString(String granteeTypeString) { + for (Type granteeType : Type.values()) { + if (granteeTypeString.equals(granteeType.value)) { + return granteeType; + } + } + + throw new IllegalArgumentException("Unknown grantee type '" + granteeTypeString + "'"); + } + + /** XML converter class of Grantee {@link AccessControlList.Type}. */ + public static class TypeConverter implements Converter { + @Override + public Type read(InputNode node) throws Exception { + return Type.fromString(node.getValue()); + } + + @Override + public void write(OutputNode node, Type granteeType) throws Exception { + node.setValue(granteeType.toString()); + } + } + } + + /** Grant permission of {@link AccessControlList.Grant}. */ + @Root(name = "Permission") + public static enum Permission { + FULL_CONTROL, + WRITE, + WRITE_ACP, + READ, + READ_ACP; + } } diff --git a/api/src/main/java/io/minio/messages/AccessControlPolicy.java b/api/src/main/java/io/minio/messages/AccessControlPolicy.java index f9b95921e..a307c96cb 100644 --- a/api/src/main/java/io/minio/messages/AccessControlPolicy.java +++ b/api/src/main/java/io/minio/messages/AccessControlPolicy.java @@ -18,12 +18,13 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import io.minio.Utils; import java.util.List; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; /** - * Object representation of response XML of GetObjectAcl * API. */ @@ -54,18 +55,20 @@ public AccessControlList accessControlList() { public String cannedAcl() { if (accessControlList == null) return ""; - List grants = accessControlList.grants(); + List grants = accessControlList.grants(); int size = grants.size(); if (size < 1 || size > 3) return ""; - for (Grant grant : grants) { + for (AccessControlList.Grant grant : grants) { if (grant == null) continue; String uri = grant.granteeUri(); - if (grant.permission() == Permission.FULL_CONTROL && size == 1 && "".equals(uri)) { + if (grant.permission() == AccessControlList.Permission.FULL_CONTROL + && size == 1 + && "".equals(uri)) { return "private"; - } else if (grant.permission() == Permission.READ && size == 2) { + } else if (grant.permission() == AccessControlList.Permission.READ && size == 2) { if ("http://acs.amazonaws.com/groups/global/AuthenticatedUsers".equals(uri)) { return "authenticated-read"; } @@ -75,7 +78,7 @@ public String cannedAcl() { && owner.id().equals(grant.granteeId())) { return "bucket-owner-read"; } - } else if (grant.permission() == Permission.WRITE + } else if (grant.permission() == AccessControlList.Permission.WRITE && size == 3 && "http://acs.amazonaws.com/groups/global/AllUsers".equals(uri)) { return "public-read-write"; @@ -90,19 +93,19 @@ public Multimap grantAcl() { if (accessControlList != null) { map = HashMultimap.create(); - for (Grant grant : accessControlList.grants()) { + for (AccessControlList.Grant grant : accessControlList.grants()) { if (grant == null) continue; String value = "id=" + grant.granteeId(); - if (grant.permission() == Permission.READ) { + if (grant.permission() == AccessControlList.Permission.READ) { map.put("X-Amz-Grant-Read", value); - } else if (grant.permission() == Permission.WRITE) { + } else if (grant.permission() == AccessControlList.Permission.WRITE) { map.put("X-Amz-Grant-Write", value); - } else if (grant.permission() == Permission.READ_ACP) { + } else if (grant.permission() == AccessControlList.Permission.READ_ACP) { map.put("X-Amz-Grant-Read-Acp", value); - } else if (grant.permission() == Permission.WRITE_ACP) { + } else if (grant.permission() == AccessControlList.Permission.WRITE_ACP) { map.put("X-Amz-Grant-Write-Acp", value); - } else if (grant.permission() == Permission.FULL_CONTROL) { + } else if (grant.permission() == AccessControlList.Permission.FULL_CONTROL) { map.put("X-Amz-Grant-Full-Control", value); } } @@ -110,4 +113,11 @@ public Multimap grantAcl() { return map; } + + @Override + public String toString() { + return String.format( + "AccessControlPolicy(owner=%s, accessControlList=%s)", + Utils.stringify(owner), Utils.stringify(accessControlList)); + } } diff --git a/api/src/main/java/io/minio/messages/AccessControlTranslation.java b/api/src/main/java/io/minio/messages/AccessControlTranslation.java deleted file mode 100644 index 0461e05f0..000000000 --- a/api/src/main/java/io/minio/messages/AccessControlTranslation.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote access control translation information for {@link ReplicationDestination}. - */ -@Root(name = "AccessControlTranslation") -public class AccessControlTranslation { - @Element(name = "Owner") - private String owner = "Destination"; - - public AccessControlTranslation(@Nonnull @Element(name = "Owner") String owner) { - this.owner = Objects.requireNonNull(owner, "Owner must not be null"); - } - - public String owner() { - return this.owner; - } -} diff --git a/api/src/main/java/io/minio/messages/AndOperator.java b/api/src/main/java/io/minio/messages/AndOperator.java deleted file mode 100644 index 0e6774e2e..000000000 --- a/api/src/main/java/io/minio/messages/AndOperator.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.messages; - -import io.minio.Utils; -import java.util.Map; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.ElementMap; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; - -/** Helper class to denote AND operator information for {@link RuleFilter}. */ -@Root(name = "And") -public class AndOperator { - @Element(name = "Prefix", required = false) - @Convert(PrefixConverter.class) - private String prefix; - - @Element(name = "ObjectSizeLessThan", required = false) - private Long objectSizeLessThan; - - @Element(name = "ObjectSizeGreaterThan", required = false) - private Long objectSizeGreaterThan; - - @ElementMap( - attribute = false, - entry = "Tag", - inline = true, - key = "Key", - value = "Value", - required = false) - private Map tags; - - public AndOperator( - @Nullable @Element(name = "Prefix", required = false) String prefix, - @Nullable - @ElementMap( - attribute = false, - entry = "Tag", - inline = true, - key = "Key", - value = "Value", - required = false) - Map tags) { - if (prefix == null && tags == null) { - throw new IllegalArgumentException("At least Prefix or Tags must be set"); - } - - if (tags != null) { - for (String key : tags.keySet()) { - if (key.isEmpty()) { - throw new IllegalArgumentException("Tags must not contain empty key"); - } - } - } - - this.prefix = prefix; - this.tags = Utils.unmodifiableMap(tags); - } - - public AndOperator( - @Nullable @Element(name = "Prefix", required = false) String prefix, - @Nullable - @ElementMap( - attribute = false, - entry = "Tag", - inline = true, - key = "Key", - value = "Value", - required = false) - Map tags, - @Nullable @Element(name = "ObjectSizeLessThan", required = false) Long objectSizeLessThan, - @Nullable @Element(name = "ObjectSizeGreaterThan", required = false) - Long objectSizeGreaterThan) { - this(prefix, tags); - this.objectSizeLessThan = objectSizeLessThan; - this.objectSizeGreaterThan = objectSizeGreaterThan; - } - - public String prefix() { - return this.prefix; - } - - public Long objectSizeLessThan() { - return this.objectSizeLessThan; - } - - public Long objectSizeGreaterThan() { - return this.objectSizeGreaterThan; - } - - public Map tags() { - return this.tags; - } -} diff --git a/api/src/main/java/io/minio/messages/BasePartsResult.java b/api/src/main/java/io/minio/messages/BasePartsResult.java new file mode 100644 index 000000000..183260271 --- /dev/null +++ b/api/src/main/java/io/minio/messages/BasePartsResult.java @@ -0,0 +1,78 @@ +/* + * 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.messages; + +import io.minio.Utils; +import java.util.List; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Root; + +/** + * Base part information for {@link ListPartsResult} and {@link + * GetObjectAttributesOutput.ObjectParts}. + */ +@Root(name = "BasePartsResult", strict = false) +public abstract class BasePartsResult { + @Element(name = "IsTruncated", required = false) + private boolean isTruncated; + + @Element(name = "MaxParts", required = false) + private Integer maxParts; + + @Element(name = "NextPartNumberMarker", required = false) + private Integer nextPartNumberMarker; + + @Element(name = "PartNumberMarker", required = false) + private Integer partNumberMarker; + + @ElementList(name = "Part", inline = true, required = false) + private List parts; + + public BasePartsResult() {} + + public boolean isTruncated() { + return isTruncated; + } + + public Integer maxParts() { + return maxParts; + } + + public Integer nextPartNumberMarker() { + return nextPartNumberMarker; + } + + public Integer partNumberMarker() { + return partNumberMarker; + } + + public List parts() { + return Utils.unmodifiableList(parts); + } + + @Override + public String toString() { + return String.format( + "isTruncated=%s, maxParts=%s, nextPartNumberMarker=%s, partNumberMarker=%s, parts=%s", + Utils.stringify(isTruncated), + Utils.stringify(maxParts), + Utils.stringify(nextPartNumberMarker), + Utils.stringify(partNumberMarker), + Utils.stringify(parts)); + } +} diff --git a/api/src/main/java/io/minio/messages/SelectObjectContentRequestBase.java b/api/src/main/java/io/minio/messages/BaseSelectParameters.java similarity index 89% rename from api/src/main/java/io/minio/messages/SelectObjectContentRequestBase.java rename to api/src/main/java/io/minio/messages/BaseSelectParameters.java index 3bce8fc8b..436f86a4a 100644 --- a/api/src/main/java/io/minio/messages/SelectObjectContentRequestBase.java +++ b/api/src/main/java/io/minio/messages/BaseSelectParameters.java @@ -20,9 +20,11 @@ import javax.annotation.Nonnull; import org.simpleframework.xml.Element; -/** Base class for {@link SelectObjectContentRequest} and {@link SelectParameters}. */ +/** + * Base class for {@link SelectObjectContentRequest} and {@link RestoreRequest.SelectParameters}. + */ @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public abstract class SelectObjectContentRequestBase { +public abstract class BaseSelectParameters { @Element(name = "Expression") private String expression; @@ -35,7 +37,7 @@ public abstract class SelectObjectContentRequestBase { @Element(name = "OutputSerialization") private OutputSerialization outputSerialization; - public SelectObjectContentRequestBase( + public BaseSelectParameters( @Nonnull String expression, @Nonnull InputSerialization is, @Nonnull OutputSerialization os) { this.expression = Objects.requireNonNull(expression, "Expression must not be null"); this.inputSerialization = Objects.requireNonNull(is, "InputSerialization must not be null"); diff --git a/api/src/main/java/io/minio/messages/Bucket.java b/api/src/main/java/io/minio/messages/Bucket.java deleted file mode 100644 index bd4d150ec..000000000 --- a/api/src/main/java/io/minio/messages/Bucket.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2015 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.messages; - -import java.time.ZonedDateTime; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote bucket information for {@link ListAllMyBucketsResult}. */ -@Root(name = "Bucket", strict = false) -public class Bucket { - @Element(name = "Name") - private String name; - - @Element(name = "CreationDate") - private ResponseDate creationDate; - - @Element(name = "BucketRegion", required = false) - private String bucketRegion; - - public Bucket() {} - - /** Returns bucket name. */ - public String name() { - return name; - } - - /** Returns creation date. */ - public ZonedDateTime creationDate() { - return creationDate.zonedDateTime(); - } - - public String bucketRegion() { - return bucketRegion; - } -} diff --git a/api/src/main/java/io/minio/messages/BucketMetadata.java b/api/src/main/java/io/minio/messages/BucketMetadata.java deleted file mode 100644 index 91be4e8de..000000000 --- a/api/src/main/java/io/minio/messages/BucketMetadata.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.messages; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** Helper class to denote bucket information for {@link EventMetadata}. */ -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( - value = "UwF", - justification = "Everything in this class is initialized by JSON unmarshalling.") -public class BucketMetadata { - @JsonProperty private String name; - @JsonProperty private Identity ownerIdentity; - @JsonProperty private String arn; - - public String name() { - return name; - } - - public String owner() { - if (ownerIdentity == null) { - return null; - } - - return ownerIdentity.principalId(); - } - - public String arn() { - return arn; - } -} diff --git a/api/src/main/java/io/minio/messages/CORSConfiguration.java b/api/src/main/java/io/minio/messages/CORSConfiguration.java index aedc3d253..e674ce039 100644 --- a/api/src/main/java/io/minio/messages/CORSConfiguration.java +++ b/api/src/main/java/io/minio/messages/CORSConfiguration.java @@ -25,7 +25,7 @@ import org.simpleframework.xml.Root; /** - * Object representation of request/response XML of PutBucketCors * API and GetBucketCors @@ -46,6 +46,12 @@ public List rules() { return Utils.unmodifiableList(rules); } + @Override + public String toString() { + return String.format("CORSConfiguration{rules=%s}", Utils.stringify(rules)); + } + + /** Object represents CORS rule of {@link CORSConfiguration}. */ public static class CORSRule { @ElementList(entry = "AllowedHeader", inline = true, required = false) private List allowedHeaders; @@ -107,5 +113,18 @@ public String id() { public Integer maxAgeSeconds() { return maxAgeSeconds; } + + @Override + public String toString() { + return String.format( + "CORSRule{allowedHeaders=%s, allowedMethods=%s, allowedOrigins=%s, exposeHeaders=%s, " + + "id=%s, maxAgeSeconds=%s}", + Utils.stringify(allowedHeaders), + Utils.stringify(allowedMethods), + Utils.stringify(allowedOrigins), + Utils.stringify(exposeHeaders), + Utils.stringify(id), + Utils.stringify(maxAgeSeconds)); + } } } diff --git a/api/src/main/java/io/minio/messages/CannedAcl.java b/api/src/main/java/io/minio/messages/CannedAcl.java deleted file mode 100644 index dc067d34a..000000000 --- a/api/src/main/java/io/minio/messages/CannedAcl.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import com.fasterxml.jackson.annotation.JsonCreator; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -/** CannedAcl representing retrieval cannedAcl value. */ -@Root(name = "CannedAcl") -@Convert(CannedAcl.CannedAclConverter.class) -public enum CannedAcl { - PRIVATE("private"), - PUBLIC_READ("public-read"), - PUBLIC_READ_WRITE("public-read-write"), - AUTHENTICATED_READ("authenticated-read"), - AWS_EXEC_READ("aws-exec-read"), - BUCKET_OWNER_READ("bucket-owner-read"), - BUCKET_OWNER_FULL_CONTROL("bucket-owner-full-control"); - - private final String value; - - private CannedAcl(String value) { - this.value = value; - } - - public String toString() { - return this.value; - } - - /** Returns CannedAcl of given string. */ - @JsonCreator - public static CannedAcl fromString(String cannedAclString) { - for (CannedAcl cannedAcl : CannedAcl.values()) { - if (cannedAclString.equals(cannedAcl.value)) { - return cannedAcl; - } - } - - throw new IllegalArgumentException("Unknown canned ACL '" + cannedAclString + "'"); - } - - /** XML converter class. */ - public static class CannedAclConverter implements Converter { - @Override - public CannedAcl read(InputNode node) throws Exception { - return CannedAcl.fromString(node.getValue()); - } - - @Override - public void write(OutputNode node, CannedAcl cannedAcl) throws Exception { - node.setValue(cannedAcl.toString()); - } - } -} diff --git a/api/src/main/java/io/minio/messages/Checksum.java b/api/src/main/java/io/minio/messages/Checksum.java index 2263fa138..2c965ad2c 100644 --- a/api/src/main/java/io/minio/messages/Checksum.java +++ b/api/src/main/java/io/minio/messages/Checksum.java @@ -16,13 +16,12 @@ package io.minio.messages; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import java.util.Locale; +import io.minio.Http; +import io.minio.Utils; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; -/** Helper class for. */ +/** Object checksum information. */ @Root(name = "Checksum", strict = false) public class Checksum { @Element(name = "ChecksumCRC32", required = false) @@ -45,6 +44,30 @@ public class Checksum { public Checksum() {} + public Checksum(Checksum checksum) { + this.checksumCRC32 = checksum.checksumCRC32; + this.checksumCRC32C = checksum.checksumCRC32C; + this.checksumCRC64NVME = checksum.checksumCRC64NVME; + this.checksumSHA1 = checksum.checksumSHA1; + this.checksumSHA256 = checksum.checksumSHA256; + this.checksumType = checksum.checksumType; + } + + public Checksum( + String checksumCRC32, + String checksumCRC32C, + String checksumCRC64NVME, + String checksumSHA1, + String checksumSHA256, + String checksumType) { + this.checksumCRC32 = checksumCRC32; + this.checksumCRC32C = checksumCRC32C; + this.checksumCRC64NVME = checksumCRC64NVME; + this.checksumSHA1 = checksumSHA1; + this.checksumSHA256 = checksumSHA256; + this.checksumType = checksumType; + } + public String checksumCRC32() { return checksumCRC32; } @@ -69,20 +92,36 @@ public String checksumType() { return checksumType; } - private void addHeader(Multimap map, String algorithm, String value) { - if (value != null || !value.isEmpty()) { - map.put("x-amz-checksum-algorithm", algorithm); - map.put("x-amz-checksum-algorithm-" + algorithm.toLowerCase(Locale.US), value); - } + private void addHeader(Http.Headers headers, String algorithm, String value) { + if (value == null || value.isEmpty()) return; + headers.put("x-amz-checksum-algorithm-" + algorithm, value); + headers.put("x-amz-checksum-algorithm", algorithm); + } + + public Http.Headers headers() { + Http.Headers headers = new Http.Headers(); + addHeader(headers, "crc32", checksumCRC32); + addHeader(headers, "crc32c", checksumCRC32C); + addHeader(headers, "crc64nvme", checksumCRC64NVME); + addHeader(headers, "sha1", checksumSHA1); + addHeader(headers, "sha256", checksumSHA256); + return headers; + } + + protected String stringify() { + return String.format( + "checksumCRC32=%s, checksumCRC32C=%s, checksumCRC64NVME=%s, checksumSHA1=%s," + + " checksumSHA256=%s, checksumType=%s", + Utils.stringify(checksumCRC32), + Utils.stringify(checksumCRC32C), + Utils.stringify(checksumCRC64NVME), + Utils.stringify(checksumSHA1), + Utils.stringify(checksumSHA256), + Utils.stringify(checksumType)); } - public Multimap headers() { - Multimap map = HashMultimap.create(); - addHeader(map, "CRC32", checksumCRC32); - addHeader(map, "CRC32C", checksumCRC32C); - addHeader(map, "CRC64NVME", checksumCRC64NVME); - addHeader(map, "SHA1", checksumSHA1); - addHeader(map, "SHA256", checksumSHA256); - return map; + @Override + public String toString() { + return String.format("Checksum{%s}", stringify()); } } diff --git a/api/src/main/java/io/minio/messages/CloudFunctionConfiguration.java b/api/src/main/java/io/minio/messages/CloudFunctionConfiguration.java deleted file mode 100644 index 013a28a2b..000000000 --- a/api/src/main/java/io/minio/messages/CloudFunctionConfiguration.java +++ /dev/null @@ -1,41 +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 - * - * 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote CloudFunction configuration of {@link NotificationConfiguration}. */ -@Root(name = "CloudFunctionConfiguration", strict = false) -public class CloudFunctionConfiguration extends NotificationCommonConfiguration { - @Element(name = "CloudFunction") - private String cloudFunction; - - public CloudFunctionConfiguration() { - super(); - } - - /** Returns cloudFunction. */ - public String cloudFunction() { - return cloudFunction; - } - - /** Sets cloudFunction. */ - public void setCloudFunction(String cloudFunction) { - this.cloudFunction = cloudFunction; - } -} diff --git a/api/src/main/java/io/minio/messages/CompleteMultipartUpload.java b/api/src/main/java/io/minio/messages/CompleteMultipartUpload.java index 94a9b91dc..1d069b690 100644 --- a/api/src/main/java/io/minio/messages/CompleteMultipartUpload.java +++ b/api/src/main/java/io/minio/messages/CompleteMultipartUpload.java @@ -26,7 +26,7 @@ import org.simpleframework.xml.Root; /** - * Object representation of request XML of CompleteMultipartUpload * API. */ @@ -35,13 +35,13 @@ @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") public class CompleteMultipartUpload { @ElementList(name = "Part", inline = true) - private List partList; + private List parts; /** Constucts a new CompleteMultipartUpload object with given parts. */ public CompleteMultipartUpload(@Nonnull Part[] parts) throws IllegalArgumentException { if (Objects.requireNonNull(parts, "parts must not be null").length == 0) { throw new IllegalArgumentException("parts cannot be empty"); } - this.partList = Utils.unmodifiableList(Arrays.asList(parts)); + this.parts = Utils.unmodifiableList(Arrays.asList(parts)); } } diff --git a/api/src/main/java/io/minio/messages/CompleteMultipartUploadOutput.java b/api/src/main/java/io/minio/messages/CompleteMultipartUploadOutput.java deleted file mode 100644 index 78d504a79..000000000 --- a/api/src/main/java/io/minio/messages/CompleteMultipartUploadOutput.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2015 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.messages; - -import org.simpleframework.xml.Namespace; -import org.simpleframework.xml.Root; - -/** - * Object representation of response XML of CompleteMultipartUpload - * API. - */ -@Root(name = "CompleteMultipartUploadOutput") -@Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -public class CompleteMultipartUploadOutput extends CompleteMultipartUploadResult {} diff --git a/api/src/main/java/io/minio/messages/CompleteMultipartUploadResult.java b/api/src/main/java/io/minio/messages/CompleteMultipartUploadResult.java index fed1a0901..c194054d8 100644 --- a/api/src/main/java/io/minio/messages/CompleteMultipartUploadResult.java +++ b/api/src/main/java/io/minio/messages/CompleteMultipartUploadResult.java @@ -16,18 +16,19 @@ package io.minio.messages; +import io.minio.Utils; import org.simpleframework.xml.Element; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; /** - * Object representation of response XML of CompleteMultipartUpload * API. */ @Root(name = "CompleteMultipartUploadResult") @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -public class CompleteMultipartUploadResult { +public class CompleteMultipartUploadResult extends Checksum { @Element(name = "Location") private String location; @@ -40,24 +41,6 @@ public class CompleteMultipartUploadResult { @Element(name = "ETag") private String etag; - @Element(name = "ChecksumCRC32", required = false) - private String checksumCRC32; - - @Element(name = "ChecksumCRC32C", required = false) - private String checksumCRC32C; - - @Element(name = "ChecksumCRC64NVME", required = false) - private String checksumCRC64NVME; - - @Element(name = "ChecksumSHA1", required = false) - private String checksumSHA1; - - @Element(name = "ChecksumSHA256", required = false) - private String checksumSHA256; - - @Element(name = "ChecksumType", required = false) - private String checksumType; - public CompleteMultipartUploadResult() {} public String location() { @@ -76,27 +59,14 @@ 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; + @Override + public String toString() { + return String.format( + "CompleteMultipartUploadResult{location=%s, bucket=%s, object=%s, etag=%s, %s}", + Utils.stringify(location), + Utils.stringify(bucket), + Utils.stringify(object), + Utils.stringify(etag), + super.stringify()); } } diff --git a/api/src/main/java/io/minio/messages/CompressionType.java b/api/src/main/java/io/minio/messages/CompressionType.java deleted file mode 100644 index 7b6424cfc..000000000 --- a/api/src/main/java/io/minio/messages/CompressionType.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -/** CSV/JSON object's compression format for select object content. */ -public enum CompressionType { - NONE, - GZIP, - BZIP2; -} diff --git a/api/src/main/java/io/minio/messages/Contents.java b/api/src/main/java/io/minio/messages/Contents.java index 873e800e7..fce2c0978 100644 --- a/api/src/main/java/io/minio/messages/Contents.java +++ b/api/src/main/java/io/minio/messages/Contents.java @@ -19,8 +19,7 @@ import org.simpleframework.xml.Root; /** - * Helper class to denote Object information in {@link ListBucketResultV1} and {@link - * ListBucketResultV2} + * Object represents Object information in {@link ListBucketResultV1} and {@link ListBucketResultV2} */ @Root(name = "Contents", strict = false) public class Contents extends Item { @@ -31,4 +30,9 @@ public Contents() { public Contents(String prefix) { super(prefix); } + + @Override + public String toString() { + return String.format("Contents{%s}", super.toString()); + } } diff --git a/api/src/main/java/io/minio/messages/CopyObjectResult.java b/api/src/main/java/io/minio/messages/CopyObjectResult.java index 628484b9a..3e9d86c32 100644 --- a/api/src/main/java/io/minio/messages/CopyObjectResult.java +++ b/api/src/main/java/io/minio/messages/CopyObjectResult.java @@ -17,41 +17,25 @@ package io.minio.messages; +import io.minio.Time; +import io.minio.Utils; import java.time.ZonedDateTime; import org.simpleframework.xml.Element; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; /** - * Object representation of response XML of CopyObject API. */ @Root(name = "CopyObjectResult", strict = false) @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -public class CopyObjectResult { +public class CopyObjectResult extends Checksum { @Element(name = "ETag") private String etag; @Element(name = "LastModified") - private ResponseDate lastModified; - - @Element(name = "ChecksumType", required = false) - private String checksumType; - - @Element(name = "ChecksumCRC32", required = false) - private String checksumCRC32; - - @Element(name = "ChecksumCRC32C", required = false) - private String checksumCRC32C; - - @Element(name = "ChecksumCRC64NVME", required = false) - private String checksumCRC64NVME; - - @Element(name = "ChecksumSHA1", required = false) - private String checksumSHA1; - - @Element(name = "ChecksumSHA256", required = false) - private String checksumSHA256; + private Time.S3Time lastModified; public CopyObjectResult() {} @@ -62,30 +46,17 @@ public String etag() { /** Returns last modified time. */ public ZonedDateTime lastModified() { - return lastModified.zonedDateTime(); - } - - public String checksumType() { - return checksumType; - } - - public String checksumCRC32() { - return checksumCRC32; - } - - public String checksumCRC32C() { - return checksumCRC32C; - } - - public String checksumCRC64NVME() { - return checksumCRC64NVME; + return lastModified == null ? null : lastModified.toZonedDateTime(); } - public String checksumSHA1() { - return checksumSHA1; + protected String stringify() { + return String.format( + "etag=%s, lastModified=%s, %s", + Utils.stringify(etag), Utils.stringify(lastModified), super.stringify()); } - public String checksumSHA256() { - return checksumSHA256; + @Override + public String toString() { + return String.format("CopyObjectResult{%s}", stringify()); } } diff --git a/api/src/main/java/io/minio/messages/CopyPartResult.java b/api/src/main/java/io/minio/messages/CopyPartResult.java index 6c6ad3c80..3943f3ac7 100644 --- a/api/src/main/java/io/minio/messages/CopyPartResult.java +++ b/api/src/main/java/io/minio/messages/CopyPartResult.java @@ -21,10 +21,15 @@ import org.simpleframework.xml.Root; /** - * Object representation of response XML of UploadPartCopy * API. */ @Root(name = "CopyPartResult", strict = false) @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -public class CopyPartResult extends CopyObjectResult {} +public class CopyPartResult extends CopyObjectResult { + @Override + public String toString() { + return String.format("CopyPartResult{%s}", super.stringify()); + } +} diff --git a/api/src/main/java/io/minio/messages/CreateBucketConfiguration.java b/api/src/main/java/io/minio/messages/CreateBucketConfiguration.java index 415498b5c..634d8ba50 100644 --- a/api/src/main/java/io/minio/messages/CreateBucketConfiguration.java +++ b/api/src/main/java/io/minio/messages/CreateBucketConfiguration.java @@ -21,7 +21,7 @@ import org.simpleframework.xml.Root; /** - * Object representation of response XML of CreateBucket * API. */ @@ -49,6 +49,7 @@ public CreateBucketConfiguration(String locationConstraint, Location location, B this.bucket = bucket; } + /** Object represents bucket location information of {@link CreateBucketConfiguration}. */ @Root(name = "Location", strict = false) @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") public static class Location { @@ -64,6 +65,7 @@ public Location(String name, String type) { } } + /** Object represents bucket properties of {@link CreateBucketConfiguration}. */ @Root(name = "Bucket", strict = false) @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") public static class Bucket { diff --git a/api/src/main/java/io/minio/messages/CsvInputSerialization.java b/api/src/main/java/io/minio/messages/CsvInputSerialization.java deleted file mode 100644 index 843bf57c8..000000000 --- a/api/src/main/java/io/minio/messages/CsvInputSerialization.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote CSV input serialization request XML as per {@link - * SelectObjectContentRequest}. - */ -@Root(name = "CSV") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class CsvInputSerialization { - @Element(name = "AllowQuotedRecordDelimiter", required = false) - private boolean allowQuotedRecordDelimiter; - - @Element(name = "Comments", required = false) - private Character comments; - - @Element(name = "FieldDelimiter", required = false) - private Character fieldDelimiter; - - @Element(name = "FileHeaderInfo", required = false) - private FileHeaderInfo fileHeaderInfo; - - @Element(name = "QuoteCharacter", required = false) - private Character quoteCharacter; - - @Element(name = "QuoteEscapeCharacter", required = false) - private Character quoteEscapeCharacter; - - @Element(name = "RecordDelimiter", required = false) - private Character recordDelimiter; - - /** Constructs a new CsvInputSerialization object. */ - public CsvInputSerialization( - boolean allowQuotedRecordDelimiter, - Character comments, - Character fieldDelimiter, - FileHeaderInfo fileHeaderInfo, - Character quoteCharacter, - Character quoteEscapeCharacter, - Character recordDelimiter) { - this.allowQuotedRecordDelimiter = allowQuotedRecordDelimiter; - this.comments = comments; - this.fieldDelimiter = fieldDelimiter; - this.fileHeaderInfo = fileHeaderInfo; - this.quoteCharacter = quoteCharacter; - this.quoteEscapeCharacter = quoteEscapeCharacter; - this.recordDelimiter = recordDelimiter; - } -} diff --git a/api/src/main/java/io/minio/messages/CsvOutputSerialization.java b/api/src/main/java/io/minio/messages/CsvOutputSerialization.java deleted file mode 100644 index 40a46a3bc..000000000 --- a/api/src/main/java/io/minio/messages/CsvOutputSerialization.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote CSV output serialization request XML as per {@link - * SelectObjectContentRequest}. - */ -@Root(name = "CSV") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class CsvOutputSerialization { - @Element(name = "FieldDelimiter", required = false) - private Character fieldDelimiter; - - @Element(name = "QuoteCharacter", required = false) - private Character quoteCharacter; - - @Element(name = "QuoteEscapeCharacter", required = false) - private Character quoteEscapeCharacter; - - @Element(name = "QuoteFields", required = false) - private QuoteFields quoteFields; - - @Element(name = "RecordDelimiter", required = false) - private Character recordDelimiter; - - /** Constructs a new CsvOutputSerialization object. */ - public CsvOutputSerialization( - Character fieldDelimiter, - Character quoteCharacter, - Character quoteEscapeCharacter, - QuoteFields quoteFields, - Character recordDelimiter) { - this.fieldDelimiter = fieldDelimiter; - this.quoteCharacter = quoteCharacter; - this.quoteEscapeCharacter = quoteEscapeCharacter; - this.quoteFields = quoteFields; - this.recordDelimiter = recordDelimiter; - } -} diff --git a/api/src/main/java/io/minio/messages/DeleteError.java b/api/src/main/java/io/minio/messages/DeleteError.java deleted file mode 100644 index c4c9a51ba..000000000 --- a/api/src/main/java/io/minio/messages/DeleteError.java +++ /dev/null @@ -1,27 +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 - * - * 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.messages; - -import org.simpleframework.xml.Namespace; -import org.simpleframework.xml.Root; - -/** Helper class to denote error for {@link DeleteResult}. */ -@Root(name = "Error", strict = false) -@Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -public class DeleteError extends ErrorResponse { - private static final long serialVersionUID = 1905162041950251407L; // fix SE_BAD_FIELD -} diff --git a/api/src/main/java/io/minio/messages/DeleteMarker.java b/api/src/main/java/io/minio/messages/DeleteMarker.java deleted file mode 100644 index 0e95b74ed..000000000 --- a/api/src/main/java/io/minio/messages/DeleteMarker.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.messages; - -import org.simpleframework.xml.Root; - -/** Helper class to denote delete marker information in {@link ListVersionsResult}. */ -@Root(name = "DeleteMarker", strict = false) -public class DeleteMarker extends Item { - public DeleteMarker() { - super(); - } - - public DeleteMarker(String prefix) { - super(prefix); - } -} diff --git a/api/src/main/java/io/minio/messages/DeleteMarkerReplication.java b/api/src/main/java/io/minio/messages/DeleteMarkerReplication.java deleted file mode 100644 index 9141fc2fa..000000000 --- a/api/src/main/java/io/minio/messages/DeleteMarkerReplication.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.messages; - -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote delete marker replication information for {@link ReplicationRule}. */ -@Root(name = "DeleteMarkerReplication") -public class DeleteMarkerReplication { - @Element(name = "Status", required = false) - private Status status; - - /** Constructs new server-side encryption configuration rule. */ - public DeleteMarkerReplication( - @Nullable @Element(name = "Status", required = false) Status status) { - this.status = (status == null) ? Status.DISABLED : status; - } - - public Status status() { - return status; - } -} diff --git a/api/src/main/java/io/minio/messages/DeleteObject.java b/api/src/main/java/io/minio/messages/DeleteObject.java deleted file mode 100644 index 136a9a6b1..000000000 --- a/api/src/main/java/io/minio/messages/DeleteObject.java +++ /dev/null @@ -1,95 +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 - * - * 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.messages; - -import io.minio.Time; -import java.time.ZonedDateTime; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -/** Helper class to denote Object information for {@link DeleteRequest}. */ -@Root(name = "Object") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class DeleteObject { - @Element(name = "Key") - private String name; - - @Element(name = "VersionId", required = false) - private String versionId; - - @Element(name = "ETag", required = false) - private String etag; - - @Element(name = "LastModifiedTime", required = false) - private HttpHeaderDate lastModifiedTime; - - @Element(name = "Size", required = false) - private Long size; - - public DeleteObject(String name) { - this.name = name; - } - - public DeleteObject(String name, String versionId) { - this(name); - this.versionId = versionId; - } - - public DeleteObject( - String name, String versionId, String etag, ZonedDateTime lastModifiedTime, Long size) { - this(name, versionId); - this.etag = etag; - this.lastModifiedTime = lastModifiedTime == null ? null : new HttpHeaderDate(lastModifiedTime); - this.size = size; - } - - /** HTTP header date wrapping {@link ZonedDateTime}. */ - @Root - @Convert(HttpHeaderDate.HttpHeaderDateConverter.class) - public static class HttpHeaderDate { - private ZonedDateTime zonedDateTime; - - public HttpHeaderDate(ZonedDateTime zonedDateTime) { - this.zonedDateTime = zonedDateTime; - } - - public String toString() { - return zonedDateTime.format(Time.HTTP_HEADER_DATE_FORMAT); - } - - public static HttpHeaderDate fromString(String dateString) { - return new HttpHeaderDate(ZonedDateTime.parse(dateString, Time.HTTP_HEADER_DATE_FORMAT)); - } - - /** XML converter class. */ - public static class HttpHeaderDateConverter implements Converter { - @Override - public HttpHeaderDate read(InputNode node) throws Exception { - return HttpHeaderDate.fromString(node.getValue()); - } - - @Override - public void write(OutputNode node, HttpHeaderDate date) { - node.setValue(date.toString()); - } - } - } -} diff --git a/api/src/main/java/io/minio/messages/DeleteReplication.java b/api/src/main/java/io/minio/messages/DeleteReplication.java deleted file mode 100644 index c24ce7437..000000000 --- a/api/src/main/java/io/minio/messages/DeleteReplication.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.messages; - -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote delete replication information for {@link ReplicationRule}. This is MinIO - * specific extension. - */ -@Root(name = "DeleteReplication") -public class DeleteReplication { - @Element(name = "Status", required = false) - private Status status; - - public DeleteReplication(@Nullable @Element(name = "Status", required = false) Status status) { - this.status = (status == null) ? Status.DISABLED : status; - } - - public Status status() { - return status; - } -} diff --git a/api/src/main/java/io/minio/messages/DeleteRequest.java b/api/src/main/java/io/minio/messages/DeleteRequest.java index eef72d46a..2f1baf921 100644 --- a/api/src/main/java/io/minio/messages/DeleteRequest.java +++ b/api/src/main/java/io/minio/messages/DeleteRequest.java @@ -16,7 +16,9 @@ package io.minio.messages; +import io.minio.Time; import io.minio.Utils; +import java.time.ZonedDateTime; import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; @@ -24,9 +26,13 @@ import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; +import org.simpleframework.xml.convert.Convert; +import org.simpleframework.xml.convert.Converter; +import org.simpleframework.xml.stream.InputNode; +import org.simpleframework.xml.stream.OutputNode; /** - * Object representation of request XML of DeleteObjects * API. */ @@ -38,12 +44,82 @@ public class DeleteRequest { private boolean quiet; @ElementList(name = "Object", inline = true) - private List objectList; + private List objects; /** Constructs new delete request for given object list and quiet flag. */ - public DeleteRequest(@Nonnull List objectList, boolean quiet) { - this.objectList = - Utils.unmodifiableList(Objects.requireNonNull(objectList, "Object list must not be null")); + public DeleteRequest(@Nonnull List objects, boolean quiet) { + this.objects = + Utils.unmodifiableList(Objects.requireNonNull(objects, "Object list must not be null")); this.quiet = quiet; } + + /** Object represents object information of {@link DeleteRequest}. */ + @Root(name = "Object") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class Object { + @Element(name = "Key") + private String name; + + @Element(name = "VersionId", required = false) + private String versionId; + + @Element(name = "ETag", required = false) + private String etag; + + @Element(name = "LastModifiedTime", required = false) + private HttpHeaderDate lastModifiedTime; + + @Element(name = "Size", required = false) + private Long size; + + public Object(String name) { + this.name = name; + } + + public Object(String name, String versionId) { + this(name); + this.versionId = versionId; + } + + public Object( + String name, String versionId, String etag, ZonedDateTime lastModifiedTime, Long size) { + this(name, versionId); + this.etag = etag; + this.lastModifiedTime = + lastModifiedTime == null ? null : new HttpHeaderDate(lastModifiedTime); + this.size = size; + } + + /** HTTP header date wrapping {@link ZonedDateTime}. */ + @Root + @Convert(HttpHeaderDate.HttpHeaderDateConverter.class) + public static class HttpHeaderDate { + private ZonedDateTime zonedDateTime; + + public HttpHeaderDate(ZonedDateTime zonedDateTime) { + this.zonedDateTime = zonedDateTime; + } + + public String toString() { + return zonedDateTime.format(Time.HTTP_HEADER_DATE_FORMAT); + } + + public static HttpHeaderDate fromString(String dateString) { + return new HttpHeaderDate(ZonedDateTime.parse(dateString, Time.HTTP_HEADER_DATE_FORMAT)); + } + + /** XML converter class of {@link DeleteRequest.Object.HttpHeaderDate}. */ + public static class HttpHeaderDateConverter implements Converter { + @Override + public HttpHeaderDate read(InputNode node) throws Exception { + return HttpHeaderDate.fromString(node.getValue()); + } + + @Override + public void write(OutputNode node, HttpHeaderDate date) { + node.setValue(date.toString()); + } + } + } + } } diff --git a/api/src/main/java/io/minio/messages/DeleteResult.java b/api/src/main/java/io/minio/messages/DeleteResult.java index c5c1a04e1..64d42ddfe 100644 --- a/api/src/main/java/io/minio/messages/DeleteResult.java +++ b/api/src/main/java/io/minio/messages/DeleteResult.java @@ -19,12 +19,13 @@ import io.minio.Utils; import java.util.LinkedList; import java.util.List; +import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; /** - * Object representation of response XML of DeleteObjects * API. */ @@ -32,26 +33,88 @@ @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") public class DeleteResult { @ElementList(name = "Deleted", inline = true, required = false) - private List objectList; + private List objects; @ElementList(name = "Error", inline = true, required = false) - private List errorList; + private List errors; public DeleteResult() {} /** Constructs new delete result with an error. */ - public DeleteResult(DeleteError error) { - this.errorList = new LinkedList(); - this.errorList.add(error); + public DeleteResult(Error error) { + this.errors = new LinkedList(); + this.errors.add(error); } /** Returns deleted object list. */ - public List objectList() { - return Utils.unmodifiableList(objectList); + public List objects() { + return Utils.unmodifiableList(objects); } /** Returns delete error list. */ - public List errorList() { - return Utils.unmodifiableList(errorList); + public List errors() { + return Utils.unmodifiableList(errors); + } + + @Override + public String toString() { + return String.format( + "DeleteResult{objects=%s, errors=%s}", Utils.stringify(objects), Utils.stringify(errors)); + } + + /** Object represents deleted object of {@link DeletedResult}. */ + @Root(name = "Deleted", strict = false) + public static class Deleted { + @Element(name = "Key") + private String name; + + @Element(name = "VersionId", required = false) + private String versionId; + + @Element(name = "DeleteMarker", required = false) + private boolean deleteMarker; + + @Element(name = "DeleteMarkerVersionId", required = false) + private String deleteMarkerVersionId; + + public Deleted() {} + + public String name() { + return name; + } + + public String versionId() { + return versionId; + } + + public boolean deleteMarker() { + return deleteMarker; + } + + public String deleteMarkerVersionId() { + return deleteMarkerVersionId; + } + + @Override + public String toString() { + return String.format( + "Deleted{name=%s, versionId=%s, deleteMarker=%s, deleteMarkerVersionId=%s}", + Utils.stringify(name), + Utils.stringify(versionId), + Utils.stringify(deleteMarker), + Utils.stringify(deleteMarkerVersionId)); + } + } + + /** Object represents error information of {@link DeletedResult}. */ + @Root(name = "Error", strict = false) + @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") + public static class Error extends ErrorResponse { + private static final long serialVersionUID = 1905162041950251407L; // fix SE_BAD_FIELD + + @Override + public String toString() { + return String.format("Error{%s}", super.stringify()); + } } } diff --git a/api/src/main/java/io/minio/messages/DeletedObject.java b/api/src/main/java/io/minio/messages/DeletedObject.java deleted file mode 100644 index 8338909e5..000000000 --- a/api/src/main/java/io/minio/messages/DeletedObject.java +++ /dev/null @@ -1,54 +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 - * - * 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote deleted object for {@link DeleteResult}. */ -@Root(name = "Deleted", strict = false) -public class DeletedObject { - @Element(name = "Key") - private String name; - - @Element(name = "VersionId", required = false) - private String versionId; - - @Element(name = "DeleteMarker", required = false) - private boolean deleteMarker; - - @Element(name = "DeleteMarkerVersionId", required = false) - private String deleteMarkerVersionId; - - public DeletedObject() {} - - public String name() { - return name; - } - - public String versionId() { - return versionId; - } - - public boolean deleteMarker() { - return deleteMarker; - } - - public String deleteMarkerVersionId() { - return deleteMarkerVersionId; - } -} diff --git a/api/src/main/java/io/minio/messages/Encryption.java b/api/src/main/java/io/minio/messages/Encryption.java deleted file mode 100644 index b37f34c9c..000000000 --- a/api/src/main/java/io/minio/messages/Encryption.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote encryption information of {@link S3OutputLocation}. */ -@Root(name = "Encryption") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class Encryption { - @Element(name = "EncryptionType") - private SseAlgorithm encryptionType; - - @Element(name = "KMSContext", required = false) - private String kmsContext; - - @Element(name = "KMSKeyId", required = false) - private String kmsKeyId; - - public Encryption( - @Nonnull SseAlgorithm encryptionType, - @Nullable String kmsContext, - @Nullable String kmsKeyId) { - this.encryptionType = - Objects.requireNonNull(encryptionType, "Encryption type must not be null"); - this.kmsContext = kmsContext; - this.kmsKeyId = kmsKeyId; - } -} diff --git a/api/src/main/java/io/minio/messages/EncryptionConfiguration.java b/api/src/main/java/io/minio/messages/EncryptionConfiguration.java deleted file mode 100644 index fb641c5a9..000000000 --- a/api/src/main/java/io/minio/messages/EncryptionConfiguration.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.messages; - -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote encryption configuration information for {@link ReplicationDestination}. - */ -@Root(name = "EncryptionConfiguration") -public class EncryptionConfiguration { - @Element(name = "ReplicaKmsKeyID", required = false) - private String replicaKmsKeyID; - - public EncryptionConfiguration( - @Nullable @Element(name = "ReplicaKmsKeyID", required = false) String replicaKmsKeyID) { - this.replicaKmsKeyID = replicaKmsKeyID; - } - - public String replicaKmsKeyID() { - return this.replicaKmsKeyID; - } -} diff --git a/api/src/main/java/io/minio/messages/ErrorResponse.java b/api/src/main/java/io/minio/messages/ErrorResponse.java index 5e45f2bfb..e5546acdb 100644 --- a/api/src/main/java/io/minio/messages/ErrorResponse.java +++ b/api/src/main/java/io/minio/messages/ErrorResponse.java @@ -16,12 +16,13 @@ package io.minio.messages; +import io.minio.Utils; import java.io.Serializable; import org.simpleframework.xml.Element; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; -/** Object representation of error response XML of any S3 REST APIs. */ +/** Object represents error response XML of any S3 REST APIs. */ @Root(name = "ErrorResponse", strict = false) @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") public class ErrorResponse implements Serializable { @@ -106,28 +107,21 @@ public String resource() { return resource; } - /** Returns string representation of this object. */ + protected String stringify() { + return String.format( + "code=%s, message=%s, bucketName=%s, objectName=%s, resource=%s," + + " requestId=%s, hostId=%s", + Utils.stringify(code), + Utils.stringify(message), + Utils.stringify(bucketName), + Utils.stringify(objectName), + Utils.stringify(resource), + Utils.stringify(requestId), + Utils.stringify(hostId)); + } + + @Override public String toString() { - return "ErrorResponse(code = " - + code - + ", " - + "message = " - + message - + ", " - + "bucketName = " - + bucketName - + ", " - + "objectName = " - + objectName - + ", " - + "resource = " - + resource - + ", " - + "requestId = " - + requestId - + ", " - + "hostId = " - + hostId - + ")"; + return String.format("ErrorResponse{%s}", stringify()); } } diff --git a/api/src/main/java/io/minio/messages/Event.java b/api/src/main/java/io/minio/messages/Event.java deleted file mode 100644 index 7e59e22aa..000000000 --- a/api/src/main/java/io/minio/messages/Event.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, - * (C) 2018 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.messages; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.minio.Utils; -import java.time.ZonedDateTime; -import java.util.Map; - -/** Helper class to denote single event record for {@link NotificationRecords}. */ -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( - value = "UuF", - justification = "eventVersion and eventSource are available for completeness") -public class Event { - @JsonProperty private String eventVersion; - @JsonProperty private String eventSource; - @JsonProperty private String awsRegion; - @JsonProperty private EventType eventName; - @JsonProperty private Identity userIdentity; - @JsonProperty private Map requestParameters; - @JsonProperty private Map responseElements; - @JsonProperty private EventMetadata s3; - @JsonProperty private Source source; - @JsonProperty private ResponseDate eventTime; - - public String region() { - return awsRegion; - } - - public ZonedDateTime eventTime() { - return eventTime.zonedDateTime(); - } - - public EventType eventType() { - return eventName; - } - - public String userId() { - if (userIdentity == null) { - return null; - } - - return userIdentity.principalId(); - } - - public Map requestParameters() { - return Utils.unmodifiableMap(requestParameters); - } - - public Map responseElements() { - return Utils.unmodifiableMap(responseElements); - } - - public String bucketName() { - if (s3 == null) { - return null; - } - - return s3.bucketName(); - } - - public String bucketOwner() { - if (s3 == null) { - return null; - } - - return s3.bucketOwner(); - } - - public String bucketArn() { - if (s3 == null) { - return null; - } - - return s3.bucketArn(); - } - - public String objectName() { - if (s3 == null) { - return null; - } - - return s3.objectName(); - } - - public long objectSize() { - if (s3 == null) { - return -1; - } - - return s3.objectSize(); - } - - public String etag() { - if (s3 == null) { - return null; - } - - return s3.etag(); - } - - public String objectVersionId() { - if (s3 == null) { - return null; - } - - return s3.objectVersionId(); - } - - public String sequencer() { - if (s3 == null) { - return null; - } - - return s3.sequencer(); - } - - public Map userMetadata() { - if (s3 == null) { - return null; - } - - return s3.userMetadata(); - } - - public String host() { - if (source == null) { - return null; - } - - return source.host(); - } - - public String port() { - if (source == null) { - return null; - } - - return source.port(); - } - - public String userAgent() { - if (source == null) { - return null; - } - - return source.userAgent(); - } -} diff --git a/api/src/main/java/io/minio/messages/EventMetadata.java b/api/src/main/java/io/minio/messages/EventMetadata.java deleted file mode 100644 index 35811ca05..000000000 --- a/api/src/main/java/io/minio/messages/EventMetadata.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.messages; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Map; - -/** Helper class to denote event metadata for {@link EventMetadata}. */ -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( - value = {"UwF", "UuF"}, - justification = - "Everything in this class is initialized by JSON unmarshalling " - + "and s3SchemaVersion/configurationId are available for completeness.") -public class EventMetadata { - @JsonProperty private String s3SchemaVersion; - @JsonProperty private String configurationId; - @JsonProperty private BucketMetadata bucket; - @JsonProperty private ObjectMetadata object; - - public String bucketName() { - if (bucket == null) { - return null; - } - - return bucket.name(); - } - - public String bucketOwner() { - if (bucket == null) { - return null; - } - - return bucket.owner(); - } - - public String bucketArn() { - if (bucket == null) { - return null; - } - - return bucket.arn(); - } - - public String objectName() { - if (object == null) { - return null; - } - - return object.key(); - } - - public long objectSize() { - if (object == null) { - return -1; - } - - return object.size(); - } - - public String etag() { - if (object == null) { - return null; - } - - return object.etag(); - } - - public String objectVersionId() { - if (object == null) { - return null; - } - - return object.versionId(); - } - - public String sequencer() { - if (object == null) { - return null; - } - - return object.sequencer(); - } - - public Map userMetadata() { - if (object == null) { - return null; - } - - return object.userMetadata(); - } -} diff --git a/api/src/main/java/io/minio/messages/EventType.java b/api/src/main/java/io/minio/messages/EventType.java index 1e7965c28..bf702293f 100644 --- a/api/src/main/java/io/minio/messages/EventType.java +++ b/api/src/main/java/io/minio/messages/EventType.java @@ -72,7 +72,7 @@ public static EventType fromString(String eventTypeString) { throw new IllegalArgumentException("unknown event '" + eventTypeString + "'"); } - /** XML converter class. */ + /** XML converter class of {@link EventType}. */ public static class EventTypeConverter implements Converter { @Override public EventType read(InputNode node) throws Exception { diff --git a/api/src/main/java/io/minio/messages/ExistingObjectReplication.java b/api/src/main/java/io/minio/messages/ExistingObjectReplication.java deleted file mode 100644 index d4b4aa01f..000000000 --- a/api/src/main/java/io/minio/messages/ExistingObjectReplication.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote existing object replication information for {@link ReplicationRule}. */ -@Root(name = "ExistingObjectReplication") -public class ExistingObjectReplication { - @Element(name = "Status") - private Status status; - - public ExistingObjectReplication(@Nonnull @Element(name = "Status") Status status) { - this.status = Objects.requireNonNull(status, "Status must not be null"); - } - - public Status status() { - return this.status; - } -} diff --git a/api/src/main/java/io/minio/messages/Expiration.java b/api/src/main/java/io/minio/messages/Expiration.java deleted file mode 100644 index b2fc13541..000000000 --- a/api/src/main/java/io/minio/messages/Expiration.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.messages; - -import java.time.ZonedDateTime; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote expiration information for {@link LifecycleRule}. */ -@Root(name = "Expiration") -public class Expiration extends DateDays { - @Element(name = "ExpiredObjectDeleteMarker", required = false) - private Boolean expiredObjectDeleteMarker; - - @Element(name = "ExpiredObjectAllVersions", required = false) - private Boolean expiredObjectAllVersions; // This is MinIO specific extension. - - public Expiration( - @Nullable @Element(name = "Date", required = false) ResponseDate date, - @Nullable @Element(name = "Days", required = false) Integer days, - @Nullable @Element(name = "ExpiredObjectDeleteMarker", required = false) - Boolean expiredObjectDeleteMarker) { - if (expiredObjectDeleteMarker != null) { - if (date != null || days != null) { - throw new IllegalArgumentException( - "ExpiredObjectDeleteMarker must not be provided along with Date and Days"); - } - } else if (date != null ^ days != null) { - this.date = date; - this.days = days; - } else { - throw new IllegalArgumentException("Only one of date or days must be set"); - } - - this.expiredObjectDeleteMarker = expiredObjectDeleteMarker; - } - - public Expiration(ZonedDateTime date, Integer days, Boolean expiredObjectDeleteMarker) { - this(date == null ? null : new ResponseDate(date), days, expiredObjectDeleteMarker); - } - - public Expiration( - @Nullable @Element(name = "Date", required = false) ResponseDate date, - @Nullable @Element(name = "Days", required = false) Integer days, - @Nullable @Element(name = "ExpiredObjectDeleteMarker", required = false) - Boolean expiredObjectDeleteMarker, - @Element(name = "ExpiredObjectAllVersions", required = false) - Boolean expiredObjectAllVersions) { - this(date, days, expiredObjectDeleteMarker); - this.expiredObjectAllVersions = expiredObjectAllVersions; - } - - public Boolean expiredObjectDeleteMarker() { - return expiredObjectDeleteMarker; - } - - /** This is MinIO specific extension. */ - public Boolean expiredObjectAllVersions() { - return expiredObjectAllVersions; - } -} diff --git a/api/src/main/java/io/minio/messages/FileHeaderInfo.java b/api/src/main/java/io/minio/messages/FileHeaderInfo.java deleted file mode 100644 index 46943f18f..000000000 --- a/api/src/main/java/io/minio/messages/FileHeaderInfo.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -/** Description the first line of input in CSV object. */ -public enum FileHeaderInfo { - USE, - IGNORE, - NONE; -} diff --git a/api/src/main/java/io/minio/messages/Filter.java b/api/src/main/java/io/minio/messages/Filter.java index dce52114c..ddd90a72b 100644 --- a/api/src/main/java/io/minio/messages/Filter.java +++ b/api/src/main/java/io/minio/messages/Filter.java @@ -1,5 +1,5 @@ /* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2017 MinIO, Inc. + * 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. @@ -17,56 +17,233 @@ package io.minio.messages; import io.minio.Utils; -import java.util.LinkedList; -import java.util.List; -import org.simpleframework.xml.ElementList; +import java.util.Map; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.ElementMap; import org.simpleframework.xml.Root; +import org.simpleframework.xml.convert.Convert; /** - * Helper class to denote Filter configuration of {@link CloudFunctionConfiguration}, {@link - * QueueConfiguration} or {@link TopicConfiguration}. + * Object represents filter information for {@link ReplicationConfiguration.Rule} and {@link + * LifecycleConfiguration.Rule}. */ -@Root(name = "Filter", strict = false) +@Root(name = "Filter") public class Filter { - @ElementList(name = "S3Key") - private List filterRuleList; - - public Filter() {} - - /** - * Sets filter rule to list. As per Amazon AWS S3 server behavior, its not possible to set more - * than one rule for "prefix" or "suffix". However the spec - * http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTnotification.html is not clear - * about this behavior. - */ - private void setRule(String name, String value) throws IllegalArgumentException { - if (value.length() > 1024) { - throw new IllegalArgumentException("value '" + value + "' is more than 1024 long"); - } + @Element(name = "And", required = false) + private And andOperator; - if (filterRuleList == null) { - filterRuleList = new LinkedList<>(); - } + @Element(name = "Prefix", required = false) + @Convert(PrefixConverter.class) + private String prefix; - for (FilterRule rule : filterRuleList) { - // Remove rule.name is same as given name. - if (rule.name().equals(name)) { - filterRuleList.remove(rule); - } + @Element(name = "Tag", required = false) + private Tag tag; + + @Element(name = "ObjectSizeLessThan", required = false) + private Long objectSizeLessThan; + + @Element(name = "ObjectSizeGreaterThan", required = false) + private Long objectSizeGreaterThan; + + public Filter( + @Nullable @Element(name = "And", required = false) And andOperator, + @Nullable @Element(name = "Prefix", required = false) String prefix, + @Nullable @Element(name = "Tag", required = false) Tag tag) { + if (andOperator != null ^ prefix != null ^ tag != null) { + this.andOperator = andOperator; + this.prefix = prefix; + this.tag = tag; + } else { + throw new IllegalArgumentException("Only one of And, Prefix or Tag must be set"); } + } + + public Filter( + @Nullable @Element(name = "And", required = false) And andOperator, + @Nullable @Element(name = "Prefix", required = false) String prefix, + @Nullable @Element(name = "Tag", required = false) Tag tag, + @Nullable @Element(name = "ObjectSizeLessThan", required = false) Long objectSizeLessThan, + @Nullable @Element(name = "ObjectSizeGreaterThan", required = false) + Long objectSizeGreaterThan) { + this(andOperator, prefix, tag); + this.objectSizeLessThan = objectSizeLessThan; + this.objectSizeGreaterThan = objectSizeGreaterThan; + } + + public Filter(@Nonnull And andOperator) { + this.andOperator = Objects.requireNonNull(andOperator, "And operator must not be null"); + } + + public Filter(@Nonnull String prefix) { + this.prefix = Objects.requireNonNull(prefix, "Prefix must not be null"); + } + + public Filter(@Nonnull Tag tag) { + this.tag = Objects.requireNonNull(tag, "Tag must not be null"); + } + + public And andOperator() { + return this.andOperator; + } + + public String prefix() { + return this.prefix; + } + + public Tag tag() { + return this.tag; + } - filterRuleList.add(new FilterRule(name, value)); + public Long objectSizeLessThan() { + return this.objectSizeLessThan; } - public void setPrefixRule(String value) throws IllegalArgumentException { - setRule("prefix", value); + public Long objectSizeGreaterThan() { + return this.objectSizeGreaterThan; } - public void setSuffixRule(String value) throws IllegalArgumentException { - setRule("suffix", value); + @Override + public String toString() { + return String.format( + "Filter{andOperator=%s, prefix=%s, tag=%s, objectSizeLessThan=%s," + + " objectSizeGreaterThan=%s}", + Utils.stringify(andOperator), + Utils.stringify(prefix), + Utils.stringify(tag), + Utils.stringify(objectSizeLessThan), + Utils.stringify(objectSizeGreaterThan)); } - public List filterRuleList() { - return Utils.unmodifiableList(filterRuleList); + /** Object represents AND operator of {@link Rule}. */ + @Root(name = "And") + public static class And { + @Element(name = "Prefix", required = false) + @Convert(PrefixConverter.class) + private String prefix; + + @Element(name = "ObjectSizeLessThan", required = false) + private Long objectSizeLessThan; + + @Element(name = "ObjectSizeGreaterThan", required = false) + private Long objectSizeGreaterThan; + + @ElementMap( + attribute = false, + entry = "Tag", + inline = true, + key = "Key", + value = "Value", + required = false) + private Map tags; + + public And( + @Nullable @Element(name = "Prefix", required = false) String prefix, + @Nullable + @ElementMap( + attribute = false, + entry = "Tag", + inline = true, + key = "Key", + value = "Value", + required = false) + Map tags) { + if (prefix == null && tags == null) { + throw new IllegalArgumentException("At least Prefix or Tags must be set"); + } + + if (tags != null) { + for (String key : tags.keySet()) { + if (key.isEmpty()) { + throw new IllegalArgumentException("Tags must not contain empty key"); + } + } + } + + this.prefix = prefix; + this.tags = Utils.unmodifiableMap(tags); + } + + public And( + @Nullable @Element(name = "Prefix", required = false) String prefix, + @Nullable + @ElementMap( + attribute = false, + entry = "Tag", + inline = true, + key = "Key", + value = "Value", + required = false) + Map tags, + @Nullable @Element(name = "ObjectSizeLessThan", required = false) Long objectSizeLessThan, + @Nullable @Element(name = "ObjectSizeGreaterThan", required = false) + Long objectSizeGreaterThan) { + this(prefix, tags); + this.objectSizeLessThan = objectSizeLessThan; + this.objectSizeGreaterThan = objectSizeGreaterThan; + } + + public String prefix() { + return this.prefix; + } + + public Long objectSizeLessThan() { + return this.objectSizeLessThan; + } + + public Long objectSizeGreaterThan() { + return this.objectSizeGreaterThan; + } + + public Map tags() { + return this.tags; + } + + @Override + public String toString() { + return String.format( + "And{prefix=%s, objectSizeLessThan=%s, objectSizeGreaterThan=%s, tags=%s}", + Utils.stringify(prefix), + Utils.stringify(objectSizeLessThan), + Utils.stringify(objectSizeGreaterThan), + Utils.stringify(tags)); + } + } + + /** Object represents Tag information of {@link Rule}. */ + @Root(name = "Tag") + public static class Tag { + @Element(name = "Key") + private String key; + + @Element(name = "Value") + private String value; + + public Tag( + @Nonnull @Element(name = "Key") String key, + @Nonnull @Element(name = "Value") String value) { + Objects.requireNonNull(key, "Key must not be null"); + if (key.isEmpty()) { + throw new IllegalArgumentException("Key must not be empty"); + } + + this.key = key; + this.value = Objects.requireNonNull(value, "Value must not be null"); + } + + public String key() { + return this.key; + } + + public String value() { + return this.value; + } + + @Override + public String toString() { + return String.format("Tag{key=%s, value=%s}", Utils.stringify(key), Utils.stringify(value)); + } } } diff --git a/api/src/main/java/io/minio/messages/FilterRule.java b/api/src/main/java/io/minio/messages/FilterRule.java deleted file mode 100644 index 4eef68a27..000000000 --- a/api/src/main/java/io/minio/messages/FilterRule.java +++ /dev/null @@ -1,50 +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 - * - * 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote FilterRule configuration of {@link CloudFunctionConfiguration}, {@link - * QueueConfiguration} or {@link TopicConfiguration}. - */ -@Root(name = "FilterRule", strict = false) -public class FilterRule { - @Element(name = "Name") - private String name; - - @Element(name = "Value") - private String value; - - public FilterRule() {} - - public FilterRule(String name, String value) { - this.name = name; - this.value = value; - } - - /** Returns filter name. */ - public String name() { - return name; - } - - /** Returns filter value. */ - public String value() { - return value; - } -} diff --git a/api/src/main/java/io/minio/messages/GetObjectAttributesOutput.java b/api/src/main/java/io/minio/messages/GetObjectAttributesOutput.java index f1b259763..b9eba3f04 100644 --- a/api/src/main/java/io/minio/messages/GetObjectAttributesOutput.java +++ b/api/src/main/java/io/minio/messages/GetObjectAttributesOutput.java @@ -18,14 +18,12 @@ import io.minio.Utils; import java.time.ZonedDateTime; -import java.util.List; import org.simpleframework.xml.Element; -import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; /** - * Object representation of response XML of GetObjectAttributes * API. */ @@ -97,50 +95,39 @@ public Long objectSize() { return objectSize; } - @Root(name = "ObjectParts", strict = false) - public static class ObjectParts { - @Element(name = "IsTruncated", required = false) - private boolean isTruncated; - - @Element(name = "MaxParts", required = false) - private Integer maxParts; - - @Element(name = "NextPartNumberMarker", required = false) - private Integer nextPartNumberMarker; - - @Element(name = "PartNumberMarker", required = false) - private Integer partNumberMarker; - - @ElementList(name = "Part", inline = true, required = false) - private List parts; + @Override + public String toString() { + return String.format( + "GetObjectAttributesOutput{etag=%s, checksum=%s, objectParts=%s, storageClass=%s," + + " objectSize=%s, deleteMarker=%s, lastModified=%s, versionId=%s}", + Utils.stringify(etag), + Utils.stringify(checksum), + Utils.stringify(objectParts), + Utils.stringify(storageClass), + Utils.stringify(objectSize), + Utils.stringify(deleteMarker), + Utils.stringify(lastModified), + Utils.stringify(versionId)); + } + /** Object represents object part information of {@link GetObjectAttributesOutput}. */ + @Root(name = "ObjectParts", strict = false) + public static class ObjectParts extends BasePartsResult { @Element(name = "PartsCount", required = false) private Integer partsCount; - public ObjectParts() {} - - public boolean isTruncated() { - return isTruncated; - } - - public Integer maxParts() { - return maxParts; - } - - public Integer nextPartNumberMarker() { - return nextPartNumberMarker; - } - - public Integer partNumberMarker() { - return partNumberMarker; - } - - public List parts() { - return Utils.unmodifiableList(parts); + public ObjectParts() { + super(); } public Integer partsCount() { return partsCount; } + + @Override + public String toString() { + return String.format( + "ObjectParts{partsCount=%s, %s}", Utils.stringify(partsCount), super.toString()); + } } } diff --git a/api/src/main/java/io/minio/messages/GlacierJobParameters.java b/api/src/main/java/io/minio/messages/GlacierJobParameters.java deleted file mode 100644 index 6e894e7b3..000000000 --- a/api/src/main/java/io/minio/messages/GlacierJobParameters.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote S3 Glacier job parameters of {@link RestoreRequest}. */ -@Root(name = "GlacierJobParameters") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class GlacierJobParameters { - @Element(name = "Tier") - private Tier tier; - - public GlacierJobParameters(@Nonnull Tier tier) { - this.tier = Objects.requireNonNull(tier, "Tier must not be null"); - } -} diff --git a/api/src/main/java/io/minio/messages/Grant.java b/api/src/main/java/io/minio/messages/Grant.java deleted file mode 100644 index 940240447..000000000 --- a/api/src/main/java/io/minio/messages/Grant.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote grant information of {@link AccessControlList}. */ -@Root(name = "Grant") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class Grant { - @Element(name = "Grantee", required = false) - private Grantee grantee; - - @Element(name = "Permission", required = false) - private Permission permission; - - public Grant( - @Nullable @Element(name = "Grantee", required = false) Grantee grantee, - @Nullable @Element(name = "Permission", required = false) Permission permission) { - if (grantee == null && permission == null) { - throw new IllegalArgumentException("Either Grantee or Permission must be provided"); - } - this.grantee = grantee; - this.permission = permission; - } - - public Grantee grantee() { - return grantee; - } - - public Permission permission() { - return permission; - } - - public String granteeUri() { - return grantee == null ? null : grantee.uri(); - } - - public String granteeId() { - return grantee == null ? null : grantee.id(); - } -} diff --git a/api/src/main/java/io/minio/messages/Grantee.java b/api/src/main/java/io/minio/messages/Grantee.java deleted file mode 100644 index 42b17107d..000000000 --- a/api/src/main/java/io/minio/messages/Grantee.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.simpleframework.xml.Attribute; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Namespace; -import org.simpleframework.xml.Root; - -/** Helper class to denote for the person being granted permissions of {@link Grant}. */ -@Root(name = "Grantee") -@Namespace(prefix = "xsi", reference = "http://www.w3.org/2001/XMLSchema-instance") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class Grantee { - @Attribute(name = "type") - private String xsiType; - - @Element(name = "DisplayName", required = false) - private String displayName; - - @Element(name = "EmailAddress", required = false) - private String emailAddress; - - @Element(name = "ID", required = false) - private String id; - - @Element(name = "Type") - private GranteeType type; - - @Element(name = "URI", required = false) - private String uri; - - public Grantee( - @Nonnull GranteeType type, - @Nullable String displayName, - @Nullable String emailAddress, - @Nullable String id, - @Nullable String uri) { - this.type = Objects.requireNonNull(type, "Type must not be null"); - this.displayName = displayName; - this.emailAddress = emailAddress; - this.id = id; - this.uri = uri; - } - - public Grantee( - @Nonnull @Attribute(name = "type") String xsiType, - @Nonnull @Element(name = "Type") GranteeType type, - @Nullable @Element(name = "DisplayName", required = false) String displayName, - @Nullable @Element(name = "EmailAddress", required = false) String emailAddress, - @Nullable @Element(name = "ID", required = false) String id, - @Nullable @Element(name = "URI", required = false) String uri) { - this(type, displayName, emailAddress, id, uri); - this.xsiType = xsiType; - } - - public String displayName() { - return displayName; - } - - public String emailAddress() { - return emailAddress; - } - - public String id() { - return id; - } - - public GranteeType type() { - return type; - } - - public String uri() { - return uri; - } -} diff --git a/api/src/main/java/io/minio/messages/GranteeType.java b/api/src/main/java/io/minio/messages/GranteeType.java deleted file mode 100644 index 74311e2ea..000000000 --- a/api/src/main/java/io/minio/messages/GranteeType.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -/** GranteeType represents type of grantee. */ -@Root(name = "Type") -@Convert(GranteeType.GranteeTypeConverter.class) -public enum GranteeType { - CANONICAL_USER("CanonicalUser"), - AMAZON_CUSTOMER_BY_EMAIL("AmazonCustomerByEmail"), - GROUP("Group"); - - private final String value; - - private GranteeType(String value) { - this.value = value; - } - - public String toString() { - return this.value; - } - - /** Returns GranteeType of given string. */ - public static GranteeType fromString(String granteeTypeString) { - for (GranteeType granteeType : GranteeType.values()) { - if (granteeTypeString.equals(granteeType.value)) { - return granteeType; - } - } - - throw new IllegalArgumentException("Unknown grantee type '" + granteeTypeString + "'"); - } - - /** XML converter class. */ - public static class GranteeTypeConverter implements Converter { - @Override - public GranteeType read(InputNode node) throws Exception { - return GranteeType.fromString(node.getValue()); - } - - @Override - public void write(OutputNode node, GranteeType granteeType) throws Exception { - node.setValue(granteeType.toString()); - } - } -} diff --git a/api/src/main/java/io/minio/messages/Identity.java b/api/src/main/java/io/minio/messages/Identity.java deleted file mode 100644 index 615f47343..000000000 --- a/api/src/main/java/io/minio/messages/Identity.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.messages; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** Helper class to denote user or owner identity for {@link Event} and {@link BucketMetadata}. */ -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( - value = "UwF", - justification = "Everything in this class is initialized by JSON unmarshalling.") -public class Identity { - @JsonProperty private String principalId; - - public String principalId() { - return principalId; - } -} diff --git a/api/src/main/java/io/minio/messages/InitiateMultipartUploadResult.java b/api/src/main/java/io/minio/messages/InitiateMultipartUploadResult.java index b8a3c1519..2b6a53100 100644 --- a/api/src/main/java/io/minio/messages/InitiateMultipartUploadResult.java +++ b/api/src/main/java/io/minio/messages/InitiateMultipartUploadResult.java @@ -16,12 +16,13 @@ package io.minio.messages; +import io.minio.Utils; import org.simpleframework.xml.Element; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; /** - * Object representation of response XML of CreateMultipartUpload * API. */ @@ -53,4 +54,11 @@ public String objectName() { public String uploadId() { return uploadId; } + + @Override + public String toString() { + return String.format( + "InitiateMultipartUploadResult{bucketName=%s, objectName=%s, uploadId=%s}", + Utils.stringify(bucketName), Utils.stringify(objectName), Utils.stringify(uploadId)); + } } diff --git a/api/src/main/java/io/minio/messages/Initiator.java b/api/src/main/java/io/minio/messages/Initiator.java index 84091c91b..9bf7cf04e 100644 --- a/api/src/main/java/io/minio/messages/Initiator.java +++ b/api/src/main/java/io/minio/messages/Initiator.java @@ -16,12 +16,13 @@ package io.minio.messages; +import io.minio.Utils; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; /** - * Helper class to denote Initator information of a multipart upload and used in {@link - * ListMultipartUploadsResult} and {@link ListPartsResult}. + * Object represents initiator information of {@link ListMultipartUploadsResult} and {@link + * ListPartsResult}. */ @Root(name = "Initiator", strict = false) public class Initiator { @@ -42,4 +43,10 @@ public String id() { public String displayName() { return displayName; } + + @Override + public String toString() { + return String.format( + "Initiator{id=%s, displayName=%s}", Utils.stringify(id), Utils.stringify(displayName)); + } } diff --git a/api/src/main/java/io/minio/messages/InputSerialization.java b/api/src/main/java/io/minio/messages/InputSerialization.java index a0e4e9003..8d2241705 100644 --- a/api/src/main/java/io/minio/messages/InputSerialization.java +++ b/api/src/main/java/io/minio/messages/InputSerialization.java @@ -19,7 +19,7 @@ import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; -/** Helper class to denote Input Serialization information of {@link SelectObjectContentRequest}. */ +/** Object represents Input Serialization information of {@link SelectObjectContentRequest}. */ @Root(name = "InputSerialization") @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") public class InputSerialization { @@ -27,16 +27,23 @@ public class InputSerialization { private CompressionType compressionType; @Element(name = "CSV", required = false) - private CsvInputSerialization csv; + private CSV csv; @Element(name = "JSON", required = false) - private JsonInputSerialization json; + private JSON json; @Element(name = "Parquet", required = false) - private ParquetInputSerialization parquet; + private Parquet parquet; + + private InputSerialization(CompressionType compressionType, CSV csv, JSON json, Parquet parquet) { + this.compressionType = compressionType; + this.csv = csv; + this.json = json; + this.parquet = parquet; + } /** Constructs a new InputSerialization object with CSV. */ - public InputSerialization( + public static InputSerialization newCSV( CompressionType compressionType, boolean allowQuotedRecordDelimiter, Character comments, @@ -45,26 +52,106 @@ public InputSerialization( Character quoteCharacter, Character quoteEscapeCharacter, Character recordDelimiter) { - this.compressionType = compressionType; - this.csv = - new CsvInputSerialization( + return new InputSerialization( + compressionType, + new CSV( allowQuotedRecordDelimiter, comments, fieldDelimiter, fileHeaderInfo, quoteCharacter, quoteEscapeCharacter, - recordDelimiter); + recordDelimiter), + null, + null); } /** Constructs a new InputSerialization object with JSON. */ - public InputSerialization(CompressionType compressionType, JsonType type) { - this.compressionType = compressionType; - this.json = new JsonInputSerialization(type); + public static InputSerialization newJSON(CompressionType compressionType, JsonType type) { + return new InputSerialization(compressionType, null, new JSON(type), null); } /** Constructs a new InputSerialization object with Parquet. */ - public InputSerialization() { - this.parquet = new ParquetInputSerialization(); + public static InputSerialization newParquet() { + return new InputSerialization(null, null, null, new Parquet()); + } + + /** Object represents CSV input serialization information of {@link InputSerialization}. */ + @Root(name = "CSV") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class CSV { + @Element(name = "AllowQuotedRecordDelimiter", required = false) + private boolean allowQuotedRecordDelimiter; + + @Element(name = "Comments", required = false) + private Character comments; + + @Element(name = "FieldDelimiter", required = false) + private Character fieldDelimiter; + + @Element(name = "FileHeaderInfo", required = false) + private FileHeaderInfo fileHeaderInfo; + + @Element(name = "QuoteCharacter", required = false) + private Character quoteCharacter; + + @Element(name = "QuoteEscapeCharacter", required = false) + private Character quoteEscapeCharacter; + + @Element(name = "RecordDelimiter", required = false) + private Character recordDelimiter; + + public CSV( + boolean allowQuotedRecordDelimiter, + Character comments, + Character fieldDelimiter, + FileHeaderInfo fileHeaderInfo, + Character quoteCharacter, + Character quoteEscapeCharacter, + Character recordDelimiter) { + this.allowQuotedRecordDelimiter = allowQuotedRecordDelimiter; + this.comments = comments; + this.fieldDelimiter = fieldDelimiter; + this.fileHeaderInfo = fileHeaderInfo; + this.quoteCharacter = quoteCharacter; + this.quoteEscapeCharacter = quoteEscapeCharacter; + this.recordDelimiter = recordDelimiter; + } + } + + /** Object represents JSON input serialization information of {@link InputSerialization}. */ + @Root(name = "JSON") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class JSON { + @Element(name = "Type", required = false) + private JsonType type; + + public JSON(JsonType type) { + this.type = type; + } + } + + /** Object represents Parquet input serialization information of {@link InputSerialization}. */ + @Root(name = "Parquet") + public static class Parquet {} + + /** Compression format of CSV and JSON input serialization. */ + public enum CompressionType { + NONE, + GZIP, + BZIP2; + } + + /** First line description of CSV object. */ + public enum FileHeaderInfo { + USE, + IGNORE, + NONE; + } + + /** JSON object typee. */ + public enum JsonType { + DOCUMENT, + LINES; } } diff --git a/api/src/main/java/io/minio/messages/Item.java b/api/src/main/java/io/minio/messages/Item.java index e13573cd6..b4613d044 100644 --- a/api/src/main/java/io/minio/messages/Item.java +++ b/api/src/main/java/io/minio/messages/Item.java @@ -16,17 +16,25 @@ package io.minio.messages; +import io.minio.Time; import io.minio.Utils; import java.time.ZonedDateTime; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import javax.annotation.Nonnull; import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Root; +import org.simpleframework.xml.convert.Convert; +import org.simpleframework.xml.convert.Converter; +import org.simpleframework.xml.stream.InputNode; +import org.simpleframework.xml.stream.OutputNode; /** - * Helper class to denote Object information in {@link ListBucketResultV1}, {@link - * ListBucketResultV2} and {@link ListVersionsResult}. + * Object represents Object information in {@link ListBucketResultV1}, {@link ListBucketResultV2} + * and {@link ListVersionsResult}. */ public abstract class Item { @Element(name = "ETag", required = false) @@ -36,7 +44,7 @@ public abstract class Item { private String objectName; @Element(name = "LastModified") - private ResponseDate lastModified; + private Time.S3Time lastModified; @Element(name = "Owner", required = false) private Owner owner; @@ -54,7 +62,7 @@ public abstract class Item { private String versionId; // except ListObjects V1 @Element(name = "UserMetadata", required = false) - private Metadata userMetadata; + private UserMetadata userMetadata; @Element(name = "UserTags", required = false) private String userTags; @@ -90,7 +98,7 @@ public String objectName() { /** Returns last modified time of the object. */ public ZonedDateTime lastModified() { - return (lastModified == null) ? null : lastModified.zonedDateTime(); + return lastModified == null ? null : lastModified.toZonedDateTime(); } /** Returns ETag of the object. */ @@ -139,7 +147,7 @@ public boolean isDir() { /** Returns whether this item is a delete marker or not. */ public boolean isDeleteMarker() { - return this instanceof DeleteMarker; + return this instanceof ListVersionsResult.DeleteMarker; } public List checksumAlgorithm() { @@ -154,17 +162,41 @@ public RestoreStatus restoreStatus() { return restoreStatus; } + @Override + public String toString() { + return String.format( + "etag=%s, objectName=%s, lastModified=%s, owner=%s, size=%s, storageClass=%s, isLatest=%s, " + + "versionId=%s, userMetadata=%s, userTags=%s, checksumAlgorithm=%s, checksumType=%s, " + + "restoreStatus=%s, isDir=%s, encodingType=%s", + Utils.stringify(etag), + Utils.stringify(objectName), + Utils.stringify(lastModified), + Utils.stringify(owner), + Utils.stringify(size), + Utils.stringify(storageClass), + Utils.stringify(isLatest), + Utils.stringify(versionId), + Utils.stringify(userMetadata), + Utils.stringify(userTags), + Utils.stringify(checksumAlgorithm), + Utils.stringify(checksumType), + Utils.stringify(restoreStatus), + Utils.stringify(isDir), + Utils.stringify(encodingType)); + } + + /** Object represents restore status of object information. */ @Root(name = "RestoreStatus", strict = false) public static class RestoreStatus { @Element(name = "IsRestoreInProgress", required = false) private Boolean isRestoreInProgress; @Element(name = "RestoreExpiryDate", required = false) - private ResponseDate restoreExpiryDate; + private Time.S3Time restoreExpiryDate; public RestoreStatus( @Element(name = "IsRestoreInProgress", required = false) Boolean isRestoreInProgress, - @Element(name = "RestoreExpiryDate", required = false) ResponseDate restoreExpiryDate) { + @Element(name = "RestoreExpiryDate", required = false) Time.S3Time restoreExpiryDate) { this.isRestoreInProgress = isRestoreInProgress; this.restoreExpiryDate = restoreExpiryDate; } @@ -174,7 +206,69 @@ public Boolean isRestoreInProgress() { } public ZonedDateTime restoreExpiryDate() { - return restoreExpiryDate == null ? null : restoreExpiryDate.zonedDateTime(); + return restoreExpiryDate == null ? null : restoreExpiryDate.toZonedDateTime(); + } + + @Override + public String toString() { + return String.format( + "RestoreStatus{isRestoreInProgress=%s, restoreExpiryDate=%s}", + Utils.stringify(isRestoreInProgress), Utils.stringify(restoreExpiryDate)); + } + } + + /** Object represents user metadata of object information. */ + @Root(name = "UserMetadata") + @Convert(UserMetadata.UserMetadataConverter.class) + public static class UserMetadata { + Map map; + + public UserMetadata() {} + + public UserMetadata(@Nonnull Map map) { + this.map = + Utils.unmodifiableMap(Objects.requireNonNull(map, "User metadata must not be null")); + } + + public Map get() { + return map; + } + + @Override + public String toString() { + return String.format("UserMetadata{%s}", Utils.stringify(map)); + } + + /** XML converter class user metadata of object information. */ + public static class UserMetadataConverter implements Converter { + @Override + public UserMetadata read(InputNode node) throws Exception { + Map map = new HashMap<>(); + while (true) { + InputNode childNode = node.getNext(); + if (childNode == null) { + break; + } + + map.put(childNode.getName(), childNode.getValue()); + } + + if (map.size() > 0) { + return new UserMetadata(map); + } + + return null; + } + + @Override + public void write(OutputNode node, UserMetadata metadata) throws Exception { + for (Map.Entry entry : metadata.get().entrySet()) { + OutputNode childNode = node.getChild(entry.getKey()); + childNode.setValue(entry.getValue()); + } + + node.commit(); + } } } } diff --git a/api/src/main/java/io/minio/messages/JsonInputSerialization.java b/api/src/main/java/io/minio/messages/JsonInputSerialization.java deleted file mode 100644 index 21e7b52dd..000000000 --- a/api/src/main/java/io/minio/messages/JsonInputSerialization.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote JSON input serialization request XML as per {@link - * SelectObjectContentRequest}. - */ -@Root(name = "JSON") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class JsonInputSerialization { - @Element(name = "Type", required = false) - private JsonType type; - - /** Constructs a new JsonInputSerialization object. */ - public JsonInputSerialization(JsonType type) { - this.type = type; - } -} diff --git a/api/src/main/java/io/minio/messages/JsonOutputSerialization.java b/api/src/main/java/io/minio/messages/JsonOutputSerialization.java deleted file mode 100644 index 5fbc2b294..000000000 --- a/api/src/main/java/io/minio/messages/JsonOutputSerialization.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote JSON output serialization request XML as per {@link - * SelectObjectContentRequest}. - */ -@Root(name = "JSON") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class JsonOutputSerialization { - @Element(name = "RecordDelimiter", required = false) - private Character recordDelimiter; - - /** Constructs a new JsonOutputSerialization object. */ - public JsonOutputSerialization(Character recordDelimiter) { - this.recordDelimiter = recordDelimiter; - } -} diff --git a/api/src/main/java/io/minio/messages/JsonType.java b/api/src/main/java/io/minio/messages/JsonType.java deleted file mode 100644 index d1e79865e..000000000 --- a/api/src/main/java/io/minio/messages/JsonType.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -/** The type of JSON. */ -public enum JsonType { - DOCUMENT, - LINES; -} diff --git a/api/src/main/java/io/minio/messages/LegalHold.java b/api/src/main/java/io/minio/messages/LegalHold.java index 913c8e213..58b9f81d3 100644 --- a/api/src/main/java/io/minio/messages/LegalHold.java +++ b/api/src/main/java/io/minio/messages/LegalHold.java @@ -16,12 +16,13 @@ package io.minio.messages; +import io.minio.Utils; import org.simpleframework.xml.Element; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; /** - * Object representation of request XML of PutObjectLegalHold * API and response XML of GetObjectLegalHold @@ -48,4 +49,9 @@ public LegalHold(boolean status) { public boolean status() { return status != null && status.equals("ON"); } + + @Override + public String toString() { + return String.format("LegalHold{status=%s}", Utils.stringify(status)); + } } diff --git a/api/src/main/java/io/minio/messages/LifecycleConfiguration.java b/api/src/main/java/io/minio/messages/LifecycleConfiguration.java index 72659cb0f..dd67fc8de 100644 --- a/api/src/main/java/io/minio/messages/LifecycleConfiguration.java +++ b/api/src/main/java/io/minio/messages/LifecycleConfiguration.java @@ -16,16 +16,20 @@ package io.minio.messages; +import io.minio.Time; import io.minio.Utils; +import java.time.ZonedDateTime; import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; /** - * Object representation of request XML of PutBucketLifecycleConfiguration * API and response XML of GetBucketLifecycleConfiguration @@ -35,18 +39,356 @@ @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") public class LifecycleConfiguration { @ElementList(name = "Rule", inline = true) - private List rules; + private List rules; /** Constructs new lifecycle configuration. */ public LifecycleConfiguration( - @Nonnull @ElementList(name = "Rule", inline = true) List rules) { - this.rules = Utils.unmodifiableList(Objects.requireNonNull(rules, "Rules must not be null")); - if (rules.isEmpty()) { + @Nonnull @ElementList(name = "Rule", inline = true) List rules) { + if (Objects.requireNonNull(rules, "Rules must not be null").isEmpty()) { throw new IllegalArgumentException("Rules must not be empty"); } + this.rules = Utils.unmodifiableList(rules); } - public List rules() { + public List rules() { return rules; } + + @Override + public String toString() { + return String.format("LifecycleConfiguration{rules=%s}", Utils.stringify(rules)); + } + + @Root(name = "Rule") + public static class Rule { + @Element(name = "AbortIncompleteMultipartUpload", required = false) + private AbortIncompleteMultipartUpload abortIncompleteMultipartUpload; + + @Element(name = "Expiration", required = false) + private Expiration expiration; + + @Element(name = "Filter", required = false) + private Filter filter; + + @Element(name = "ID", required = false) + private String id; + + @Element(name = "NoncurrentVersionExpiration", required = false) + private NoncurrentVersionExpiration noncurrentVersionExpiration; + + @Element(name = "NoncurrentVersionTransition", required = false) + private NoncurrentVersionTransition noncurrentVersionTransition; + + @Element(name = "Status") + private Status status; + + @Element(name = "Transition", required = false) + private Transition transition; + + /** Constructs new server-side encryption configuration rule. */ + public Rule( + @Nonnull @Element(name = "Status") Status status, + @Nullable @Element(name = "AbortIncompleteMultipartUpload", required = false) + AbortIncompleteMultipartUpload abortIncompleteMultipartUpload, + @Nullable @Element(name = "Expiration", required = false) Expiration expiration, + @Nonnull @Element(name = "Filter", required = false) Filter filter, + @Nullable @Element(name = "ID", required = false) String id, + @Nullable @Element(name = "NoncurrentVersionExpiration", required = false) + NoncurrentVersionExpiration noncurrentVersionExpiration, + @Nullable @Element(name = "NoncurrentVersionTransition", required = false) + NoncurrentVersionTransition noncurrentVersionTransition, + @Nullable @Element(name = "Transition", required = false) Transition transition) { + if (abortIncompleteMultipartUpload == null + && expiration == null + && noncurrentVersionExpiration == null + && noncurrentVersionTransition == null + && transition == null) { + throw new IllegalArgumentException( + "At least one of action (AbortIncompleteMultipartUpload, Expiration, " + + "NoncurrentVersionExpiration, NoncurrentVersionTransition or Transition) must be " + + "specified in a rule"); + } + + if (id != null) { + id = id.trim(); + if (id.isEmpty()) throw new IllegalArgumentException("ID must be non-empty string"); + if (id.length() > 255) + throw new IllegalArgumentException("ID must not exceed 255 characters"); + } + + this.abortIncompleteMultipartUpload = abortIncompleteMultipartUpload; + this.expiration = expiration; + this.filter = Objects.requireNonNull(filter, "Filter must not be null"); + this.id = id; + this.noncurrentVersionExpiration = noncurrentVersionExpiration; + this.noncurrentVersionTransition = noncurrentVersionTransition; + this.status = Objects.requireNonNull(status, "Status must not be null"); + this.transition = transition; + } + + public AbortIncompleteMultipartUpload abortIncompleteMultipartUpload() { + return abortIncompleteMultipartUpload; + } + + public Expiration expiration() { + return expiration; + } + + public Filter filter() { + return this.filter; + } + + public String id() { + return this.id; + } + + public NoncurrentVersionExpiration noncurrentVersionExpiration() { + return noncurrentVersionExpiration; + } + + public NoncurrentVersionTransition noncurrentVersionTransition() { + return noncurrentVersionTransition; + } + + public Status status() { + return this.status; + } + + public Transition transition() { + return transition; + } + + @Override + public String toString() { + return String.format( + "Rule{abortIncompleteMultipartUpload=%s, expiration=%s, filter=%s, id=%s," + + " noncurrentVersionExpiration=%s, noncurrentVersionTransition=%s, status=%s," + + " transition=%s}", + Utils.stringify(abortIncompleteMultipartUpload), + Utils.stringify(expiration), + Utils.stringify(filter), + Utils.stringify(id), + Utils.stringify(noncurrentVersionExpiration), + Utils.stringify(noncurrentVersionTransition), + Utils.stringify(status), + Utils.stringify(transition)); + } + } + + @Root(name = "AbortIncompleteMultipartUpload") + public static class AbortIncompleteMultipartUpload { + @Element(name = "DaysAfterInitiation") + private int daysAfterInitiation; + + public AbortIncompleteMultipartUpload( + @Element(name = "DaysAfterInitiation") int daysAfterInitiation) { + this.daysAfterInitiation = daysAfterInitiation; + } + + public int daysAfterInitiation() { + return daysAfterInitiation; + } + + @Override + public String toString() { + return String.format( + "AbortIncompleteMultipartUpload{daysAfterInitiation=%d}", daysAfterInitiation); + } + } + + public abstract static class DateDays { + @Element(name = "Date", required = false) + protected Time.S3Time date; + + @Element(name = "Days", required = false) + protected Integer days; + + public ZonedDateTime date() { + return date == null ? null : date.toZonedDateTime(); + } + + public Integer days() { + return days; + } + + @Override + public String toString() { + return String.format("date=%s, days=%s", Utils.stringify(date), Utils.stringify(days)); + } + } + + @Root(name = "Expiration") + public static class Expiration extends DateDays { + @Element(name = "ExpiredObjectDeleteMarker", required = false) + private Boolean expiredObjectDeleteMarker; + + @Element(name = "ExpiredObjectAllVersions", required = false) + private Boolean expiredObjectAllVersions; // This is MinIO specific extension. + + public Expiration( + @Nullable @Element(name = "Date", required = false) Time.S3Time date, + @Nullable @Element(name = "Days", required = false) Integer days, + @Nullable @Element(name = "ExpiredObjectDeleteMarker", required = false) + Boolean expiredObjectDeleteMarker) { + if (expiredObjectDeleteMarker != null) { + if (date != null || days != null) { + throw new IllegalArgumentException( + "ExpiredObjectDeleteMarker must not be provided along with Date and Days"); + } + } else if (date != null ^ days != null) { + this.date = date; + this.days = days; + } else { + throw new IllegalArgumentException("Only one of date or days must be set"); + } + + this.expiredObjectDeleteMarker = expiredObjectDeleteMarker; + } + + public Expiration(ZonedDateTime date, Integer days, Boolean expiredObjectDeleteMarker) { + this(date == null ? null : new Time.S3Time(date), days, expiredObjectDeleteMarker); + } + + public Expiration( + @Nullable @Element(name = "Date", required = false) Time.S3Time date, + @Nullable @Element(name = "Days", required = false) Integer days, + @Nullable @Element(name = "ExpiredObjectDeleteMarker", required = false) + Boolean expiredObjectDeleteMarker, + @Element(name = "ExpiredObjectAllVersions", required = false) + Boolean expiredObjectAllVersions) { + this(date, days, expiredObjectDeleteMarker); + this.expiredObjectAllVersions = expiredObjectAllVersions; + } + + public Boolean expiredObjectDeleteMarker() { + return expiredObjectDeleteMarker; + } + + public Boolean expiredObjectAllVersions() { + return expiredObjectAllVersions; + } + + @Override + public String toString() { + return String.format( + "Expiration{%s, expiredObjectDeleteMarker=%s, expiredObjectAllVersions=%s}", + super.toString(), + Utils.stringify(expiredObjectDeleteMarker), + Utils.stringify(expiredObjectAllVersions)); + } + } + + @Root(name = "NoncurrentVersionExpiration") + public static class NoncurrentVersionExpiration { + @Element(name = "NoncurrentDays") + private int noncurrentDays; + + @Element(name = "NewerNoncurrentVersions", required = false) + private Integer newerNoncurrentVersions; + + public NoncurrentVersionExpiration( + @Element(name = "NoncurrentDays", required = false) int noncurrentDays) { + this.noncurrentDays = noncurrentDays; + } + + public NoncurrentVersionExpiration( + @Element(name = "NoncurrentDays", required = false) int noncurrentDays, + @Element(name = "NewerNoncurrentVersions", required = false) + Integer newerNoncurrentVersions) { + this.noncurrentDays = noncurrentDays; + this.newerNoncurrentVersions = newerNoncurrentVersions; + } + + public int noncurrentDays() { + return noncurrentDays; + } + + public Integer newerNoncurrentVersions() { + return newerNoncurrentVersions; + } + + protected String stringify() { + return String.format( + "noncurrentDays=%d, newerNoncurrentVersions=%s", + noncurrentDays, Utils.stringify(newerNoncurrentVersions)); + } + + @Override + public String toString() { + return String.format("NoncurrentVersionExpiration{%s}", stringify()); + } + } + + @Root(name = "NoncurrentVersionTransition") + public static class NoncurrentVersionTransition extends NoncurrentVersionExpiration { + @Element(name = "StorageClass") + private String storageClass; + + public NoncurrentVersionTransition( + @Element(name = "NoncurrentDays", required = false) int noncurrentDays, + @Nonnull @Element(name = "StorageClass", required = false) String storageClass) { + super(noncurrentDays, null); + if (storageClass == null || storageClass.isEmpty()) { + throw new IllegalArgumentException("StorageClass must be provided"); + } + this.storageClass = storageClass; + } + + public NoncurrentVersionTransition( + @Element(name = "NoncurrentDays", required = false) int noncurrentDays, + @Element(name = "NewerNoncurrentVersions", required = false) + Integer newerNoncurrentVersions, + @Nonnull @Element(name = "StorageClass", required = false) String storageClass) { + super(noncurrentDays, newerNoncurrentVersions); + if (storageClass == null || storageClass.isEmpty()) { + throw new IllegalArgumentException("StorageClass must be provided"); + } + this.storageClass = storageClass; + } + + public String storageClass() { + return storageClass; + } + + @Override + public String toString() { + return String.format( + "NoncurrentVersionTransition{%s, storageClass=%s}", super.stringify(), storageClass); + } + } + + @Root(name = "Transition") + public static class Transition extends DateDays { + @Element(name = "StorageClass") + private String storageClass; + + public Transition( + @Nullable @Element(name = "Date", required = false) Time.S3Time date, + @Nullable @Element(name = "Days", required = false) Integer days, + @Nonnull @Element(name = "StorageClass", required = false) String storageClass) { + if (date != null ^ days != null) { + this.date = date; + this.days = days; + } else { + throw new IllegalArgumentException("Only one of date or days must be set"); + } + if (storageClass == null || storageClass.isEmpty()) { + throw new IllegalArgumentException("StorageClass must be provided"); + } + this.storageClass = storageClass; + } + + public Transition(ZonedDateTime date, Integer days, String storageClass) { + this(date == null ? null : new Time.S3Time(date), days, storageClass); + } + + public String storageClass() { + return storageClass; + } + + @Override + public String toString() { + return String.format("Transition{%s, storageClass=%s}", super.toString(), storageClass); + } + } } diff --git a/api/src/main/java/io/minio/messages/LifecycleRule.java b/api/src/main/java/io/minio/messages/LifecycleRule.java deleted file mode 100644 index ab338ca3a..000000000 --- a/api/src/main/java/io/minio/messages/LifecycleRule.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote Rule information for {@link LifecycleConfiguration}. */ -@Root(name = "Rule") -public class LifecycleRule { - @Element(name = "AbortIncompleteMultipartUpload", required = false) - private AbortIncompleteMultipartUpload abortIncompleteMultipartUpload; - - @Element(name = "Expiration", required = false) - private Expiration expiration; - - @Element(name = "Filter", required = false) - private RuleFilter filter; - - @Element(name = "ID", required = false) - private String id; - - @Element(name = "NoncurrentVersionExpiration", required = false) - private NoncurrentVersionExpiration noncurrentVersionExpiration; - - @Element(name = "NoncurrentVersionTransition", required = false) - private NoncurrentVersionTransition noncurrentVersionTransition; - - @Element(name = "Status") - private Status status; - - @Element(name = "Transition", required = false) - private Transition transition; - - /** Constructs new server-side encryption configuration rule. */ - public LifecycleRule( - @Nonnull @Element(name = "Status") Status status, - @Nullable @Element(name = "AbortIncompleteMultipartUpload", required = false) - AbortIncompleteMultipartUpload abortIncompleteMultipartUpload, - @Nullable @Element(name = "Expiration", required = false) Expiration expiration, - @Nonnull @Element(name = "Filter", required = false) RuleFilter filter, - @Nullable @Element(name = "ID", required = false) String id, - @Nullable @Element(name = "NoncurrentVersionExpiration", required = false) - NoncurrentVersionExpiration noncurrentVersionExpiration, - @Nullable @Element(name = "NoncurrentVersionTransition", required = false) - NoncurrentVersionTransition noncurrentVersionTransition, - @Nullable @Element(name = "Transition", required = false) Transition transition) { - if (abortIncompleteMultipartUpload == null - && expiration == null - && noncurrentVersionExpiration == null - && noncurrentVersionTransition == null - && transition == null) { - throw new IllegalArgumentException( - "At least one of action (AbortIncompleteMultipartUpload, Expiration, " - + "NoncurrentVersionExpiration, NoncurrentVersionTransition or Transition) must be " - + "specified in a rule"); - } - - if (id != null) { - id = id.trim(); - if (id.isEmpty()) throw new IllegalArgumentException("ID must be non-empty string"); - if (id.length() > 255) throw new IllegalArgumentException("ID must be exceed 255 characters"); - } - - this.abortIncompleteMultipartUpload = abortIncompleteMultipartUpload; - this.expiration = expiration; - this.filter = Objects.requireNonNull(filter, "Filter must not be null"); - this.id = id; - this.noncurrentVersionExpiration = noncurrentVersionExpiration; - this.noncurrentVersionTransition = noncurrentVersionTransition; - this.status = Objects.requireNonNull(status, "Status must not be null"); - this.transition = transition; - } - - public AbortIncompleteMultipartUpload abortIncompleteMultipartUpload() { - return abortIncompleteMultipartUpload; - } - - public Expiration expiration() { - return expiration; - } - - public RuleFilter filter() { - return this.filter; - } - - public String id() { - return this.id; - } - - public NoncurrentVersionExpiration noncurrentVersionExpiration() { - return noncurrentVersionExpiration; - } - - public NoncurrentVersionTransition noncurrentVersionTransition() { - return noncurrentVersionTransition; - } - - public Status status() { - return this.status; - } - - public Transition transition() { - return transition; - } -} diff --git a/api/src/main/java/io/minio/messages/ListAllMyBucketsResult.java b/api/src/main/java/io/minio/messages/ListAllMyBucketsResult.java index f6909996f..8a40046d2 100644 --- a/api/src/main/java/io/minio/messages/ListAllMyBucketsResult.java +++ b/api/src/main/java/io/minio/messages/ListAllMyBucketsResult.java @@ -16,7 +16,9 @@ package io.minio.messages; +import io.minio.Time; import io.minio.Utils; +import java.time.ZonedDateTime; import java.util.List; import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; @@ -24,7 +26,7 @@ import org.simpleframework.xml.Root; /** - * Object representation of response XML of ListBuckets API. */ @Root(name = "ListAllMyBucketsResult", strict = false) @@ -61,4 +63,49 @@ public String prefix() { public String continuationToken() { return continuationToken; } + + @Override + public String toString() { + return String.format( + "ListAllMyBucketsResult{owner=%s, buckets=%s, prefix=%s, continuationToken=%s}", + Utils.stringify(owner), + Utils.stringify(buckets), + Utils.stringify(prefix), + Utils.stringify(continuationToken)); + } + + @Root(name = "Bucket", strict = false) + public static class Bucket { + @Element(name = "Name") + private String name; + + @Element(name = "CreationDate") + private Time.S3Time creationDate; + + @Element(name = "BucketRegion", required = false) + private String bucketRegion; + + public Bucket() {} + + /** Returns bucket name. */ + public String name() { + return name; + } + + /** Returns creation date. */ + public ZonedDateTime creationDate() { + return creationDate == null ? null : creationDate.toZonedDateTime(); + } + + public String bucketRegion() { + return bucketRegion; + } + + @Override + public String toString() { + return String.format( + "Bucket{name=%s, creationDate=%s, bucketRegion=%s}", + Utils.stringify(name), Utils.stringify(creationDate), Utils.stringify(bucketRegion)); + } + } } diff --git a/api/src/main/java/io/minio/messages/ListBucketResultV1.java b/api/src/main/java/io/minio/messages/ListBucketResultV1.java index a88bde5d8..a39c7c8ad 100644 --- a/api/src/main/java/io/minio/messages/ListBucketResultV1.java +++ b/api/src/main/java/io/minio/messages/ListBucketResultV1.java @@ -24,7 +24,7 @@ import org.simpleframework.xml.Root; /** - * Object representation of response XML of ListObjects API. */ @Root(name = "ListBucketResult", strict = false) @@ -51,4 +51,14 @@ public String nextMarker() { public List contents() { return Utils.unmodifiableList(contents); } + + @Override + public String toString() { + return String.format( + "ListBucketResultV1{%s, marker=%s, nextMarker=%s, contents=%s}", + super.toString(), + Utils.stringify(marker), + Utils.stringify(nextMarker), + Utils.stringify(contents)); + } } diff --git a/api/src/main/java/io/minio/messages/ListBucketResultV2.java b/api/src/main/java/io/minio/messages/ListBucketResultV2.java index 5d67d1ffd..85afe9329 100644 --- a/api/src/main/java/io/minio/messages/ListBucketResultV2.java +++ b/api/src/main/java/io/minio/messages/ListBucketResultV2.java @@ -24,7 +24,7 @@ import org.simpleframework.xml.Root; /** - * Object representation of response XML of ListObjectsV2 * API. */ @@ -71,4 +71,17 @@ public String nextContinuationToken() { public List contents() { return Utils.unmodifiableList(contents); } + + @Override + public String toString() { + return String.format( + "ListBucketResultV2{%s, keyCount=%s, startAfter=%s, continuationToken=%s," + + " nextContinuationToken=%s, contents=%s}", + super.toString(), + Utils.stringify(keyCount), + Utils.stringify(startAfter), + Utils.stringify(continuationToken), + Utils.stringify(nextContinuationToken), + Utils.stringify(contents)); + } } diff --git a/api/src/main/java/io/minio/messages/ListMultipartUploadsResult.java b/api/src/main/java/io/minio/messages/ListMultipartUploadsResult.java index 3cecf55f7..1e27eaeca 100644 --- a/api/src/main/java/io/minio/messages/ListMultipartUploadsResult.java +++ b/api/src/main/java/io/minio/messages/ListMultipartUploadsResult.java @@ -16,7 +16,9 @@ package io.minio.messages; +import io.minio.Time; import io.minio.Utils; +import java.time.ZonedDateTime; import java.util.List; import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; @@ -24,7 +26,7 @@ import org.simpleframework.xml.Root; /** - * Object representation of response XML of ListMultipartUploads * API. */ @@ -103,4 +105,122 @@ public String encodingType() { public List uploads() { return Utils.unmodifiableList(uploads); } + + @Override + public String toString() { + return String.format( + "ListMultipartUploadsResult{bucketName=%s, encodingType=%s, keyMarker=%s," + + " uploadIdMarker=%s, nextKeyMarker=%s, nextUploadIdMarker=%s, maxUploads=%s," + + " isTruncated=%s, uploads=%s}", + Utils.stringify(bucketName), + Utils.stringify(encodingType), + Utils.stringify(keyMarker), + Utils.stringify(uploadIdMarker), + Utils.stringify(nextKeyMarker), + Utils.stringify(nextUploadIdMarker), + Utils.stringify(maxUploads), + Utils.stringify(isTruncated), + Utils.stringify(uploads)); + } + + @Root(name = "Upload", strict = false) + @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") + public static class Upload { + @Element(name = "Key") + private String objectName; + + @Element(name = "UploadId") + private String uploadId; + + @Element(name = "Initiator") + private Initiator initiator; + + @Element(name = "Owner") + private Owner owner; + + @Element(name = "StorageClass") + private String storageClass; + + @Element(name = "Initiated") + private Time.S3Time initiated; + + @Element(name = "ChecksumAlgorithm", required = false) + private String checksumAlgorithm; + + @Element(name = "ChecksumType", required = false) + private String checksumType; + + private long aggregatedPartSize; + private String encodingType = null; + + public Upload() {} + + /** Returns object name. */ + public String objectName() { + return Utils.urlDecode(objectName, encodingType); + } + + /** Returns upload ID. */ + public String uploadId() { + return uploadId; + } + + /** Returns initiator information. */ + public Initiator initiator() { + return initiator; + } + + /** Returns owner information. */ + public Owner owner() { + return owner; + } + + /** Returns storage class. */ + public String storageClass() { + return storageClass; + } + + /** Returns initiated time. */ + public ZonedDateTime initiated() { + return initiated == null ? null : initiated.toZonedDateTime(); + } + + /** Returns aggregated part size. */ + public long aggregatedPartSize() { + return aggregatedPartSize; + } + + /** Sets given aggregated part size. */ + public void setAggregatedPartSize(long size) { + this.aggregatedPartSize = size; + } + + public void setEncodingType(String encodingType) { + this.encodingType = encodingType; + } + + public String checksumAlgorithm() { + return checksumAlgorithm; + } + + public String checksumType() { + return checksumType; + } + + @Override + public String toString() { + return String.format( + "Upload{objectName=%s, uploadId=%s, initiator=%s, owner=%s, storageClass=%s, " + + "initiated=%s, checksumAlgorithm=%s, checksumType=%s, aggregatedPartSize=%s}", + Utils.stringify(objectName), + Utils.stringify(uploadId), + Utils.stringify(initiator), + Utils.stringify(owner), + Utils.stringify(storageClass), + Utils.stringify(initiated), + Utils.stringify(checksumAlgorithm), + Utils.stringify(checksumType), + Utils.stringify(aggregatedPartSize)); + } + } } diff --git a/api/src/main/java/io/minio/messages/ListObjectsResult.java b/api/src/main/java/io/minio/messages/ListObjectsResult.java index 235001676..a57074998 100644 --- a/api/src/main/java/io/minio/messages/ListObjectsResult.java +++ b/api/src/main/java/io/minio/messages/ListObjectsResult.java @@ -20,6 +20,7 @@ import java.util.List; import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Root; /** * Base class of {@link ListBucketResultV1}, {@link ListBucketResultV2} and {@link @@ -47,7 +48,8 @@ public abstract class ListObjectsResult { @ElementList(name = "CommonPrefixes", inline = true, required = false) private List commonPrefixes; - private static final List deleteMarkers = Utils.unmodifiableList(null); + private static final List deleteMarkers = + Utils.unmodifiableList(null); public ListObjectsResult() {} @@ -85,9 +87,40 @@ public List commonPrefixes() { return Utils.unmodifiableList(commonPrefixes); } - public List deleteMarkers() { + public List deleteMarkers() { return deleteMarkers; } public abstract List contents(); + + @Override + public String toString() { + return String.format( + "name=%s, encodingType=%s, prefix=%s, delimiter=%s, isTruncated=%s, maxKeys=%s, commonPrefixes=%s, deleteMarkers=%s", + Utils.stringify(name), + Utils.stringify(encodingType), + Utils.stringify(prefix), + Utils.stringify(delimiter), + Utils.stringify(isTruncated), + Utils.stringify(maxKeys), + Utils.stringify(commonPrefixes), + Utils.stringify(deleteMarkers)); + } + + @Root(name = "CommonPrefixes", strict = false) + public static class Prefix { + @Element(name = "Prefix") + private String prefix; + + public Prefix() {} + + public Item toItem() { + return new Contents(prefix); + } + + @Override + public String toString() { + return String.format("Prefix{%s}", Utils.stringify(prefix)); + } + } } diff --git a/api/src/main/java/io/minio/messages/ListPartsResult.java b/api/src/main/java/io/minio/messages/ListPartsResult.java index 9bdd33489..a3580467d 100644 --- a/api/src/main/java/io/minio/messages/ListPartsResult.java +++ b/api/src/main/java/io/minio/messages/ListPartsResult.java @@ -17,19 +17,17 @@ package io.minio.messages; import io.minio.Utils; -import java.util.List; import org.simpleframework.xml.Element; -import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; /** - * Object representation of response XML of ListParts API. */ @Root(name = "ListPartsResult", strict = false) @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -public class ListPartsResult { +public class ListPartsResult extends BasePartsResult { @Element(name = "Bucket") private String bucketName; @@ -45,21 +43,6 @@ public class ListPartsResult { @Element(name = "StorageClass") private String storageClass; - @Element(name = "PartNumberMarker") - private int partNumberMarker; - - @Element(name = "NextPartNumberMarker") - private int nextPartNumberMarker; - - @Element(name = "MaxParts") - private int maxParts; - - @Element(name = "IsTruncated") - private boolean isTruncated; - - @ElementList(name = "Part", inline = true, required = false) - private List partList; - @Element(name = "UploadId", required = false) private String uploadId; @@ -69,7 +52,9 @@ public class ListPartsResult { @Element(name = "ChecksumType", required = false) private String checksumType; - public ListPartsResult() {} + public ListPartsResult() { + super(); + } /** Returns bucket name. */ public String bucketName() { @@ -96,31 +81,6 @@ public Owner owner() { return owner; } - /** Returns maximum parts information received. */ - public int maxParts() { - return maxParts; - } - - /** Returns whether the result is truncated or not. */ - public boolean isTruncated() { - return isTruncated; - } - - /** Returns part number marker. */ - public int partNumberMarker() { - return partNumberMarker; - } - - /** Returns next part number marker. */ - public int nextPartNumberMarker() { - return nextPartNumberMarker; - } - - /** Returns List of Part. */ - public List partList() { - return Utils.unmodifiableList(partList); - } - public String uploadId() { return uploadId; } @@ -132,4 +92,20 @@ public String checksumAlgorithm() { public String checksumType() { return checksumType; } + + @Override + public String toString() { + return String.format( + "ListPartsResult{bucketName=%s, objectName=%s, initiator=%s, owner=%s, storageClass=%s," + + " uploadId=%s, checksumAlgorithm=%s, checksumType=%s, %s}", + Utils.stringify(bucketName), + Utils.stringify(objectName), + Utils.stringify(initiator), + Utils.stringify(owner), + Utils.stringify(storageClass), + Utils.stringify(uploadId), + Utils.stringify(checksumAlgorithm), + Utils.stringify(checksumType), + super.toString()); + } } diff --git a/api/src/main/java/io/minio/messages/ListVersionsResult.java b/api/src/main/java/io/minio/messages/ListVersionsResult.java index 13e22f522..61a8b36f6 100644 --- a/api/src/main/java/io/minio/messages/ListVersionsResult.java +++ b/api/src/main/java/io/minio/messages/ListVersionsResult.java @@ -24,7 +24,7 @@ import org.simpleframework.xml.Root; /** - * Object representation of response XML of ListObjectVersions * API. */ @@ -74,4 +74,50 @@ public List contents() { public List deleteMarkers() { return Utils.unmodifiableList(deleteMarkers); } + + @Override + public String toString() { + return String.format( + "ListVersionsResult{%s, keyMarker=%s, nextKeyMarker=%s, versionIdMarker=%s," + + " nextVersionIdMarker=%s, contents=%s, deleteMarkers=%s}", + super.toString(), + Utils.stringify(keyMarker), + Utils.stringify(nextKeyMarker), + Utils.stringify(versionIdMarker), + Utils.stringify(nextVersionIdMarker), + Utils.stringify(contents), + Utils.stringify(deleteMarkers)); + } + + @Root(name = "Version", strict = false) + public static class Version extends Item { + public Version() { + super(); + } + + public Version(String prefix) { + super(prefix); + } + + @Override + public String toString() { + return String.format("Version{%s}", super.toString()); + } + } + + @Root(name = "DeleteMarker", strict = false) + public static class DeleteMarker extends Item { + public DeleteMarker() { + super(); + } + + public DeleteMarker(String prefix) { + super(prefix); + } + + @Override + public String toString() { + return String.format("DeleteMarker{%s}", super.toString()); + } + } } diff --git a/api/src/main/java/io/minio/messages/LocationConstraint.java b/api/src/main/java/io/minio/messages/LocationConstraint.java index d7c249cea..7de1b8b81 100644 --- a/api/src/main/java/io/minio/messages/LocationConstraint.java +++ b/api/src/main/java/io/minio/messages/LocationConstraint.java @@ -16,12 +16,13 @@ package io.minio.messages; +import io.minio.Utils; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; import org.simpleframework.xml.Text; /** - * Object representation of response XML of GetBucketLocation * API. */ @@ -36,4 +37,9 @@ public LocationConstraint() {} public String location() { return location; } + + @Override + public String toString() { + return String.format("LocationConstraint{location=%s}", Utils.stringify(location)); + } } diff --git a/api/src/main/java/io/minio/messages/Metadata.java b/api/src/main/java/io/minio/messages/Metadata.java deleted file mode 100644 index 5dab1aaff..000000000 --- a/api/src/main/java/io/minio/messages/Metadata.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.messages; - -import io.minio.Utils; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -/** XML friendly map denotes metadata. */ -@Root(name = "Metadata") -@Convert(Metadata.MetadataConverter.class) -public class Metadata { - Map map; - - public Metadata() {} - - public Metadata(@Nonnull Map map) { - this.map = Utils.unmodifiableMap(Objects.requireNonNull(map, "Metadata must not be null")); - } - - public Map get() { - return map; - } - - /** XML converter class. */ - public static class MetadataConverter implements Converter { - @Override - public Metadata read(InputNode node) throws Exception { - Map map = new HashMap<>(); - while (true) { - InputNode childNode = node.getNext(); - if (childNode == null) { - break; - } - - map.put(childNode.getName(), childNode.getValue()); - } - - if (map.size() > 0) { - return new Metadata(map); - } - - return null; - } - - @Override - public void write(OutputNode node, Metadata metadata) throws Exception { - for (Map.Entry entry : metadata.get().entrySet()) { - OutputNode childNode = node.getChild(entry.getKey()); - childNode.setValue(entry.getValue()); - } - - node.commit(); - } - } -} diff --git a/api/src/main/java/io/minio/messages/Metrics.java b/api/src/main/java/io/minio/messages/Metrics.java deleted file mode 100644 index af5386322..000000000 --- a/api/src/main/java/io/minio/messages/Metrics.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote metrics information for {@link ReplicationDestination}. */ -@Root(name = "Metrics") -public class Metrics { - @Element(name = "EventThreshold") - private ReplicationTimeValue eventThreshold; - - @Element(name = "Status") - private Status status; - - public Metrics( - @Nonnull @Element(name = "EventThreshold") ReplicationTimeValue eventThreshold, - @Nonnull @Element(name = "Status") Status status) { - this.eventThreshold = - Objects.requireNonNull(eventThreshold, "Event threshold must not be null"); - this.status = Objects.requireNonNull(status, "Status must not be null"); - } - - public ReplicationTimeValue eventThreshold() { - return this.eventThreshold; - } - - public Status status() { - return this.status; - } -} diff --git a/api/src/main/java/io/minio/messages/NoncurrentVersionExpiration.java b/api/src/main/java/io/minio/messages/NoncurrentVersionExpiration.java deleted file mode 100644 index b4b66bac0..000000000 --- a/api/src/main/java/io/minio/messages/NoncurrentVersionExpiration.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote noncurrent version expiration information for {@link LifecycleRule}. */ -@Root(name = "NoncurrentVersionExpiration") -public class NoncurrentVersionExpiration { - @Element(name = "NoncurrentDays") - private int noncurrentDays; - - @Element(name = "NewerNoncurrentVersions", required = false) - private Integer newerNoncurrentVersions; - - public NoncurrentVersionExpiration( - @Element(name = "NoncurrentDays", required = false) int noncurrentDays) { - this.noncurrentDays = noncurrentDays; - } - - public NoncurrentVersionExpiration( - @Element(name = "NoncurrentDays", required = false) int noncurrentDays, - @Element(name = "NewerNoncurrentVersions", required = false) - Integer newerNoncurrentVersions) { - this.noncurrentDays = noncurrentDays; - this.newerNoncurrentVersions = newerNoncurrentVersions; - } - - public int noncurrentDays() { - return noncurrentDays; - } - - public Integer newerNoncurrentVersions() { - return newerNoncurrentVersions; - } -} diff --git a/api/src/main/java/io/minio/messages/NoncurrentVersionTransition.java b/api/src/main/java/io/minio/messages/NoncurrentVersionTransition.java deleted file mode 100644 index ddabe5c14..000000000 --- a/api/src/main/java/io/minio/messages/NoncurrentVersionTransition.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.messages; - -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote noncurrent version transition information for {@link LifecycleRule}. */ -@Root(name = "NoncurrentVersionTransition") -public class NoncurrentVersionTransition extends NoncurrentVersionExpiration { - @Element(name = "StorageClass") - private String storageClass; - - public NoncurrentVersionTransition( - @Element(name = "NoncurrentDays", required = false) int noncurrentDays, - @Nonnull @Element(name = "StorageClass", required = false) String storageClass) { - super(noncurrentDays, null); - if (storageClass == null || storageClass.isEmpty()) { - throw new IllegalArgumentException("StorageClass must be provided"); - } - this.storageClass = storageClass; - } - - public NoncurrentVersionTransition( - @Element(name = "NoncurrentDays", required = false) int noncurrentDays, - @Element(name = "NewerNoncurrentVersions", required = false) Integer newerNoncurrentVersions, - @Nonnull @Element(name = "StorageClass", required = false) String storageClass) { - super(noncurrentDays, newerNoncurrentVersions); - if (storageClass == null || storageClass.isEmpty()) { - throw new IllegalArgumentException("StorageClass must be provided"); - } - this.storageClass = storageClass; - } - - public String storageClass() { - return storageClass; - } -} diff --git a/api/src/main/java/io/minio/messages/NotificationCommonConfiguration.java b/api/src/main/java/io/minio/messages/NotificationCommonConfiguration.java deleted file mode 100644 index 77c70fffe..000000000 --- a/api/src/main/java/io/minio/messages/NotificationCommonConfiguration.java +++ /dev/null @@ -1,84 +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 - * - * 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.messages; - -import io.minio.Utils; -import java.util.List; -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.ElementList; - -/** - * Helper class to denote common fields of {@link CloudFunctionConfiguration}, {@link - * QueueConfiguration} and {@link TopicConfiguration}. - */ -public abstract class NotificationCommonConfiguration { - @Element(name = "Id", required = false) - private String id; - - @ElementList(name = "Event", inline = true) - private List events; - - @Element(name = "Filter", required = false) - private Filter filter; - - public NotificationCommonConfiguration() {} - - /** Returns id. */ - public String id() { - return id; - } - - /** Sets id. */ - public void setId(String id) { - this.id = id; - } - - /** Returns events. */ - public List events() { - return Utils.unmodifiableList(events); - } - - /** Sets event. */ - public void setEvents(@Nonnull List events) { - this.events = Utils.unmodifiableList(Objects.requireNonNull(events, "Events must not be null")); - } - - /** sets filter prefix rule. */ - public void setPrefixRule(String value) throws IllegalArgumentException { - if (filter == null) { - filter = new Filter(); - } - - filter.setPrefixRule(value); - } - - /** sets filter suffix rule. */ - public void setSuffixRule(String value) throws IllegalArgumentException { - if (filter == null) { - filter = new Filter(); - } - - filter.setSuffixRule(value); - } - - /** returns filter rule list. */ - public List filterRuleList() { - return Utils.unmodifiableList(filter == null ? null : filter.filterRuleList()); - } -} diff --git a/api/src/main/java/io/minio/messages/NotificationConfiguration.java b/api/src/main/java/io/minio/messages/NotificationConfiguration.java index ad43defdb..80909b8e4 100644 --- a/api/src/main/java/io/minio/messages/NotificationConfiguration.java +++ b/api/src/main/java/io/minio/messages/NotificationConfiguration.java @@ -17,14 +17,18 @@ package io.minio.messages; import io.minio.Utils; +import java.util.LinkedList; import java.util.List; +import java.util.Objects; +import javax.annotation.Nonnull; import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.Path; import org.simpleframework.xml.Root; /** - * Object representation of request XML of PutBucketNotificationConfiguration * API and response XML of GetBucketNotificationConfiguration @@ -34,63 +38,271 @@ @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") public class NotificationConfiguration { @ElementList(name = "CloudFunctionConfiguration", inline = true, required = false) - private List cloudFunctionConfigurationList; + private List cloudFunctionConfigurations; @ElementList(name = "QueueConfiguration", inline = true, required = false) - private List queueConfigurationList; + private List queueConfigurations; @ElementList(name = "TopicConfiguration", inline = true, required = false) - private List topicConfigurationList; + private List topicConfigurations; @Element(name = "EventBridgeConfiguration", required = false) private EventBridgeConfiguration eventBridgeConfiguration; - public NotificationConfiguration() {} + private NotificationConfiguration() {} - /** Returns cloud function configuration. */ - public List cloudFunctionConfigurationList() { - return Utils.unmodifiableList(cloudFunctionConfigurationList); + public NotificationConfiguration( + @ElementList(name = "CloudFunctionConfiguration", inline = true, required = false) + List cloudFunctionConfigurations, + @ElementList(name = "QueueConfiguration", inline = true, required = false) + List queueConfigurations, + @ElementList(name = "TopicConfiguration", inline = true, required = false) + List topicConfigurations, + @Element(name = "EventBridgeConfiguration", required = false) + EventBridgeConfiguration eventBridgeConfiguration) { + this.cloudFunctionConfigurations = cloudFunctionConfigurations; + this.queueConfigurations = queueConfigurations; + this.topicConfigurations = topicConfigurations; + this.eventBridgeConfiguration = eventBridgeConfiguration; } - /** Sets cloud function configuration list. */ - public void setCloudFunctionConfigurationList( - List cloudFunctionConfigurationList) { - this.cloudFunctionConfigurationList = - cloudFunctionConfigurationList == null - ? null - : Utils.unmodifiableList(cloudFunctionConfigurationList); + /** Returns cloud function configuration. */ + public List cloudFunctionConfigurations() { + return Utils.unmodifiableList(cloudFunctionConfigurations); } /** Returns queue configuration list. */ - public List queueConfigurationList() { - return Utils.unmodifiableList(queueConfigurationList); - } - - /** Sets queue configuration list. */ - public void setQueueConfigurationList(List queueConfigurationList) { - this.queueConfigurationList = - queueConfigurationList == null ? null : Utils.unmodifiableList(queueConfigurationList); + public List queueConfigurations() { + return Utils.unmodifiableList(queueConfigurations); } /** Returns topic configuration list. */ - public List topicConfigurationList() { - return Utils.unmodifiableList(topicConfigurationList); - } - - /** Sets topic configuration list. */ - public void setTopicConfigurationList(List topicConfigurationList) { - this.topicConfigurationList = - topicConfigurationList == null ? null : Utils.unmodifiableList(topicConfigurationList); + public List topicConfigurations() { + return Utils.unmodifiableList(topicConfigurations); } public EventBridgeConfiguration eventBridgeConfiguration() { return this.eventBridgeConfiguration; } - public void setEventBridgeConfiguration(EventBridgeConfiguration config) { - this.eventBridgeConfiguration = config; + @Override + public String toString() { + return String.format( + "NotificationConfiguration{cloudFunctionConfigurations=%s, queueConfigurations=%s," + + " topicConfigurations=%s, eventBridgeConfiguration=%s}", + Utils.stringify(cloudFunctionConfigurations), + Utils.stringify(queueConfigurations), + Utils.stringify(topicConfigurations), + Utils.stringify(eventBridgeConfiguration)); } @Root(name = "EventBridgeConfiguration") - public static class EventBridgeConfiguration {} + public static class EventBridgeConfiguration { + @Override + public String toString() { + return String.format("EventBridgeConfiguration{}"); + } + } + + public abstract static class BaseConfiguration { + @Element(name = "Id", required = false) + private String id; + + @ElementList(entry = "Event", inline = true) + private List events; + + @Element(name = "Filter", required = false) + private Filter filter; + + public BaseConfiguration(String id, @Nonnull List events, Filter filter) { + this.id = id; + this.events = + Utils.unmodifiableList(Objects.requireNonNull(events, "Events must not be null")); + this.filter = filter; + } + + public String id() { + return id; + } + + public List events() { + return Utils.unmodifiableList(events); + } + + public Filter filter() { + return filter; + } + + @Override + public String toString() { + return String.format( + "id=%s, events=%s, filter=%s", + Utils.stringify(id), Utils.stringify(events), Utils.stringify(filter)); + } + } + + @Root(name = "Filter") + public static class Filter { + @Path(value = "S3Key") + @ElementList(name = "FilterRule", inline = true) + private List rules; + + public Filter( + @Nonnull @Path(value = "S3Key") @ElementList(name = "FilterRule", inline = true) + List rules) { + Objects.requireNonNull(rules, "Filter rules must not be null"); + if (rules.size() < 1) { + throw new IllegalArgumentException("At least one rule must be provided"); + } + if (rules.size() > 2) { + throw new IllegalArgumentException("Maximum two rules must be provided"); + } + if (rules.size() == 2 && rules.get(0).name().equals(rules.get(1).name())) { + throw new IllegalArgumentException( + "Two rules '" + rules.get(0).name() + "' must not be same"); + } + this.rules = Utils.unmodifiableList(rules); + } + + public Filter(@Nonnull String prefix, @Nonnull String suffix) { + if (prefix == null && suffix == null) { + throw new IllegalArgumentException("Either prefix or suffix must be provided"); + } + List rules = new LinkedList<>(); + if (prefix != null) rules.add(FilterRule.newPrefixFilterRule(prefix)); + if (suffix != null) rules.add(FilterRule.newSuffixFilterRule(suffix)); + this.rules = Utils.unmodifiableList(rules); + } + + public List rules() { + return rules; + } + + @Override + public String toString() { + return String.format("Filter{rules=%s}", Utils.stringify(rules)); + } + } + + @Root(name = "FilterRule") + public static class FilterRule { + @Element(name = "Name") + private String name; + + @Element(name = "Value") + private String value; + + public FilterRule( + @Nonnull @Element(name = "Name") String name, + @Nonnull @Element(name = "Value") String value) { + Objects.requireNonNull(name, "Name must not be null"); + if (!"prefix".equals(name) && !"suffix".equals(name)) { + throw new IllegalArgumentException("Name must be 'prefix' or 'suffix'"); + } + Objects.requireNonNull(value, "Value must not be null"); + this.name = name; + this.value = value; + } + + public static FilterRule newPrefixFilterRule(@Nonnull String value) { + return new FilterRule("prefix", value); + } + + public static FilterRule newSuffixFilterRule(@Nonnull String value) { + return new FilterRule("suffix", value); + } + + public String name() { + return name; + } + + public String value() { + return value; + } + + @Override + public String toString() { + return String.format( + "FilterRule{name=%s, value=%s}", Utils.stringify(name), Utils.stringify(value)); + } + } + + @Root(name = "CloudFunctionConfiguration", strict = false) + public static class CloudFunctionConfiguration extends BaseConfiguration { + @Element(name = "CloudFunction") + private String cloudFunction; + + public CloudFunctionConfiguration( + @Nonnull @Element(name = "CloudFunction") String cloudFunction, + @Element(name = "Id", required = false) String id, + @Nonnull @ElementList(entry = "Event", inline = true) List events, + @Element(name = "Filter", required = false) Filter filter) { + super(id, events, filter); + this.cloudFunction = cloudFunction; + } + + /** Returns cloudFunction. */ + public String cloudFunction() { + return cloudFunction; + } + + @Override + public String toString() { + return String.format( + "CloudFunctionConfiguration{cloudFunction=%s, %s}", + Utils.stringify(cloudFunction), super.toString()); + } + } + + @Root(name = "QueueConfiguration", strict = false) + public static class QueueConfiguration extends BaseConfiguration { + @Element(name = "Queue") + private String queue; + + public QueueConfiguration( + @Nonnull @Element(name = "Queue") String queue, + @Element(name = "Id", required = false) String id, + @Nonnull @ElementList(entry = "Event", inline = true) List events, + @Element(name = "Filter", required = false) Filter filter) { + super(id, events, filter); + this.queue = queue; + } + + /** Returns queue. */ + public String queue() { + return queue; + } + + @Override + public String toString() { + return String.format( + "QueueConfiguration{queue=%s, %s}", Utils.stringify(queue), super.toString()); + } + } + + @Root(name = "TopicConfiguration", strict = false) + public static class TopicConfiguration extends BaseConfiguration { + @Element(name = "Topic") + private String topic; + + public TopicConfiguration( + @Nonnull @Element(name = "Topic") String topic, + @Element(name = "Id", required = false) String id, + @Nonnull @ElementList(entry = "Event", inline = true) List events, + @Element(name = "Filter", required = false) Filter filter) { + super(id, events, filter); + this.topic = topic; + } + + /** Returns topic. */ + public String topic() { + return topic; + } + + @Override + public String toString() { + return String.format( + "TopicConfiguration{topic=%s, %s}", Utils.stringify(topic), super.toString()); + } + } } diff --git a/api/src/main/java/io/minio/messages/NotificationRecords.java b/api/src/main/java/io/minio/messages/NotificationRecords.java index 8f0dabe8a..f49314f1b 100644 --- a/api/src/main/java/io/minio/messages/NotificationRecords.java +++ b/api/src/main/java/io/minio/messages/NotificationRecords.java @@ -18,13 +18,16 @@ package io.minio.messages; import com.fasterxml.jackson.annotation.JsonProperty; +import io.minio.Time; import io.minio.Utils; +import java.time.ZonedDateTime; import java.util.List; +import java.util.Map; /** - * Object representation of JSON format of Event - * Message Structure. + * Object represents JSON format of Notification + * Content Structure. */ @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value = "UwF", @@ -36,4 +39,234 @@ public class NotificationRecords { public List events() { return Utils.unmodifiableList(events); } + + @Override + public String toString() { + return String.format("NotificationRecords{events=%s}", Utils.stringify(events)); + } + + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "UuF", + justification = "eventVersion and eventSource are available for completeness") + public static class Event { + @JsonProperty private String eventVersion; + @JsonProperty private String eventSource; + @JsonProperty private String awsRegion; + @JsonProperty private String eventName; + @JsonProperty private Identity userIdentity; + @JsonProperty private Map requestParameters; + @JsonProperty private Map responseElements; + @JsonProperty private S3 s3; + @JsonProperty private Source source; + @JsonProperty private Time.S3Time eventTime; + + public String eventVersion() { + return eventVersion; + } + + public String eventSource() { + return eventSource; + } + + public String awsRegion() { + return awsRegion; + } + + public String eventName() { + return eventName; + } + + public String userIdentity() { + return userIdentity == null ? null : userIdentity.principalId(); + } + + public Map requestParameters() { + return Utils.unmodifiableMap(requestParameters); + } + + public Map responseElements() { + return Utils.unmodifiableMap(responseElements); + } + + public Bucket bucket() { + return s3 == null ? null : s3.bucket(); + } + + public Object object() { + return s3 == null ? null : s3.object(); + } + + public Source source() { + return source; + } + + public ZonedDateTime eventTime() { + return eventTime == null ? null : eventTime.toZonedDateTime(); + } + + @Override + public String toString() { + return String.format( + "Event{eventVersion=%s, eventSource=%s, awsRegion=%s, eventName=%s, userIdentity=%s," + + " requestParameters=%s, responseElements=%s, s3=%s, source=%s, eventTime=%s}", + Utils.stringify(eventVersion), + Utils.stringify(eventSource), + Utils.stringify(awsRegion), + Utils.stringify(eventName), + Utils.stringify(userIdentity), + Utils.stringify(requestParameters), + Utils.stringify(responseElements), + Utils.stringify(s3), + Utils.stringify(source), + Utils.stringify(eventTime)); + } + + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "UwF", + justification = "Everything in this class is initialized by JSON unmarshalling.") + public static class Identity { + @JsonProperty private String principalId; + + public String principalId() { + return principalId; + } + + @Override + public String toString() { + return String.format("Identity{principalId=%s}", Utils.stringify(principalId)); + } + } + + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "UwF", + justification = "Everything in this class is initialized by JSON unmarshalling.") + public static class Bucket { + @JsonProperty private String name; + @JsonProperty private Identity ownerIdentity; + @JsonProperty private String arn; + + public String name() { + return name; + } + + public String ownerIdentity() { + return ownerIdentity == null ? null : ownerIdentity.principalId(); + } + + public String arn() { + return arn; + } + + @Override + public String toString() { + return String.format( + "Bucket{name=%s, ownerIdentity=%s, arn=%s}", + Utils.stringify(name), Utils.stringify(ownerIdentity), Utils.stringify(arn)); + } + } + + public static class Object { + @JsonProperty private String key; + @JsonProperty private long size; + @JsonProperty private String eTag; + @JsonProperty private String versionId; + @JsonProperty private String sequencer; + @JsonProperty private Map userMetadata; // MinIO specific extension. + + public String key() { + return key; + } + + public long size() { + return size; + } + + public String etag() { + return eTag; + } + + public String versionId() { + return versionId; + } + + public String sequencer() { + return sequencer; + } + + public Map userMetadata() { + return Utils.unmodifiableMap(userMetadata); + } + + @Override + public String toString() { + return String.format( + "Object{key=%s, size=%d, eTag=%s, versionId=%s, sequencer=%s, userMetadata=%s}", + Utils.stringify(key), + size, + Utils.stringify(eTag), + Utils.stringify(versionId), + Utils.stringify(sequencer), + Utils.stringify(userMetadata)); + } + } + + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = {"UwF", "UuF"}, + justification = + "Everything in this class is initialized by JSON unmarshalling " + + "and s3SchemaVersion/configurationId are available for completeness.") + public static class S3 { + @JsonProperty private String s3SchemaVersion; + @JsonProperty private String configurationId; + @JsonProperty private Bucket bucket; + @JsonProperty private Object object; + + public Bucket bucket() { + return bucket; + } + + public Object object() { + return object; + } + + @Override + public String toString() { + return String.format( + "S3{s3SchemaVersion=%s, configurationId=%s, bucket=%s, object=%s}", + Utils.stringify(s3SchemaVersion), + Utils.stringify(configurationId), + Utils.stringify(bucket), + Utils.stringify(object)); + } + } + + /** This is MinIO extension. */ + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "UwF", + justification = "Everything in this class is initialized by JSON unmarshalling.") + public static class Source { + @JsonProperty private String host; + @JsonProperty private String port; + @JsonProperty private String userAgent; + + public String host() { + return host; + } + + public String port() { + return port; + } + + public String userAgent() { + return userAgent; + } + + @Override + public String toString() { + return String.format( + "Source{host=%s, port=%s, userAgent=%s}", + Utils.stringify(host), Utils.stringify(port), Utils.stringify(userAgent)); + } + } + } } diff --git a/api/src/main/java/io/minio/messages/ObjectLockConfiguration.java b/api/src/main/java/io/minio/messages/ObjectLockConfiguration.java index 463054f7f..03281bef5 100644 --- a/api/src/main/java/io/minio/messages/ObjectLockConfiguration.java +++ b/api/src/main/java/io/minio/messages/ObjectLockConfiguration.java @@ -16,12 +16,16 @@ package io.minio.messages; +import io.minio.Utils; import org.simpleframework.xml.Element; +import org.simpleframework.xml.ElementUnion; import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.Path; import org.simpleframework.xml.Root; +import org.simpleframework.xml.Text; /** - * Object representation of request XML of PutObjectLockConfiguration * API and response XML of GetObjectLockConfiguration @@ -54,4 +58,118 @@ public RetentionMode mode() { public RetentionDuration duration() { return (rule != null) ? rule.duration() : null; } + + @Override + public String toString() { + return String.format( + "ObjectLockConfiguration{objectLockEnabled=%s, rule=%s}", + Utils.stringify(objectLockEnabled), Utils.stringify(rule)); + } + + @Root(name = "Rule", strict = false) + public static class Rule { + @Path(value = "DefaultRetention") + @Element(name = "Mode", required = false) + private RetentionMode mode; + + @Path(value = "DefaultRetention") + @ElementUnion({ + @Element(name = "Days", type = RetentionDurationDays.class, required = false), + @Element(name = "Years", type = RetentionDurationYears.class, required = false) + }) + private RetentionDuration duration; + + public Rule( + @Element(name = "Mode", required = false) RetentionMode mode, + @ElementUnion({ + @Element(name = "Days", type = RetentionDurationDays.class, required = false), + @Element(name = "Years", type = RetentionDurationYears.class, required = false) + }) + RetentionDuration duration) { + if (mode != null && duration != null) { + this.mode = mode; + this.duration = duration; + } else if (mode != null || duration != null) { + if (mode == null) { + throw new IllegalArgumentException("mode is null"); + } + throw new IllegalArgumentException("duration is null"); + } + } + + public RetentionMode mode() { + return mode; + } + + public RetentionDuration duration() { + return duration; + } + + @Override + public String toString() { + return String.format( + "Rule{mode=%s, duration=%s}", Utils.stringify(mode), Utils.stringify(duration)); + } + } + + public static interface RetentionDuration { + public RetentionDurationUnit unit(); + + public int duration(); + } + + public static enum RetentionDurationUnit { + DAYS, + YEARS; + } + + @Root(name = "Days") + public static class RetentionDurationDays implements RetentionDuration { + @Text(required = false) + private Integer days; + + public RetentionDurationDays() {} + + public RetentionDurationDays(int days) { + this.days = Integer.valueOf(days); + } + + public RetentionDurationUnit unit() { + return RetentionDurationUnit.DAYS; + } + + public int duration() { + return days; + } + + @Override + public String toString() { + return String.format("RetentionDurationDays{%s}", Utils.stringify(days)); + } + } + + @Root(name = "Years") + public static class RetentionDurationYears implements RetentionDuration { + @Text(required = false) + private Integer years; + + public RetentionDurationYears() {} + + public RetentionDurationYears(int years) { + this.years = Integer.valueOf(years); + } + + public RetentionDurationUnit unit() { + return RetentionDurationUnit.YEARS; + } + + public int duration() { + return years; + } + + @Override + public String toString() { + return String.format("RetentionDurationYears{%s}", Utils.stringify(years)); + } + } } diff --git a/api/src/main/java/io/minio/messages/ObjectMetadata.java b/api/src/main/java/io/minio/messages/ObjectMetadata.java deleted file mode 100644 index d705ca79e..000000000 --- a/api/src/main/java/io/minio/messages/ObjectMetadata.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.messages; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.minio.Utils; -import java.util.Map; - -/** Helper class to denote object information for {@link EventMetadata}. */ -public class ObjectMetadata { - @JsonProperty private String key; - @JsonProperty private long size; - @JsonProperty private String eTag; - @JsonProperty private String versionId; - @JsonProperty private String sequencer; - @JsonProperty private Map userMetadata; // MinIO specific extension. - - public String key() { - return key; - } - - public long size() { - return size; - } - - public String etag() { - return eTag; - } - - public String versionId() { - return versionId; - } - - public String sequencer() { - return sequencer; - } - - public Map userMetadata() { - return Utils.unmodifiableMap(userMetadata); - } -} diff --git a/api/src/main/java/io/minio/messages/OutputLocation.java b/api/src/main/java/io/minio/messages/OutputLocation.java deleted file mode 100644 index e2f8091a7..000000000 --- a/api/src/main/java/io/minio/messages/OutputLocation.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote output location information of {@link RestoreRequest}. */ -@Root(name = "OutputLocation") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class OutputLocation { - @Element(name = "S3") - private S3OutputLocation s3OutputLocation; - - public OutputLocation(@Nonnull S3OutputLocation s3OutputLocation) { - this.s3OutputLocation = - Objects.requireNonNull(s3OutputLocation, "S3OutputLocation must not be null"); - } -} diff --git a/api/src/main/java/io/minio/messages/OutputSerialization.java b/api/src/main/java/io/minio/messages/OutputSerialization.java index 20ebe898e..cd7121bdb 100644 --- a/api/src/main/java/io/minio/messages/OutputSerialization.java +++ b/api/src/main/java/io/minio/messages/OutputSerialization.java @@ -19,32 +19,86 @@ import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; -/** - * Helper class to denote Output Serialization information of {@link SelectObjectContentRequest}. - */ +/** Object represents Output Serialization information of {@link SelectObjectContentRequest}. */ @Root(name = "OutputSerialization") @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") public class OutputSerialization { @Element(name = "CSV", required = false) - private CsvOutputSerialization csv; + private CSV csv; @Element(name = "JSON", required = false) - private JsonOutputSerialization json; + private JSON json; + + private OutputSerialization(CSV csv, JSON json) { + this.csv = csv; + this.json = json; + } /** Constructs a new OutputSerialization object with CSV. */ - public OutputSerialization( + public static OutputSerialization newCSV( Character fieldDelimiter, Character quoteCharacter, Character quoteEscapeCharacter, QuoteFields quoteFields, Character recordDelimiter) { - this.csv = - new CsvOutputSerialization( - fieldDelimiter, quoteCharacter, quoteEscapeCharacter, quoteFields, recordDelimiter); + return new OutputSerialization( + new CSV(fieldDelimiter, quoteCharacter, quoteEscapeCharacter, quoteFields, recordDelimiter), + null); } /** Constructs a new OutputSerialization object with JSON. */ - public OutputSerialization(Character recordDelimiter) { - this.json = new JsonOutputSerialization(recordDelimiter); + public static OutputSerialization newJSON(Character recordDelimiter) { + return new OutputSerialization(null, new JSON(recordDelimiter)); + } + + /** Object represents CSV output serialization information of {@link OutputSerialization}. */ + @Root(name = "CSV") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class CSV { + @Element(name = "FieldDelimiter", required = false) + private Character fieldDelimiter; + + @Element(name = "QuoteCharacter", required = false) + private Character quoteCharacter; + + @Element(name = "QuoteEscapeCharacter", required = false) + private Character quoteEscapeCharacter; + + @Element(name = "QuoteFields", required = false) + private QuoteFields quoteFields; + + @Element(name = "RecordDelimiter", required = false) + private Character recordDelimiter; + + public CSV( + Character fieldDelimiter, + Character quoteCharacter, + Character quoteEscapeCharacter, + QuoteFields quoteFields, + Character recordDelimiter) { + this.fieldDelimiter = fieldDelimiter; + this.quoteCharacter = quoteCharacter; + this.quoteEscapeCharacter = quoteEscapeCharacter; + this.quoteFields = quoteFields; + this.recordDelimiter = recordDelimiter; + } + } + + /** Object represents JSON output serialization information of {@link OutputSerialization}. */ + @Root(name = "JSON") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class JSON { + @Element(name = "RecordDelimiter", required = false) + private Character recordDelimiter; + + public JSON(Character recordDelimiter) { + this.recordDelimiter = recordDelimiter; + } + } + + /** Type represents quotation to be used for CSV output serialization. */ + public static enum QuoteFields { + ALWAYS, + ASNEEDED; } } diff --git a/api/src/main/java/io/minio/messages/Owner.java b/api/src/main/java/io/minio/messages/Owner.java index 28ffa998b..5eb325fd2 100644 --- a/api/src/main/java/io/minio/messages/Owner.java +++ b/api/src/main/java/io/minio/messages/Owner.java @@ -16,11 +16,12 @@ package io.minio.messages; +import io.minio.Utils; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; /** - * Helper class to denote owner information for {@link ListAllMyBucketsResult}, {@link + * Object represents owner information for {@link ListAllMyBucketsResult}, {@link * ListBucketResultV1}, {@link ListBucketResultV2}, {@link ListVersionsResult}, {@link * ListMultipartUploadsResult} and {@link ListPartsResult}. */ @@ -43,4 +44,10 @@ public String id() { public String displayName() { return displayName; } + + @Override + public String toString() { + return String.format( + "Owner{id=%s, displayName=%s}", Utils.stringify(id), Utils.stringify(displayName)); + } } diff --git a/api/src/main/java/io/minio/messages/Part.java b/api/src/main/java/io/minio/messages/Part.java index d8aac178e..9f99978ce 100644 --- a/api/src/main/java/io/minio/messages/Part.java +++ b/api/src/main/java/io/minio/messages/Part.java @@ -16,16 +16,18 @@ package io.minio.messages; +import io.minio.Time; +import io.minio.Utils; import java.time.ZonedDateTime; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; /** - * Helper class to denote Part information of a multipart upload and used in {@link + * Object represents Part information of a multipart upload and used in {@link * CompleteMultipartUpload} and {@link ListPartsResult}. */ @Root(name = "Part", strict = false) -public class Part { +public class Part extends Checksum { @Element(name = "PartNumber", required = false) private int partNumber; @@ -33,36 +35,18 @@ public class Part { private String etag; @Element(name = "LastModified", required = false) - private ResponseDate lastModified; + private Time.S3Time lastModified; @Element(name = "Size", required = false) private Long size; - @Element(name = "ChecksumCRC32", required = false) - private String checksumCRC32; - - @Element(name = "ChecksumCRC32C", required = false) - private String checksumCRC32C; - - @Element(name = "ChecksumCRC64NVME", required = false) - private String checksumCRC64NVME; - - @Element(name = "ChecksumSHA1", required = false) - private String checksumSHA1; - - @Element(name = "ChecksumSHA256", required = false) - private String checksumSHA256; - public Part() {} - /** Constructs a new Part object with given part number and ETag. */ public Part(int partNumber, String etag) { - this.partNumber = partNumber; this.etag = etag; } - /** Constructs a new Part object with given values. */ public Part( int partNumber, String etag, @@ -71,52 +55,41 @@ public Part( String checksumCRC64NVME, String checksumSHA1, String checksumSHA256) { + super(checksumCRC32, checksumCRC32C, checksumCRC64NVME, checksumSHA1, checksumSHA256, null); this.partNumber = partNumber; this.etag = etag; - this.checksumCRC32 = checksumCRC32; - this.checksumCRC32C = checksumCRC32C; - this.checksumCRC64NVME = checksumCRC64NVME; - this.checksumSHA1 = checksumSHA1; - this.checksumSHA256 = checksumSHA256; } - /** Returns part number. */ + public Part(CopyPartResult result, int partNumber) { + super(result); + this.etag = result.etag(); + this.partNumber = partNumber; + } + public int partNumber() { return partNumber; } - /** Returns ETag. */ public String etag() { return etag.replaceAll("\"", ""); } - /** Returns last modified time. */ public ZonedDateTime lastModified() { - return lastModified.zonedDateTime(); + return lastModified == null ? null : lastModified.toZonedDateTime(); } - /** Returns part size. */ public long partSize() { return size; } - 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; + @Override + public String toString() { + return String.format( + "Part{partNumber=%s, etag=%s, lastModified=%s, size=%s, %s}", + Utils.stringify(partNumber), + Utils.stringify(etag), + Utils.stringify(lastModified), + Utils.stringify(size), + super.stringify()); } } diff --git a/api/src/main/java/io/minio/messages/Permission.java b/api/src/main/java/io/minio/messages/Permission.java deleted file mode 100644 index a86d43007..000000000 --- a/api/src/main/java/io/minio/messages/Permission.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import org.simpleframework.xml.Root; - -/** Permission represents type of grantee. */ -@Root(name = "Permission") -public enum Permission { - FULL_CONTROL, - WRITE, - WRITE_ACP, - READ, - READ_ACP; -} diff --git a/api/src/main/java/io/minio/messages/Prefix.java b/api/src/main/java/io/minio/messages/Prefix.java deleted file mode 100644 index 4a27a7bc4..000000000 --- a/api/src/main/java/io/minio/messages/Prefix.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2015 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote Prefix information in {@link ListBucketResultV1}, {@link - * ListBucketResultV2} and {@link ListVersionsResult}. - */ -@Root(name = "CommonPrefixes", strict = false) -public class Prefix { - @Element(name = "Prefix") - private String prefix; - - public Prefix() {} - - public Item toItem() { - return new Contents(prefix); - } -} diff --git a/api/src/main/java/io/minio/messages/Progress.java b/api/src/main/java/io/minio/messages/Progress.java index 71ba29590..d56dbd6ce 100644 --- a/api/src/main/java/io/minio/messages/Progress.java +++ b/api/src/main/java/io/minio/messages/Progress.java @@ -16,10 +16,23 @@ package io.minio.messages; +import org.simpleframework.xml.Element; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; -/** Helper class to denote Progress information of S3 select response message. */ +/** Object represents Progress information of S3 select response message. */ @Root(name = "Progress", strict = false) @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -public class Progress extends Stats {} +public class Progress extends Stats { + public Progress( + @Element(name = "BytesScanned", required = false) Long bytesScanned, + @Element(name = "BytesProcessed", required = false) Long bytesProcessed, + @Element(name = "BytesReturned", required = false) Long bytesReturned) { + super(bytesScanned, bytesProcessed, bytesReturned); + } + + @Override + public String toString() { + return String.format("Progress{%s}", super.stringify()); + } +} diff --git a/api/src/main/java/io/minio/messages/QuoteFields.java b/api/src/main/java/io/minio/messages/QuoteFields.java deleted file mode 100644 index a33e1871f..000000000 --- a/api/src/main/java/io/minio/messages/QuoteFields.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -/** Indicates whether to use quotation marks around output fields. */ -public enum QuoteFields { - ALWAYS, - ASNEEDED; -} diff --git a/api/src/main/java/io/minio/messages/ReplicaModifications.java b/api/src/main/java/io/minio/messages/ReplicaModifications.java deleted file mode 100644 index dc18cf9ed..000000000 --- a/api/src/main/java/io/minio/messages/ReplicaModifications.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2022 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote replica modifications information for {@link SourceSelectionCriteria}. */ -@Root(name = "ReplicaModifications") -public class ReplicaModifications { - @Element(name = "Status") - private Status status; - - public ReplicaModifications(@Nonnull @Element(name = "Status") Status status) { - this.status = Objects.requireNonNull(status, "Status must not be null"); - } - - public Status status() { - return this.status; - } -} diff --git a/api/src/main/java/io/minio/messages/ReplicationConfiguration.java b/api/src/main/java/io/minio/messages/ReplicationConfiguration.java index 3dcb87629..0b8618e31 100644 --- a/api/src/main/java/io/minio/messages/ReplicationConfiguration.java +++ b/api/src/main/java/io/minio/messages/ReplicationConfiguration.java @@ -25,9 +25,10 @@ import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; +import org.simpleframework.xml.convert.Convert; /** - * Object representation of request XML of PutBucketReplication * API and response XML of GetBucketReplication @@ -40,12 +41,11 @@ public class ReplicationConfiguration { private String role; @ElementList(name = "Rule", inline = true) - private List rules; + private List rules; - /** Constructs new replication configuration. */ public ReplicationConfiguration( @Nullable @Element(name = "Role", required = false) String role, - @Nonnull @ElementList(name = "Rule", inline = true) List rules) { + @Nonnull @ElementList(name = "Rule", inline = true) List rules) { this.role = role; // Role is not applicable in MinIO server and it is optional. this.rules = Utils.unmodifiableList(Objects.requireNonNull(rules, "Rules must not be null")); @@ -61,7 +61,513 @@ public String role() { return role; } - public List rules() { + public List rules() { return Utils.unmodifiableList(rules); } + + @Override + public String toString() { + return String.format( + "ReplicationConfiguration{role=%s, rules=%s}", + Utils.stringify(role), Utils.stringify(rules)); + } + + @Root(name = "Rule") + public static class Rule { + @Element(name = "DeleteMarkerReplication", required = false) + private DeleteMarkerReplication deleteMarkerReplication; + + @Element(name = "Destination") + private Destination destination; + + @Element(name = "ExistingObjectReplication", required = false) + private ExistingObjectReplication existingObjectReplication; + + @Element(name = "Filter", required = false) + private Filter filter; + + @Element(name = "ID", required = false) + private String id; + + @Element(name = "Prefix", required = false) + @Convert(PrefixConverter.class) + private String prefix; + + @Element(name = "Priority", required = false) + private Integer priority; + + @Element(name = "SourceSelectionCriteria", required = false) + private SourceSelectionCriteria sourceSelectionCriteria; + + @Element(name = "DeleteReplication", required = false) + private DeleteReplication deleteReplication; // This is MinIO specific extension. + + @Element(name = "Status") + private Status status; + + public Rule( + @Nullable @Element(name = "DeleteMarkerReplication", required = false) + DeleteMarkerReplication deleteMarkerReplication, + @Nonnull @Element(name = "Destination") Destination destination, + @Nullable @Element(name = "ExistingObjectReplication", required = false) + ExistingObjectReplication existingObjectReplication, + @Nullable @Element(name = "Filter", required = false) Filter filter, + @Nullable @Element(name = "ID", required = false) String id, + @Nullable @Element(name = "Prefix", required = false) String prefix, + @Nullable @Element(name = "Priority", required = false) Integer priority, + @Nullable @Element(name = "SourceSelectionCriteria", required = false) + SourceSelectionCriteria sourceSelectionCriteria, + @Nullable @Element(name = "DeleteReplication", required = false) + DeleteReplication deleteReplication, + @Nonnull @Element(name = "Status") Status status) { + + if (filter != null && deleteMarkerReplication == null) { + deleteMarkerReplication = new DeleteMarkerReplication(null); + } + + if (id != null) { + id = id.trim(); + if (id.isEmpty()) throw new IllegalArgumentException("ID must be non-empty string"); + if (id.length() > 255) + throw new IllegalArgumentException("ID must be exceed 255 characters"); + } + + this.deleteMarkerReplication = deleteMarkerReplication; + this.destination = Objects.requireNonNull(destination, "Destination must not be null"); + this.existingObjectReplication = existingObjectReplication; + this.filter = filter; + this.id = id; + this.prefix = prefix; + this.priority = priority; + this.sourceSelectionCriteria = sourceSelectionCriteria; + this.deleteReplication = deleteReplication; + this.status = Objects.requireNonNull(status, "Status must not be null"); + } + + public Rule( + @Nullable @Element(name = "DeleteMarkerReplication", required = false) + DeleteMarkerReplication deleteMarkerReplication, + @Nonnull @Element(name = "Destination") Destination destination, + @Nullable @Element(name = "ExistingObjectReplication", required = false) + ExistingObjectReplication existingObjectReplication, + @Nullable @Element(name = "Filter", required = false) Filter filter, + @Nullable @Element(name = "ID", required = false) String id, + @Nullable @Element(name = "Prefix", required = false) String prefix, + @Nullable @Element(name = "Priority", required = false) Integer priority, + @Nullable @Element(name = "SourceSelectionCriteria", required = false) + SourceSelectionCriteria sourceSelectionCriteria, + @Nonnull @Element(name = "Status") Status status) { + this( + deleteMarkerReplication, + destination, + existingObjectReplication, + filter, + id, + prefix, + priority, + sourceSelectionCriteria, + null, + status); + } + + public DeleteMarkerReplication deleteMarkerReplication() { + return this.deleteMarkerReplication; + } + + public Destination destination() { + return this.destination; + } + + public ExistingObjectReplication existingObjectReplication() { + return this.existingObjectReplication; + } + + public Filter filter() { + return this.filter; + } + + public String id() { + return this.id; + } + + public String prefix() { + return this.prefix; + } + + public Integer priority() { + return this.priority; + } + + public SourceSelectionCriteria sourceSelectionCriteria() { + return this.sourceSelectionCriteria; + } + + public DeleteReplication deleteReplication() { + return this.deleteReplication; + } + + public Status status() { + return this.status; + } + + @Override + public String toString() { + return String.format( + "Rule{deleteMarkerReplication=%s, destination=%s, existingObjectReplication=%s," + + " filter=%s, id=%s, prefix=%s, priority=%s, sourceSelectionCriteria=%s," + + " deleteReplication=%s, status=%s}", + Utils.stringify(deleteMarkerReplication), + Utils.stringify(destination), + Utils.stringify(existingObjectReplication), + Utils.stringify(filter), + Utils.stringify(id), + Utils.stringify(prefix), + Utils.stringify(priority), + Utils.stringify(sourceSelectionCriteria), + Utils.stringify(deleteReplication), + Utils.stringify(status)); + } + } + + @Root(name = "DeleteMarkerReplication") + public static class DeleteMarkerReplication { + @Element(name = "Status", required = false) + private Status status; + + public DeleteMarkerReplication( + @Nullable @Element(name = "Status", required = false) Status status) { + this.status = (status == null) ? Status.DISABLED : status; + } + + public Status status() { + return status; + } + + @Override + public String toString() { + return String.format("DeleteMarkerReplication{status=%s}", Utils.stringify(status)); + } + } + + @Root(name = "Destination") + public static class Destination { + @Element(name = "AccessControlTranslation", required = false) + private AccessControlTranslation accessControlTranslation; + + @Element(name = "Account", required = false) + private String account; + + @Element(name = "Bucket") + private String bucketArn; + + @Element(name = "EncryptionConfiguration", required = false) + private EncryptionConfiguration encryptionConfiguration; + + @Element(name = "Metrics", required = false) + private Metrics metrics; + + @Element(name = "ReplicationTime", required = false) + private ReplicationTime replicationTime; + + @Element(name = "StorageClass", required = false) + private String storageClass; + + public Destination( + @Nullable @Element(name = "AccessControlTranslation", required = false) + AccessControlTranslation accessControlTranslation, + @Nullable @Element(name = "Account", required = false) String account, + @Nonnull @Element(name = "Bucket") String bucketArn, + @Nullable @Element(name = "EncryptionConfiguration", required = false) + EncryptionConfiguration encryptionConfiguration, + @Nullable @Element(name = "Metrics", required = false) Metrics metrics, + @Nullable @Element(name = "ReplicationTime", required = false) + ReplicationTime replicationTime, + @Nullable @Element(name = "StorageClass", required = false) String storageClass) { + this.accessControlTranslation = accessControlTranslation; + this.account = account; + this.bucketArn = Objects.requireNonNull(bucketArn, "Bucket ARN must not be null"); + this.encryptionConfiguration = encryptionConfiguration; + this.metrics = metrics; + this.replicationTime = replicationTime; + this.storageClass = storageClass; + } + + public AccessControlTranslation accessControlTranslation() { + return this.accessControlTranslation; + } + + public String account() { + return this.account; + } + + public String bucketArn() { + return this.bucketArn; + } + + public EncryptionConfiguration encryptionConfiguration() { + return encryptionConfiguration; + } + + public Metrics metrics() { + return this.metrics; + } + + public ReplicationTime replicationTime() { + return this.replicationTime; + } + + public String storageClass() { + return this.storageClass; + } + + @Override + public String toString() { + return String.format( + "Destination{accessControlTranslation=%s, account=%s, bucketArn=%s," + + " encryptionConfiguration=%s, metrics=%s, replicationTime=%s, storageClass=%s}", + Utils.stringify(accessControlTranslation), + Utils.stringify(account), + Utils.stringify(bucketArn), + Utils.stringify(encryptionConfiguration), + Utils.stringify(metrics), + Utils.stringify(replicationTime), + Utils.stringify(storageClass)); + } + } + + @Root(name = "AccessControlTranslation") + public static class AccessControlTranslation { + @Element(name = "Owner") + private String owner = "Destination"; + + public AccessControlTranslation(@Nonnull @Element(name = "Owner") String owner) { + this.owner = Objects.requireNonNull(owner, "Owner must not be null"); + } + + public String owner() { + return this.owner; + } + + @Override + public String toString() { + return String.format("AccessControlTranslation{owner=%s}", Utils.stringify(owner)); + } + } + + @Root(name = "EncryptionConfiguration") + public static class EncryptionConfiguration { + @Element(name = "ReplicaKmsKeyID", required = false) + private String replicaKmsKeyID; + + public EncryptionConfiguration( + @Nullable @Element(name = "ReplicaKmsKeyID", required = false) String replicaKmsKeyID) { + this.replicaKmsKeyID = replicaKmsKeyID; + } + + public String replicaKmsKeyID() { + return this.replicaKmsKeyID; + } + + @Override + public String toString() { + return String.format( + "EncryptionConfiguration{replicaKmsKeyID=%s}", Utils.stringify(replicaKmsKeyID)); + } + } + + @Root(name = "Metrics") + public static class Metrics { + @Element(name = "EventThreshold") + private ReplicationTimeValue eventThreshold; + + @Element(name = "Status") + private Status status; + + public Metrics( + @Nonnull @Element(name = "EventThreshold") ReplicationTimeValue eventThreshold, + @Nonnull @Element(name = "Status") Status status) { + this.eventThreshold = + Objects.requireNonNull(eventThreshold, "Event threshold must not be null"); + this.status = Objects.requireNonNull(status, "Status must not be null"); + } + + public ReplicationTimeValue eventThreshold() { + return this.eventThreshold; + } + + public Status status() { + return this.status; + } + + @Override + public String toString() { + return String.format( + "Metrics{eventThreshold=%s, status=%s}", + Utils.stringify(eventThreshold), Utils.stringify(status)); + } + } + + @Root(name = "ReplicationTime") + public static class ReplicationTime { + @Element(name = "Time") + private ReplicationTimeValue time; + + @Element(name = "Status") + private Status status; + + public ReplicationTime( + @Nonnull @Element(name = "Time") ReplicationTimeValue time, + @Nonnull @Element(name = "Status") Status status) { + this.time = Objects.requireNonNull(time, "Time must not be null"); + this.status = Objects.requireNonNull(status, "Status must not be null"); + } + + public ReplicationTimeValue time() { + return this.time; + } + + public Status status() { + return this.status; + } + + @Override + public String toString() { + return String.format( + "ReplicationTime{time=%s, status=%s}", Utils.stringify(time), Utils.stringify(status)); + } + } + + @Root(name = "ReplicationTimeValue") + public static class ReplicationTimeValue { + @Element(name = "Minutes", required = false) + private Integer minutes = 15; + + public ReplicationTimeValue( + @Nullable @Element(name = "Minutes", required = false) Integer minutes) { + this.minutes = minutes; + } + + public Integer minutes() { + return this.minutes; + } + + @Override + public String toString() { + return String.format("ReplicationTimeValue{minutes=%s}", Utils.stringify(minutes)); + } + } + + @Root(name = "ExistingObjectReplication") + public static class ExistingObjectReplication { + @Element(name = "Status") + private Status status; + + public ExistingObjectReplication(@Nonnull @Element(name = "Status") Status status) { + this.status = Objects.requireNonNull(status, "Status must not be null"); + } + + public Status status() { + return this.status; + } + + @Override + public String toString() { + return String.format("ExistingObjectReplication{status=%s}", Utils.stringify(status)); + } + } + + @Root(name = "SourceSelectionCriteria") + public static class SourceSelectionCriteria { + @Element(name = "ReplicaModifications", required = false) + private ReplicaModifications replicaModifications; + + @Element(name = "SseKmsEncryptedObjects", required = false) + private SseKmsEncryptedObjects sseKmsEncryptedObjects; + + public SourceSelectionCriteria( + @Nullable @Element(name = "SseKmsEncryptedObjects", required = false) + SseKmsEncryptedObjects sseKmsEncryptedObjects, + @Nullable @Element(name = "ReplicaModifications", required = false) + ReplicaModifications replicaModifications) { + this.sseKmsEncryptedObjects = sseKmsEncryptedObjects; + this.replicaModifications = replicaModifications; + } + + public SourceSelectionCriteria(@Nullable SseKmsEncryptedObjects sseKmsEncryptedObjects) { + this(sseKmsEncryptedObjects, null); + } + + public ReplicaModifications replicaModifications() { + return this.replicaModifications; + } + + public SseKmsEncryptedObjects sseKmsEncryptedObjects() { + return this.sseKmsEncryptedObjects; + } + + @Override + public String toString() { + return String.format( + "SourceSelectionCriteria{replicaModifications=%s, sseKmsEncryptedObjects=%s}", + Utils.stringify(replicaModifications), Utils.stringify(sseKmsEncryptedObjects)); + } + } + + @Root(name = "ReplicaModifications") + public static class ReplicaModifications { + @Element(name = "Status") + private Status status; + + public ReplicaModifications(@Nonnull @Element(name = "Status") Status status) { + this.status = Objects.requireNonNull(status, "Status must not be null"); + } + + public Status status() { + return this.status; + } + + @Override + public String toString() { + return String.format("ReplicaModifications{status=%s}", Utils.stringify(status)); + } + } + + @Root(name = "SseKmsEncryptedObjects") + public static class SseKmsEncryptedObjects { + @Element(name = "Status") + private Status status; + + public SseKmsEncryptedObjects(@Nonnull @Element(name = "Status") Status status) { + this.status = Objects.requireNonNull(status, "Status must not be null"); + } + + public Status status() { + return this.status; + } + + @Override + public String toString() { + return String.format("SseKmsEncryptedObjects{status=%s}", Utils.stringify(status)); + } + } + + /** This is MinIO specific extension. */ + @Root(name = "DeleteReplication") + public static class DeleteReplication { + @Element(name = "Status", required = false) + private Status status; + + public DeleteReplication(@Nullable @Element(name = "Status", required = false) Status status) { + this.status = (status == null) ? Status.DISABLED : status; + } + + public Status status() { + return status; + } + + @Override + public String toString() { + return String.format("DeleteReplication{status=%s}", Utils.stringify(status)); + } + } } diff --git a/api/src/main/java/io/minio/messages/ReplicationDestination.java b/api/src/main/java/io/minio/messages/ReplicationDestination.java deleted file mode 100644 index 2a308d639..000000000 --- a/api/src/main/java/io/minio/messages/ReplicationDestination.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote Destination information for {@link ReplicationRule}. */ -@Root(name = "Destination") -public class ReplicationDestination { - @Element(name = "AccessControlTranslation", required = false) - private AccessControlTranslation accessControlTranslation; - - @Element(name = "Account", required = false) - private String account; - - @Element(name = "Bucket") - private String bucketArn; - - @Element(name = "EncryptionConfiguration", required = false) - private EncryptionConfiguration encryptionConfiguration; - - @Element(name = "Metrics", required = false) - private Metrics metrics; - - @Element(name = "ReplicationTime", required = false) - private ReplicationTime replicationTime; - - @Element(name = "StorageClass", required = false) - private String storageClass; - - public ReplicationDestination( - @Nullable @Element(name = "AccessControlTranslation", required = false) - AccessControlTranslation accessControlTranslation, - @Nullable @Element(name = "Account", required = false) String account, - @Nonnull @Element(name = "Bucket") String bucketArn, - @Nullable @Element(name = "EncryptionConfiguration", required = false) - EncryptionConfiguration encryptionConfiguration, - @Nullable @Element(name = "Metrics", required = false) Metrics metrics, - @Nullable @Element(name = "ReplicationTime", required = false) - ReplicationTime replicationTime, - @Nullable @Element(name = "StorageClass", required = false) String storageClass) { - this.accessControlTranslation = accessControlTranslation; - this.account = account; - this.bucketArn = Objects.requireNonNull(bucketArn, "Bucket ARN must not be null"); - this.encryptionConfiguration = encryptionConfiguration; - this.metrics = metrics; - this.replicationTime = replicationTime; - this.storageClass = storageClass; - } - - public AccessControlTranslation accessControlTranslation() { - return this.accessControlTranslation; - } - - public String account() { - return this.account; - } - - public String bucketArn() { - return this.bucketArn; - } - - public EncryptionConfiguration encryptionConfiguration() { - return encryptionConfiguration; - } - - public Metrics metrics() { - return this.metrics; - } - - public ReplicationTime replicationTime() { - return this.replicationTime; - } - - public String storageClass() { - return this.storageClass; - } -} diff --git a/api/src/main/java/io/minio/messages/ReplicationRule.java b/api/src/main/java/io/minio/messages/ReplicationRule.java deleted file mode 100644 index 75993da97..000000000 --- a/api/src/main/java/io/minio/messages/ReplicationRule.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; - -/** Helper class to denote Rule information for {@link ReplicationConfiguration}. */ -@Root(name = "Rule") -public class ReplicationRule { - @Element(name = "DeleteMarkerReplication", required = false) - private DeleteMarkerReplication deleteMarkerReplication; - - @Element(name = "Destination") - private ReplicationDestination destination; - - @Element(name = "ExistingObjectReplication", required = false) - private ExistingObjectReplication existingObjectReplication; - - @Element(name = "Filter", required = false) - private RuleFilter filter; - - @Element(name = "ID", required = false) - private String id; - - @Element(name = "Prefix", required = false) - @Convert(PrefixConverter.class) - private String prefix; - - @Element(name = "Priority", required = false) - private Integer priority; - - @Element(name = "SourceSelectionCriteria", required = false) - private SourceSelectionCriteria sourceSelectionCriteria; - - @Element(name = "DeleteReplication", required = false) - private DeleteReplication deleteReplication; // This is MinIO specific extension. - - @Element(name = "Status") - private Status status; - - /** Constructs new server-side encryption configuration rule. */ - public ReplicationRule( - @Nullable @Element(name = "DeleteMarkerReplication", required = false) - DeleteMarkerReplication deleteMarkerReplication, - @Nonnull @Element(name = "Destination") ReplicationDestination destination, - @Nullable @Element(name = "ExistingObjectReplication", required = false) - ExistingObjectReplication existingObjectReplication, - @Nullable @Element(name = "Filter", required = false) RuleFilter filter, - @Nullable @Element(name = "ID", required = false) String id, - @Nullable @Element(name = "Prefix", required = false) String prefix, - @Nullable @Element(name = "Priority", required = false) Integer priority, - @Nullable @Element(name = "SourceSelectionCriteria", required = false) - SourceSelectionCriteria sourceSelectionCriteria, - @Nullable @Element(name = "DeleteReplication", required = false) - DeleteReplication deleteReplication, - @Nonnull @Element(name = "Status") Status status) { - - if (filter != null && deleteMarkerReplication == null) { - deleteMarkerReplication = new DeleteMarkerReplication(null); - } - - if (id != null) { - id = id.trim(); - if (id.isEmpty()) throw new IllegalArgumentException("ID must be non-empty string"); - if (id.length() > 255) throw new IllegalArgumentException("ID must be exceed 255 characters"); - } - - this.deleteMarkerReplication = deleteMarkerReplication; - this.destination = Objects.requireNonNull(destination, "Destination must not be null"); - this.existingObjectReplication = existingObjectReplication; - this.filter = filter; - this.id = id; - this.prefix = prefix; - this.priority = priority; - this.sourceSelectionCriteria = sourceSelectionCriteria; - this.deleteReplication = deleteReplication; - this.status = Objects.requireNonNull(status, "Status must not be null"); - } - - /** Constructs new server-side encryption configuration rule. */ - public ReplicationRule( - @Nullable @Element(name = "DeleteMarkerReplication", required = false) - DeleteMarkerReplication deleteMarkerReplication, - @Nonnull @Element(name = "Destination") ReplicationDestination destination, - @Nullable @Element(name = "ExistingObjectReplication", required = false) - ExistingObjectReplication existingObjectReplication, - @Nullable @Element(name = "Filter", required = false) RuleFilter filter, - @Nullable @Element(name = "ID", required = false) String id, - @Nullable @Element(name = "Prefix", required = false) String prefix, - @Nullable @Element(name = "Priority", required = false) Integer priority, - @Nullable @Element(name = "SourceSelectionCriteria", required = false) - SourceSelectionCriteria sourceSelectionCriteria, - @Nonnull @Element(name = "Status") Status status) { - this( - deleteMarkerReplication, - destination, - existingObjectReplication, - filter, - id, - prefix, - priority, - sourceSelectionCriteria, - null, - status); - } - - public DeleteMarkerReplication deleteMarkerReplication() { - return this.deleteMarkerReplication; - } - - public ReplicationDestination destination() { - return this.destination; - } - - public ExistingObjectReplication existingObjectReplication() { - return this.existingObjectReplication; - } - - public RuleFilter filter() { - return this.filter; - } - - public String id() { - return this.id; - } - - public String prefix() { - return this.prefix; - } - - public Integer priority() { - return this.priority; - } - - public SourceSelectionCriteria sourceSelectionCriteria() { - return this.sourceSelectionCriteria; - } - - public DeleteReplication deleteReplication() { - return this.deleteReplication; - } - - public Status status() { - return this.status; - } -} diff --git a/api/src/main/java/io/minio/messages/ReplicationTime.java b/api/src/main/java/io/minio/messages/ReplicationTime.java deleted file mode 100644 index 9ba40cadb..000000000 --- a/api/src/main/java/io/minio/messages/ReplicationTime.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote replication time information for {@link ReplicationDestination}. */ -@Root(name = "ReplicationTime") -public class ReplicationTime { - @Element(name = "Time") - private ReplicationTimeValue time; - - @Element(name = "Status") - private Status status; - - public ReplicationTime( - @Nonnull @Element(name = "Time") ReplicationTimeValue time, - @Nonnull @Element(name = "Status") Status status) { - this.time = Objects.requireNonNull(time, "Time must not be null"); - this.status = Objects.requireNonNull(status, "Status must not be null"); - } - - public ReplicationTimeValue time() { - return this.time; - } - - public Status status() { - return this.status; - } -} diff --git a/api/src/main/java/io/minio/messages/ReplicationTimeValue.java b/api/src/main/java/io/minio/messages/ReplicationTimeValue.java deleted file mode 100644 index 41886278c..000000000 --- a/api/src/main/java/io/minio/messages/ReplicationTimeValue.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.messages; - -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote replication time value information for {@link Metrics}. */ -@Root(name = "ReplicationTimeValue") -public class ReplicationTimeValue { - @Element(name = "Minutes", required = false) - private Integer minutes = 15; - - public ReplicationTimeValue( - @Nullable @Element(name = "Minutes", required = false) Integer minutes) { - this.minutes = minutes; - } - - public Integer minutes() { - return this.minutes; - } -} diff --git a/api/src/main/java/io/minio/messages/RequestProgress.java b/api/src/main/java/io/minio/messages/RequestProgress.java deleted file mode 100644 index 1f3eb9c7c..000000000 --- a/api/src/main/java/io/minio/messages/RequestProgress.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote progress request in select object content request XML for {@link - * SelectObjectContentRequest}. - */ -@Root(name = "RequestProgress", strict = false) -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class RequestProgress { - @Element(name = "Enabled") - private boolean enabled = true; - - /** Constructs a new RequestProgress object. */ - public RequestProgress() {} -} diff --git a/api/src/main/java/io/minio/messages/ResponseDate.java b/api/src/main/java/io/minio/messages/ResponseDate.java deleted file mode 100644 index 9e3f426ac..000000000 --- a/api/src/main/java/io/minio/messages/ResponseDate.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2015 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.messages; - -import com.fasterxml.jackson.annotation.JsonCreator; -import io.minio.Time; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.Locale; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -/** S3 specified response time wrapping {@link ZonedDateTime}. */ -@Root -@Convert(ResponseDate.ResponseDateConverter.class) -public class ResponseDate { - public static final DateTimeFormatter MINIO_RESPONSE_DATE_FORMAT = - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH':'mm':'ss'Z'", Locale.US).withZone(Time.UTC); - - private ZonedDateTime zonedDateTime; - - public ResponseDate() {} - - public ResponseDate(ZonedDateTime zonedDateTime) { - this.zonedDateTime = zonedDateTime; - } - - public ZonedDateTime zonedDateTime() { - return zonedDateTime; - } - - public String toString() { - return zonedDateTime.format(Time.RESPONSE_DATE_FORMAT); - } - - @JsonCreator - public static ResponseDate fromString(String responseDateString) { - try { - return new ResponseDate(ZonedDateTime.parse(responseDateString, Time.RESPONSE_DATE_FORMAT)); - } catch (DateTimeParseException e) { - return new ResponseDate(ZonedDateTime.parse(responseDateString, MINIO_RESPONSE_DATE_FORMAT)); - } - } - - /** XML converter class. */ - public static class ResponseDateConverter implements Converter { - @Override - public ResponseDate read(InputNode node) throws Exception { - return ResponseDate.fromString(node.getValue()); - } - - @Override - public void write(OutputNode node, ResponseDate amzDate) { - node.setValue(amzDate.toString()); - } - } -} diff --git a/api/src/main/java/io/minio/messages/RestoreRequest.java b/api/src/main/java/io/minio/messages/RestoreRequest.java index a7718604e..bfe0fc208 100644 --- a/api/src/main/java/io/minio/messages/RestoreRequest.java +++ b/api/src/main/java/io/minio/messages/RestoreRequest.java @@ -16,13 +16,23 @@ package io.minio.messages; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.minio.Utils; +import java.util.Map; +import java.util.Objects; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.simpleframework.xml.Element; +import org.simpleframework.xml.ElementMap; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; +import org.simpleframework.xml.convert.Convert; +import org.simpleframework.xml.convert.Converter; +import org.simpleframework.xml.stream.InputNode; +import org.simpleframework.xml.stream.OutputNode; /** - * Object representation of request XML of RestoreObject * API. */ @@ -67,4 +77,219 @@ public RestoreRequest( this.selectParameters = selectParameters; this.outputLocation = outputLocation; } + + @Root(name = "Tier") + @Convert(Tier.TierConverter.class) + public static enum Tier { + STANDARD("Standard"), + BULK("Bulk"), + EXPEDITED("Expedited"); + + private final String value; + + private Tier(String value) { + this.value = value; + } + + public String toString() { + return this.value; + } + + /** Returns Tier of given string. */ + @JsonCreator + public static Tier fromString(String tierString) { + for (Tier tier : Tier.values()) { + if (tierString.equals(tier.value)) { + return tier; + } + } + + throw new IllegalArgumentException("Unknown tier '" + tierString + "'"); + } + + /** XML converter class. */ + public static class TierConverter implements Converter { + @Override + public Tier read(InputNode node) throws Exception { + return Tier.fromString(node.getValue()); + } + + @Override + public void write(OutputNode node, Tier tier) throws Exception { + node.setValue(tier.toString()); + } + } + } + + @Root(name = "GlacierJobParameters") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class GlacierJobParameters { + @Element(name = "Tier") + private Tier tier; + + public GlacierJobParameters(@Nonnull Tier tier) { + this.tier = Objects.requireNonNull(tier, "Tier must not be null"); + } + } + + @Root(name = "SelectParameters") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class SelectParameters extends BaseSelectParameters { + public SelectParameters( + @Nonnull String expression, + @Nonnull InputSerialization is, + @Nonnull OutputSerialization os) { + super(expression, is, os); + } + } + + @Root(name = "OutputLocation") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class OutputLocation { + @Element(name = "S3") + private S3 s3; + + public OutputLocation(@Nonnull S3 s3) { + this.s3 = Objects.requireNonNull(s3, "S3 must not be null"); + } + } + + @Root(name = "S3") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class S3 { + @Element(name = "AccessControlList", required = false) + private AccessControlList accessControlList; + + @Element(name = "BucketName") + private String bucketName; + + @Element(name = "CannedACL", required = false) + private CannedAcl cannedAcl; + + @Element(name = "Encryption", required = false) + private Encryption encryption; + + @Element(name = "Prefix") + private String prefix; + + @Element(name = "StorageClass", required = false) + private String storageClass; + + @Element(name = "Tagging", required = false) + private Tags tagging; + + @Element(name = "UserMetadata", required = false) + private UserMetadata userMetadata; + + public S3( + @Nonnull String bucketName, + @Nonnull String prefix, + @Nullable AccessControlList accessControlList, + @Nullable CannedAcl cannedAcl, + @Nullable Encryption encryption, + @Nullable String storageClass, + @Nullable Tags tagging, + @Nullable UserMetadata userMetadata) { + this.bucketName = Objects.requireNonNull(bucketName, "Bucket name must not be null"); + this.prefix = Objects.requireNonNull(prefix, "Prefix must not be null"); + this.accessControlList = accessControlList; + this.cannedAcl = cannedAcl; + this.encryption = encryption; + this.storageClass = storageClass; + this.tagging = tagging; + this.userMetadata = userMetadata; + } + } + + @Root(name = "CannedAcl") + @Convert(CannedAcl.CannedAclConverter.class) + public static enum CannedAcl { + PRIVATE("private"), + PUBLIC_READ("public-read"), + PUBLIC_READ_WRITE("public-read-write"), + AUTHENTICATED_READ("authenticated-read"), + AWS_EXEC_READ("aws-exec-read"), + BUCKET_OWNER_READ("bucket-owner-read"), + BUCKET_OWNER_FULL_CONTROL("bucket-owner-full-control"); + + private final String value; + + private CannedAcl(String value) { + this.value = value; + } + + public String toString() { + return this.value; + } + + /** Returns CannedAcl of given string. */ + @JsonCreator + public static CannedAcl fromString(String cannedAclString) { + for (CannedAcl cannedAcl : CannedAcl.values()) { + if (cannedAclString.equals(cannedAcl.value)) { + return cannedAcl; + } + } + + throw new IllegalArgumentException("Unknown canned ACL '" + cannedAclString + "'"); + } + + /** XML converter class. */ + public static class CannedAclConverter implements Converter { + @Override + public CannedAcl read(InputNode node) throws Exception { + return CannedAcl.fromString(node.getValue()); + } + + @Override + public void write(OutputNode node, CannedAcl cannedAcl) throws Exception { + node.setValue(cannedAcl.toString()); + } + } + } + + @Root(name = "Encryption") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class Encryption { + @Element(name = "EncryptionType") + private SseAlgorithm encryptionType; + + @Element(name = "KMSContext", required = false) + private String kmsContext; + + @Element(name = "KMSKeyId", required = false) + private String kmsKeyId; + + public Encryption( + @Nonnull SseAlgorithm encryptionType, + @Nullable String kmsContext, + @Nullable String kmsKeyId) { + this.encryptionType = + Objects.requireNonNull(encryptionType, "Encryption type must not be null"); + this.kmsContext = kmsContext; + this.kmsKeyId = kmsKeyId; + } + } + + @Root(name = "UserMetadata", strict = false) + @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class UserMetadata { + @ElementMap( + attribute = false, + entry = "MetadataEntry", + inline = true, + key = "Name", + value = "Value", + required = false) + Map metadataEntries; + + private UserMetadata(@Nonnull Map metadataEntries) { + Objects.requireNonNull(metadataEntries, "Metadata entries must not be null"); + if (metadataEntries.size() == 0) { + throw new IllegalArgumentException("Metadata entries must not be empty"); + } + this.metadataEntries = Utils.unmodifiableMap(metadataEntries); + } + } } diff --git a/api/src/main/java/io/minio/messages/Retention.java b/api/src/main/java/io/minio/messages/Retention.java index ade0cd21a..04b54373b 100644 --- a/api/src/main/java/io/minio/messages/Retention.java +++ b/api/src/main/java/io/minio/messages/Retention.java @@ -16,13 +16,15 @@ package io.minio.messages; +import io.minio.Time; +import io.minio.Utils; import java.time.ZonedDateTime; import org.simpleframework.xml.Element; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; /** - * Object representation of request XML of PutObjectRetention * API and response XML of GetObjectRetention @@ -35,7 +37,7 @@ public class Retention { private RetentionMode mode; @Element(name = "RetainUntilDate", required = false) - private ResponseDate retainUntilDate; + private Time.S3Time retainUntilDate; public Retention() {} @@ -50,7 +52,7 @@ public Retention(RetentionMode mode, ZonedDateTime retainUntilDate) { } this.mode = mode; - this.retainUntilDate = new ResponseDate(retainUntilDate); + this.retainUntilDate = new Time.S3Time(retainUntilDate); } /** Returns mode. */ @@ -60,10 +62,13 @@ public RetentionMode mode() { /** Returns retain until date. */ public ZonedDateTime retainUntilDate() { - if (retainUntilDate != null) { - return retainUntilDate.zonedDateTime(); - } + return retainUntilDate == null ? null : retainUntilDate.toZonedDateTime(); + } - return null; + @Override + public String toString() { + return String.format( + "Retention{mode=%s, retainUntilDate=%s}", + Utils.stringify(mode), Utils.stringify(retainUntilDate)); } } diff --git a/api/src/main/java/io/minio/messages/RetentionDuration.java b/api/src/main/java/io/minio/messages/RetentionDuration.java deleted file mode 100644 index 7550dbbd2..000000000 --- a/api/src/main/java/io/minio/messages/RetentionDuration.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.messages; - -/** Retention duration of {@link ObjectLockConfiguration} */ -public interface RetentionDuration { - public RetentionDurationUnit unit(); - - public int duration(); -} diff --git a/api/src/main/java/io/minio/messages/RetentionDurationDays.java b/api/src/main/java/io/minio/messages/RetentionDurationDays.java deleted file mode 100644 index ad0eaa5ad..000000000 --- a/api/src/main/java/io/minio/messages/RetentionDurationDays.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.messages; - -import org.simpleframework.xml.Root; -import org.simpleframework.xml.Text; - -/** Days type retention duration of {@link ObjectLockConfiguration} */ -@Root(name = "Days") -public class RetentionDurationDays implements RetentionDuration { - @Text(required = false) - private Integer days; - - public RetentionDurationDays() {} - - public RetentionDurationDays(int days) { - this.days = Integer.valueOf(days); - } - - public RetentionDurationUnit unit() { - return RetentionDurationUnit.DAYS; - } - - public int duration() { - return days; - } - - /** Returns RetentionDurationDays as string. */ - @Override - public String toString() { - if (days == null) { - return ""; - } - return days.toString() + ((days == 1) ? " day" : " days"); - } -} diff --git a/api/src/main/java/io/minio/messages/RetentionDurationUnit.java b/api/src/main/java/io/minio/messages/RetentionDurationUnit.java deleted file mode 100644 index 22823805d..000000000 --- a/api/src/main/java/io/minio/messages/RetentionDurationUnit.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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.messages; - -/** Duration unit of default retention configuration. */ -public enum RetentionDurationUnit { - DAYS, - YEARS; -} diff --git a/api/src/main/java/io/minio/messages/RetentionDurationYears.java b/api/src/main/java/io/minio/messages/RetentionDurationYears.java deleted file mode 100644 index 50b75e663..000000000 --- a/api/src/main/java/io/minio/messages/RetentionDurationYears.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.messages; - -import org.simpleframework.xml.Root; -import org.simpleframework.xml.Text; - -/** Years type retention duration of {@link ObjectLockConfiguration} */ -@Root(name = "Years") -public class RetentionDurationYears implements RetentionDuration { - @Text(required = false) - private Integer years; - - public RetentionDurationYears() {} - - public RetentionDurationYears(int years) { - this.years = Integer.valueOf(years); - } - - public RetentionDurationUnit unit() { - return RetentionDurationUnit.YEARS; - } - - public int duration() { - return years; - } - - /** Returns RetentionDurationYears as string. */ - @Override - public String toString() { - if (years == null) { - return ""; - } - return years.toString() + ((years == 1) ? " year" : " years"); - } -} diff --git a/api/src/main/java/io/minio/messages/Rule.java b/api/src/main/java/io/minio/messages/Rule.java deleted file mode 100644 index 65cf0c829..000000000 --- a/api/src/main/java/io/minio/messages/Rule.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.ElementUnion; -import org.simpleframework.xml.Path; -import org.simpleframework.xml.Root; - -/** Helper class to denote Rule information for {@link ObjectLockConfiguration}. */ -@Root(name = "Rule", strict = false) -public class Rule { - @Path(value = "DefaultRetention") - @Element(name = "Mode", required = false) - private RetentionMode mode; - - @Path(value = "DefaultRetention") - @ElementUnion({ - @Element(name = "Days", type = RetentionDurationDays.class, required = false), - @Element(name = "Years", type = RetentionDurationYears.class, required = false) - }) - private RetentionDuration duration; - - public Rule( - @Element(name = "Mode", required = false) RetentionMode mode, - @ElementUnion({ - @Element(name = "Days", type = RetentionDurationDays.class, required = false), - @Element(name = "Years", type = RetentionDurationYears.class, required = false) - }) - RetentionDuration duration) { - if (mode != null && duration != null) { - this.mode = mode; - this.duration = duration; - } else if (mode != null || duration != null) { - if (mode == null) { - throw new IllegalArgumentException("mode is null"); - } - throw new IllegalArgumentException("duration is null"); - } - } - - public RetentionMode mode() { - return mode; - } - - public RetentionDuration duration() { - return duration; - } -} diff --git a/api/src/main/java/io/minio/messages/RuleFilter.java b/api/src/main/java/io/minio/messages/RuleFilter.java deleted file mode 100644 index c5da2464a..000000000 --- a/api/src/main/java/io/minio/messages/RuleFilter.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; - -/** - * Helper class to denote filter information for {@link ReplicationRule} and {@link LifecycleRule}. - */ -@Root(name = "Filter") -public class RuleFilter { - @Element(name = "And", required = false) - private AndOperator andOperator; - - @Element(name = "Prefix", required = false) - @Convert(PrefixConverter.class) - private String prefix; - - @Element(name = "Tag", required = false) - private Tag tag; - - @Element(name = "ObjectSizeLessThan", required = false) - private Long objectSizeLessThan; - - @Element(name = "ObjectSizeGreaterThan", required = false) - private Long objectSizeGreaterThan; - - public RuleFilter( - @Nullable @Element(name = "And", required = false) AndOperator andOperator, - @Nullable @Element(name = "Prefix", required = false) String prefix, - @Nullable @Element(name = "Tag", required = false) Tag tag) { - if (andOperator != null ^ prefix != null ^ tag != null) { - this.andOperator = andOperator; - this.prefix = prefix; - this.tag = tag; - } else { - throw new IllegalArgumentException("Only one of And, Prefix or Tag must be set"); - } - } - - public RuleFilter( - @Nullable @Element(name = "And", required = false) AndOperator andOperator, - @Nullable @Element(name = "Prefix", required = false) String prefix, - @Nullable @Element(name = "Tag", required = false) Tag tag, - @Nullable @Element(name = "ObjectSizeLessThan", required = false) Long objectSizeLessThan, - @Nullable @Element(name = "ObjectSizeGreaterThan", required = false) - Long objectSizeGreaterThan) { - this(andOperator, prefix, tag); - this.objectSizeLessThan = objectSizeLessThan; - this.objectSizeGreaterThan = objectSizeGreaterThan; - } - - public RuleFilter(@Nonnull AndOperator andOperator) { - this.andOperator = Objects.requireNonNull(andOperator, "And operator must not be null"); - } - - public RuleFilter(@Nonnull String prefix) { - this.prefix = Objects.requireNonNull(prefix, "Prefix must not be null"); - } - - public RuleFilter(@Nonnull Tag tag) { - this.tag = Objects.requireNonNull(tag, "Tag must not be null"); - } - - public AndOperator andOperator() { - return this.andOperator; - } - - public String prefix() { - return this.prefix; - } - - public Tag tag() { - return this.tag; - } - - public Long objectSizeLessThan() { - return this.objectSizeLessThan; - } - - public Long objectSizeGreaterThan() { - return this.objectSizeGreaterThan; - } -} diff --git a/api/src/main/java/io/minio/messages/S3OutputLocation.java b/api/src/main/java/io/minio/messages/S3OutputLocation.java deleted file mode 100644 index 17c0237c7..000000000 --- a/api/src/main/java/io/minio/messages/S3OutputLocation.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote S3 output location information of {@link OutputLocation}. */ -@Root(name = "S3OutputLocation") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class S3OutputLocation { - @Element(name = "AccessControlList", required = false) - private AccessControlList accessControlList; - - @Element(name = "BucketName") - private String bucketName; - - @Element(name = "CannedACL", required = false) - private CannedAcl cannedAcl; - - @Element(name = "Encryption", required = false) - private Encryption encryption; - - @Element(name = "Prefix") - private String prefix; - - @Element(name = "StorageClass", required = false) - private String storageClass; - - @Element(name = "Tagging", required = false) - private Tags tagging; - - @Element(name = "UserMetadata", required = false) - private UserMetadata userMetadata; - - public S3OutputLocation( - @Nonnull String bucketName, - @Nonnull String prefix, - @Nullable AccessControlList accessControlList, - @Nullable CannedAcl cannedAcl, - @Nullable Encryption encryption, - @Nullable String storageClass, - @Nullable Tags tagging, - @Nullable UserMetadata userMetadata) { - this.bucketName = Objects.requireNonNull(bucketName, "Bucket name must not be null"); - this.prefix = Objects.requireNonNull(prefix, "Prefix must not be null"); - this.accessControlList = accessControlList; - this.cannedAcl = cannedAcl; - this.encryption = encryption; - this.storageClass = storageClass; - this.tagging = tagging; - this.userMetadata = userMetadata; - } -} diff --git a/api/src/main/java/io/minio/messages/ScanRange.java b/api/src/main/java/io/minio/messages/ScanRange.java deleted file mode 100644 index 8e2c8b7dd..000000000 --- a/api/src/main/java/io/minio/messages/ScanRange.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2019 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote scan range in select object content request XML for {@link - * SelectObjectContentRequest}. - */ -@Root(name = "ScanRange") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class ScanRange { - @Element(name = "Start", required = false) - private Long start; - - @Element(name = "End", required = false) - private Long end; - - /** Constructs new ScanRange object for given start and end. */ - public ScanRange(Long start, Long end) { - this.start = start; - this.end = end; - } -} diff --git a/api/src/main/java/io/minio/messages/SelectObjectContentRequest.java b/api/src/main/java/io/minio/messages/SelectObjectContentRequest.java index 59641837b..22680be4d 100644 --- a/api/src/main/java/io/minio/messages/SelectObjectContentRequest.java +++ b/api/src/main/java/io/minio/messages/SelectObjectContentRequest.java @@ -23,21 +23,20 @@ import org.simpleframework.xml.Root; /** - * Object representation of request XML of SelectObjectContent * API. */ @Root(name = "SelectObjectContentRequest") @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class SelectObjectContentRequest extends SelectObjectContentRequestBase { +public class SelectObjectContentRequest extends BaseSelectParameters { @Element(name = "RequestProgress", required = false) private RequestProgress requestProgress; @Element(name = "ScanRange", required = false) private ScanRange scanRange; - /** Constructs new SelectObjectContentRequest object for given parameters. */ public SelectObjectContentRequest( @Nonnull String expression, boolean requestProgress, @@ -53,4 +52,28 @@ public SelectObjectContentRequest( this.scanRange = new ScanRange(scanStartRange, scanEndRange); } } + + @Root(name = "RequestProgress", strict = false) + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class RequestProgress { + @Element(name = "Enabled") + private boolean enabled = true; + + public RequestProgress() {} + } + + @Root(name = "ScanRange") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class ScanRange { + @Element(name = "Start", required = false) + private Long start; + + @Element(name = "End", required = false) + private Long end; + + public ScanRange(Long start, Long end) { + this.start = start; + this.end = end; + } + } } diff --git a/api/src/main/java/io/minio/messages/SelectParameters.java b/api/src/main/java/io/minio/messages/SelectParameters.java deleted file mode 100644 index cf8326ab9..000000000 --- a/api/src/main/java/io/minio/messages/SelectParameters.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import javax.annotation.Nonnull; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote the parameters for Select job types information of {@link RestoreRequest}. - */ -@Root(name = "SelectParameters") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class SelectParameters extends SelectObjectContentRequestBase { - public SelectParameters( - @Nonnull String expression, @Nonnull InputSerialization is, @Nonnull OutputSerialization os) { - super(expression, is, os); - } -} diff --git a/api/src/main/java/io/minio/messages/Source.java b/api/src/main/java/io/minio/messages/Source.java deleted file mode 100644 index fe5896e4c..000000000 --- a/api/src/main/java/io/minio/messages/Source.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.messages; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Helper class to denote client information causes this event. This is MinIO extension to Event - * Message Structure - */ -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( - value = "UwF", - justification = "Everything in this class is initialized by JSON unmarshalling.") -public class Source { - @JsonProperty private String host; - @JsonProperty private String port; - @JsonProperty private String userAgent; - - public String host() { - return host; - } - - public String port() { - return port; - } - - public String userAgent() { - return userAgent; - } -} diff --git a/api/src/main/java/io/minio/messages/SourceSelectionCriteria.java b/api/src/main/java/io/minio/messages/SourceSelectionCriteria.java deleted file mode 100644 index 542fc9617..000000000 --- a/api/src/main/java/io/minio/messages/SourceSelectionCriteria.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.messages; - -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote source selection criteria information for {@link ReplicationRule}. */ -@Root(name = "SourceSelectionCriteria") -public class SourceSelectionCriteria { - @Element(name = "ReplicaModifications", required = false) - private ReplicaModifications replicaModifications; - - @Element(name = "SseKmsEncryptedObjects", required = false) - private SseKmsEncryptedObjects sseKmsEncryptedObjects; - - public SourceSelectionCriteria( - @Nullable @Element(name = "SseKmsEncryptedObjects", required = false) - SseKmsEncryptedObjects sseKmsEncryptedObjects, - @Nullable @Element(name = "ReplicaModifications", required = false) - ReplicaModifications replicaModifications) { - this.sseKmsEncryptedObjects = sseKmsEncryptedObjects; - this.replicaModifications = replicaModifications; - } - - public SourceSelectionCriteria(@Nullable SseKmsEncryptedObjects sseKmsEncryptedObjects) { - this(sseKmsEncryptedObjects, null); - } - - public ReplicaModifications replicaModifications() { - return this.replicaModifications; - } - - public SseKmsEncryptedObjects sseKmsEncryptedObjects() { - return this.sseKmsEncryptedObjects; - } -} diff --git a/api/src/main/java/io/minio/messages/SseConfiguration.java b/api/src/main/java/io/minio/messages/SseConfiguration.java index 1107be67d..503dc348c 100644 --- a/api/src/main/java/io/minio/messages/SseConfiguration.java +++ b/api/src/main/java/io/minio/messages/SseConfiguration.java @@ -16,39 +16,83 @@ package io.minio.messages; +import io.minio.Utils; +import java.util.Objects; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.simpleframework.xml.Element; import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.Path; import org.simpleframework.xml.Root; /** - * Object representation of request XML of PutBucketEncryption * API and response XML of GetBucketEncryption * API. */ -@Root(name = "ServerSideEncryptionConfiguration") +@Root(name = "ServerSideEncryptionConfiguration", strict = false) @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") public class SseConfiguration { @Element(name = "Rule", required = false) - private SseConfigurationRule rule; + private Rule rule; - public SseConfiguration( - @Nullable @Element(name = "Rule", required = false) SseConfigurationRule rule) { + public SseConfiguration(@Nullable @Element(name = "Rule", required = false) Rule rule) { this.rule = rule; } public static SseConfiguration newConfigWithSseS3Rule() { - return new SseConfiguration(new SseConfigurationRule(SseAlgorithm.AES256, null)); + return new SseConfiguration(new Rule(SseAlgorithm.AES256, null)); } public static SseConfiguration newConfigWithSseKmsRule(@Nullable String kmsMasterKeyId) { - return new SseConfiguration(new SseConfigurationRule(SseAlgorithm.AWS_KMS, kmsMasterKeyId)); + return new SseConfiguration(new Rule(SseAlgorithm.AWS_KMS, kmsMasterKeyId)); } - public SseConfigurationRule rule() { + public Rule rule() { return this.rule; } + + @Override + public String toString() { + return String.format("SseConfiguration{rule=%s}", Utils.stringify(rule)); + } + + @Root(name = "Rule", strict = false) + @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") + public static class Rule { + @Path(value = "ApplyServerSideEncryptionByDefault") + @Element(name = "KMSMasterKeyID", required = false) + private String kmsMasterKeyId; + + @Path(value = "ApplyServerSideEncryptionByDefault") + @Element(name = "SSEAlgorithm") + private SseAlgorithm sseAlgorithm; + + /** Constructs new server-side encryption configuration rule. */ + public Rule( + @Nonnull @Element(name = "SSEAlgorithm") SseAlgorithm sseAlgorithm, + @Nullable @Element(name = "KMSMasterKeyID", required = false) String kmsMasterKeyId) { + this.sseAlgorithm = Objects.requireNonNull(sseAlgorithm, "SSE Algorithm must be provided"); + this.kmsMasterKeyId = kmsMasterKeyId; + } + + public String kmsMasterKeyId() { + return this.kmsMasterKeyId; + } + + public SseAlgorithm sseAlgorithm() { + return this.sseAlgorithm; + } + + @Override + public String toString() { + return String.format( + "Rule{sseAlgorithm=%s, kmsMasterKeyId=%s}", + Utils.stringify(sseAlgorithm), Utils.stringify(kmsMasterKeyId)); + } + } } diff --git a/api/src/main/java/io/minio/messages/SseConfigurationRule.java b/api/src/main/java/io/minio/messages/SseConfigurationRule.java deleted file mode 100644 index ff997a39c..000000000 --- a/api/src/main/java/io/minio/messages/SseConfigurationRule.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Namespace; -import org.simpleframework.xml.Path; -import org.simpleframework.xml.Root; - -/** Helper class to denote Rule information for {@link SseConfiguration}. */ -@Root(name = "Rule") -@Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class SseConfigurationRule { - @Path(value = "ApplyServerSideEncryptionByDefault") - @Element(name = "KMSMasterKeyID", required = false) - private String kmsMasterKeyId; - - @Path(value = "ApplyServerSideEncryptionByDefault") - @Element(name = "SSEAlgorithm") - private SseAlgorithm sseAlgorithm; - - /** Constructs new server-side encryption configuration rule. */ - public SseConfigurationRule( - @Nonnull @Element(name = "SSEAlgorithm") SseAlgorithm sseAlgorithm, - @Nullable @Element(name = "KMSMasterKeyID", required = false) String kmsMasterKeyId) { - this.sseAlgorithm = Objects.requireNonNull(sseAlgorithm, "SSE Algorithm must be provided"); - this.kmsMasterKeyId = kmsMasterKeyId; - } - - public String kmsMasterKeyId() { - return this.kmsMasterKeyId; - } - - public SseAlgorithm sseAlgorithm() { - return this.sseAlgorithm; - } -} diff --git a/api/src/main/java/io/minio/messages/SseKmsEncryptedObjects.java b/api/src/main/java/io/minio/messages/SseKmsEncryptedObjects.java deleted file mode 100644 index 1cdf045eb..000000000 --- a/api/src/main/java/io/minio/messages/SseKmsEncryptedObjects.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote SSE KMS encrypted objects information for {@link SourceSelectionCriteria}. - */ -@Root(name = "SseKmsEncryptedObjects") -public class SseKmsEncryptedObjects { - @Element(name = "Status") - private Status status; - - public SseKmsEncryptedObjects(@Nonnull @Element(name = "Status") Status status) { - this.status = Objects.requireNonNull(status, "Status must not be null"); - } - - public Status status() { - return this.status; - } -} diff --git a/api/src/main/java/io/minio/messages/Stats.java b/api/src/main/java/io/minio/messages/Stats.java index da8471b83..828e2dec8 100644 --- a/api/src/main/java/io/minio/messages/Stats.java +++ b/api/src/main/java/io/minio/messages/Stats.java @@ -20,34 +20,52 @@ import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; -/** Helper class to denote Stats information of S3 select response message. */ +/** Object represents Stats information of S3 select response message. */ @Root(name = "Stats", strict = false) @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") public class Stats { - @Element(name = "BytesScanned") - private long bytesScanned = -1; + @Element(name = "BytesScanned", required = false) + private Long bytesScanned; - @Element(name = "BytesProcessed") - private long bytesProcessed = -1; + @Element(name = "BytesProcessed", required = false) + private Long bytesProcessed; - @Element(name = "BytesReturned") - private long bytesReturned = -1; + @Element(name = "BytesReturned", required = false) + private Long bytesReturned; /** Constructs a new Stats object. */ - public Stats() {} + public Stats( + @Element(name = "BytesScanned", required = false) Long bytesScanned, + @Element(name = "BytesProcessed", required = false) Long bytesProcessed, + @Element(name = "BytesReturned", required = false) Long bytesReturned) { + this.bytesScanned = bytesScanned; + this.bytesProcessed = bytesProcessed; + this.bytesReturned = bytesReturned; + } /** Returns bytes scanned. */ - public long bytesScanned() { - return this.bytesScanned; + public Long bytesScanned() { + return bytesScanned; } /** Returns bytes processed. */ - public long bytesProcessed() { - return this.bytesProcessed; + public Long bytesProcessed() { + return bytesProcessed; } /** Returns bytes returned. */ - public long bytesReturned() { - return this.bytesReturned; + public Long bytesReturned() { + return bytesReturned; + } + + protected String stringify() { + return String.format( + "bytesScanned=%s, bytesProcessed=%s, bytesReturned=%s", + bytesScanned, bytesProcessed, bytesReturned); + } + + @Override + public String toString() { + return String.format("Stats{%s}", stringify()); } } diff --git a/api/src/main/java/io/minio/messages/Tag.java b/api/src/main/java/io/minio/messages/Tag.java deleted file mode 100644 index 841e38ae9..000000000 --- a/api/src/main/java/io/minio/messages/Tag.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.messages; - -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote tag information for {@link RuleFilter}. */ -@Root(name = "Tag") -public class Tag { - @Element(name = "Key") - private String key; - - @Element(name = "Value") - private String value; - - public Tag( - @Nonnull @Element(name = "Key") String key, @Nonnull @Element(name = "Value") String value) { - Objects.requireNonNull(key, "Key must not be null"); - if (key.isEmpty()) { - throw new IllegalArgumentException("Key must not be empty"); - } - - this.key = key; - this.value = Objects.requireNonNull(value, "Value must not be null"); - } - - public String key() { - return this.key; - } - - public String value() { - return this.value; - } -} diff --git a/api/src/main/java/io/minio/messages/Tags.java b/api/src/main/java/io/minio/messages/Tags.java index 1d191c109..4368967ed 100644 --- a/api/src/main/java/io/minio/messages/Tags.java +++ b/api/src/main/java/io/minio/messages/Tags.java @@ -24,11 +24,11 @@ import org.simpleframework.xml.Root; /** - * Object representation of request XML of PutBucketTagging - * API and , PutObjectTagging - * API response XML of , GetBucketTagging * API and GetObjectTagging @@ -103,4 +103,9 @@ public static Tags newObjectTags(Map tags) throws IllegalArgumen public Map get() { return Utils.unmodifiableMap(tags); } + + @Override + public String toString() { + return String.format("Tags{%s}", Utils.stringify(tags)); + } } diff --git a/api/src/main/java/io/minio/messages/Tier.java b/api/src/main/java/io/minio/messages/Tier.java deleted file mode 100644 index f22df7dee..000000000 --- a/api/src/main/java/io/minio/messages/Tier.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import com.fasterxml.jackson.annotation.JsonCreator; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -/** Tier representing retrieval tier value. */ -@Root(name = "Tier") -@Convert(Tier.TierConverter.class) -public enum Tier { - STANDARD("Standard"), - BULK("Bulk"), - EXPEDITED("Expedited"); - - private final String value; - - private Tier(String value) { - this.value = value; - } - - public String toString() { - return this.value; - } - - /** Returns Tier of given string. */ - @JsonCreator - public static Tier fromString(String tierString) { - for (Tier tier : Tier.values()) { - if (tierString.equals(tier.value)) { - return tier; - } - } - - throw new IllegalArgumentException("Unknown tier '" + tierString + "'"); - } - - /** XML converter class. */ - public static class TierConverter implements Converter { - @Override - public Tier read(InputNode node) throws Exception { - return Tier.fromString(node.getValue()); - } - - @Override - public void write(OutputNode node, Tier tier) throws Exception { - node.setValue(tier.toString()); - } - } -} diff --git a/api/src/main/java/io/minio/messages/TopicConfiguration.java b/api/src/main/java/io/minio/messages/TopicConfiguration.java deleted file mode 100644 index 6c9a49df0..000000000 --- a/api/src/main/java/io/minio/messages/TopicConfiguration.java +++ /dev/null @@ -1,41 +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 - * - * 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.messages; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote Topic configuration of {@link NotificationConfiguration}. */ -@Root(name = "TopicConfiguration", strict = false) -public class TopicConfiguration extends NotificationCommonConfiguration { - @Element(name = "Topic") - private String topic; - - public TopicConfiguration() { - super(); - } - - /** Returns topic. */ - public String topic() { - return topic; - } - - /** Sets topic. */ - public void setTopic(String topic) { - this.topic = topic; - } -} diff --git a/api/src/main/java/io/minio/messages/Transition.java b/api/src/main/java/io/minio/messages/Transition.java deleted file mode 100644 index 483364e45..000000000 --- a/api/src/main/java/io/minio/messages/Transition.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.messages; - -import java.time.ZonedDateTime; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** Helper class to denote transition information for {@link LifecycleRule}. */ -@Root(name = "Transition") -public class Transition extends DateDays { - @Element(name = "StorageClass") - private String storageClass; - - public Transition( - @Nullable @Element(name = "Date", required = false) ResponseDate date, - @Nullable @Element(name = "Days", required = false) Integer days, - @Nonnull @Element(name = "StorageClass", required = false) String storageClass) { - if (date != null ^ days != null) { - this.date = date; - this.days = days; - } else { - throw new IllegalArgumentException("Only one of date or days must be set"); - } - if (storageClass == null || storageClass.isEmpty()) { - throw new IllegalArgumentException("StorageClass must be provided"); - } - this.storageClass = storageClass; - } - - public Transition(ZonedDateTime date, Integer days, String storageClass) { - this(date == null ? null : new ResponseDate(date), days, storageClass); - } - - public String storageClass() { - return storageClass; - } -} diff --git a/api/src/main/java/io/minio/messages/Upload.java b/api/src/main/java/io/minio/messages/Upload.java deleted file mode 100644 index bf9b8a1ed..000000000 --- a/api/src/main/java/io/minio/messages/Upload.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2015 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.messages; - -import io.minio.Utils; -import java.time.ZonedDateTime; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Namespace; -import org.simpleframework.xml.Root; - -/** - * Helper class to denote Upload information of a multipart upload and used in {@link - * ListMultipartUploadsResult}. - */ -@Root(name = "Upload", strict = false) -@Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -public class Upload { - @Element(name = "Key") - private String objectName; - - @Element(name = "UploadId") - private String uploadId; - - @Element(name = "Initiator") - private Initiator initiator; - - @Element(name = "Owner") - private Owner owner; - - @Element(name = "StorageClass") - private String storageClass; - - @Element(name = "Initiated") - private ResponseDate initiated; - - @Element(name = "ChecksumAlgorithm", required = false) - private String checksumAlgorithm; - - @Element(name = "ChecksumType", required = false) - private String checksumType; - - private long aggregatedPartSize; - private String encodingType = null; - - public Upload() {} - - /** Returns object name. */ - public String objectName() { - return Utils.urlDecode(objectName, encodingType); - } - - /** Returns upload ID. */ - public String uploadId() { - return uploadId; - } - - /** Returns initator information. */ - public Initiator initiator() { - return initiator; - } - - /** Returns owner information. */ - public Owner owner() { - return owner; - } - - /** Returns storage class. */ - public String storageClass() { - return storageClass; - } - - /** Returns initated time. */ - public ZonedDateTime initiated() { - return initiated.zonedDateTime(); - } - - /** Returns aggregated part size. */ - public long aggregatedPartSize() { - return aggregatedPartSize; - } - - /** Sets given aggregated part size. */ - public void setAggregatedPartSize(long size) { - this.aggregatedPartSize = size; - } - - public void setEncodingType(String encodingType) { - this.encodingType = encodingType; - } - - public String checksumAlgorithm() { - return checksumAlgorithm; - } - - public String checksumType() { - return checksumType; - } -} diff --git a/api/src/main/java/io/minio/messages/UserMetadata.java b/api/src/main/java/io/minio/messages/UserMetadata.java deleted file mode 100644 index 727e2b3fc..000000000 --- a/api/src/main/java/io/minio/messages/UserMetadata.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 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.messages; - -import io.minio.Utils; -import java.util.Map; -import java.util.Objects; -import javax.annotation.Nonnull; -import org.simpleframework.xml.ElementMap; -import org.simpleframework.xml.Namespace; -import org.simpleframework.xml.Root; - -/** Helper class to denote user metadata information of {@link S3OutputLocation}. */ -@Root(name = "UserMetadata", strict = false) -@Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") -@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") -public class UserMetadata { - @ElementMap( - attribute = false, - entry = "MetadataEntry", - inline = true, - key = "Name", - value = "Value", - required = false) - Map metadataEntries; - - private UserMetadata(@Nonnull Map metadataEntries) { - Objects.requireNonNull(metadataEntries, "Metadata entries must not be null"); - if (metadataEntries.size() == 0) { - throw new IllegalArgumentException("Metadata entries must not be empty"); - } - this.metadataEntries = Utils.unmodifiableMap(metadataEntries); - } -} diff --git a/api/src/main/java/io/minio/messages/Version.java b/api/src/main/java/io/minio/messages/Version.java deleted file mode 100644 index 65eb57d97..000000000 --- a/api/src/main/java/io/minio/messages/Version.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.messages; - -import org.simpleframework.xml.Root; - -/** Helper class to denote object and it's version information in {@link ListVersionsResult}. */ -@Root(name = "Version", strict = false) -public class Version extends Item { - public Version() { - super(); - } - - public Version(String prefix) { - super(prefix); - } -} diff --git a/api/src/main/java/io/minio/messages/VersioningConfiguration.java b/api/src/main/java/io/minio/messages/VersioningConfiguration.java index d67536586..22a9582dd 100644 --- a/api/src/main/java/io/minio/messages/VersioningConfiguration.java +++ b/api/src/main/java/io/minio/messages/VersioningConfiguration.java @@ -16,6 +16,7 @@ package io.minio.messages; +import io.minio.Utils; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -24,7 +25,7 @@ import org.simpleframework.xml.Root; /** - * Object representation of request XML of PutBucketVersioning * API and response XML of GetBucketVersioning @@ -63,6 +64,13 @@ public Boolean isMfaDeleteEnabled() { return flag; } + @Override + public String toString() { + return String.format( + "VersioningConfiguration{status=%s, mfaDelete=%s}", + Utils.stringify(status), Utils.stringify(mfaDelete)); + } + public static enum Status { OFF(""), ENABLED("Enabled"), diff --git a/api/src/main/java/io/minio/org/apache/commons/validator/routines/InetAddressValidator.java b/api/src/main/java/io/minio/org/apache/commons/validator/routines/InetAddressValidator.java deleted file mode 100644 index 5101f593b..000000000 --- a/api/src/main/java/io/minio/org/apache/commons/validator/routines/InetAddressValidator.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.org.apache.commons.validator.routines; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * InetAddress validation and conversion routines (java.net.InetAddress). - * - *

- * - *

- * - *

This class provides methods to validate a candidate IP address. - * - *

- * - *

This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} - * method. - * - * @version $Revision$ - * @since Validator 1.4 - */ -public class InetAddressValidator { - - private static final int IPV4_MAX_OCTET_VALUE = 255; - - private static final int MAX_UNSIGNED_SHORT = 0xffff; - - private static final int BASE_16 = 16; - - private static final long serialVersionUID = -919201640201914789L; - - private static final String IPV4_REGEX = "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"; - - // Max number of hex groups (separated by :) in an IPV6 address - private static final int IPV6_MAX_HEX_GROUPS = 8; - - // Max hex digits in each IPv6 group - private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4; - - /** Singleton instance of this class. */ - private static final InetAddressValidator VALIDATOR = new InetAddressValidator(); - - /** IPv4 RegexValidator. */ - private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX); - - private InetAddressValidator() {} - - /** - * Returns the singleton instance of this validator. - * - * @return the singleton instance of this validator - */ - public static InetAddressValidator getInstance() { - return VALIDATOR; - } - - /** - * Checks if the specified string is a valid IP address. - * - * @param inetAddress the string to validate - * @return true if the string validates as an IP address - */ - public boolean isValid(String inetAddress) { - return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress); - } - - /** - * Validates an IPv4 address. Returns true if valid. - * - * @param inet4Address the IPv4 address to validate - * @return true if the argument contains a valid IPv4 address - */ - public boolean isValidInet4Address(String inet4Address) { - // verify that address conforms to generic IPv4 format - String[] groups = ipv4Validator.match(inet4Address); - - if (groups == null) { - return false; - } - - // verify that address subgroups are legal - for (String ipSegment : groups) { - if (ipSegment == null || ipSegment.length() == 0) { - return false; - } - - int iIpSegment = 0; - - try { - iIpSegment = Integer.parseInt(ipSegment); - } catch (NumberFormatException e) { - return false; - } - - if (iIpSegment > IPV4_MAX_OCTET_VALUE) { - return false; - } - - if (ipSegment.length() > 1 && ipSegment.startsWith("0")) { - return false; - } - } - - return true; - } - - /** - * Validates an IPv6 address. Returns true if valid. - * - * @param inet6Address the IPv6 address to validate - * @return true if the argument contains a valid IPv6 address - * @since 1.4.1 - */ - public boolean isValidInet6Address(String inet6Address) { - boolean containsCompressedZeroes = inet6Address.contains("::"); - if (containsCompressedZeroes && inet6Address.indexOf("::") != inet6Address.lastIndexOf("::")) { - return false; - } - if (inet6Address.startsWith(":") && !inet6Address.startsWith("::") - || inet6Address.endsWith(":") && !inet6Address.endsWith("::")) { - return false; - } - String[] octets = inet6Address.split(":"); - if (containsCompressedZeroes) { - List octetList = new ArrayList(Arrays.asList(octets)); - if (inet6Address.endsWith("::")) { - // String.split() drops ending empty segments - octetList.add(""); - } else if (inet6Address.startsWith("::") && !octetList.isEmpty()) { - octetList.remove(0); - } - octets = octetList.toArray(new String[octetList.size()]); - } - if (octets.length > IPV6_MAX_HEX_GROUPS) { - return false; - } - int validOctets = 0; - int emptyOctets = 0; - for (int index = 0; index < octets.length; index++) { - String octet = octets[index]; - if (octet.length() == 0) { - emptyOctets++; - if (emptyOctets > 1) { - return false; - } - } else { - emptyOctets = 0; - if (octet.contains(".")) { // contains is Java 1.5+ - if (!inet6Address.endsWith(octet)) { - return false; - } - if (index > octets.length - 1 || index > 6) { // CHECKSTYLE IGNORE MagicNumber - // IPV4 occupies last two octets - return false; - } - if (!isValidInet4Address(octet)) { - return false; - } - validOctets += 2; - continue; - } - if (octet.length() > IPV6_MAX_HEX_DIGITS_PER_GROUP) { - return false; - } - int octetInt = 0; - try { - octetInt = Integer.valueOf(octet, BASE_16).intValue(); - } catch (NumberFormatException e) { - return false; - } - if (octetInt < 0 || octetInt > MAX_UNSIGNED_SHORT) { - return false; - } - } - validOctets++; - } - if (validOctets < IPV6_MAX_HEX_GROUPS && !containsCompressedZeroes) { - return false; - } - return true; - } -} diff --git a/api/src/main/java/io/minio/org/apache/commons/validator/routines/RegexValidator.java b/api/src/main/java/io/minio/org/apache/commons/validator/routines/RegexValidator.java deleted file mode 100644 index 31745dae4..000000000 --- a/api/src/main/java/io/minio/org/apache/commons/validator/routines/RegexValidator.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.org.apache.commons.validator.routines; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import java.io.Serializable; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Regular Expression validation (using JDK 1.4+ regex support). - * - *

Construct the validator either for a single regular expression or a set (array) of regular - * expressions. By default validation is case sensitive but constructors are provided to - * allow case in-sensitive validation. For example to create a validator which does case - * in-sensitive validation for a set of regular expressions: - * - *

- * 
- * String[] regexs = new String[] {...};
- * RegexValidator validator = new RegexValidator(regexs, false);
- * 
- * 
- * - *

- * - *

    - *
  • Validate true or false: - *
  • - *
      - *
    • boolean valid = validator.isValid(value); - *
    - *
  • Validate returning an aggregated String of the matched groups: - *
  • - *
      - *
    • String result = validator.validate(value); - *
    - *
  • Validate returning the matched groups: - *
  • - *
      - *
    • String[] result = validator.match(value); - *
    - *
- * - *

Note that patterns are matched against the entire input. - * - *

- * - *

Cached instances pre-compile and re-use {@link Pattern}(s) - which according to the {@link - * Pattern} API are safe to use in a multi-threaded environment. - * - * @version $Revision$ - * @since Validator 1.4 - */ -public class RegexValidator implements Serializable { - - private static final long serialVersionUID = -8832409930574867162L; - - private final Pattern[] patterns; - - /** - * Construct a case sensitive validator for a single regular expression. - * - * @param regex The regular expression this validator will validate against - */ - public RegexValidator(String regex) { - this(regex, true); - } - - /** - * Construct a validator for a single regular expression with the specified case sensitivity. - * - * @param regex The regular expression this validator will validate against - * @param caseSensitive when true matching is case sensitive, otherwise - * matching is case in-sensitive - */ - public RegexValidator(String regex, boolean caseSensitive) { - this(new String[] {regex}, caseSensitive); - } - - /** - * Construct a case sensitive validator that matches any one of the set of regular - * expressions. - * - * @param regexs The set of regular expressions this validator will validate against - */ - public RegexValidator(String[] regexs) { - this(regexs, true); - } - - /** - * Construct a validator that matches any one of the set of regular expressions with the specified - * case sensitivity. - * - * @param regexs The set of regular expressions this validator will validate against - * @param caseSensitive when true matching is case sensitive, otherwise - * matching is case in-sensitive - */ - public RegexValidator(String[] regexs, boolean caseSensitive) { - if (regexs == null || regexs.length == 0) { - throw new IllegalArgumentException("Regular expressions are missing"); - } - patterns = new Pattern[regexs.length]; - int flags = (caseSensitive ? 0 : Pattern.CASE_INSENSITIVE); - for (int i = 0; i < regexs.length; i++) { - if (regexs[i] == null || regexs[i].length() == 0) { - throw new IllegalArgumentException("Regular expression[" + i + "] is missing"); - } - patterns[i] = Pattern.compile(regexs[i], flags); - } - } - - /** - * Validate a value against the set of regular expressions. - * - * @param value The value to validate. - * @return true if the value is valid otherwise false. - */ - public boolean isValid(String value) { - if (value == null) { - return false; - } - for (int i = 0; i < patterns.length; i++) { - if (patterns[i].matcher(value).matches()) { - return true; - } - } - return false; - } - - /** - * Validate a value against the set of regular expressions returning the array of matched groups. - * - * @param value The value to validate. - * @return String array of the groups matched if valid or null if invalid - */ - @SuppressFBWarnings( - value = "PZLA", - justification = "Null is checked, not empty array. API is clear as well.") - public String[] match(String value) { - if (value == null) { - return null; - } - for (int i = 0; i < patterns.length; i++) { - Matcher matcher = patterns[i].matcher(value); - if (matcher.matches()) { - int count = matcher.groupCount(); - String[] groups = new String[count]; - for (int j = 0; j < count; j++) { - groups[j] = matcher.group(j + 1); - } - return groups; - } - } - return null; - } - - /** - * Validate a value against the set of regular expressions returning a String value of the - * aggregated groups. - * - * @param value The value to validate. - * @return Aggregated String value comprised of the groups matched if valid or null - * if invalid - */ - public String validate(String value) { - if (value == null) { - return null; - } - for (int i = 0; i < patterns.length; i++) { - Matcher matcher = patterns[i].matcher(value); - if (matcher.matches()) { - int count = matcher.groupCount(); - if (count == 1) { - return matcher.group(1); - } - StringBuilder buffer = new StringBuilder(); - for (int j = 0; j < count; j++) { - String component = matcher.group(j + 1); - if (component != null) { - buffer.append(component); - } - } - return buffer.toString(); - } - } - return null; - } - - /** - * Provide a String representation of this validator. - * - * @return A String representation of this validator - */ - @Override - public String toString() { - StringBuilder buffer = new StringBuilder(); - buffer.append("RegexValidator{"); - for (int i = 0; i < patterns.length; i++) { - if (i > 0) { - buffer.append(","); - } - buffer.append(patterns[i].pattern()); - } - buffer.append("}"); - return buffer.toString(); - } -} diff --git a/api/src/test/java/io/minio/MakeBucketArgsTest.java b/api/src/test/java/io/minio/MakeBucketArgsTest.java index a3cee0322..a47b57994 100644 --- a/api/src/test/java/io/minio/MakeBucketArgsTest.java +++ b/api/src/test/java/io/minio/MakeBucketArgsTest.java @@ -21,25 +21,25 @@ import org.junit.Test; public class MakeBucketArgsTest { - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEmptyBuild() { MakeBucketArgs.builder().build(); Assert.fail("exception should be thrown"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEmptyBucketBuild1() { MakeBucketArgs.builder().objectLock(false).build(); Assert.fail("exception should be thrown"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEmptyBucketBuild2() { MakeBucketArgs.builder().bucket(null).build(); Assert.fail("exception should be thrown"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEmptyBucketBuild3() { MakeBucketArgs.builder().bucket("mybucket").bucket(null).build(); Assert.fail("exception should be thrown"); diff --git a/api/src/test/java/io/minio/MinioClientTest.java b/api/src/test/java/io/minio/MinioClientTest.java index bc97d607b..edb18fb3a 100644 --- a/api/src/test/java/io/minio/MinioClientTest.java +++ b/api/src/test/java/io/minio/MinioClientTest.java @@ -19,8 +19,6 @@ import io.minio.errors.InvalidResponseException; import io.minio.errors.MinioException; -import io.minio.http.Method; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -38,7 +36,7 @@ public class MinioClientTest { private static final String CONTENT_TYPE = "Content-Type"; private static final String CONTENT_LENGTH = "Content-Length"; - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEndpoint1() throws MinioException { MinioClient.builder().endpoint((String) null).build(); Assert.fail("exception should be thrown"); @@ -103,7 +101,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -118,7 +116,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -129,7 +127,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -144,7 +142,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -156,7 +154,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -169,7 +167,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -185,7 +183,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -197,7 +195,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -213,7 +211,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -226,7 +224,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -243,7 +241,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -258,7 +256,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -273,7 +271,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -289,7 +287,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -306,7 +304,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -322,7 +320,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -338,7 +336,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -355,7 +353,7 @@ public void testAwsEndpoints() url = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("mybucket") .object("myobject") .build()); @@ -382,7 +380,7 @@ public void testCustomHttpClientClose() throws Exception { Assert.assertTrue(httpClient.dispatcher().executorService().isShutdown()); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testBucketName1() throws NoSuchAlgorithmException, IOException, InvalidKeyException, MinioException { StatObjectArgs.builder().bucket(null); @@ -432,7 +430,7 @@ public void testBucketName7() Assert.fail("exception should be thrown"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testObjectName1() throws NoSuchAlgorithmException, IOException, InvalidKeyException, MinioException { StatObjectArgs.builder().object(null); @@ -470,7 +468,7 @@ public void testReadSse1() StatObjectArgs.builder() .bucket("mybucket") .object("myobject") - .ssec(new ServerSideEncryptionCustomerKey(keyGen.generateKey())) + .ssec(new ServerSideEncryption.CustomerKey(keyGen.generateKey())) .build()); Assert.fail("exception should be thrown"); } @@ -482,9 +480,11 @@ public void testWriteSse1() KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); client.putObject( - PutObjectArgs.builder().bucket("mybucket").object("myobject").stream( - new ByteArrayInputStream(new byte[] {}), 0, -1) - .sse(new ServerSideEncryptionCustomerKey(keyGen.generateKey())) + PutObjectArgs.builder() + .bucket("mybucket") + .object("myobject") + .data(new byte[0], 0) + .sse(new ServerSideEncryption.CustomerKey(keyGen.generateKey())) .build()); Assert.fail("exception should be thrown"); } @@ -496,9 +496,11 @@ public void testWriteSse2() Map myContext = new HashMap<>(); myContext.put("key1", "value1"); client.putObject( - PutObjectArgs.builder().bucket("mybucket").object("myobject").stream( - new ByteArrayInputStream(new byte[] {}), 0, -1) - .sse(new ServerSideEncryptionKms("keyId", myContext)) + PutObjectArgs.builder() + .bucket("mybucket") + .object("myobject") + .data(new byte[0], 0) + .sse(new ServerSideEncryption.KMS("keyId", myContext)) .build()); Assert.fail("exception should be thrown"); } diff --git a/api/src/test/java/io/minio/StatObjectArgsTest.java b/api/src/test/java/io/minio/StatObjectArgsTest.java index e40c419d7..b481f0c41 100644 --- a/api/src/test/java/io/minio/StatObjectArgsTest.java +++ b/api/src/test/java/io/minio/StatObjectArgsTest.java @@ -24,25 +24,25 @@ import org.junit.Test; public class StatObjectArgsTest { - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEmptyBuild() { StatObjectArgs.builder().build(); Assert.fail("exception should be thrown"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEmptyBucketBuild1() { StatObjectArgs.builder().object("myobject").build(); Assert.fail("exception should be thrown"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEmptyBucketBuild2() { StatObjectArgs.builder().object("myobject").bucket(null).build(); Assert.fail("exception should be thrown"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEmptyBucketBuild3() { StatObjectArgs.builder().bucket("mybucket").bucket(null).build(); Assert.fail("exception should be thrown"); @@ -54,7 +54,7 @@ public void testEmptyRegionBuild() { Assert.fail("exception should be thrown"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testEmptyObjectBuild1() { StatObjectArgs.builder().object(null).build(); Assert.fail("exception should be thrown"); @@ -70,8 +70,8 @@ public void testEmptyObjectBuild2() { public void testBuild() throws NoSuchAlgorithmException, InvalidKeyException { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); StatObjectArgs args = StatObjectArgs.builder() .bucket("mybucket") diff --git a/examples/ComposeObject.java b/examples/ComposeObject.java index 3cad7faa2..57891f317 100644 --- a/examples/ComposeObject.java +++ b/examples/ComposeObject.java @@ -15,10 +15,9 @@ */ import io.minio.ComposeObjectArgs; -import io.minio.ComposeSource; import io.minio.MinioClient; import io.minio.ServerSideEncryption; -import io.minio.ServerSideEncryptionCustomerKey; +import io.minio.SourceObject; import io.minio.errors.MinioException; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -48,18 +47,12 @@ public static void main(String[] args) // .build(); { - // Create a ComposeSource to compose Object. - List sources = new ArrayList(); + // Create a SourceObject to compose Object. + List sources = new ArrayList(); sources.add( - ComposeSource.builder() - .bucket("my-bucketname-one") - .object("my-objectname-one") - .build()); + SourceObject.builder().bucket("my-bucketname-one").object("my-objectname-one").build()); sources.add( - ComposeSource.builder() - .bucket("my-bucketname-two") - .object("my-objectname-two") - .build()); + SourceObject.builder().bucket("my-bucketname-two").object("my-objectname-two").build()); minioClient.composeObject( ComposeObjectArgs.builder() @@ -71,25 +64,25 @@ public static void main(String[] args) } { - ServerSideEncryptionCustomerKey srcSsec = - new ServerSideEncryptionCustomerKey( + ServerSideEncryption.CustomerKey srcSsec = + new ServerSideEncryption.CustomerKey( new SecretKeySpec( "01234567890123456789012345678901".getBytes(StandardCharsets.UTF_8), "AES")); ServerSideEncryption sse = - new ServerSideEncryptionCustomerKey( + new ServerSideEncryption.CustomerKey( new SecretKeySpec( "12345678912345678912345678912345".getBytes(StandardCharsets.UTF_8), "AES")); - List sources = new ArrayList(); + List sources = new ArrayList(); sources.add( - ComposeSource.builder() + SourceObject.builder() .bucket("my-bucketname") .object("my-objectname-one") .ssec(srcSsec) .build()); sources.add( - ComposeSource.builder() + SourceObject.builder() .bucket("my-bucketname") .object("my-objectname-two") .ssec(srcSsec) diff --git a/examples/CopyObject.java b/examples/CopyObject.java index 6b6508f2a..4839066aa 100644 --- a/examples/CopyObject.java +++ b/examples/CopyObject.java @@ -16,12 +16,9 @@ */ import io.minio.CopyObjectArgs; -import io.minio.CopySource; import io.minio.MinioClient; import io.minio.ServerSideEncryption; -import io.minio.ServerSideEncryptionCustomerKey; -import io.minio.ServerSideEncryptionKms; -import io.minio.ServerSideEncryptionS3; +import io.minio.SourceObject; import io.minio.errors.MinioException; import java.io.IOException; import java.security.InvalidKeyException; @@ -51,14 +48,14 @@ public static void main(String[] args) KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); Map myContext = new HashMap<>(); myContext.put("key1", "value1"); - ServerSideEncryption sseKms = new ServerSideEncryptionKms("Key-Id", myContext); + ServerSideEncryption sseKms = new ServerSideEncryption.KMS("Key-Id", myContext); - ServerSideEncryption sseS3 = new ServerSideEncryptionS3(); + ServerSideEncryption sseS3 = new ServerSideEncryption.S3(); Map headers = new HashMap<>(); headers.put("Content-Type", "application/json"); @@ -74,7 +71,7 @@ public static void main(String[] args) .bucket("my-bucketname") .object("my-objectname") .source( - CopySource.builder() + SourceObject.builder() .bucket("my-source-bucketname") .object("my-objectname") .build()) @@ -92,7 +89,7 @@ public static void main(String[] args) .bucket("my-bucketname") .object("my-objectname") .source( - CopySource.builder() + SourceObject.builder() .bucket("my-source-bucketname") .object("my-source-objectname") .build()) @@ -110,7 +107,7 @@ public static void main(String[] args) .bucket("my-bucketname") .object("my-objectname") .source( - CopySource.builder() + SourceObject.builder() .bucket("my-source-bucketname") .object("my-objectname") .build()) @@ -129,7 +126,7 @@ public static void main(String[] args) .bucket("my-bucketname") .object("my-objectname") .source( - CopySource.builder() + SourceObject.builder() .bucket("my-source-bucketname") .object("my-objectname") .build()) @@ -148,7 +145,7 @@ public static void main(String[] args) .bucket("my-bucketname") .object("my-objectname") .source( - CopySource.builder() + SourceObject.builder() .bucket("my-source-bucketname") .object("my-objectname") .build()) @@ -167,7 +164,7 @@ public static void main(String[] args) .bucket("my-bucketname") .object("my-objectname") .source( - CopySource.builder() + SourceObject.builder() .bucket("my-source-bucketname") .object("my-source-objectname") .ssec(ssec) // Replace with actual key. @@ -186,7 +183,7 @@ public static void main(String[] args) .bucket("my-bucketname") .object("my-objectname") .source( - CopySource.builder() + SourceObject.builder() .bucket("my-source-bucketname") .object("my-objectname") .matchETag(etag) // Replace with actual etag. diff --git a/examples/DownloadObject.java b/examples/DownloadObject.java index f357842d0..0402ce356 100644 --- a/examples/DownloadObject.java +++ b/examples/DownloadObject.java @@ -16,7 +16,7 @@ import io.minio.DownloadObjectArgs; import io.minio.MinioClient; -import io.minio.ServerSideEncryptionCustomerKey; +import io.minio.ServerSideEncryption; import io.minio.errors.MinioException; import java.io.IOException; import java.security.InvalidKeyException; @@ -56,8 +56,8 @@ public static void main(String[] args) { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); // Download SSE-C encrypted 'my-objectname' from 'my-bucketname' to 'my-filename' minioClient.downloadObject( diff --git a/examples/GetPresignedObjectUrl.java b/examples/GetPresignedObjectUrl.java index e154b88cb..bf8342e86 100644 --- a/examples/GetPresignedObjectUrl.java +++ b/examples/GetPresignedObjectUrl.java @@ -15,9 +15,9 @@ */ import io.minio.GetPresignedObjectUrlArgs; +import io.minio.Http; import io.minio.MinioClient; import io.minio.errors.MinioException; -import io.minio.http.Method; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -46,7 +46,7 @@ public static void main(String[] args) String url = minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.DELETE) + .method(Http.Method.DELETE) .bucket("my-bucketname") .object("my-objectname") .expiry(60 * 60 * 24) diff --git a/examples/ListBuckets.java b/examples/ListBuckets.java index f0e15263d..aa297d755 100644 --- a/examples/ListBuckets.java +++ b/examples/ListBuckets.java @@ -18,7 +18,7 @@ import io.minio.MinioClient; import io.minio.Result; import io.minio.errors.MinioException; -import io.minio.messages.Bucket; +import io.minio.messages.ListAllMyBucketsResult; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -43,9 +43,10 @@ public static void main(String[] args) // .build(); // List buckets we have atleast read access. - Iterable> results = minioClient.listBuckets(ListBucketsArgs.builder().build()); - for (Result result : results) { - Bucket bucket = result.get(); + Iterable> results = + minioClient.listBuckets(ListBucketsArgs.builder().build()); + for (Result result : results) { + ListAllMyBucketsResult.Bucket bucket = result.get(); System.out.println( String.format( "Bucket: %s, Region: %s, CreationDate: %s", diff --git a/examples/ListenBucketNotification.java b/examples/ListenBucketNotification.java index d0b5bc80b..3ef5ecc01 100644 --- a/examples/ListenBucketNotification.java +++ b/examples/ListenBucketNotification.java @@ -19,7 +19,6 @@ import io.minio.MinioClient; import io.minio.Result; import io.minio.errors.MinioException; -import io.minio.messages.Event; import io.minio.messages.NotificationRecords; import java.io.IOException; import java.security.InvalidKeyException; @@ -55,8 +54,9 @@ public static void main(String[] args) .build())) { while (ci.hasNext()) { NotificationRecords records = ci.next().get(); - Event event = records.events().get(0); - System.out.println(event.bucketName() + "/" + event.objectName() + " has been created"); + NotificationRecords.Event event = records.events().get(0); + System.out.println( + event.bucket().name() + "/" + event.object().key() + " has been created"); } } catch (IOException e) { System.out.println("Error occurred: " + e); diff --git a/examples/PresignedGetObject.java b/examples/PresignedGetObject.java index cf9c889d2..6e2edc5b0 100644 --- a/examples/PresignedGetObject.java +++ b/examples/PresignedGetObject.java @@ -15,9 +15,9 @@ */ import io.minio.GetPresignedObjectUrlArgs; +import io.minio.Http; import io.minio.MinioClient; import io.minio.errors.MinioException; -import io.minio.http.Method; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -46,7 +46,7 @@ public static void main(String[] args) String url = minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket("my-bucketname") .object("my-objectname") .expiry(60 * 60 * 24) diff --git a/examples/PresignedPutObject.java b/examples/PresignedPutObject.java index e231de21e..d1f03af3b 100644 --- a/examples/PresignedPutObject.java +++ b/examples/PresignedPutObject.java @@ -15,9 +15,9 @@ */ import io.minio.GetPresignedObjectUrlArgs; +import io.minio.Http; import io.minio.MinioClient; import io.minio.errors.MinioException; -import io.minio.http.Method; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -52,7 +52,7 @@ public static void main(String[] args) String url = minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.PUT) + .method(Http.Method.PUT) .bucket("my-bucketname") .object("my-objectname") .expiry(60 * 60 * 24) diff --git a/examples/PutObject.java b/examples/PutObject.java index 06fdd75ef..090cd3958 100644 --- a/examples/PutObject.java +++ b/examples/PutObject.java @@ -17,11 +17,7 @@ import io.minio.MinioClient; import io.minio.PutObjectArgs; import io.minio.ServerSideEncryption; -import io.minio.ServerSideEncryptionCustomerKey; -import io.minio.ServerSideEncryptionKms; -import io.minio.ServerSideEncryptionS3; import io.minio.errors.MinioException; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -74,81 +70,71 @@ public static void main(String[] args) builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); builder.append("---\n"); } + byte[] data = builder.toString().getBytes("UTF-8"); { - // 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( - PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream( - bais, bais.available(), -1) + PutObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .data(data, data.length) .build()); - bais.close(); System.out.println("my-objectname is uploaded successfully"); } { - // 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. KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); // Create encrypted object 'my-objectname' using SSE-C in 'my-bucketname' with content from // the input stream. minioClient.putObject( - PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream( - bais, bais.available(), -1) + PutObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .data(data, data.length) .sse(ssec) .build()); - bais.close(); System.out.println("my-objectname is uploaded successfully"); } { - // Create a InputStream for object upload. - ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - Map myContext = new HashMap<>(); myContext.put("key1", "value1"); - ServerSideEncryption sseKms = new ServerSideEncryptionKms("Key-Id", myContext); + ServerSideEncryption sseKms = new ServerSideEncryption.KMS("Key-Id", myContext); // Create encrypted object 'my-objectname' using SSE-KMS in 'my-bucketname' with content // from the input stream. minioClient.putObject( - PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream( - bais, bais.available(), -1) + PutObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .data(data, data.length) .sse(sseKms) .build()); - bais.close(); System.out.println("my-objectname is uploaded successfully"); } { - // Create a InputStream for object upload. - ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - - ServerSideEncryption sseS3 = new ServerSideEncryptionS3(); + ServerSideEncryption sseS3 = new ServerSideEncryption.S3(); // Create encrypted object 'my-objectname' using SSE-S3 in 'my-bucketname' with content // from the input stream. minioClient.putObject( - PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream( - bais, bais.available(), -1) + PutObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .data(data, data.length) .sse(sseS3) .build()); - bais.close(); System.out.println("my-objectname is uploaded successfully"); } { - // Create a InputStream for object upload. - ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - // Create headers Map headers = new HashMap<>(); // Add custom content type @@ -164,20 +150,23 @@ public static void main(String[] args) // with content // from the input stream. minioClient.putObject( - PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream( - bais, bais.available(), -1) + PutObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .data(data, data.length) .headers(headers) .userMetadata(userMetadata) .build()); - bais.close(); System.out.println("my-objectname is uploaded successfully"); } { // Create object name ending with '/' (mostly called folder or directory). minioClient.putObject( - PutObjectArgs.builder().bucket("my-bucketname").object("path/to/").stream( - new ByteArrayInputStream(new byte[] {}), 1, -1) + PutObjectArgs.builder() + .bucket("my-bucketname") + .object("path/to/") + .data(new byte[0], 0) .build()); System.out.println("path/to/ is created successfully"); } diff --git a/examples/PutObjectProgressBar.java b/examples/PutObjectProgressBar.java index 557a20c15..9a8da5cfe 100644 --- a/examples/PutObjectProgressBar.java +++ b/examples/PutObjectProgressBar.java @@ -51,7 +51,7 @@ public static void main(String[] args) new BufferedInputStream(new ProgressStream("Uploading... ", new FileInputStream(file))); minioClient.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - pis, pis.available(), -1) + pis, (long) pis.available(), null) .build()); pis.close(); System.out.println("my-objectname is uploaded successfully"); diff --git a/examples/PutObjectUiProgressBar.java b/examples/PutObjectUiProgressBar.java index 655eae64d..41c8ef1d2 100644 --- a/examples/PutObjectUiProgressBar.java +++ b/examples/PutObjectUiProgressBar.java @@ -80,7 +80,7 @@ private void uploadFile(String fileName) pmis.getProgressMonitor().setMillisToPopup(10); minioClient.putObject( PutObjectArgs.builder().bucket("bank").object("my-objectname").stream( - pmis, pmis.available(), -1) + pmis, (long) pmis.available(), null) .build()); System.out.println("my-objectname is uploaded successfully"); } catch (FileNotFoundException e) { diff --git a/examples/RemoveObjects.java b/examples/RemoveObjects.java index 11827c3a7..48d6e7b13 100644 --- a/examples/RemoveObjects.java +++ b/examples/RemoveObjects.java @@ -18,8 +18,8 @@ import io.minio.RemoveObjectsArgs; import io.minio.Result; import io.minio.errors.MinioException; -import io.minio.messages.DeleteError; -import io.minio.messages.DeleteObject; +import io.minio.messages.DeleteRequest; +import io.minio.messages.DeleteResult; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -45,15 +45,15 @@ public static void main(String[] args) // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") // .build(); - List objects = new LinkedList<>(); - objects.add(new DeleteObject("my-objectname1")); - objects.add(new DeleteObject("my-objectname2")); - objects.add(new DeleteObject("my-objectname3")); - Iterable> results = + List objects = new LinkedList<>(); + objects.add(new DeleteRequest.Object("my-objectname1")); + objects.add(new DeleteRequest.Object("my-objectname2")); + objects.add(new DeleteRequest.Object("my-objectname3")); + Iterable> results = minioClient.removeObjects( RemoveObjectsArgs.builder().bucket("my-bucketname").objects(objects).build()); - for (Result result : results) { - DeleteError error = result.get(); + for (Result result : results) { + DeleteResult.Error error = result.get(); System.out.println( "Error in deleting object " + error.objectName() + "; " + error.message()); } diff --git a/examples/SelectObjectContent.java b/examples/SelectObjectContent.java index 247a08a1a..db3e23943 100644 --- a/examples/SelectObjectContent.java +++ b/examples/SelectObjectContent.java @@ -19,12 +19,9 @@ import io.minio.SelectObjectContentArgs; import io.minio.SelectResponseStream; import io.minio.errors.MinioException; -import io.minio.messages.FileHeaderInfo; import io.minio.messages.InputSerialization; import io.minio.messages.OutputSerialization; -import io.minio.messages.QuoteFields; import io.minio.messages.Stats; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; @@ -57,17 +54,20 @@ public static void main(String[] args) + "1996,Jeep,Grand Cherokee,\"MUST SELL!\n" + "air, moon roof, loaded\",4799.00\n") .getBytes(StandardCharsets.UTF_8); - ByteArrayInputStream bais = new ByteArrayInputStream(data); minioClient.putObject( - PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream( - bais, data.length, -1) + PutObjectArgs.builder() + .bucket("my-bucketname") + .object("my-objectname") + .data(data, data.length) .build()); String sqlExpression = "select * from S3Object"; InputSerialization is = - new InputSerialization(null, false, null, null, FileHeaderInfo.USE, null, null, null); + InputSerialization.newCSV( + null, false, null, null, InputSerialization.FileHeaderInfo.USE, null, null, null); OutputSerialization os = - new OutputSerialization(null, null, null, QuoteFields.ASNEEDED, null); + OutputSerialization.newCSV( + null, null, null, OutputSerialization.QuoteFields.ASNEEDED, null); SelectResponseStream stream = minioClient.selectObjectContent( diff --git a/examples/SetBucketLifecycle.java b/examples/SetBucketLifecycle.java index 2b512a5c1..630f830cc 100644 --- a/examples/SetBucketLifecycle.java +++ b/examples/SetBucketLifecycle.java @@ -17,10 +17,8 @@ import io.minio.MinioClient; import io.minio.SetBucketLifecycleArgs; import io.minio.errors.MinioException; -import io.minio.messages.Expiration; +import io.minio.messages.Filter; import io.minio.messages.LifecycleConfiguration; -import io.minio.messages.LifecycleRule; -import io.minio.messages.RuleFilter; import io.minio.messages.Status; import java.io.IOException; import java.security.InvalidKeyException; @@ -48,13 +46,13 @@ public static void main(String[] args) // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") // .build(); - List rules = new LinkedList<>(); + List rules = new LinkedList<>(); rules.add( - new LifecycleRule( + new LifecycleConfiguration.Rule( Status.ENABLED, null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter("logs/"), + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null), + new Filter("logs/"), "rule2", null, null, diff --git a/examples/SetBucketNotification.java b/examples/SetBucketNotification.java index a3f1cf496..90d137037 100644 --- a/examples/SetBucketNotification.java +++ b/examples/SetBucketNotification.java @@ -19,12 +19,10 @@ import io.minio.errors.MinioException; import io.minio.messages.EventType; import io.minio.messages.NotificationConfiguration; -import io.minio.messages.QueueConfiguration; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.util.LinkedList; -import java.util.List; +import java.util.Arrays; public class SetBucketNotification { /** MinioClient.setBucketNotification() example. */ @@ -45,22 +43,24 @@ public static void main(String[] args) // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") // .build(); - NotificationConfiguration config = new NotificationConfiguration(); - - // Add a new SQS configuration. - List queueConfigurationList = new LinkedList<>(); - QueueConfiguration queueConfiguration = new QueueConfiguration(); - queueConfiguration.setQueue("arn:minio:sqs::1:webhook"); - - List eventList = new LinkedList<>(); - eventList.add(EventType.OBJECT_CREATED_PUT); - eventList.add(EventType.OBJECT_CREATED_COPY); - queueConfiguration.setEvents(eventList); - queueConfiguration.setPrefixRule("images"); - queueConfiguration.setSuffixRule("pg"); - - queueConfigurationList.add(queueConfiguration); - config.setQueueConfigurationList(queueConfigurationList); + NotificationConfiguration config = + new NotificationConfiguration( + null, + Arrays.asList( + new NotificationConfiguration.QueueConfiguration[] { + // Add a new SQS configuration. + new NotificationConfiguration.QueueConfiguration( + "arn:minio:sqs::1:webhook", + null, + Arrays.asList( + new String[] { + EventType.OBJECT_CREATED_PUT.toString(), + EventType.OBJECT_CREATED_COPY.toString() + }), + new NotificationConfiguration.Filter("images", "pg")) + }), + null, + null); // Set updated notification configuration. minioClient.setBucketNotification( diff --git a/examples/SetBucketReplication.java b/examples/SetBucketReplication.java index 5f35de2ce..4dceec983 100644 --- a/examples/SetBucketReplication.java +++ b/examples/SetBucketReplication.java @@ -17,12 +17,8 @@ import io.minio.MinioClient; import io.minio.SetBucketReplicationArgs; import io.minio.errors.MinioException; -import io.minio.messages.AndOperator; -import io.minio.messages.DeleteMarkerReplication; +import io.minio.messages.Filter; import io.minio.messages.ReplicationConfiguration; -import io.minio.messages.ReplicationDestination; -import io.minio.messages.ReplicationRule; -import io.minio.messages.RuleFilter; import io.minio.messages.Status; import java.io.IOException; import java.security.InvalidKeyException; @@ -55,20 +51,20 @@ public static void main(String[] args) tags.put("key1", "value1"); tags.put("key2", "value2"); - ReplicationRule rule = - new ReplicationRule( - new DeleteMarkerReplication(Status.DISABLED), - new ReplicationDestination( + ReplicationConfiguration.Rule rule = + new ReplicationConfiguration.Rule( + new ReplicationConfiguration.DeleteMarkerReplication(Status.DISABLED), + new ReplicationConfiguration.Destination( null, null, "REPLACE-WITH-ACTUAL-DESTINATION-BUCKET-ARN", null, null, null, null), null, - new RuleFilter(new AndOperator("TaxDocs", tags)), + new Filter(new Filter.And("TaxDocs", tags)), "rule1", null, 1, null, Status.ENABLED); - List rules = new LinkedList<>(); + List rules = new LinkedList<>(); rules.add(rule); ReplicationConfiguration config = diff --git a/examples/SetObjectLockConfiguration.java b/examples/SetObjectLockConfiguration.java index 716518c9f..d057554a5 100644 --- a/examples/SetObjectLockConfiguration.java +++ b/examples/SetObjectLockConfiguration.java @@ -18,7 +18,6 @@ import io.minio.SetObjectLockConfigurationArgs; import io.minio.errors.MinioException; import io.minio.messages.ObjectLockConfiguration; -import io.minio.messages.RetentionDurationDays; import io.minio.messages.RetentionMode; import java.io.IOException; import java.security.InvalidKeyException; @@ -45,7 +44,8 @@ public static void main(String[] args) // Declaring config with Retention mode as Compliance and duration as 100 days ObjectLockConfiguration config = - new ObjectLockConfiguration(RetentionMode.COMPLIANCE, new RetentionDurationDays(100)); + new ObjectLockConfiguration( + RetentionMode.COMPLIANCE, new ObjectLockConfiguration.RetentionDurationDays(100)); minioClient.setObjectLockConfiguration( SetObjectLockConfigurationArgs.builder() diff --git a/examples/StatObject.java b/examples/StatObject.java index f6a3e13d1..99bac0721 100644 --- a/examples/StatObject.java +++ b/examples/StatObject.java @@ -15,7 +15,7 @@ */ import io.minio.MinioClient; -import io.minio.ServerSideEncryptionCustomerKey; +import io.minio.ServerSideEncryption; import io.minio.StatObjectArgs; import io.minio.StatObjectResponse; import io.minio.errors.MinioException; @@ -46,8 +46,8 @@ public static void main(String[] args) KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); String versionId = "ac38316c-fe14-4f96-9f76-8f675ae5a79e"; { diff --git a/examples/UploadObject.java b/examples/UploadObject.java index 35abedc48..26ca3601a 100644 --- a/examples/UploadObject.java +++ b/examples/UploadObject.java @@ -15,7 +15,7 @@ */ import io.minio.MinioClient; -import io.minio.ServerSideEncryptionCustomerKey; +import io.minio.ServerSideEncryption; import io.minio.UploadObjectArgs; import io.minio.errors.MinioException; import java.io.IOException; @@ -56,8 +56,8 @@ public static void main(String[] args) { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); // Upload 'my-filename' as object encrypted 'my-objectname' in 'my-bucketname'. minioClient.uploadObject( diff --git a/functional/FunctionalTest.java b/functional/FunctionalTest.java index 3f2234333..4f8e966dd 100644 --- a/functional/FunctionalTest.java +++ b/functional/FunctionalTest.java @@ -23,9 +23,7 @@ import io.minio.BucketExistsArgs; import io.minio.CloseableIterator; import io.minio.ComposeObjectArgs; -import io.minio.ComposeSource; import io.minio.CopyObjectArgs; -import io.minio.CopySource; import io.minio.DeleteBucketCorsArgs; import io.minio.DeleteBucketEncryptionArgs; import io.minio.DeleteBucketLifecycleArgs; @@ -55,6 +53,8 @@ import io.minio.GetObjectRetentionArgs; import io.minio.GetObjectTagsArgs; import io.minio.GetPresignedObjectUrlArgs; +import io.minio.HeadObjectResponse; +import io.minio.Http; import io.minio.IsObjectLegalHoldEnabledArgs; import io.minio.ListBucketsArgs; import io.minio.ListObjectsArgs; @@ -73,9 +73,6 @@ import io.minio.SelectObjectContentArgs; import io.minio.SelectResponseStream; import io.minio.ServerSideEncryption; -import io.minio.ServerSideEncryptionCustomerKey; -import io.minio.ServerSideEncryptionKms; -import io.minio.ServerSideEncryptionS3; import io.minio.SetBucketCorsArgs; import io.minio.SetBucketEncryptionArgs; import io.minio.SetBucketLifecycleArgs; @@ -88,6 +85,7 @@ import io.minio.SetObjectRetentionArgs; import io.minio.SetObjectTagsArgs; import io.minio.SnowballObject; +import io.minio.SourceObject; import io.minio.StatObjectArgs; import io.minio.StatObjectResponse; import io.minio.Time; @@ -96,38 +94,23 @@ import io.minio.Xml; import io.minio.admin.MinioAdminClient; import io.minio.errors.ErrorResponseException; -import io.minio.http.HttpUtils; -import io.minio.http.Method; +import io.minio.errors.MinioException; +import io.minio.messages.AccessControlList; import io.minio.messages.AccessControlPolicy; -import io.minio.messages.AndOperator; -import io.minio.messages.Bucket; import io.minio.messages.CORSConfiguration; -import io.minio.messages.DeleteMarkerReplication; -import io.minio.messages.DeleteObject; -import io.minio.messages.Event; +import io.minio.messages.DeleteRequest; import io.minio.messages.EventType; -import io.minio.messages.Expiration; -import io.minio.messages.FileHeaderInfo; -import io.minio.messages.GranteeType; +import io.minio.messages.Filter; import io.minio.messages.InputSerialization; import io.minio.messages.LifecycleConfiguration; -import io.minio.messages.LifecycleRule; +import io.minio.messages.ListAllMyBucketsResult; import io.minio.messages.NotificationConfiguration; import io.minio.messages.NotificationRecords; import io.minio.messages.ObjectLockConfiguration; import io.minio.messages.OutputSerialization; -import io.minio.messages.Permission; -import io.minio.messages.QueueConfiguration; -import io.minio.messages.QuoteFields; import io.minio.messages.ReplicationConfiguration; -import io.minio.messages.ReplicationDestination; -import io.minio.messages.ReplicationRule; import io.minio.messages.Retention; -import io.minio.messages.RetentionDuration; -import io.minio.messages.RetentionDurationDays; -import io.minio.messages.RetentionDurationYears; import io.minio.messages.RetentionMode; -import io.minio.messages.RuleFilter; import io.minio.messages.SseAlgorithm; import io.minio.messages.SseConfiguration; import io.minio.messages.Stats; @@ -146,7 +129,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.security.InvalidKeyException; -import java.security.KeyManagementException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -205,8 +187,8 @@ public class FunctionalTest { private static MinioClient client = null; private static TestMinioAdminClient adminClientTests; - private static ServerSideEncryptionCustomerKey ssec = null; - private static ServerSideEncryption sseS3 = new ServerSideEncryptionS3(); + private static ServerSideEncryption.CustomerKey ssec = null; + private static ServerSideEncryption sseS3 = new ServerSideEncryption.S3(); private static ServerSideEncryption sseKms = null; static { @@ -220,7 +202,7 @@ public class FunctionalTest { try { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); - ssec = new ServerSideEncryptionCustomerKey(keyGen.generateKey()); + ssec = new ServerSideEncryption.CustomerKey(keyGen.generateKey()); } catch (InvalidKeyException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } @@ -228,12 +210,8 @@ public class FunctionalTest { public static OkHttpClient newHttpClient() { try { - return HttpUtils.disableCertCheck( - HttpUtils.newDefaultHttpClient( - TimeUnit.MINUTES.toMillis(5), - TimeUnit.MINUTES.toMillis(5), - TimeUnit.MINUTES.toMillis(5))); - } catch (KeyManagementException | NoSuchAlgorithmException e) { + return Http.disableCertCheck(Http.newDefaultClient()); + } catch (MinioException e) { throw new RuntimeException(e); } } @@ -574,9 +552,9 @@ public static void listBuckets() throws Exception { expectedBucketNames.add(bucketName); List bucketNames = new LinkedList<>(); - for (Result result : + for (Result result : client.listBuckets(ListBucketsArgs.builder().maxBuckets(1).build())) { - Bucket bucket = result.get(); + ListAllMyBucketsResult.Bucket bucket = result.get(); if (expectedBucketNames.contains(bucket.name())) { bucketNames.add(bucket.name()); } @@ -847,7 +825,7 @@ public static void putObject() throws Exception { testPutObject( "[single upload]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .contentType(customContentType) .build(), null); @@ -859,7 +837,7 @@ public static void putObject() throws Exception { testPutObject( "[multi-part upload]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(11 * MB), 11 * MB, -1) + new ContentInputStream(11 * MB), 11L * MB, null) .contentType(customContentType) .build(), null); @@ -867,7 +845,7 @@ public static void putObject() throws Exception { testPutObject( "[object name with path segments]", PutObjectArgs.builder().bucket(bucketName).object("path/to/" + getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .contentType(customContentType) .build(), null); @@ -875,14 +853,14 @@ public static void putObject() throws Exception { testPutObject( "[zero sized object]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(0), 0, -1) + new ContentInputStream(0), 0L, null) .build(), null); testPutObject( "[object name ends with '/']", PutObjectArgs.builder().bucket(bucketName).object("path/to/" + getRandomName() + "/") - .stream(new ContentInputStream(0), 0, -1) + .stream(new ContentInputStream(0), 0L, null) .contentType(customContentType) .build(), null); @@ -890,7 +868,7 @@ public static void putObject() throws Exception { testPutObject( "[unknown stream size, single upload]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), -1, PutObjectArgs.MIN_MULTIPART_SIZE) + new ContentInputStream(1 * KB), null, (long) PutObjectArgs.MIN_MULTIPART_SIZE) .contentType(customContentType) .build(), null); @@ -898,7 +876,7 @@ public static void putObject() throws Exception { testPutObject( "[unknown stream size, multi-part upload]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(11 * MB), -1, PutObjectArgs.MIN_MULTIPART_SIZE) + new ContentInputStream(11 * MB), null, (long) PutObjectArgs.MIN_MULTIPART_SIZE) .contentType(customContentType) .build(), null); @@ -912,7 +890,7 @@ public static void putObject() throws Exception { testPutObject( "[user metadata]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .userMetadata(userMetadata) .build(), null); @@ -923,7 +901,7 @@ public static void putObject() throws Exception { testPutObject( "[storage-class=REDUCED_REDUNDANCY]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .headers(headers) .build(), null); @@ -932,7 +910,7 @@ public static void putObject() throws Exception { testPutObject( "[storage-class=STANDARD]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .headers(headers) .build(), null); @@ -941,7 +919,7 @@ public static void putObject() throws Exception { testPutObject( "[storage-class=INVALID negative case]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .headers(headers) .build(), "InvalidStorageClass"); @@ -949,7 +927,7 @@ public static void putObject() throws Exception { testPutObject( "[SSE-S3]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .contentType(customContentType) .sse(sseS3) .build(), @@ -959,7 +937,7 @@ public static void putObject() throws Exception { testPutObject( "[with retention]", PutObjectArgs.builder().bucket(bucketNameWithLock).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .retention( new Retention(RetentionMode.GOVERNANCE, ZonedDateTime.now(Time.UTC).plusDays(1))) .build(), @@ -975,7 +953,7 @@ public static void putObject() throws Exception { testPutObject( "[SSE-C single upload]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .contentType(customContentType) .sse(ssec) .build(), @@ -984,7 +962,7 @@ public static void putObject() throws Exception { testPutObject( "[SSE-C multi-part upload]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(11 * MB), 11 * MB, -1) + new ContentInputStream(11 * MB), 11L * MB, null) .contentType(customContentType) .sse(ssec) .build(), @@ -998,7 +976,7 @@ public static void putObject() throws Exception { testPutObject( "[SSE-KMS]", PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .contentType(customContentType) .sse(sseKms) .build(), @@ -1012,9 +990,9 @@ public static void testStatObject( try { client.putObject(args); try { - ServerSideEncryptionCustomerKey ssec = null; - if (args.sse() instanceof ServerSideEncryptionCustomerKey) { - ssec = (ServerSideEncryptionCustomerKey) args.sse(); + ServerSideEncryption.CustomerKey ssec = null; + if (args.sse() instanceof ServerSideEncryption.CustomerKey) { + ssec = (ServerSideEncryption.CustomerKey) args.sse(); } StatObjectResponse stat = client.statObject( @@ -1080,7 +1058,7 @@ public static void statObject() throws Exception { PutObjectArgs.Builder builder = PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1024), 1024, -1); + new ContentInputStream(1024), 1024L, null); Headers.Builder headersBuilder = new Headers.Builder() .add("Content-Type: application/octet-stream") @@ -1090,24 +1068,26 @@ public static void statObject() throws Exception { testStatObject( "[basic check]", builder.build(), - new StatObjectResponse(headersBuilder.build(), bucketName, null, objectName)); + new StatObjectResponse( + new HeadObjectResponse(headersBuilder.build(), bucketName, null, objectName))); Map headers = new HashMap<>(); headers.put("Content-Type", customContentType); Map userMetadata = new HashMap<>(); userMetadata.put("My-Project", "Project One"); builder = builder.headers(headers).userMetadata(userMetadata); - builder = builder.stream(new ContentInputStream(1024), 1024, -1); + builder = builder.stream(new ContentInputStream(1024), 1024L, null); StatObjectResponse stat = new StatObjectResponse( - headersBuilder - .set("Content-Type", customContentType) - .add("X-Amz-Meta-My-Project: Project One") - .build(), - bucketName, - null, - objectName); + new HeadObjectResponse( + headersBuilder + .set("Content-Type", customContentType) + .add("X-Amz-Meta-My-Project: Project One") + .build(), + bucketName, + null, + objectName)); testStatObject("[user metadata]", builder.build(), stat); @@ -1115,7 +1095,7 @@ public static void statObject() throws Exception { return; } - builder = builder.stream(new ContentInputStream(1024), 1024, -1); + builder = builder.stream(new ContentInputStream(1024), 1024L, null); testStatObject("[SSE-S3]", builder.sse(sseS3).build(), stat); if (!isSecureEndpoint) { @@ -1123,7 +1103,7 @@ public static void statObject() throws Exception { return; } - builder = builder.stream(new ContentInputStream(1024), 1024, -1); + builder = builder.stream(new ContentInputStream(1024), 1024L, null); testStatObject("[SSE-C]", builder.sse(ssec).build(), stat); if (sseKms == null) { @@ -1131,7 +1111,7 @@ public static void statObject() throws Exception { return; } - builder = builder.stream(new ContentInputStream(1024), 1024, -1); + builder = builder.stream(new ContentInputStream(1024), 1024L, null); testStatObject("[SSE-KMS]", builder.sse(sseKms).build(), stat); } @@ -1148,7 +1128,7 @@ public static void testGetObject( try { PutObjectArgs.Builder builder = PutObjectArgs.builder().bucket(args.bucket()).object(args.object()).stream( - new ContentInputStream(objectSize), objectSize, -1); + new ContentInputStream(objectSize), objectSize, null); if (sse != null) { builder.sse(sse); } @@ -1289,7 +1269,7 @@ public static void testDownloadObject( try { PutObjectArgs.Builder builder = PutObjectArgs.builder().bucket(args.bucket()).object(args.object()).stream( - new ContentInputStream(objectSize), objectSize, -1); + new ContentInputStream(objectSize), (long) objectSize, null); if (sse != null) { builder.sse(sse); } @@ -1363,14 +1343,14 @@ public static List createObjects(String bucketName, int cou results.add( client.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1), 1, -1) + new ContentInputStream(1), 1L, null) .build())); if (versions > 1) { for (int j = 0; j < versions - 1; j++) { results.add( client.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1), 1, -1) + new ContentInputStream(1), 1L, null) .build())); } } @@ -1381,11 +1361,11 @@ public static List createObjects(String bucketName, int cou public static void removeObjects(String bucketName, List results) throws Exception { - List objects = + List objects = results.stream() .map( result -> { - return new DeleteObject(result.object(), result.versionId()); + return new DeleteRequest.Object(result.object(), result.versionId()); }) .collect(Collectors.toList()); for (Result r : @@ -1489,7 +1469,7 @@ public static void testRemoveObject( try { PutObjectArgs.Builder builder = PutObjectArgs.builder().bucket(args.bucket()).object(args.object()).stream( - new ContentInputStream(1), 1, -1); + new ContentInputStream(1), 1L, null); if (sse != null) { builder.sse(sse); } @@ -1618,14 +1598,14 @@ public static void testGetPresignedObjectUrlForGet() throws Exception { String objectName = getRandomName(); client.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); try { testTags = "[GET]"; testGetPresignedUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket(bucketName) .object(objectName) .build(), @@ -1634,7 +1614,7 @@ public static void testGetPresignedObjectUrlForGet() throws Exception { testTags = "[GET, expiry]"; testGetPresignedUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket(bucketName) .object(objectName) .expiry(1, TimeUnit.DAYS) @@ -1646,7 +1626,7 @@ public static void testGetPresignedObjectUrlForGet() throws Exception { queryParams.put("response-content-type", "application/json"); testGetPresignedUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket(bucketName) .object(objectName) .expiry(1, TimeUnit.DAYS) @@ -1702,7 +1682,7 @@ public static void testGetPresignedObjectUrlForPut() throws Exception { data, expectedChecksum, GetPresignedObjectUrlArgs.builder() - .method(Method.PUT) + .method(Http.Method.PUT) .bucket(bucketName) .object(objectName) .build()); @@ -1712,7 +1692,7 @@ public static void testGetPresignedObjectUrlForPut() throws Exception { data, expectedChecksum, GetPresignedObjectUrlArgs.builder() - .method(Method.PUT) + .method(Http.Method.PUT) .bucket(bucketName) .object(objectName) .expiry(1, TimeUnit.DAYS) @@ -1760,7 +1740,7 @@ public static void getPresignedPostFormData() throws Exception { String urlString = client.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() - .method(Method.GET) + .method(Http.Method.GET) .bucket(bucketName) .object("x") .build()); @@ -1798,7 +1778,7 @@ public static void testCopyObject( try { PutObjectArgs.Builder builder = PutObjectArgs.builder().bucket(args.source().bucket()).object(args.source().object()) - .stream(new ContentInputStream(1 * KB), 1 * KB, -1); + .stream(new ContentInputStream(1 * KB), 1L * KB, null); if (sse != null) { builder.sse(sse); } @@ -1815,9 +1795,9 @@ public static void testCopyObject( } else { client.copyObject(args); - ServerSideEncryptionCustomerKey ssec = null; - if (sse instanceof ServerSideEncryptionCustomerKey) { - ssec = (ServerSideEncryptionCustomerKey) sse; + ServerSideEncryption.CustomerKey ssec = null; + if (sse instanceof ServerSideEncryption.CustomerKey) { + ssec = (ServerSideEncryption.CustomerKey) sse; } client.statObject( StatObjectArgs.builder() @@ -1854,7 +1834,7 @@ public static void testCopyObjectMatchETag() throws Exception { ObjectWriteResponse result = client.putObject( PutObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); client.copyObject( @@ -1862,7 +1842,7 @@ public static void testCopyObjectMatchETag() throws Exception { .bucket(bucketName) .object(srcObjectName + "-copy") .source( - CopySource.builder() + SourceObject.builder() .bucket(srcBucketName) .object(srcObjectName) .matchETag(result.etag()) @@ -1896,7 +1876,7 @@ public static void testCopyObjectMetadataReplace() throws Exception { try { client.putObject( PutObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); Map headers = new HashMap<>(); @@ -1906,7 +1886,7 @@ public static void testCopyObjectMetadataReplace() throws Exception { CopyObjectArgs.builder() .bucket(bucketName) .object(srcObjectName + "-copy") - .source(CopySource.builder().bucket(srcBucketName).object(srcObjectName).build()) + .source(SourceObject.builder().bucket(srcBucketName).object(srcObjectName).build()) .headers(headers) .metadataDirective(Directive.REPLACE) .build()); @@ -1948,7 +1928,7 @@ public static void testCopyObjectEmptyMetadataReplace() throws Exception { headers.put("X-Amz-Meta-My-Project", "Project One"); client.putObject( PutObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .headers(headers) .build()); @@ -1956,7 +1936,7 @@ public static void testCopyObjectEmptyMetadataReplace() throws Exception { CopyObjectArgs.builder() .bucket(bucketName) .object(srcObjectName + "-copy") - .source(CopySource.builder().bucket(srcBucketName).object(srcObjectName).build()) + .source(SourceObject.builder().bucket(srcBucketName).object(srcObjectName).build()) .metadataDirective(Directive.REPLACE) .build()); @@ -1995,7 +1975,7 @@ public static void copyObject() throws Exception { CopyObjectArgs.builder() .bucket(bucketName) .object(getRandomName()) - .source(CopySource.builder().bucket(getRandomName()).object(objectName).build()) + .source(SourceObject.builder().bucket(getRandomName()).object(objectName).build()) .build(), false); @@ -2010,7 +1990,7 @@ public static void copyObject() throws Exception { .bucket(bucketName) .object(getRandomName()) .source( - CopySource.builder() + SourceObject.builder() .bucket(getRandomName()) .object(getRandomName()) .matchETag("invalid-etag") @@ -2027,7 +2007,7 @@ public static void copyObject() throws Exception { .bucket(bucketName) .object(getRandomName()) .source( - CopySource.builder() + SourceObject.builder() .bucket(getRandomName()) .object(getRandomName()) .notMatchETag("not-etag-of-source-object") @@ -2042,7 +2022,7 @@ public static void copyObject() throws Exception { .bucket(bucketName) .object(getRandomName()) .source( - CopySource.builder() + SourceObject.builder() .bucket(getRandomName()) .object(getRandomName()) .modifiedSince(ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)) @@ -2057,7 +2037,7 @@ public static void copyObject() throws Exception { .bucket(bucketName) .object(getRandomName()) .source( - CopySource.builder() + SourceObject.builder() .bucket(getRandomName()) .object(getRandomName()) .unmodifiedSince(ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)) @@ -2075,7 +2055,7 @@ public static void copyObject() throws Exception { .bucket(bucketName) .object(getRandomName()) .sse(sseS3) - .source(CopySource.builder().bucket(getRandomName()).object(getRandomName()).build()) + .source(SourceObject.builder().bucket(getRandomName()).object(getRandomName()).build()) .build(), false); @@ -2093,7 +2073,7 @@ public static void copyObject() throws Exception { .object(getRandomName()) .sse(ssec) .source( - CopySource.builder() + SourceObject.builder() .bucket(getRandomName()) .object(getRandomName()) .ssec(ssec) @@ -2113,7 +2093,7 @@ public static void copyObject() throws Exception { .bucket(bucketName) .object(getRandomName()) .sse(sseKms) - .source(CopySource.builder().bucket(getRandomName()).object(getRandomName()).build()) + .source(SourceObject.builder().bucket(getRandomName()).object(getRandomName()).build()) .build(), false); } @@ -2131,7 +2111,7 @@ public static void testComposeObject(String testTags, ComposeObjectArgs args) th } } - public static List createComposeSourceList(ComposeSource... sources) { + public static List createSourceObjectList(SourceObject... sources) { return Arrays.asList(sources); } @@ -2143,8 +2123,8 @@ public static void composeObjectTests(String object1Mb, String object6Mb, String .bucket(bucketName) .object(getRandomName()) .sources( - createComposeSourceList( - ComposeSource.builder().bucket(bucketName).object(object1Mb).build())) + createSourceObjectList( + SourceObject.builder().bucket(bucketName).object(object1Mb).build())) .build()); testComposeObject( @@ -2153,8 +2133,8 @@ public static void composeObjectTests(String object1Mb, String object6Mb, String .bucket(bucketName) .object(getRandomName()) .sources( - createComposeSourceList( - ComposeSource.builder() + createSourceObjectList( + SourceObject.builder() .bucket(bucketName) .object(object1Mb) .offset(2L * KB) @@ -2167,8 +2147,8 @@ public static void composeObjectTests(String object1Mb, String object6Mb, String .bucket(bucketName) .object(getRandomName()) .sources( - createComposeSourceList( - ComposeSource.builder() + createSourceObjectList( + SourceObject.builder() .bucket(bucketName) .object(object1Mb) .offset(2L * KB) @@ -2182,8 +2162,8 @@ public static void composeObjectTests(String object1Mb, String object6Mb, String .bucket(bucketName) .object(getRandomName()) .sources( - createComposeSourceList( - ComposeSource.builder().bucket(bucketName).object(object6Mb).build())) + createSourceObjectList( + SourceObject.builder().bucket(bucketName).object(object6Mb).build())) .build()); testComposeObject( @@ -2192,9 +2172,9 @@ public static void composeObjectTests(String object1Mb, String object6Mb, String .bucket(bucketName) .object(getRandomName()) .sources( - createComposeSourceList( - ComposeSource.builder().bucket(bucketName).object(object6Mb).build(), - ComposeSource.builder().bucket(bucketName).object(object6Mb).build())) + createSourceObjectList( + SourceObject.builder().bucket(bucketName).object(object6Mb).build(), + SourceObject.builder().bucket(bucketName).object(object6Mb).build())) .build()); testComposeObject( @@ -2203,14 +2183,14 @@ public static void composeObjectTests(String object1Mb, String object6Mb, String .bucket(bucketName) .object(getRandomName()) .sources( - createComposeSourceList( - ComposeSource.builder() + createSourceObjectList( + SourceObject.builder() .bucket(bucketName) .object(object6Mb) .offset(10L) .length(6291436L) .build(), - ComposeSource.builder().bucket(bucketName).object(object6Mb).build())) + SourceObject.builder().bucket(bucketName).object(object6Mb).build())) .build()); if (isQuickTest) { @@ -2228,13 +2208,13 @@ public static void composeObjectTests(String object1Mb, String object6Mb, String .object(getRandomName()) .sse(ssec) .sources( - createComposeSourceList( - ComposeSource.builder() + createSourceObjectList( + SourceObject.builder() .bucket(bucketName) .object(object6MbSsec) .ssec(ssec) .build(), - ComposeSource.builder() + SourceObject.builder() .bucket(bucketName) .object(object6MbSsec) .ssec(ssec) @@ -2247,13 +2227,13 @@ public static void composeObjectTests(String object1Mb, String object6Mb, String .bucket(bucketName) .object(getRandomName()) .sources( - createComposeSourceList( - ComposeSource.builder() + createSourceObjectList( + SourceObject.builder() .bucket(bucketName) .object(object6MbSsec) .ssec(ssec) .build(), - ComposeSource.builder().bucket(bucketName).object(object6Mb).build())) + SourceObject.builder().bucket(bucketName).object(object6Mb).build())) .build()); } @@ -2275,7 +2255,7 @@ public static void composeObject() throws Exception { response = client.putObject( PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * MB), 1 * MB, -1) + new ContentInputStream(1 * MB), 1L * MB, null) .build()); createdObjects.add(response); object1Mb = response.object(); @@ -2283,7 +2263,7 @@ public static void composeObject() throws Exception { response = client.putObject( PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(6 * MB), 6 * MB, -1) + new ContentInputStream(6 * MB), 6L * MB, null) .build()); createdObjects.add(response); object6Mb = response.object(); @@ -2292,7 +2272,7 @@ public static void composeObject() throws Exception { response = client.putObject( PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(6 * MB), 6 * MB, -1) + new ContentInputStream(6 * MB), 6L * MB, null) .sse(ssec) .build()); createdObjects.add(response); @@ -2340,7 +2320,7 @@ public static void enableObjectLegalHold() throws Exception { objectInfo = client.putObject( PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); checkObjectLegalHold(bucketNameWithLock, objectName, true); @@ -2380,7 +2360,7 @@ public static void disableObjectLegalHold() throws Exception { objectInfo = client.putObject( PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); checkObjectLegalHold(bucketNameWithLock, objectName, false); @@ -2422,7 +2402,7 @@ public static void isObjectLegalHoldEnabled() throws Exception { objectInfo = client.putObject( PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); boolean result = @@ -2464,7 +2444,8 @@ public static void setObjectLockConfiguration() throws Exception { client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); try { ObjectLockConfiguration config = - new ObjectLockConfiguration(RetentionMode.COMPLIANCE, new RetentionDurationDays(10)); + new ObjectLockConfiguration( + RetentionMode.COMPLIANCE, new ObjectLockConfiguration.RetentionDurationDays(10)); client.setObjectLockConfiguration( SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(config).build()); } finally { @@ -2477,7 +2458,8 @@ public static void setObjectLockConfiguration() throws Exception { } public static void testGetObjectLockConfiguration( - String bucketName, RetentionMode mode, RetentionDuration duration) throws Exception { + String bucketName, RetentionMode mode, ObjectLockConfiguration.RetentionDuration duration) + throws Exception { ObjectLockConfiguration expectedConfig = new ObjectLockConfiguration(mode, duration); client.setObjectLockConfiguration( SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(expectedConfig).build()); @@ -2506,9 +2488,13 @@ public static void getObjectLockConfiguration() throws Exception { client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); try { testGetObjectLockConfiguration( - bucketName, RetentionMode.COMPLIANCE, new RetentionDurationDays(10)); + bucketName, + RetentionMode.COMPLIANCE, + new ObjectLockConfiguration.RetentionDurationDays(10)); testGetObjectLockConfiguration( - bucketName, RetentionMode.GOVERNANCE, new RetentionDurationYears(1)); + bucketName, + RetentionMode.GOVERNANCE, + new ObjectLockConfiguration.RetentionDurationYears(1)); } finally { client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); } @@ -2533,7 +2519,8 @@ public static void deleteObjectLockConfiguration() throws Exception { client.deleteObjectLockConfiguration( DeleteObjectLockConfigurationArgs.builder().bucket(bucketName).build()); ObjectLockConfiguration config = - new ObjectLockConfiguration(RetentionMode.COMPLIANCE, new RetentionDurationDays(10)); + new ObjectLockConfiguration( + RetentionMode.COMPLIANCE, new ObjectLockConfiguration.RetentionDurationDays(10)); client.setObjectLockConfiguration( SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(config).build()); client.deleteObjectLockConfiguration( @@ -2563,7 +2550,7 @@ public static void setObjectRetention() throws Exception { objectInfo = client.putObject( PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); client.setObjectRetention( @@ -2647,7 +2634,7 @@ public static void getObjectRetention() throws Exception { objectInfo = client.putObject( PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); testGetObjectRetention( @@ -2787,7 +2774,7 @@ public static void deleteBucketPolicy() throws Exception { } } - public static void testSetBucketLifecycle(String bucketName, LifecycleRule... rules) + public static void testSetBucketLifecycle(String bucketName, LifecycleConfiguration.Rule... rules) throws Exception { LifecycleConfiguration config = new LifecycleConfiguration(Arrays.asList(rules)); client.setBucketLifecycle( @@ -2807,11 +2794,11 @@ public static void setBucketLifecycle() throws Exception { try { testSetBucketLifecycle( bucketName, - new LifecycleRule( + new LifecycleConfiguration.Rule( Status.ENABLED, null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter("logs/"), + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null), + new Filter("logs/"), "rule2", null, null, @@ -2840,11 +2827,11 @@ public static void deleteBucketLifecycle() throws Exception { DeleteBucketLifecycleArgs.builder().bucket(bucketName).build()); testSetBucketLifecycle( bucketName, - new LifecycleRule( + new LifecycleConfiguration.Rule( Status.ENABLED, null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter("logs/"), + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null), + new Filter("logs/"), "rule2", null, null, @@ -2876,11 +2863,11 @@ public static void getBucketLifecycle() throws Exception { Assert.assertNull("config: expected: , got: ", config); testSetBucketLifecycle( bucketName, - new LifecycleRule( + new LifecycleConfiguration.Rule( Status.ENABLED, null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter("logs/"), + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null), + new Filter("logs/"), "rule2", null, null, @@ -2888,12 +2875,12 @@ public static void getBucketLifecycle() throws Exception { config = client.getBucketLifecycle(GetBucketLifecycleArgs.builder().bucket(bucketName).build()); Assert.assertNotNull("config: expected: , got: ", config); - List rules = config.rules(); + List rules = config.rules(); Assert.assertEquals( "config.rules().size(): expected: 1, got: " + config.rules().size(), 1, config.rules().size()); - LifecycleRule rule = rules.get(0); + LifecycleConfiguration.Rule rule = rules.get(0); Assert.assertEquals( "rule.status(): expected: " + Status.ENABLED + ", got: " + rule.status(), rule.status(), @@ -2913,11 +2900,11 @@ public static void getBucketLifecycle() throws Exception { testSetBucketLifecycle( bucketName, - new LifecycleRule( + new LifecycleConfiguration.Rule( Status.ENABLED, null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter(""), + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null), + new Filter(""), null, null, null, @@ -2961,21 +2948,23 @@ public static void setBucketNotification() throws Exception { String bucketName = getRandomName(); client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build()); try { - List eventList = new LinkedList<>(); - eventList.add(EventType.OBJECT_CREATED_PUT); - eventList.add(EventType.OBJECT_CREATED_COPY); - QueueConfiguration queueConfig = new QueueConfiguration(); - queueConfig.setQueue(sqsArn); - queueConfig.setEvents(eventList); - queueConfig.setPrefixRule("images"); - queueConfig.setSuffixRule("pg"); - - List queueConfigList = new LinkedList<>(); - queueConfigList.add(queueConfig); - - NotificationConfiguration config = new NotificationConfiguration(); - config.setQueueConfigurationList(queueConfigList); - + NotificationConfiguration config = + new NotificationConfiguration( + null, + Arrays.asList( + new NotificationConfiguration.QueueConfiguration[] { + new NotificationConfiguration.QueueConfiguration( + sqsArn, + null, + Arrays.asList( + new String[] { + EventType.OBJECT_CREATED_PUT.toString(), + EventType.OBJECT_CREATED_COPY.toString() + }), + new NotificationConfiguration.Filter("images", "pg")) + }), + null, + null); client.setBucketNotification( SetBucketNotificationArgs.builder().bucket(bucketName).config(config).build()); } finally { @@ -3003,18 +2992,19 @@ public static void getBucketNotification() throws Exception { String bucketName = getRandomName(); client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build()); try { - List eventList = new LinkedList<>(); - eventList.add(EventType.OBJECT_CREATED_PUT); - QueueConfiguration queueConfig = new QueueConfiguration(); - queueConfig.setQueue(sqsArn); - queueConfig.setEvents(eventList); - - List queueConfigList = new LinkedList<>(); - queueConfigList.add(queueConfig); - - NotificationConfiguration expectedConfig = new NotificationConfiguration(); - expectedConfig.setQueueConfigurationList(queueConfigList); - + NotificationConfiguration expectedConfig = + new NotificationConfiguration( + null, + Arrays.asList( + new NotificationConfiguration.QueueConfiguration[] { + new NotificationConfiguration.QueueConfiguration( + sqsArn, + null, + Arrays.asList(new String[] {EventType.OBJECT_CREATED_PUT.toString()}), + new NotificationConfiguration.Filter("images", "pg")) + }), + null, + null); client.setBucketNotification( SetBucketNotificationArgs.builder().bucket(bucketName).config(expectedConfig).build()); @@ -3022,11 +3012,12 @@ public static void getBucketNotification() throws Exception { client.getBucketNotification( GetBucketNotificationArgs.builder().bucket(bucketName).build()); - if (config.queueConfigurationList().size() != 1 - || !sqsArn.equals(config.queueConfigurationList().get(0).queue()) - || config.queueConfigurationList().get(0).events().size() != 1 - || config.queueConfigurationList().get(0).events().get(0) - != EventType.OBJECT_CREATED_PUT) { + if (config.queueConfigurations().size() != 1 + || !sqsArn.equals(config.queueConfigurations().get(0).queue()) + || config.queueConfigurations().get(0).events().size() != 1 + || !EventType.OBJECT_CREATED_PUT + .toString() + .equals(config.queueConfigurations().get(0).events().get(0))) { System.out.println( "config: expected: " + Xml.marshal(expectedConfig) + ", got: " + Xml.marshal(config)); } @@ -3055,21 +3046,23 @@ public static void deleteBucketNotification() throws Exception { String bucketName = getRandomName(); client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build()); try { - List eventList = new LinkedList<>(); - eventList.add(EventType.OBJECT_CREATED_PUT); - eventList.add(EventType.OBJECT_CREATED_COPY); - QueueConfiguration queueConfig = new QueueConfiguration(); - queueConfig.setQueue(sqsArn); - queueConfig.setEvents(eventList); - queueConfig.setPrefixRule("images"); - queueConfig.setSuffixRule("pg"); - - List queueConfigList = new LinkedList<>(); - queueConfigList.add(queueConfig); - - NotificationConfiguration config = new NotificationConfiguration(); - config.setQueueConfigurationList(queueConfigList); - + NotificationConfiguration config = + new NotificationConfiguration( + null, + Arrays.asList( + new NotificationConfiguration.QueueConfiguration[] { + new NotificationConfiguration.QueueConfiguration( + sqsArn, + null, + Arrays.asList( + new String[] { + EventType.OBJECT_CREATED_PUT.toString(), + EventType.OBJECT_CREATED_COPY.toString() + }), + new NotificationConfiguration.Filter("images", "pg")) + }), + null, + null); client.setBucketNotification( SetBucketNotificationArgs.builder().bucket(bucketName).config(config).build()); @@ -3079,7 +3072,7 @@ public static void deleteBucketNotification() throws Exception { config = client.getBucketNotification( GetBucketNotificationArgs.builder().bucket(bucketName).build()); - if (config.queueConfigurationList().size() != 0) { + if (config.queueConfigurations().size() != 0) { System.out.println("config: expected: , got: " + Xml.marshal(config)); } } finally { @@ -3118,7 +3111,7 @@ public static void listenBucketNotification() throws Exception { client.putObject( PutObjectArgs.builder().bucket(bucketName).object("prefix-random-suffix").stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); while (ci.hasNext()) { @@ -3128,8 +3121,8 @@ public static void listenBucketNotification() throws Exception { } boolean found = false; - for (Event event : records.events()) { - if (event.objectName().equals("prefix-random-suffix")) { + for (NotificationRecords.Event event : records.events()) { + if ("prefix-random-suffix".equals(event.object().key())) { found = true; break; } @@ -3179,13 +3172,15 @@ public static void selectObjectContent() throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(data); client.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - bais, data.length, -1) + bais, (long) data.length, null) .build()); InputSerialization is = - new InputSerialization(null, false, null, null, FileHeaderInfo.USE, null, null, null); + InputSerialization.newCSV( + null, false, null, null, InputSerialization.FileHeaderInfo.USE, null, null, null); OutputSerialization os = - new OutputSerialization(null, null, null, QuoteFields.ASNEEDED, null); + OutputSerialization.newCSV( + null, null, null, OutputSerialization.QuoteFields.ASNEEDED, null); responseStream = client.selectObjectContent( @@ -3206,18 +3201,15 @@ public static void selectObjectContent() throws Exception { Stats stats = responseStream.stats(); Assert.assertNotNull("stats is null", stats); - Assert.assertEquals( + Assert.assertTrue( "stats.bytesScanned mismatch; expected: 258, got: " + stats.bytesScanned(), - stats.bytesScanned(), - 256); - Assert.assertEquals( + stats.bytesScanned() == 256); + Assert.assertTrue( "stats.bytesProcessed mismatch; expected: 258, got: " + stats.bytesProcessed(), - stats.bytesProcessed(), - 256); - Assert.assertEquals( + stats.bytesProcessed() == 256); + Assert.assertTrue( "stats.bytesReturned mismatch; expected: 222, got: " + stats.bytesReturned(), - stats.bytesReturned(), - 222); + stats.bytesReturned() == 222); mintSuccessLog(methodName, testArgs, startTime); } catch (Exception e) { handleException(methodName, testArgs, startTime, e); @@ -3488,7 +3480,7 @@ public static void setObjectTags() throws Exception { try { client.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); Map map = new HashMap<>(); map.put("Project", "Project One"); @@ -3517,7 +3509,7 @@ public static void getObjectTags() throws Exception { try { client.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); Map map = new HashMap<>(); Tags tags = @@ -3555,7 +3547,7 @@ public static void deleteObjectTags() throws Exception { try { client.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); client.deleteObjectTags( DeleteObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); @@ -3593,25 +3585,25 @@ public static void getObjectAcl() throws Exception { try { client.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); AccessControlPolicy policy = client.getObjectAcl( GetObjectAclArgs.builder().bucket(bucketName).object(objectName).build()); Assert.assertEquals( "granteeType: expected: " - + GranteeType.CANONICAL_USER + + AccessControlList.Type.CANONICAL_USER + ", got: " + policy.accessControlList().grants().get(0).grantee().type(), policy.accessControlList().grants().get(0).grantee().type(), - GranteeType.CANONICAL_USER); + AccessControlList.Type.CANONICAL_USER); Assert.assertEquals( "permission: expected: " - + Permission.FULL_CONTROL + + AccessControlList.Permission.FULL_CONTROL + ", got: " + policy.accessControlList().grants().get(0).permission(), policy.accessControlList().grants().get(0).permission(), - Permission.FULL_CONTROL); + AccessControlList.Permission.FULL_CONTROL); mintSuccessLog(methodName, null, startTime); } finally { client.removeObject( @@ -3634,7 +3626,7 @@ public static void getObjectAttributes() throws Exception { try { client.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) + new ContentInputStream(1 * KB), 1L * KB, null) .build()); GetObjectAttributesResponse response = client.getObjectAttributes( @@ -3686,19 +3678,20 @@ public static void setBucketReplication() throws Exception { tags.put("key1", "value1"); tags.put("key2", "value2"); - ReplicationRule rule = - new ReplicationRule( - new DeleteMarkerReplication(Status.DISABLED), - new ReplicationDestination(null, null, replicationBucketArn, null, null, null, null), + ReplicationConfiguration.Rule rule = + new ReplicationConfiguration.Rule( + new ReplicationConfiguration.DeleteMarkerReplication(Status.DISABLED), + new ReplicationConfiguration.Destination( + null, null, replicationBucketArn, null, null, null, null), null, - new RuleFilter(new AndOperator("TaxDocs", tags)), + new Filter(new Filter.And("TaxDocs", tags)), "rule1", null, 1, null, Status.ENABLED); - List rules = new LinkedList<>(); + List rules = new LinkedList<>(); rules.add(rule); ReplicationConfiguration config = new ReplicationConfiguration(replicationRole, rules); @@ -3735,19 +3728,20 @@ public static void getBucketReplication() throws Exception { tags.put("key1", "value1"); tags.put("key2", "value2"); - ReplicationRule rule = - new ReplicationRule( - new DeleteMarkerReplication(Status.DISABLED), - new ReplicationDestination(null, null, replicationBucketArn, null, null, null, null), + ReplicationConfiguration.Rule rule = + new ReplicationConfiguration.Rule( + new ReplicationConfiguration.DeleteMarkerReplication(Status.DISABLED), + new ReplicationConfiguration.Destination( + null, null, replicationBucketArn, null, null, null, null), null, - new RuleFilter(new AndOperator("TaxDocs", tags)), + new Filter(new Filter.And("TaxDocs", tags)), "rule1", null, 1, null, Status.ENABLED); - List rules = new LinkedList<>(); + List rules = new LinkedList<>(); rules.add(rule); config = new ReplicationConfiguration(replicationRole, rules); @@ -3785,19 +3779,20 @@ public static void deleteBucketReplication() throws Exception { tags.put("key1", "value1"); tags.put("key2", "value2"); - ReplicationRule rule = - new ReplicationRule( - new DeleteMarkerReplication(Status.DISABLED), - new ReplicationDestination(null, null, replicationBucketArn, null, null, null, null), + ReplicationConfiguration.Rule rule = + new ReplicationConfiguration.Rule( + new ReplicationConfiguration.DeleteMarkerReplication(Status.DISABLED), + new ReplicationConfiguration.Destination( + null, null, replicationBucketArn, null, null, null, null), null, - new RuleFilter(new AndOperator("TaxDocs", tags)), + new Filter(new Filter.And("TaxDocs", tags)), "rule1", null, 1, null, Status.ENABLED); - List rules = new LinkedList<>(); + List rules = new LinkedList<>(); rules.add(rule); ReplicationConfiguration config = new ReplicationConfiguration(replicationRole, rules); @@ -3902,11 +3897,7 @@ public static void putObjectFanOut() throws Exception { "userMetadata: expected = " + map + ", got = " + stat.userMetadata(), map.size() == stat.userMetadata().size() && map.entrySet().stream() - .allMatch( - e -> - e.getValue() - .equals( - stat.userMetadata().get(e.getKey().toLowerCase(Locale.US))))); + .allMatch(e -> e.getValue().equals(stat.userMetadata().getFirst(e.getKey())))); Tags tags = client.getObjectTags( @@ -4224,7 +4215,7 @@ public static void main(String[] args) throws Exception { if (kmsKeyName != null) { Map myContext = new HashMap<>(); myContext.put("key1", "value1"); - sseKms = new ServerSideEncryptionKms(kmsKeyName, myContext); + sseKms = new ServerSideEncryption.KMS(kmsKeyName, myContext); } int exitValue = 0;