From 4dd4a48898309688cdb14d3c3f1ae6e9f40800f5 Mon Sep 17 00:00:00 2001 From: "Bala.FA" Date: Thu, 23 Jan 2025 19:03:41 +0530 Subject: [PATCH] Refactor entire code for v9.0.0 * Move dependent classes as subclasses in messages * PutObject supports parallel uploads and checksum * Examples are updated * Dependencies are upgraded * Remove all deprecated methods * Add appendObject API Signed-off-by: Bala.FA --- .github/workflows/gradle.yml | 2 +- ...sp.java => AddServiceAccountResponse.java} | 2 +- ...nfo.java => AddUpdateRemoveGroupArgs.java} | 22 +- .../src/main/java/io/minio/admin/Crypto.java | 71 +- .../minio/admin/GetDataUsageInfoResponse.java | 255 + ...oupInfo.java => GetGroupInfoResponse.java} | 6 +- .../io/minio/admin/GetServerInfoResponse.java | 868 ++++ ...ava => GetServiceAccountInfoResponse.java} | 2 +- ...p.java => ListServiceAccountResponse.java} | 2 +- .../java/io/minio/admin/MinioAdminClient.java | 459 +- ...ts.java => PolicyAssociationResponse.java} | 36 +- .../main/java/io/minio/admin/UserInfo.java | 39 +- .../io/minio/admin/messages/AllTierStats.java | 39 - .../admin/messages/BucketTargetUsageInfo.java | 72 - .../minio/admin/messages/BucketUsageInfo.java | 109 - .../minio/admin/messages/DataUsageInfo.java | 97 - .../io/minio/admin/messages/info/Backend.java | 83 - .../io/minio/admin/messages/info/Buckets.java | 44 - .../io/minio/admin/messages/info/Disk.java | 206 - .../admin/messages/info/DiskMetrics.java | 88 - .../admin/messages/info/ErasureSetInfo.java | 80 - .../io/minio/admin/messages/info/GCStats.java | 67 - .../admin/messages/info/HealingDisk.java | 181 - .../minio/admin/messages/info/MemStats.java | 65 - .../io/minio/admin/messages/info/Message.java | 97 - .../io/minio/admin/messages/info/Objects.java | 44 - .../admin/messages/info/ServerProperties.java | 139 - .../admin/messages/info/TimedAction.java | 51 - .../io/minio/admin/messages/info/Usage.java | 44 - .../minio/admin/messages/info/Versions.java | 44 - .../admin/messages/DataUsageInfoTest.java | 54 - .../io/minio/AbortMultipartUploadArgs.java | 73 + .../minio/AbortMultipartUploadResponse.java | 2 +- .../main/java/io/minio/AppendObjectArgs.java | 140 + api/src/main/java/io/minio/BaseArgs.java | 103 +- api/src/main/java/io/minio/BaseS3Client.java | 1394 ++++++ api/src/main/java/io/minio/BucketArgs.java | 36 +- .../main/java/io/minio/BucketExistsArgs.java | 4 +- api/src/main/java/io/minio/ByteBuffer.java | 138 + .../main/java/io/minio/ByteBufferPool.java | 58 + api/src/main/java/io/minio/Checksum.java | 533 ++ .../io/minio/CompleteMultipartUploadArgs.java | 90 + .../main/java/io/minio/ComposeObjectArgs.java | 46 +- api/src/main/java/io/minio/ComposeSource.java | 134 - .../main/java/io/minio/CopyObjectArgs.java | 49 +- api/src/main/java/io/minio/CopySource.java | 46 - .../Version.java => CreateBucketArgs.java} | 23 +- .../java/io/minio/CreateBucketBaseArgs.java | 89 + .../io/minio/CreateMultipartUploadArgs.java | 78 + .../minio/CreateMultipartUploadResponse.java | 2 +- .../java/io/minio/DeleteBucketCorsArgs.java | 5 +- .../io/minio/DeleteBucketEncryptionArgs.java | 4 +- .../io/minio/DeleteBucketLifecycleArgs.java | 4 +- .../minio/DeleteBucketNotificationArgs.java | 4 +- .../java/io/minio/DeleteBucketPolicyArgs.java | 4 +- .../io/minio/DeleteBucketReplicationArgs.java | 4 +- .../java/io/minio/DeleteBucketTagsArgs.java | 5 +- .../DeleteObjectLockConfigurationArgs.java | 4 +- .../java/io/minio/DeleteObjectTagsArgs.java | 5 +- .../main/java/io/minio/DeleteObjectsArgs.java | 92 + .../java/io/minio/DeleteObjectsResponse.java | 2 +- api/src/main/java/io/minio/Digest.java | 157 - .../io/minio/DisableObjectLegalHoldArgs.java | 4 +- .../java/io/minio/DownloadObjectArgs.java | 7 +- .../io/minio/EnableObjectLegalHoldArgs.java | 4 +- .../main/java/io/minio/GenericResponse.java | 2 +- .../java/io/minio/GenericUploadResponse.java | 2 +- .../main/java/io/minio/GetBucketCorsArgs.java | 6 +- .../io/minio/GetBucketEncryptionArgs.java | 4 +- .../java/io/minio/GetBucketLifecycleArgs.java | 4 +- ...Marker.java => GetBucketLocationArgs.java} | 20 +- .../io/minio/GetBucketNotificationArgs.java | 4 +- .../java/io/minio/GetBucketPolicyArgs.java | 5 +- .../io/minio/GetBucketReplicationArgs.java | 4 +- .../main/java/io/minio/GetBucketTagsArgs.java | 6 +- .../io/minio/GetBucketVersioningArgs.java | 4 +- .../main/java/io/minio/GetObjectAclArgs.java | 4 +- api/src/main/java/io/minio/GetObjectArgs.java | 4 +- .../io/minio/GetObjectAttributesArgs.java | 6 +- .../io/minio/GetObjectAttributesResponse.java | 2 +- .../minio/GetObjectLockConfigurationArgs.java | 4 +- .../main/java/io/minio/GetObjectResponse.java | 6 +- .../java/io/minio/GetObjectRetentionArgs.java | 4 +- .../main/java/io/minio/GetObjectTagsArgs.java | 6 +- .../io/minio/GetPresignedObjectUrlArgs.java | 15 +- ...eEncryptionS3.java => HeadObjectArgs.java} | 37 +- .../java/io/minio/HeadObjectBaseArgs.java | 42 + .../java/io/minio/HeadObjectResponse.java | 157 + api/src/main/java/io/minio/Http.java | 1685 +++++++ .../main/java/io/minio/HttpRequestBody.java | 62 - .../minio/IsObjectLegalHoldEnabledArgs.java | 4 +- .../main/java/io/minio/ListBucketsArgs.java | 13 +- .../java/io/minio/ListBucketsResponse.java | 2 +- .../io/minio/ListMultipartUploadsArgs.java | 114 + .../minio/ListMultipartUploadsResponse.java | 2 +- .../java/io/minio/ListObjectVersionsArgs.java | 129 + .../io/minio/ListObjectVersionsResponse.java | 2 +- .../main/java/io/minio/ListObjectsArgs.java | 10 +- .../main/java/io/minio/ListObjectsV1Args.java | 116 + .../java/io/minio/ListObjectsV1Response.java | 2 +- .../main/java/io/minio/ListObjectsV2Args.java | 161 + .../java/io/minio/ListObjectsV2Response.java | 2 +- api/src/main/java/io/minio/ListPartsArgs.java | 84 + .../main/java/io/minio/ListPartsResponse.java | 2 +- .../minio/ListenBucketNotificationArgs.java | 8 +- .../main/java/io/minio/MakeBucketArgs.java | 37 +- .../main/java/io/minio/MinioAsyncClient.java | 3186 ++++++------ api/src/main/java/io/minio/MinioClient.java | 1458 ++---- .../main/java/io/minio/MinioProperties.java | 81 - api/src/main/java/io/minio/ObjectArgs.java | 18 +- .../io/minio/ObjectConditionalReadArgs.java | 84 +- .../main/java/io/minio/ObjectReadArgs.java | 29 +- .../main/java/io/minio/ObjectVersionArgs.java | 21 +- .../main/java/io/minio/ObjectWriteArgs.java | 94 +- .../java/io/minio/ObjectWriteResponse.java | 7 +- api/src/main/java/io/minio/PartReader.java | 237 +- api/src/main/java/io/minio/PartSource.java | 115 - api/src/main/java/io/minio/PostPolicy.java | 28 +- .../main/java/io/minio/PromptObjectArgs.java | 20 +- .../java/io/minio/PromptObjectResponse.java | 6 +- .../main/java/io/minio/PutObjectAPIArgs.java | 119 + .../java/io/minio/PutObjectAPIBaseArgs.java | 177 + api/src/main/java/io/minio/PutObjectArgs.java | 90 +- .../main/java/io/minio/PutObjectBaseArgs.java | 110 +- .../java/io/minio/PutObjectFanOutArgs.java | 16 +- .../java/io/minio/PutObjectFanOutEntry.java | 4 +- .../io/minio/PutObjectFanOutResponse.java | 14 +- .../main/java/io/minio/RemoveBucketArgs.java | 4 +- .../main/java/io/minio/RemoveObjectArgs.java | 4 +- .../main/java/io/minio/RemoveObjectsArgs.java | 18 +- .../main/java/io/minio/RestoreObjectArgs.java | 6 +- api/src/main/java/io/minio/Result.java | 81 +- api/src/main/java/io/minio/S3Base.java | 3909 --------------- api/src/main/java/io/minio/S3Escaper.java | 110 - .../io/minio/SelectObjectContentArgs.java | 10 +- .../java/io/minio/ServerSideEncryption.java | 136 +- .../ServerSideEncryptionCustomerKey.java | 93 - .../io/minio/ServerSideEncryptionKms.java | 60 - .../main/java/io/minio/SetBucketCorsArgs.java | 8 +- .../io/minio/SetBucketEncryptionArgs.java | 6 +- .../java/io/minio/SetBucketLifecycleArgs.java | 6 +- .../io/minio/SetBucketNotificationArgs.java | 6 +- .../java/io/minio/SetBucketPolicyArgs.java | 7 +- .../io/minio/SetBucketReplicationArgs.java | 8 +- .../main/java/io/minio/SetBucketTagsArgs.java | 10 +- .../io/minio/SetBucketVersioningArgs.java | 6 +- .../minio/SetObjectLockConfigurationArgs.java | 6 +- .../java/io/minio/SetObjectRetentionArgs.java | 6 +- .../main/java/io/minio/SetObjectTagsArgs.java | 10 +- api/src/main/java/io/minio/Signer.java | 93 +- api/src/main/java/io/minio/SourceObject.java | 93 + .../main/java/io/minio/StatObjectArgs.java | 21 +- .../java/io/minio/StatObjectResponse.java | 114 +- api/src/main/java/io/minio/Time.java | 62 +- .../main/java/io/minio/UploadObjectArgs.java | 65 +- .../main/java/io/minio/UploadPartArgs.java | 105 + .../java/io/minio/UploadPartCopyArgs.java | 99 + .../java/io/minio/UploadPartCopyResponse.java | 7 +- .../java/io/minio/UploadPartResponse.java | 25 +- .../io/minio/UploadSnowballObjectsArgs.java | 6 +- api/src/main/java/io/minio/Utils.java | 712 ++- .../minio/credentials/AssumeRoleProvider.java | 38 +- .../minio/credentials/AwsConfigProvider.java | 18 +- .../credentials/AwsEnvironmentProvider.java | 31 +- ...rovider.java => BaseIdentityProvider.java} | 36 +- .../CertificateIdentityProvider.java | 19 +- .../io/minio/credentials/ChainedProvider.java | 4 +- .../credentials/ClientGrantsProvider.java | 12 +- .../io/minio/credentials/Credentials.java | 19 +- .../credentials/EnvironmentProvider.java | 5 +- .../io/minio/credentials/IamAwsProvider.java | 16 +- .../main/java/io/minio/credentials/Jwt.java | 2 +- .../credentials/LdapIdentityProvider.java | 14 +- .../MinioClientConfigProvider.java | 39 +- .../java/io/minio/credentials/Provider.java | 2 +- .../io/minio/credentials/StaticProvider.java | 2 +- .../WebIdentityClientGrantsProvider.java | 19 +- .../credentials/WebIdentityProvider.java | 12 +- .../minio/errors/ErrorResponseException.java | 41 +- .../io/minio/errors/InternalException.java | 8 +- .../java/io/minio/errors/MinioException.java | 66 +- .../io/minio/errors/XmlParserException.java | 2 +- .../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 | 192 +- .../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} | 9 +- .../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 | 76 +- .../messages/CloudFunctionConfiguration.java | 41 - .../messages/CompleteMultipartUpload.java | 10 +- .../CompleteMultipartUploadOutput.java | 29 - .../CompleteMultipartUploadResult.java | 54 +- .../io/minio/messages/CompressionType.java | 24 - .../main/java/io/minio/messages/Contents.java | 10 +- .../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 - .../main/java/io/minio/messages/DateDays.java | 37 - .../java/io/minio/messages/DeleteError.java | 27 - .../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 | 85 +- .../java/io/minio/messages/DeletedObject.java | 54 - .../java/io/minio/messages/Encryption.java | 47 - .../messages/EncryptionConfiguration.java | 39 - .../java/io/minio/messages/ErrorResponse.java | 58 +- .../main/java/io/minio/messages/Event.java | 164 - .../java/io/minio/messages/EventMetadata.java | 106 - .../java/io/minio/messages/EventType.java | 11 +- .../messages/ExistingObjectReplication.java | 37 - .../java/io/minio/messages/Expiration.java | 76 - .../io/minio/messages/FileHeaderInfo.java | 24 - .../main/java/io/minio/messages/Filter.java | 191 +- .../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 | 12 +- .../io/minio/messages/InputSerialization.java | 115 +- api/src/main/java/io/minio/messages/Item.java | 109 +- .../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 | 346 +- .../java/io/minio/messages/LifecycleRule.java | 123 - .../messages/ListAllMyBucketsResult.java | 52 +- .../io/minio/messages/ListBucketResultV1.java | 12 +- .../io/minio/messages/ListBucketResultV2.java | 15 +- .../messages/ListMultipartUploadsResult.java | 123 +- .../io/minio/messages/ListObjectsResult.java | 40 +- .../io/minio/messages/ListPartsResult.java | 68 +- .../io/minio/messages/ListVersionsResult.java | 50 +- .../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 | 290 +- .../minio/messages/NotificationRecords.java | 245 +- .../messages/ObjectLockConfiguration.java | 126 +- .../io/minio/messages/ObjectMetadata.java | 56 - .../io/minio/messages/OutputLocation.java | 35 - .../minio/messages/OutputSerialization.java | 76 +- .../main/java/io/minio/messages/Owner.java | 13 +- .../messages/ParquetInputSerialization.java | 26 - api/src/main/java/io/minio/messages/Part.java | 73 +- .../java/io/minio/messages/Permission.java | 29 - .../main/java/io/minio/messages/Prefix.java | 36 - .../main/java/io/minio/messages/Progress.java | 17 +- .../io/minio/messages/QueueConfiguration.java | 41 - .../java/io/minio/messages/QuoteFields.java | 23 - .../minio/messages/ReplicaModifications.java | 37 - .../messages/ReplicationConfiguration.java | 497 +- .../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 | 235 +- .../java/io/minio/messages/Retention.java | 24 +- .../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 | 31 +- .../io/minio/messages/SelectParameters.java | 32 - .../main/java/io/minio/messages/Source.java | 46 - .../messages/SourceSelectionCriteria.java | 52 - .../java/io/minio/messages/SseAlgorithm.java | 14 +- .../io/minio/messages/SseConfiguration.java | 61 +- .../minio/messages/SseConfigurationRule.java | 55 - .../messages/SseKmsEncryptedObjects.java | 39 - .../main/java/io/minio/messages/Stats.java | 46 +- .../main/java/io/minio/messages/Status.java | 4 +- ...fixConverter.java => StringConverter.java} | 10 +- api/src/main/java/io/minio/messages/Tag.java | 25 +- api/src/main/java/io/minio/messages/Tags.java | 50 +- 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 - .../messages/VersioningConfiguration.java | 99 +- .../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 | 17 +- .../java/io/minio/TimeLocaleTest.java} | 24 +- build.gradle | 79 +- docs/API.md | 33 +- docs/zh_CN/API.md | 1446 ------ docs/zh_CN/CONTRIBUTING.md | 9 - examples/BucketExists.java | 48 +- examples/ComposeObject.java | 124 +- examples/CopyObject.java | 291 +- examples/DeleteBucketCors.java | 38 +- examples/DeleteBucketEncryption.java | 40 +- examples/DeleteBucketLifecycle.java | 40 +- examples/DeleteBucketNotification.java | 40 +- examples/DeleteBucketPolicy.java | 39 +- examples/DeleteBucketReplication.java | 38 +- examples/DeleteBucketTags.java | 38 +- examples/DeleteObjectLockConfiguration.java | 40 +- examples/DeleteObjectTags.java | 40 +- examples/DisableObjectLegalHold.java | 46 +- examples/DownloadObject.java | 81 +- examples/EnableObjectLegalHold.java | 50 +- examples/GetBucketCors.java | 40 +- examples/GetBucketEncryption.java | 52 +- examples/GetBucketLifecycle.java | 42 +- examples/GetBucketNotification.java | 42 +- examples/GetBucketPolicy.java | 41 +- examples/GetBucketReplication.java | 42 +- examples/GetBucketTags.java | 39 +- examples/GetBucketVersioning.java | 42 +- examples/GetObject.java | 57 +- examples/GetObjectAcl.java | 42 +- examples/GetObjectAttributes.java | 54 +- examples/GetObjectLockConfiguration.java | 48 +- examples/GetObjectProgressBar.java | 89 +- examples/GetObjectResume.java | 14 +- examples/GetObjectRetention.java | 49 +- examples/GetObjectTags.java | 42 +- examples/GetPartialObject.java | 69 +- examples/GetPresignedObjectUrl.java | 58 +- examples/GetPresignedPostFormData.java | 109 +- examples/IsObjectLegalHoldEnabled.java | 98 +- examples/ListBuckets.java | 53 +- examples/ListObjects.java | 144 +- examples/ListenBucketNotification.java | 63 +- examples/MakeBucket.java | 79 +- .../MinioClientWithAssumeRoleProvider.java | 5 +- .../MinioClientWithAwsConfigProvider.java | 5 +- ...ClientWithCertificateIdentityProvider.java | 11 +- examples/MinioClientWithChainedProvider.java | 5 +- .../MinioClientWithClientGrantsProvider.java | 5 +- examples/MinioClientWithIamAwsProvider.java | 5 +- .../MinioClientWithLdapIdentityProvider.java | 5 +- ...ioClientWithMinioClientConfigProvider.java | 5 +- .../MinioClientWithWebIdentityProvider.java | 5 +- examples/PresignedGetObject.java | 58 +- examples/PresignedPutObject.java | 66 +- examples/PutObject.java | 309 +- examples/PutObjectFanOut.java | 64 +- examples/PutObjectProgressBar.java | 21 +- examples/PutObjectUiProgressBar.java | 21 +- examples/RemoveBucket.java | 52 +- examples/RemoveObject.java | 70 +- examples/RemoveObjects.java | 61 +- examples/RestoreObject.java | 64 +- examples/SelectObjectContent.java | 111 +- examples/SetBucketCors.java | 84 +- examples/SetBucketEncryption.java | 46 +- examples/SetBucketLifecycle.java | 68 +- examples/SetBucketNotification.java | 80 +- examples/SetBucketPolicy.java | 80 +- examples/SetBucketReplication.java | 85 +- examples/SetBucketTags.java | 43 +- examples/SetBucketVersioning.java | 66 +- examples/SetObjectLockConfiguration.java | 54 +- examples/SetObjectRetention.java | 59 +- examples/SetObjectTags.java | 48 +- examples/StatObject.java | 156 +- examples/UploadObject.java | 82 +- examples/UploadSnowballObjects.java | 68 +- functional/FunctionalTest.java | 4275 +---------------- functional/MintLogger.java | 6 +- functional/TestArgs.java | 421 ++ functional/TestMinioAdminClient.java | 109 +- functional/TestMinioClient.java | 3620 ++++++++++++++ 394 files changed, 20800 insertions(+), 24067 deletions(-) rename adminapi/src/main/java/io/minio/admin/{AddServiceAccountResp.java => AddServiceAccountResponse.java} (96%) rename adminapi/src/main/java/io/minio/admin/{GroupAddUpdateRemoveInfo.java => AddUpdateRemoveGroupArgs.java} (81%) create mode 100644 adminapi/src/main/java/io/minio/admin/GetDataUsageInfoResponse.java rename adminapi/src/main/java/io/minio/admin/{GroupInfo.java => GetGroupInfoResponse.java} (89%) create mode 100644 adminapi/src/main/java/io/minio/admin/GetServerInfoResponse.java rename adminapi/src/main/java/io/minio/admin/{GetServiceAccountInfoResp.java => GetServiceAccountInfoResponse.java} (97%) rename adminapi/src/main/java/io/minio/admin/{ListServiceAccountResp.java => ListServiceAccountResponse.java} (98%) rename adminapi/src/main/java/io/minio/admin/{messages/TierStats.java => PolicyAssociationResponse.java} (59%) delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/AllTierStats.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/BucketTargetUsageInfo.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/BucketUsageInfo.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/DataUsageInfo.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/Backend.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/Buckets.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/Disk.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/DiskMetrics.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/ErasureSetInfo.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/GCStats.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/HealingDisk.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/MemStats.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/Message.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/Objects.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/ServerProperties.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/TimedAction.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/Usage.java delete mode 100644 adminapi/src/main/java/io/minio/admin/messages/info/Versions.java delete mode 100644 adminapi/src/test/java/io/minio/admin/messages/DataUsageInfoTest.java create mode 100644 api/src/main/java/io/minio/AbortMultipartUploadArgs.java create mode 100644 api/src/main/java/io/minio/AppendObjectArgs.java 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/Version.java => CreateBucketArgs.java} (57%) 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/DeleteMarker.java => GetBucketLocationArgs.java} (63%) rename api/src/main/java/io/minio/{ServerSideEncryptionS3.java => HeadObjectArgs.java} (51%) create mode 100644 api/src/main/java/io/minio/HeadObjectBaseArgs.java 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 delete mode 100644 api/src/main/java/io/minio/PartSource.java create mode 100644 api/src/main/java/io/minio/PutObjectAPIArgs.java create mode 100644 api/src/main/java/io/minio/PutObjectAPIBaseArgs.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 rename api/src/main/java/io/minio/credentials/{AssumeRoleBaseProvider.java => BaseIdentityProvider.java} (82%) 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} (88%) 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/DateDays.java delete mode 100644 api/src/main/java/io/minio/messages/DeleteError.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/ParquetInputSerialization.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/QueueConfiguration.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 rename api/src/main/java/io/minio/messages/{PrefixConverter.java => StringConverter.java} (77%) 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/org/apache/commons/validator/routines/InetAddressValidator.java delete mode 100644 api/src/main/java/io/minio/org/apache/commons/validator/routines/RegexValidator.java rename api/src/{main/java/io/minio/ByteBufferStream.java => test/java/io/minio/TimeLocaleTest.java} (52%) delete mode 100644 docs/zh_CN/API.md delete mode 100644 docs/zh_CN/CONTRIBUTING.md mode change 100755 => 100644 functional/MintLogger.java create mode 100644 functional/TestArgs.java create mode 100644 functional/TestMinioClient.java diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index e42ddc2e9..b1be4aa21 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -35,7 +35,7 @@ jobs: run: | echo "DEV_VERSION=$(./gradlew properties | awk '/^version:/ { print $2 }')" >> $GITHUB_ENV echo "RELEASE_VERSION=$(./gradlew properties -Prelease | awk '/^version:/ { print $2 }')" >> $GITHUB_ENV - - name: Gradle build on Ununtu + - name: Gradle build on Ubuntu if: matrix.os == 'ubuntu-latest' run: | ./gradlew build diff --git a/adminapi/src/main/java/io/minio/admin/AddServiceAccountResp.java b/adminapi/src/main/java/io/minio/admin/AddServiceAccountResponse.java similarity index 96% rename from adminapi/src/main/java/io/minio/admin/AddServiceAccountResp.java rename to adminapi/src/main/java/io/minio/admin/AddServiceAccountResponse.java index c48c6f606..4967709fd 100644 --- a/adminapi/src/main/java/io/minio/admin/AddServiceAccountResp.java +++ b/adminapi/src/main/java/io/minio/admin/AddServiceAccountResponse.java @@ -28,7 +28,7 @@ * "https://github.com/minio/madmin-go/blob/main/user-commands.go#L388">user-commands.go */ @JsonIgnoreProperties(ignoreUnknown = true) -public class AddServiceAccountResp { +public class AddServiceAccountResponse { @JsonProperty("credentials") private Credentials credentials; diff --git a/adminapi/src/main/java/io/minio/admin/GroupAddUpdateRemoveInfo.java b/adminapi/src/main/java/io/minio/admin/AddUpdateRemoveGroupArgs.java similarity index 81% rename from adminapi/src/main/java/io/minio/admin/GroupAddUpdateRemoveInfo.java rename to adminapi/src/main/java/io/minio/admin/AddUpdateRemoveGroupArgs.java index 039c5acda..709d502a3 100644 --- a/adminapi/src/main/java/io/minio/admin/GroupAddUpdateRemoveInfo.java +++ b/adminapi/src/main/java/io/minio/admin/AddUpdateRemoveGroupArgs.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; @@ -28,7 +27,8 @@ /** Represents groupAddUpdateRemove information. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class GroupAddUpdateRemoveInfo { +@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD") +public class AddUpdateRemoveGroupArgs { @JsonProperty("group") private String group; @@ -41,7 +41,7 @@ public class GroupAddUpdateRemoveInfo { @JsonProperty("isRemove") private boolean isRemove; - public GroupAddUpdateRemoveInfo( + public AddUpdateRemoveGroupArgs( @Nonnull @JsonProperty("group") String group, @Nullable @JsonProperty("groupStatus") Status groupStatus, @Nullable @JsonProperty("members") List members, @@ -51,20 +51,4 @@ public GroupAddUpdateRemoveInfo( this.members = (members != null) ? Collections.unmodifiableList(members) : null; this.isRemove = isRemove; } - - public String group() { - return group; - } - - public Status groupStatus() { - return groupStatus; - } - - public List members() { - return Collections.unmodifiableList(members == null ? new LinkedList<>() : members); - } - - public boolean isRemove() { - return isRemove; - } } 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/GetDataUsageInfoResponse.java b/adminapi/src/main/java/io/minio/admin/GetDataUsageInfoResponse.java new file mode 100644 index 000000000..0bf3a1b5f --- /dev/null +++ b/adminapi/src/main/java/io/minio/admin/GetDataUsageInfoResponse.java @@ -0,0 +1,255 @@ +/* + * 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.admin; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; +import java.util.Collections; +import java.util.Map; + +/** + * Represents data usage stats of the current object API. + * + * @see data-usage-utils.go + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetDataUsageInfoResponse { + @JsonProperty("lastUpdate") + private ZonedDateTime lastUpdate; + + @JsonProperty("objectsCount") + private long objectsCount; + + @JsonProperty("versionsCount") + private long versionsCount; + + @JsonProperty("objectsTotalSize") + private long objectsTotalSize; + + @JsonProperty("objectsReplicationInfo") + private Map objectsReplicationInfo; + + @JsonProperty("bucketsCount") + private long bucketsCount; + + @JsonProperty("bucketsUsageInfo") + private Map bucketsUsageInfo; + + @JsonProperty("bucketsSizes") + private Map bucketsSizes; + + @JsonProperty("tierStats") + private AllTierStats tierStats; + + public ZonedDateTime lastUpdate() { + return lastUpdate; + } + + public long objectsCount() { + return objectsCount; + } + + public long versionsCount() { + return versionsCount; + } + + public long objectsTotalSize() { + return objectsTotalSize; + } + + public Map objectsReplicationInfo() { + return Collections.unmodifiableMap(this.objectsReplicationInfo); + } + + public long bucketsCount() { + return bucketsCount; + } + + public Map bucketsUsageInfo() { + return Collections.unmodifiableMap(this.bucketsUsageInfo); + } + + public Map bucketsSizes() { + return Collections.unmodifiableMap(bucketsSizes); + } + + public AllTierStats tierStats() { + return tierStats; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class BucketTargetUsageInfo { + @JsonProperty("objectsPendingReplicationTotalSize") + private long objectsPendingReplicationTotalSize; + + @JsonProperty("objectsFailedReplicationTotalSize") + private long objectsFailedReplicationTotalSize; + + @JsonProperty("objectsReplicatedTotalSize") + private long objectsReplicatedTotalSize; + + @JsonProperty("objectReplicaTotalSize") + private long objectReplicaTotalSize; + + @JsonProperty("objectsPendingReplicationCount") + private long objectsPendingReplicationCount; + + @JsonProperty("objectsFailedReplicationCount") + private long objectsFailedReplicationCount; + + public long objectsPendingReplicationTotalSize() { + return objectsPendingReplicationTotalSize; + } + + public long objectsFailedReplicationTotalSize() { + return objectsFailedReplicationTotalSize; + } + + public long objectsReplicatedTotalSize() { + return objectsReplicatedTotalSize; + } + + public long objectReplicaTotalSize() { + return objectReplicaTotalSize; + } + + public long objectsPendingReplicationCount() { + return objectsPendingReplicationCount; + } + + public long objectsFailedReplicationCount() { + return objectsFailedReplicationCount; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class BucketUsageInfo { + @JsonProperty("size") + private long size; + + @JsonProperty("objectsPendingReplicationTotalSize") + private long objectsPendingReplicationTotalSize; + + @JsonProperty("objectsFailedReplicationTotalSize") + private long objectsFailedReplicationTotalSize; + + @JsonProperty("objectsReplicatedTotalSize") + private long objectsReplicatedTotalSize; + + @JsonProperty("objectsPendingReplicationCount") + private long objectsPendingReplicationCount; + + @JsonProperty("objectsFailedReplicationCount") + private long objectsFailedReplicationCount; + + @JsonProperty("objectsCount") + private long objectsCount; + + @JsonProperty("objectsSizesHistogram") + private Map objectsSizesHistogram; + + @JsonProperty("versionsCount") + private long versionsCount; + + @JsonProperty("objectReplicaTotalSize") + private long objectReplicaTotalSize; + + @JsonProperty("objectsReplicationInfo") + private Map objectsReplicationInfo; + + public long size() { + return size; + } + + public long objectsPendingReplicationTotalSize() { + return objectsPendingReplicationTotalSize; + } + + public long objectsFailedReplicationTotalSize() { + return objectsFailedReplicationTotalSize; + } + + public long objectsReplicatedTotalSize() { + return objectsReplicatedTotalSize; + } + + public long objectsPendingReplicationCount() { + return objectsPendingReplicationCount; + } + + public long objectsFailedReplicationCount() { + return objectsFailedReplicationCount; + } + + public long objectsCount() { + return objectsCount; + } + + public Map objectsSizesHistogram() { + return Collections.unmodifiableMap(this.objectsSizesHistogram); + } + + public long versionsCount() { + return versionsCount; + } + + public long objectReplicaTotalSize() { + return objectReplicaTotalSize; + } + + public Map objectsReplicationInfo() { + return Collections.unmodifiableMap(this.objectsReplicationInfo); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class TierStats { + @JsonProperty("TotalSize") + private long totalSize; + + @JsonProperty("NumVersions") + private int numVersions; + + @JsonProperty("NumObjects") + private int numObjects; + + public long totalSize() { + return totalSize; + } + + public int numVersions() { + return numVersions; + } + + public int numObjects() { + return numObjects; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class AllTierStats { + @JsonProperty("Tiers") + private Map tiers; + + public Map tiers() { + return Collections.unmodifiableMap(this.tiers); + } + } +} diff --git a/adminapi/src/main/java/io/minio/admin/GroupInfo.java b/adminapi/src/main/java/io/minio/admin/GetGroupInfoResponse.java similarity index 89% rename from adminapi/src/main/java/io/minio/admin/GroupInfo.java rename to adminapi/src/main/java/io/minio/admin/GetGroupInfoResponse.java index 3d53def2d..3c4524203 100644 --- a/adminapi/src/main/java/io/minio/admin/GroupInfo.java +++ b/adminapi/src/main/java/io/minio/admin/GetGroupInfoResponse.java @@ -19,13 +19,13 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; /** Represents group information. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class GroupInfo { +public class GetGroupInfoResponse { @JsonProperty("name") private String name; @@ -47,7 +47,7 @@ public Status status() { } public List members() { - return Collections.unmodifiableList(members == null ? new LinkedList<>() : members); + return Collections.unmodifiableList(members == null ? new ArrayList<>() : members); } public String policy() { diff --git a/adminapi/src/main/java/io/minio/admin/GetServerInfoResponse.java b/adminapi/src/main/java/io/minio/admin/GetServerInfoResponse.java new file mode 100644 index 000000000..e7ddef460 --- /dev/null +++ b/adminapi/src/main/java/io/minio/admin/GetServerInfoResponse.java @@ -0,0 +1,868 @@ +/* + * 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.admin; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * InfoMessage container to hold server admin related information. + * + * @see heal-commands.go + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetServerInfoResponse { + @JsonProperty("mode") + private String mode; + + @JsonProperty("deploymentID") + private String deploymentID; + + @JsonProperty("buckets") + private Buckets buckets; + + @JsonProperty("objects") + private Objects objects; + + @JsonProperty("versions") + private Versions versions; + + @JsonProperty("usage") + private Usage usage; + + @JsonProperty("backend") + private Backend backend; + + @JsonProperty("servers") + private List servers; + + @JsonProperty("pools") + private Map> pools; + + public String mode() { + return mode; + } + + public String deploymentID() { + return deploymentID; + } + + public Buckets buckets() { + return buckets; + } + + public Objects objects() { + return objects; + } + + public Versions versions() { + return versions; + } + + public Usage usage() { + return usage; + } + + public Backend backend() { + return backend; + } + + public List servers() { + return Collections.unmodifiableList(servers == null ? new ArrayList<>() : servers); + } + + public Map> pools() { + return pools; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Buckets { + @JsonProperty("count") + private Integer count; + + @JsonProperty("error") + private String error; + + public Integer count() { + return count; + } + + public String error() { + return error; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Objects { + @JsonProperty("count") + private Integer count; + + @JsonProperty("error") + private String error; + + public Integer count() { + return count; + } + + public String error() { + return error; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Versions { + @JsonProperty("count") + private Integer count; + + @JsonProperty("error") + private String error; + + public Integer count() { + return count; + } + + public String error() { + return error; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Usage { + @JsonProperty("size") + private Long size; + + @JsonProperty("error") + private String error; + + public Long size() { + return size; + } + + public String error() { + return error; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Backend { + @JsonProperty("backendType") + private String backendType; + + @JsonProperty("onlineDisks") + private Integer onlineDisks; + + @JsonProperty("offlineDisks") + private Integer offlineDisks; + + @JsonProperty("standardSCParity") + private Integer standardSCParity; + + @JsonProperty("rrSCParity") + private Integer rrSCParity; + + @JsonProperty("totalSets") + private List totalSets; + + @JsonProperty("totalDrivesPerSet") + private List totalDrivesPerSet; + + public String backendType() { + return backendType; + } + + public Integer onlineDisks() { + return onlineDisks; + } + + public Integer offlineDisks() { + return offlineDisks; + } + + public Integer standardSCParity() { + return standardSCParity; + } + + public Integer rrSCParity() { + return rrSCParity; + } + + public List totalSets() { + return Collections.unmodifiableList(totalSets == null ? new ArrayList<>() : totalSets); + } + + public List totalDrivesPerSet() { + return Collections.unmodifiableList( + totalDrivesPerSet == null ? new ArrayList<>() : totalDrivesPerSet); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ServerProperties { + @JsonProperty("state") + private String state; + + @JsonProperty("endpoint") + private String endpoint; + + @JsonProperty("scheme") + private String scheme; + + @JsonProperty("uptime") + private Integer uptime; + + @JsonProperty("version") + private String version; + + @JsonProperty("commitID") + private String commitID; + + @JsonProperty("network") + private Map network; + + @JsonProperty("drives") + private List disks; + + @JsonProperty("poolNumber") + private Integer poolNumber; + + @JsonProperty("mem_stats") + private MemStats memStats; + + @JsonProperty("go_max_procs") + private Integer goMaxProcs; + + @JsonProperty("num_cpu") + private Integer numCPU; + + @JsonProperty("runtime_version") + private String runtimeVersion; + + @JsonProperty("gc_stats") + private GCStats gCStats; + + @JsonProperty("minio_env_vars") + private Map minioEnvVars; + + public String state() { + return state; + } + + public String endpoint() { + return endpoint; + } + + public String scheme() { + return scheme; + } + + public Integer uptime() { + return uptime; + } + + public String version() { + return version; + } + + public String commitID() { + return commitID; + } + + public Map network() { + return Collections.unmodifiableMap(this.network); + } + + public List disks() { + return Collections.unmodifiableList(disks == null ? new ArrayList<>() : disks); + } + + public Integer poolNumber() { + return poolNumber; + } + + public MemStats memStats() { + return memStats; + } + + public Integer goMaxProcs() { + return goMaxProcs; + } + + public Integer numCPU() { + return numCPU; + } + + public String runtimeVersion() { + return runtimeVersion; + } + + public GCStats gCStats() { + return gCStats; + } + + public Map minioEnvVars() { + return Collections.unmodifiableMap(this.minioEnvVars); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Disk { + @JsonProperty("endpoint") + private String endpoint; + + @JsonProperty("rootDisk") + private boolean rootDisk; + + @JsonProperty("path") + private String path; + + @JsonProperty("healing") + private boolean healing; + + @JsonProperty("scanning") + private boolean scanning; + + @JsonProperty("state") + private String state; + + @JsonProperty("uuid") + private String uuid; + + @JsonProperty("major") + private BigDecimal major; + + @JsonProperty("minor") + private BigDecimal minor; + + @JsonProperty("model") + private String model; + + @JsonProperty("totalspace") + private BigDecimal totalSpace; + + @JsonProperty("usedspace") + private BigDecimal usedSpace; + + @JsonProperty("availspace") + private BigDecimal availableSpace; + + @JsonProperty("readthroughput") + private BigDecimal readThroughput; + + @JsonProperty("writethroughput") + private BigDecimal writeThroughput; + + @JsonProperty("readlatency") + private BigDecimal readLatency; + + @JsonProperty("writelatency") + private BigDecimal writeLatency; + + @JsonProperty("utilization") + private BigDecimal utilization; + + @JsonProperty("metrics") + private DiskMetrics metrics; + + @JsonProperty("heal_info") + private HealingDisk healInfo; + + @JsonProperty("used_inodes") + private BigDecimal usedInodes; + + @JsonProperty("free_inodes") + private BigDecimal freeInodes; + + @JsonProperty("pool_index") + private Integer poolIndex; + + @JsonProperty("set_index") + private Integer setIndex; + + @JsonProperty("disk_index") + private Integer diskIndex; + + public String endpoint() { + return endpoint; + } + + public boolean isRootDisk() { + return rootDisk; + } + + public String path() { + return path; + } + + public boolean isHealing() { + return healing; + } + + public boolean isScanning() { + return scanning; + } + + public String state() { + return state; + } + + public String uuid() { + return uuid; + } + + public BigDecimal major() { + return major; + } + + public BigDecimal minor() { + return minor; + } + + public String model() { + return model; + } + + public BigDecimal totalSpace() { + return totalSpace; + } + + public BigDecimal usedSpace() { + return usedSpace; + } + + public BigDecimal availableSpace() { + return availableSpace; + } + + public BigDecimal readThroughput() { + return readThroughput; + } + + public BigDecimal writeThroughput() { + return writeThroughput; + } + + public BigDecimal readLatency() { + return readLatency; + } + + public BigDecimal writeLatency() { + return writeLatency; + } + + public BigDecimal utilization() { + return utilization; + } + + public DiskMetrics metrics() { + return metrics; + } + + public HealingDisk healInfo() { + return healInfo; + } + + public BigDecimal usedInodes() { + return usedInodes; + } + + public BigDecimal freeInodes() { + return freeInodes; + } + + public Integer poolIndex() { + return poolIndex; + } + + public Integer setIndex() { + return setIndex; + } + + public Integer diskIndex() { + return diskIndex; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class MemStats { + @JsonProperty("Alloc") + private BigDecimal alloc; + + @JsonProperty("TotalAlloc") + private BigDecimal totalAlloc; + + @JsonProperty("Mallocs") + private BigDecimal mallocs; + + @JsonProperty("Frees") + private BigDecimal frees; + + @JsonProperty("HeapAlloc") + private BigDecimal heapAlloc; + + public BigDecimal alloc() { + return alloc; + } + + public BigDecimal totalAlloc() { + return totalAlloc; + } + + public BigDecimal mallocs() { + return mallocs; + } + + public BigDecimal frees() { + return frees; + } + + public BigDecimal heapAlloc() { + return heapAlloc; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class GCStats { + @JsonProperty("last_gc") + private String lastGC; + + @JsonProperty("num_gc") + private Integer numGC; + + @JsonProperty("pause_total") + private Long pauseTotal; + + @JsonProperty("pause") + private List pause; + + @JsonProperty("pause_end") + private List pauseEnd; + + public String lastGC() { + return lastGC; + } + + public Integer numGC() { + return numGC; + } + + public Long pauseTotal() { + return pauseTotal; + } + + public List pause() { + return Collections.unmodifiableList(pause == null ? new ArrayList<>() : pause); + } + + public List pauseEnd() { + return Collections.unmodifiableList(pauseEnd == null ? new ArrayList<>() : pauseEnd); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class DiskMetrics { + @JsonProperty("lastMinute") + private Map lastMinute; + + @JsonProperty("apiCalls") + private Map apiCalls; + + @JsonProperty("totalErrorsAvailability") + private Integer totalErrorsAvailability; + + @JsonProperty("totalErrorsTimeout") + private Integer totalErrorsTimeout; + + @JsonProperty("totalTokens") + private Long totalTokens; + + @JsonProperty("totalWaiting") + private Long totalWaiting; + + @JsonProperty("totalWrites") + private Long totalWrites; + + @JsonProperty("totalDeletes") + private Long totalDeletes; + + public Integer totalErrorsAvailability() { + return totalErrorsAvailability; + } + + public Integer totalErrorsTimeout() { + return totalErrorsTimeout; + } + + public Map lastMinute() { + return Collections.unmodifiableMap(lastMinute); + } + + public Map apiCalls() { + return Collections.unmodifiableMap(apiCalls); + } + + public Long totalTokens() { + return totalTokens; + } + + public Long totalWaiting() { + return totalWaiting; + } + + public Long totalWrites() { + return totalWrites; + } + + public Long totalDeletes() { + return totalDeletes; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class HealingDisk { + @JsonProperty("id") + private String id; + + @JsonProperty("heal_id") + private String healID; + + @JsonProperty("pool_index") + private Integer poolIndex; + + @JsonProperty("set_index") + private Integer setIndex; + + @JsonProperty("disk_index") + private Integer diskIndex; + + @JsonProperty("endpoint") + private String endpoint; + + @JsonProperty("path") + private String path; + + @JsonProperty("started") + private String started; + + @JsonProperty("last_update") + private String lastUpdate; + + @JsonProperty("objects_total_count") + private BigDecimal objectsTotalCount; + + @JsonProperty("objects_total_size") + private BigDecimal objectsTotalSize; + + @JsonProperty("items_healed") + private BigDecimal itemsHealed; + + @JsonProperty("items_failed") + private BigDecimal itemsFailed; + + @JsonProperty("bytes_done") + private BigDecimal bytesDone; + + @JsonProperty("bytes_failed") + private BigDecimal bytesFailed; + + @JsonProperty("objects_healed") + private BigDecimal objectsHealed; + + @JsonProperty("objects_failed") + private BigDecimal objectsFailed; + + @JsonProperty("current_bucket") + private String bucket; + + @JsonProperty("current_object") + private String object; + + @JsonProperty("queued_buckets") + private List queuedBuckets; + + @JsonProperty("healed_buckets") + private List healedBuckets; + + public String id() { + return id; + } + + public String healID() { + return healID; + } + + public Integer poolIndex() { + return poolIndex; + } + + public Integer setIndex() { + return setIndex; + } + + public Integer diskIndex() { + return diskIndex; + } + + public String endpoint() { + return endpoint; + } + + public String path() { + return path; + } + + public String started() { + return started; + } + + public String lastUpdate() { + return lastUpdate; + } + + public BigDecimal objectsTotalCount() { + return objectsTotalCount; + } + + public BigDecimal objectsTotalSize() { + return objectsTotalSize; + } + + public BigDecimal itemsHealed() { + return itemsHealed; + } + + public BigDecimal itemsFailed() { + return itemsFailed; + } + + public BigDecimal bytesDone() { + return bytesDone; + } + + public BigDecimal bytesFailed() { + return bytesFailed; + } + + public BigDecimal objectsHealed() { + return objectsHealed; + } + + public BigDecimal objectsFailed() { + return objectsFailed; + } + + public String bucket() { + return bucket; + } + + public String object() { + return object; + } + + public List queuedBuckets() { + return Collections.unmodifiableList( + queuedBuckets == null ? new ArrayList<>() : queuedBuckets); + } + + public List healedBuckets() { + return Collections.unmodifiableList( + healedBuckets == null ? new ArrayList<>() : healedBuckets); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class TimedAction { + @JsonProperty("count") + private BigDecimal count; + + @JsonProperty("acc_time_ns") + private BigDecimal accTime; + + @JsonProperty("bytes") + private BigDecimal bytes; + + public BigDecimal count() { + return count; + } + + public BigDecimal accTime() { + return accTime; + } + + public BigDecimal bytes() { + return bytes; + } + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ErasureSetInfo { + @JsonProperty("id") + private Integer id; + + @JsonProperty("rawUsage") + private BigDecimal rawUsage; + + @JsonProperty("rawCapacity") + private BigDecimal rawCapacity; + + @JsonProperty("usage") + private BigDecimal usage; + + @JsonProperty("objectsCount") + private BigDecimal objectsCount; + + @JsonProperty("versionsCount") + private BigDecimal versionsCount; + + @JsonProperty("healDisks") + private Integer healDisks; + + public Integer id() { + return id; + } + + public BigDecimal rawUsage() { + return rawUsage; + } + + public BigDecimal rawCapacity() { + return rawCapacity; + } + + public BigDecimal usage() { + return usage; + } + + public BigDecimal objectsCount() { + return objectsCount; + } + + public BigDecimal versionsCount() { + return versionsCount; + } + + public Integer healDisks() { + return healDisks; + } + } +} diff --git a/adminapi/src/main/java/io/minio/admin/GetServiceAccountInfoResp.java b/adminapi/src/main/java/io/minio/admin/GetServiceAccountInfoResponse.java similarity index 97% rename from adminapi/src/main/java/io/minio/admin/GetServiceAccountInfoResp.java rename to adminapi/src/main/java/io/minio/admin/GetServiceAccountInfoResponse.java index 1694bf113..c93f86cbb 100644 --- a/adminapi/src/main/java/io/minio/admin/GetServiceAccountInfoResp.java +++ b/adminapi/src/main/java/io/minio/admin/GetServiceAccountInfoResponse.java @@ -27,7 +27,7 @@ * "https://github.com/minio/madmin-go/blob/main/user-commands.go#L535">user-commands.go */ @JsonIgnoreProperties(ignoreUnknown = true) -public class GetServiceAccountInfoResp { +public class GetServiceAccountInfoResponse { @JsonProperty("parentUser") private String parentUser; diff --git a/adminapi/src/main/java/io/minio/admin/ListServiceAccountResp.java b/adminapi/src/main/java/io/minio/admin/ListServiceAccountResponse.java similarity index 98% rename from adminapi/src/main/java/io/minio/admin/ListServiceAccountResp.java rename to adminapi/src/main/java/io/minio/admin/ListServiceAccountResponse.java index baa7c12a6..f6fa095b5 100644 --- a/adminapi/src/main/java/io/minio/admin/ListServiceAccountResp.java +++ b/adminapi/src/main/java/io/minio/admin/ListServiceAccountResponse.java @@ -25,7 +25,7 @@ /** list service account response. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class ListServiceAccountResp { +public class ListServiceAccountResponse { @JsonProperty("accounts") private List accounts; diff --git a/adminapi/src/main/java/io/minio/admin/MinioAdminClient.java b/adminapi/src/main/java/io/minio/admin/MinioAdminClient.java index de11bd275..ad1960aa3 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,21 @@ 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.admin.messages.DataUsageInfo; -import io.minio.admin.messages.info.Message; +import io.minio.Utils; 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 +60,6 @@ import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; -import org.bouncycastle.crypto.InvalidCipherTextException; /** Client to perform MinIO administration operations. */ public class MinioAdminClient { @@ -89,7 +83,9 @@ private enum Command { UPDATE_SERVICE_ACCOUNT("update-service-account"), LIST_SERVICE_ACCOUNTS("list-service-accounts"), DELETE_SERVICE_ACCOUNT("delete-service-account"), - INFO_SERVICE_ACCOUNT("info-service-account"); + INFO_SERVICE_ACCOUNT("info-service-account"), + IDP_BUILTIN_POLICY_ATTACH("idp/builtin/policy/attach"), + IDP_BUILTIN_POLICY_DETACH("idp/builtin/policy/detach"); private final String value; private Command(String value) { @@ -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_BYTE_ARRAY; } 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,19 +237,15 @@ 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, - @Nonnull UserInfo.Status status, + @Nonnull Status status, @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,42 +331,42 @@ 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"); } - GroupAddUpdateRemoveInfo groupAddUpdateRemoveInfo = - new GroupAddUpdateRemoveInfo(group, groupStatus, members, false); + AddUpdateRemoveGroupArgs args = + new AddUpdateRemoveGroupArgs(group, groupStatus, members, false); try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.ADD_UPDATE_REMOVE_GROUP, null, - OBJECT_MAPPER.writeValueAsBytes(groupAddUpdateRemoveInfo))) {} + OBJECT_MAPPER.writeValueAsBytes(args))) { + } catch (JsonProcessingException e) { + throw new MinioException(e); + } } /** * Obtains group info for a specified MinIO group. * * @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. + * @return {@link GetGroupInfoResponse} - group info for the specified group. + * @throws MinioException thrown to indicate SDK exception. */ - public GroupInfo getGroupInfo(String group) - throws NoSuchAlgorithmException, InvalidKeyException, IOException { + public GetGroupInfoResponse 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); + return OBJECT_MAPPER.readValue(jsonData, GetGroupInfoResponse.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,24 +391,23 @@ 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"); } - GroupAddUpdateRemoveInfo groupAddUpdateRemoveInfo = - new GroupAddUpdateRemoveInfo(group, null, null, true); + AddUpdateRemoveGroupArgs args = new AddUpdateRemoveGroupArgs(group, null, null, true); try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.ADD_UPDATE_REMOVE_GROUP, null, - OBJECT_MAPPER.writeValueAsBytes(groupAddUpdateRemoveInfo))) {} + OBJECT_MAPPER.writeValueAsBytes(args))) { + } catch (JsonProcessingException e) { + throw new MinioException(e); + } } /** @@ -414,23 +416,22 @@ 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"); - } + if (size > 0) quotaEntity.put("quotatype", "hard"); 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 +439,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 +458,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 +467,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 +495,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 +507,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 +519,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 +533,7 @@ public void setPolicy( try (Response response = execute( - Method.PUT, + Http.Method.PUT, Command.SET_USER_OR_GROUP_POLICY, ImmutableMultimap.of( "userOrGroup", @@ -558,13 +550,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 +563,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 +572,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)) {} @@ -602,29 +590,28 @@ 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. + * @return {@link GetDataUsageInfoResponse} + * @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)) { - return OBJECT_MAPPER.readValue(response.body().bytes(), DataUsageInfo.class); + public GetDataUsageInfoResponse getDataUsageInfo() throws MinioException { + try (Response response = execute(Http.Method.GET, Command.DATA_USAGE_INFO, null, null)) { + return OBJECT_MAPPER.readValue(response.body().bytes(), GetDataUsageInfoResponse.class); + } catch (IOException e) { + throw new MinioException(e); } } /** * 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. + * @return {@link GetServerInfoResponse} + * @throws MinioException thrown to indicate SDK exception. */ - public Message getServerInfo() throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try (Response response = execute(Method.GET, Command.INFO, null, null)) { - return OBJECT_MAPPER.readValue(response.body().charStream(), Message.class); + public GetServerInfoResponse getServerInfo() throws MinioException { + try (Response response = execute(Http.Method.GET, Command.INFO, null, null)) { + return OBJECT_MAPPER.readValue(response.body().charStream(), GetServerInfoResponse.class); + } catch (IOException e) { + throw new MinioException(e); } } @@ -639,10 +626,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 +636,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"); } @@ -675,28 +658,28 @@ public Credentials addServiceAccount( if (targetUser != null && !targetUser.isEmpty()) { serviceAccount.put("targetUser", targetUser); } - if (policy != null && !policy.isEmpty()) { - serviceAccount.put("policy", policy); - } - if (name != null && !name.isEmpty()) { - serviceAccount.put("name", name); - } + if (policy != null && !policy.isEmpty()) serviceAccount.put("policy", policy); + if (name != null && !name.isEmpty()) serviceAccount.put("name", name); if (description != null && !description.isEmpty()) { 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(); + return OBJECT_MAPPER.readValue(jsonData, AddServiceAccountResponse.class).credentials(); + } catch (JsonProcessingException e) { + throw new MinioException(e); + } catch (IOException e) { + throw new MinioException(e); } } @@ -710,10 +693,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 +703,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"); } @@ -741,46 +720,42 @@ public void updateServiceAccount( if (newSecretKey != null && !newSecretKey.isEmpty()) { serviceAccount.put("newSecretKey", newSecretKey); } - if (newPolicy != null && !newPolicy.isEmpty()) { - serviceAccount.put("newPolicy", newPolicy); - } + if (newPolicy != null && !newPolicy.isEmpty()) serviceAccount.put("newPolicy", newPolicy); serviceAccount.put("newStatus", newStatus ? "on" : "off"); - if (newName != null && !newName.isEmpty()) { - serviceAccount.put("newName", newName); - } + if (newName != null && !newName.isEmpty()) serviceAccount.put("newName", newName); if (newDescription != null && !newDescription.isEmpty()) { 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)) {} @@ -790,28 +765,26 @@ public void deleteServiceAccount(@Nonnull String accessKey) * Obtains a list of minio service account by user name. * * @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. + * @return {@link ListServiceAccountResponse} - List of minio service account. + * @throws MinioException thrown to indicate SDK exception. */ - public ListServiceAccountResp listServiceAccount(@Nonnull String username) - throws NoSuchAlgorithmException, InvalidKeyException, IOException, - InvalidCipherTextException { + public ListServiceAccountResponse 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); + return OBJECT_MAPPER.readValue(jsonData, ListServiceAccountResponse.class); + } catch (IOException e) { + throw new MinioException(e); } } @@ -819,30 +792,78 @@ public ListServiceAccountResp listServiceAccount(@Nonnull String username) * Obtains service account info for a specified MinIO user. * * @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. + * @return {@link GetServiceAccountInfoResponse} - Service account info for the specified + * accessKey. + * @throws MinioException thrown to indicate SDK exception. */ - public GetServiceAccountInfoResp getServiceAccountInfo(@Nonnull String accessKey) - throws NoSuchAlgorithmException, InvalidKeyException, IOException, - InvalidCipherTextException { + public GetServiceAccountInfoResponse getServiceAccountInfo(@Nonnull String accessKey) + 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); + return OBJECT_MAPPER.readValue(jsonData, GetServiceAccountInfoResponse.class); + } catch (IOException e) { + throw new MinioException(e); + } + } + + private PolicyAssociationResponse attachDetachPolicy( + @Nonnull Command command, + @Nonnull String[] polices, + @Nullable String user, + @Nullable String group) + throws MinioException { + if (!(user != null ^ group != null)) { + throw new IllegalArgumentException("either user or group must be provided"); + } + + Map map = new HashMap<>(); + map.put("policies", polices); + if (user != null) { + map.put("user", user); + } else { + map.put("group", group); + } + + Credentials creds = getCredentials(); + try (Response response = + execute( + Http.Method.POST, + command, + null, + Crypto.encrypt(OBJECT_MAPPER.writeValueAsBytes(map), creds.secretKey()))) { + return OBJECT_MAPPER.readValue( + Crypto.decrypt(response.body().byteStream(), creds.secretKey()), + PolicyAssociationResponse.class); + } catch (JsonProcessingException e) { + throw new MinioException(e); + } catch (IOException e) { + throw new MinioException(e); } } + /** Attach policies to a user or group. */ + public PolicyAssociationResponse attachPolicy( + @Nonnull String[] policies, @Nullable String user, @Nullable String group) + throws MinioException { + return attachDetachPolicy(Command.IDP_BUILTIN_POLICY_ATTACH, policies, user, group); + } + + /** Detach policies from a user or group. */ + public PolicyAssociationResponse detachPolicy( + @Nonnull String[] policies, @Nullable String user, @Nullable String group) + throws MinioException { + return attachDetachPolicy(Command.IDP_BUILTIN_POLICY_DETACH, policies, user, group); + } + /** * 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. @@ -857,8 +878,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 +888,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 +904,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 +923,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 +940,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 +955,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,25 +979,21 @@ 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"); - if (httpClient == null) { - httpClient = - HttpUtils.newDefaultHttpClient( - DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); - } + Utils.validateNotNull(baseUrl, "base url"); + Utils.validateNotNull(provider, "credential provider"); + if (httpClient == null) httpClient = Http.newDefaultClient(); return new MinioAdminClient(baseUrl, region, provider, httpClient); } } diff --git a/adminapi/src/main/java/io/minio/admin/messages/TierStats.java b/adminapi/src/main/java/io/minio/admin/PolicyAssociationResponse.java similarity index 59% rename from adminapi/src/main/java/io/minio/admin/messages/TierStats.java rename to adminapi/src/main/java/io/minio/admin/PolicyAssociationResponse.java index 034fa40ca..54f8a32b1 100644 --- a/adminapi/src/main/java/io/minio/admin/messages/TierStats.java +++ b/adminapi/src/main/java/io/minio/admin/PolicyAssociationResponse.java @@ -15,37 +15,33 @@ * limitations under the License. */ -package io.minio.admin.messages; +package io.minio.admin; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.LocalDateTime; -/** - * Per-tier stats of a remote tier. - * - * @see data-usage-cache.go - */ +/** attachPolicy and detachPolicy response. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class TierStats { - @JsonProperty("TotalSize") - private long totalSize; +public class PolicyAssociationResponse { + @JsonProperty("policiesAttached") + private String[] policiesAttached; - @JsonProperty("NumVersions") - private int numVersions; + @JsonProperty("policiesDetached") + private String[] policiesDetached; - @JsonProperty("NumObjects") - private int numObjects; + @JsonProperty("updatedAt") + private LocalDateTime updatedAt; - public long totalSize() { - return totalSize; + public String[] getPoliciesAttached() { + return policiesAttached; } - public int numVersions() { - return numVersions; + public String[] getPoliciesDetached() { + return policiesDetached; } - public int numObjects() { - return numObjects; + public LocalDateTime getUpdatedAt() { + return updatedAt; } } diff --git a/adminapi/src/main/java/io/minio/admin/UserInfo.java b/adminapi/src/main/java/io/minio/admin/UserInfo.java index fdc10fa13..890526d6a 100644 --- a/adminapi/src/main/java/io/minio/admin/UserInfo.java +++ b/adminapi/src/main/java/io/minio/admin/UserInfo.java @@ -17,12 +17,10 @@ package io.minio.admin; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import javax.annotation.Nullable; @@ -61,43 +59,10 @@ public String policyName() { } public List memberOf() { - return Collections.unmodifiableList(memberOf == null ? new LinkedList<>() : memberOf); + return Collections.unmodifiableList(memberOf == null ? new ArrayList<>() : memberOf); } public Status status() { return status; } - - public static enum Status { - ENABLED("enabled"), - DISABLED("disabled"); - - private final String value; - - private Status(String value) { - this.value = value; - } - - @JsonValue - public String toString() { - return this.value; - } - - @JsonCreator - public static Status fromString(String statusString) { - if ("enabled".equals(statusString)) { - return ENABLED; - } - - if ("disabled".equals(statusString)) { - return DISABLED; - } - - if (statusString.isEmpty()) { - return null; - } - - throw new IllegalArgumentException("Unknown status " + statusString); - } - } } diff --git a/adminapi/src/main/java/io/minio/admin/messages/AllTierStats.java b/adminapi/src/main/java/io/minio/admin/messages/AllTierStats.java deleted file mode 100644 index 801178186..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/AllTierStats.java +++ /dev/null @@ -1,39 +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.admin.messages; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Collections; -import java.util.Map; - -/** - * Collection of per-tier stats across all configured remote tiers - * - * @see data-usage-cache.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class AllTierStats { - @JsonProperty("Tiers") - private Map tiers; - - public Map tiers() { - return Collections.unmodifiableMap(this.tiers); - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/BucketTargetUsageInfo.java b/adminapi/src/main/java/io/minio/admin/messages/BucketTargetUsageInfo.java deleted file mode 100644 index 40dee2728..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/BucketTargetUsageInfo.java +++ /dev/null @@ -1,72 +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.admin.messages; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Represents bucket replica stats of the current object APi. - * - * @see data-usage-utils.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class BucketTargetUsageInfo { - @JsonProperty("objectsPendingReplicationTotalSize") - private long objectsPendingReplicationTotalSize; - - @JsonProperty("objectsFailedReplicationTotalSize") - private long objectsFailedReplicationTotalSize; - - @JsonProperty("objectsReplicatedTotalSize") - private long objectsReplicatedTotalSize; - - @JsonProperty("objectReplicaTotalSize") - private long objectReplicaTotalSize; - - @JsonProperty("objectsPendingReplicationCount") - private long objectsPendingReplicationCount; - - @JsonProperty("objectsFailedReplicationCount") - private long objectsFailedReplicationCount; - - public long objectsPendingReplicationTotalSize() { - return objectsPendingReplicationTotalSize; - } - - public long objectsFailedReplicationTotalSize() { - return objectsFailedReplicationTotalSize; - } - - public long objectsReplicatedTotalSize() { - return objectsReplicatedTotalSize; - } - - public long objectReplicaTotalSize() { - return objectReplicaTotalSize; - } - - public long objectsPendingReplicationCount() { - return objectsPendingReplicationCount; - } - - public long objectsFailedReplicationCount() { - return objectsFailedReplicationCount; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/BucketUsageInfo.java b/adminapi/src/main/java/io/minio/admin/messages/BucketUsageInfo.java deleted file mode 100644 index 91b47ced1..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/BucketUsageInfo.java +++ /dev/null @@ -1,109 +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.admin.messages; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Collections; -import java.util.Map; - -/** - * Represents bucket usage stats of the current object APi. - * - * @see data-usage-utils.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class BucketUsageInfo { - @JsonProperty("size") - private long size; - - @JsonProperty("objectsPendingReplicationTotalSize") - private long objectsPendingReplicationTotalSize; - - @JsonProperty("objectsFailedReplicationTotalSize") - private long objectsFailedReplicationTotalSize; - - @JsonProperty("objectsReplicatedTotalSize") - private long objectsReplicatedTotalSize; - - @JsonProperty("objectsPendingReplicationCount") - private long objectsPendingReplicationCount; - - @JsonProperty("objectsFailedReplicationCount") - private long objectsFailedReplicationCount; - - @JsonProperty("objectsCount") - private long objectsCount; - - @JsonProperty("objectsSizesHistogram") - private Map objectsSizesHistogram; - - @JsonProperty("versionsCount") - private long versionsCount; - - @JsonProperty("objectReplicaTotalSize") - private long objectReplicaTotalSize; - - @JsonProperty("objectsReplicationInfo") - private Map objectsReplicationInfo; - - public long size() { - return size; - } - - public long objectsPendingReplicationTotalSize() { - return objectsPendingReplicationTotalSize; - } - - public long objectsFailedReplicationTotalSize() { - return objectsFailedReplicationTotalSize; - } - - public long objectsReplicatedTotalSize() { - return objectsReplicatedTotalSize; - } - - public long objectsPendingReplicationCount() { - return objectsPendingReplicationCount; - } - - public long objectsFailedReplicationCount() { - return objectsFailedReplicationCount; - } - - public long objectsCount() { - return objectsCount; - } - - public Map objectsSizesHistogram() { - return Collections.unmodifiableMap(this.objectsSizesHistogram); - } - - public long versionsCount() { - return versionsCount; - } - - public long objectReplicaTotalSize() { - return objectReplicaTotalSize; - } - - public Map objectsReplicationInfo() { - return Collections.unmodifiableMap(this.objectsReplicationInfo); - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/DataUsageInfo.java b/adminapi/src/main/java/io/minio/admin/messages/DataUsageInfo.java deleted file mode 100644 index 285aa9a04..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/DataUsageInfo.java +++ /dev/null @@ -1,97 +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.admin.messages; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.time.ZonedDateTime; -import java.util.Collections; -import java.util.Map; - -/** - * Represents data usage stats of the current object APi. - * - * @see data-usage-utils.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class DataUsageInfo { - - @JsonProperty("lastUpdate") - private ZonedDateTime lastUpdate; - - @JsonProperty("objectsCount") - private long objectsCount; - - @JsonProperty("versionsCount") - private long versionsCount; - - @JsonProperty("objectsTotalSize") - private long objectsTotalSize; - - @JsonProperty("objectsReplicationInfo") - private Map objectsReplicationInfo; - - @JsonProperty("bucketsCount") - private long bucketsCount; - - @JsonProperty("bucketsUsageInfo") - private Map bucketsUsageInfo; - - @JsonProperty("bucketsSizes") - private Map bucketsSizes; - - @JsonProperty("tierStats") - private AllTierStats tierStats; - - public ZonedDateTime lastUpdate() { - return lastUpdate; - } - - public long objectsCount() { - return objectsCount; - } - - public long versionsCount() { - return versionsCount; - } - - public long objectsTotalSize() { - return objectsTotalSize; - } - - public Map objectsReplicationInfo() { - return Collections.unmodifiableMap(this.objectsReplicationInfo); - } - - public long bucketsCount() { - return bucketsCount; - } - - public Map bucketsUsageInfo() { - return Collections.unmodifiableMap(this.bucketsUsageInfo); - } - - public Map bucketsSizes() { - return Collections.unmodifiableMap(bucketsSizes); - } - - public AllTierStats tierStats() { - return tierStats; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/Backend.java b/adminapi/src/main/java/io/minio/admin/messages/info/Backend.java deleted file mode 100644 index ae9f08319..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/Backend.java +++ /dev/null @@ -1,83 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** - * ErasureBackend contains specific erasure storage information - * - * @see info-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Backend { - @JsonProperty("backendType") - private String backendType; - - @JsonProperty("onlineDisks") - private Integer onlineDisks; - - @JsonProperty("offlineDisks") - private Integer offlineDisks; - - @JsonProperty("standardSCParity") - private Integer standardSCParity; - - @JsonProperty("rrSCParity") - private Integer rrSCParity; - - @JsonProperty("totalSets") - private List totalSets; - - @JsonProperty("totalDrivesPerSet") - private List totalDrivesPerSet; - - public String backendType() { - return backendType; - } - - public Integer onlineDisks() { - return onlineDisks; - } - - public Integer offlineDisks() { - return offlineDisks; - } - - public Integer standardSCParity() { - return standardSCParity; - } - - public Integer rrSCParity() { - return rrSCParity; - } - - public List totalSets() { - return Collections.unmodifiableList(totalSets == null ? new LinkedList<>() : totalSets); - } - - public List totalDrivesPerSet() { - return Collections.unmodifiableList( - totalDrivesPerSet == null ? new LinkedList<>() : totalDrivesPerSet); - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/Buckets.java b/adminapi/src/main/java/io/minio/admin/messages/info/Buckets.java deleted file mode 100644 index 54b16037c..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/Buckets.java +++ /dev/null @@ -1,44 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Buckets contains the number of buckets - * - * @see info-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Buckets { - @JsonProperty("count") - private Integer count; - - @JsonProperty("error") - private String error; - - public Integer count() { - return count; - } - - public String error() { - return error; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/Disk.java b/adminapi/src/main/java/io/minio/admin/messages/info/Disk.java deleted file mode 100644 index d6f2c39ed..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/Disk.java +++ /dev/null @@ -1,206 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; - -/** - * Disk holds Disk information - * - * @see info-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Disk { - @JsonProperty("endpoint") - private String endpoint; - - @JsonProperty("rootDisk") - private boolean rootDisk; - - @JsonProperty("path") - private String path; - - @JsonProperty("healing") - private boolean healing; - - @JsonProperty("scanning") - private boolean scanning; - - @JsonProperty("state") - private String state; - - @JsonProperty("uuid") - private String uuid; - - @JsonProperty("major") - private BigDecimal major; - - @JsonProperty("minor") - private BigDecimal minor; - - @JsonProperty("model") - private String model; - - @JsonProperty("totalspace") - private BigDecimal totalspace; - - @JsonProperty("usedspace") - private BigDecimal usedspace; - - @JsonProperty("availspace") - private BigDecimal availspace; - - @JsonProperty("readthroughput") - private BigDecimal readthroughput; - - @JsonProperty("writethroughput") - private BigDecimal writethroughput; - - @JsonProperty("readlatency") - private BigDecimal readlatency; - - @JsonProperty("writelatency") - private BigDecimal writelatency; - - @JsonProperty("utilization") - private BigDecimal utilization; - - @JsonProperty("metrics") - private DiskMetrics metrics; - - @JsonProperty("heal_info") - private HealingDisk healInfo; - - @JsonProperty("used_inodes") - private BigDecimal usedInodes; - - @JsonProperty("free_inodes") - private BigDecimal freeInodes; - - @JsonProperty("pool_index") - private Integer poolIndex; - - @JsonProperty("set_index") - private Integer setIndex; - - @JsonProperty("disk_index") - private Integer diskIndex; - - public String endpoint() { - return endpoint; - } - - public boolean isRootDisk() { - return rootDisk; - } - - public String path() { - return path; - } - - public boolean isHealing() { - return healing; - } - - public boolean isScanning() { - return scanning; - } - - public String state() { - return state; - } - - public String uuid() { - return uuid; - } - - public BigDecimal major() { - return major; - } - - public BigDecimal minor() { - return minor; - } - - public String model() { - return model; - } - - public BigDecimal totalspace() { - return totalspace; - } - - public BigDecimal usedspace() { - return usedspace; - } - - public BigDecimal availspace() { - return availspace; - } - - public BigDecimal readthroughput() { - return readthroughput; - } - - public BigDecimal writethroughput() { - return writethroughput; - } - - public BigDecimal readlatency() { - return readlatency; - } - - public BigDecimal writelatency() { - return writelatency; - } - - public BigDecimal utilization() { - return utilization; - } - - public DiskMetrics metrics() { - return metrics; - } - - public HealingDisk healInfo() { - return healInfo; - } - - public BigDecimal usedInodes() { - return usedInodes; - } - - public BigDecimal freeInodes() { - return freeInodes; - } - - public Integer poolIndex() { - return poolIndex; - } - - public Integer setIndex() { - return setIndex; - } - - public Integer diskIndex() { - return diskIndex; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/DiskMetrics.java b/adminapi/src/main/java/io/minio/admin/messages/info/DiskMetrics.java deleted file mode 100644 index 1b1b35078..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/DiskMetrics.java +++ /dev/null @@ -1,88 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Collections; -import java.util.Map; - -/** - * DiskMetrics has the information about XL Storage APIs - * - * @see info-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class DiskMetrics { - @JsonProperty("lastMinute") - private Map lastMinute; - - @JsonProperty("apiCalls") - private Map apiCalls; - - @JsonProperty("totalErrorsAvailability") - private Integer totalErrorsAvailability; - - @JsonProperty("totalErrorsTimeout") - private Integer totalErrorsTimeout; - - @JsonProperty("totalTokens") - private Long totalTokens; - - @JsonProperty("totalWaiting") - private Long totalWaiting; - - @JsonProperty("totalWrites") - private Long totalWrites; - - @JsonProperty("totalDeletes") - private Long totalDeletes; - - public Integer totalErrorsAvailability() { - return totalErrorsAvailability; - } - - public Integer totalErrorsTimeout() { - return totalErrorsTimeout; - } - - public Map lastMinute() { - return Collections.unmodifiableMap(lastMinute); - } - - public Map apiCalls() { - return Collections.unmodifiableMap(apiCalls); - } - - public Long totalTokens() { - return totalTokens; - } - - public Long totalWaiting() { - return totalWaiting; - } - - public Long totalWrites() { - return totalWrites; - } - - public Long totalDeletes() { - return totalDeletes; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/ErasureSetInfo.java b/adminapi/src/main/java/io/minio/admin/messages/info/ErasureSetInfo.java deleted file mode 100644 index 5b8102eec..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/ErasureSetInfo.java +++ /dev/null @@ -1,80 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; - -/** - * ErasureSetInfo provides information per erasure set - * - * @see info-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ErasureSetInfo { - @JsonProperty("id") - private Integer id; - - @JsonProperty("rawUsage") - private BigDecimal rawUsage; - - @JsonProperty("rawCapacity") - private BigDecimal rawCapacity; - - @JsonProperty("usage") - private BigDecimal usage; - - @JsonProperty("objectsCount") - private BigDecimal objectsCount; - - @JsonProperty("versionsCount") - private BigDecimal versionsCount; - - @JsonProperty("healDisks") - private Integer healDisks; - - public Integer id() { - return id; - } - - public BigDecimal rawUsage() { - return rawUsage; - } - - public BigDecimal rawCapacity() { - return rawCapacity; - } - - public BigDecimal usage() { - return usage; - } - - public BigDecimal objectsCount() { - return objectsCount; - } - - public BigDecimal versionsCount() { - return versionsCount; - } - - public Integer healDisks() { - return healDisks; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/GCStats.java b/adminapi/src/main/java/io/minio/admin/messages/info/GCStats.java deleted file mode 100644 index 9a8b52d13..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/GCStats.java +++ /dev/null @@ -1,67 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** - * GCStats collect information about recent garbage collections. - * - * @see health.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class GCStats { - @JsonProperty("last_gc") - private String lastGC; - - @JsonProperty("num_gc") - private Integer numGC; - - @JsonProperty("pause_total") - private Long pauseTotal; - - @JsonProperty("pause") - private List pause; - - @JsonProperty("pause_end") - private List pauseEnd; - - public String lastGC() { - return lastGC; - } - - public Integer numGC() { - return numGC; - } - - public Long pauseTotal() { - return pauseTotal; - } - - public List pause() { - return Collections.unmodifiableList(pause == null ? new LinkedList<>() : pause); - } - - public List pauseEnd() { - return Collections.unmodifiableList(pauseEnd == null ? new LinkedList<>() : pauseEnd); - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/HealingDisk.java b/adminapi/src/main/java/io/minio/admin/messages/info/HealingDisk.java deleted file mode 100644 index e87eb42a7..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/HealingDisk.java +++ /dev/null @@ -1,181 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** - * HealingDisk contains information about - * - * @see heal-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class HealingDisk { - @JsonProperty("id") - private String id; - - @JsonProperty("heal_id") - private String healID; - - @JsonProperty("pool_index") - private Integer poolIndex; - - @JsonProperty("set_index") - private Integer setIndex; - - @JsonProperty("disk_index") - private Integer diskIndex; - - @JsonProperty("endpoint") - private String endpoint; - - @JsonProperty("path") - private String path; - - @JsonProperty("started") - private String started; - - @JsonProperty("last_update") - private String lastUpdate; - - @JsonProperty("objects_total_count") - private BigDecimal objectsTotalCount; - - @JsonProperty("objects_total_size") - private BigDecimal objectsTotalSize; - - @JsonProperty("items_healed") - private BigDecimal itemsHealed; - - @JsonProperty("items_failed") - private BigDecimal itemsFailed; - - @JsonProperty("bytes_done") - private BigDecimal bytesDone; - - @JsonProperty("bytes_failed") - private BigDecimal bytesFailed; - - @JsonProperty("objects_healed") - private BigDecimal objectsHealed; - - @JsonProperty("objects_failed") - private BigDecimal objectsFailed; - - @JsonProperty("current_bucket") - private String bucket; - - @JsonProperty("current_object") - private String object; - - @JsonProperty("queued_buckets") - private List queuedBuckets; - - @JsonProperty("healed_buckets") - private List healedBuckets; - - public String id() { - return id; - } - - public String healID() { - return healID; - } - - public Integer poolIndex() { - return poolIndex; - } - - public Integer setIndex() { - return setIndex; - } - - public Integer diskIndex() { - return diskIndex; - } - - public String endpoint() { - return endpoint; - } - - public String path() { - return path; - } - - public String started() { - return started; - } - - public String lastUpdate() { - return lastUpdate; - } - - public BigDecimal objectsTotalCount() { - return objectsTotalCount; - } - - public BigDecimal objectsTotalSize() { - return objectsTotalSize; - } - - public BigDecimal itemsHealed() { - return itemsHealed; - } - - public BigDecimal itemsFailed() { - return itemsFailed; - } - - public BigDecimal bytesDone() { - return bytesDone; - } - - public BigDecimal bytesFailed() { - return bytesFailed; - } - - public BigDecimal objectsHealed() { - return objectsHealed; - } - - public BigDecimal objectsFailed() { - return objectsFailed; - } - - public String bucket() { - return bucket; - } - - public String object() { - return object; - } - - public List queuedBuckets() { - return Collections.unmodifiableList(queuedBuckets == null ? new LinkedList<>() : queuedBuckets); - } - - public List healedBuckets() { - return Collections.unmodifiableList(healedBuckets == null ? new LinkedList<>() : healedBuckets); - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/MemStats.java b/adminapi/src/main/java/io/minio/admin/messages/info/MemStats.java deleted file mode 100644 index 1f692e9d1..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/MemStats.java +++ /dev/null @@ -1,65 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; - -/** - * MemStats is strip down version of runtime.MemStats containing memory stats of MinIO server. - * - * @see health.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class MemStats { - @JsonProperty("Alloc") - private BigDecimal alloc; - - @JsonProperty("TotalAlloc") - private BigDecimal totalAlloc; - - @JsonProperty("Mallocs") - private BigDecimal mallocs; - - @JsonProperty("Frees") - private BigDecimal frees; - - @JsonProperty("HeapAlloc") - private BigDecimal heapAlloc; - - public BigDecimal alloc() { - return alloc; - } - - public BigDecimal totalAlloc() { - return totalAlloc; - } - - public BigDecimal mallocs() { - return mallocs; - } - - public BigDecimal frees() { - return frees; - } - - public BigDecimal heapAlloc() { - return heapAlloc; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/Message.java b/adminapi/src/main/java/io/minio/admin/messages/info/Message.java deleted file mode 100644 index 1515ddfc6..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/Message.java +++ /dev/null @@ -1,97 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * InfoMessage container to hold server admin related information. - * - * @see heal-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Message { - @JsonProperty("mode") - private String mode; - - @JsonProperty("deploymentID") - private String deploymentID; - - @JsonProperty("buckets") - private Buckets buckets; - - @JsonProperty("objects") - private Objects objects; - - @JsonProperty("versions") - private Versions versions; - - @JsonProperty("usage") - private Usage usage; - - @JsonProperty("backend") - private Backend backend; - - @JsonProperty("servers") - private List servers; - - @JsonProperty("pools") - private Map> pools; - - public String mode() { - return mode; - } - - public String deploymentID() { - return deploymentID; - } - - public Buckets buckets() { - return buckets; - } - - public Objects objects() { - return objects; - } - - public Versions versions() { - return versions; - } - - public Usage usage() { - return usage; - } - - public Backend backend() { - return backend; - } - - public List servers() { - return Collections.unmodifiableList(servers == null ? new LinkedList<>() : servers); - } - - public Map> pools() { - return pools; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/Objects.java b/adminapi/src/main/java/io/minio/admin/messages/info/Objects.java deleted file mode 100644 index 0e072260b..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/Objects.java +++ /dev/null @@ -1,44 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Objects contains the number of objects - * - * @see info-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Objects { - @JsonProperty("count") - private Integer count; - - @JsonProperty("error") - private String error; - - public Integer count() { - return count; - } - - public String error() { - return error; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/ServerProperties.java b/adminapi/src/main/java/io/minio/admin/messages/info/ServerProperties.java deleted file mode 100644 index 0b33dc282..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/ServerProperties.java +++ /dev/null @@ -1,139 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * ServerProperties holds server information - * - * @see info-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ServerProperties { - @JsonProperty("state") - private String state; - - @JsonProperty("endpoint") - private String endpoint; - - @JsonProperty("scheme") - private String scheme; - - @JsonProperty("uptime") - private Integer uptime; - - @JsonProperty("version") - private String version; - - @JsonProperty("commitID") - private String commitID; - - @JsonProperty("network") - private Map network; - - @JsonProperty("drives") - private List disks; - - @JsonProperty("poolNumber") - private Integer poolNumber; - - @JsonProperty("mem_stats") - private MemStats memStats; - - @JsonProperty("go_max_procs") - private Integer goMaxProcs; - - @JsonProperty("num_cpu") - private Integer numCPU; - - @JsonProperty("runtime_version") - private String runtimeVersion; - - @JsonProperty("gc_stats") - private GCStats gCStats; - - @JsonProperty("minio_env_vars") - private Map minioEnvVars; - - public String state() { - return state; - } - - public String endpoint() { - return endpoint; - } - - public String scheme() { - return scheme; - } - - public Integer uptime() { - return uptime; - } - - public String version() { - return version; - } - - public String commitID() { - return commitID; - } - - public Map network() { - return Collections.unmodifiableMap(this.network); - } - - public List disks() { - return Collections.unmodifiableList(disks == null ? new LinkedList<>() : disks); - } - - public Integer poolNumber() { - return poolNumber; - } - - public MemStats memStats() { - return memStats; - } - - public Integer goMaxProcs() { - return goMaxProcs; - } - - public Integer numCPU() { - return numCPU; - } - - public String runtimeVersion() { - return runtimeVersion; - } - - public GCStats gCStats() { - return gCStats; - } - - public Map minioEnvVars() { - return Collections.unmodifiableMap(this.minioEnvVars); - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/TimedAction.java b/adminapi/src/main/java/io/minio/admin/messages/info/TimedAction.java deleted file mode 100644 index f1cde4185..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/TimedAction.java +++ /dev/null @@ -1,51 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; - -/** - * TimedAction contains a number of actions and their accumulated duration in nanoseconds. - * - * @see metrics.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class TimedAction { - @JsonProperty("count") - private BigDecimal count; - - @JsonProperty("acc_time_ns") - private BigDecimal accTime; - - @JsonProperty("bytes") - private BigDecimal bytes; - - public BigDecimal count() { - return count; - } - - public BigDecimal accTime() { - return accTime; - } - - public BigDecimal bytes() { - return bytes; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/Usage.java b/adminapi/src/main/java/io/minio/admin/messages/info/Usage.java deleted file mode 100644 index 9c43d85e8..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/Usage.java +++ /dev/null @@ -1,44 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Usage contains the total size used - * - * @see info-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Usage { - @JsonProperty("size") - private Long size; - - @JsonProperty("error") - private String error; - - public Long size() { - return size; - } - - public String error() { - return error; - } -} diff --git a/adminapi/src/main/java/io/minio/admin/messages/info/Versions.java b/adminapi/src/main/java/io/minio/admin/messages/info/Versions.java deleted file mode 100644 index 67efc6fc2..000000000 --- a/adminapi/src/main/java/io/minio/admin/messages/info/Versions.java +++ /dev/null @@ -1,44 +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.admin.messages.info; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Versions contains the number of versions - * - * @see info-commands.go - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Versions { - @JsonProperty("count") - private Integer count; - - @JsonProperty("error") - private String error; - - public Integer count() { - return count; - } - - public String error() { - return error; - } -} diff --git a/adminapi/src/test/java/io/minio/admin/messages/DataUsageInfoTest.java b/adminapi/src/test/java/io/minio/admin/messages/DataUsageInfoTest.java deleted file mode 100644 index dfe01b7a0..000000000 --- a/adminapi/src/test/java/io/minio/admin/messages/DataUsageInfoTest.java +++ /dev/null @@ -1,54 +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.admin.messages; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.io.File; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import org.junit.Assert; -import org.junit.Test; - -public class DataUsageInfoTest { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - static { - OBJECT_MAPPER.registerModule(new JavaTimeModule()); - } - - @Test - public void deserializeTest() throws IOException { - - DataUsageInfo info = - OBJECT_MAPPER.readValue( - new File( - getClass().getClassLoader().getResource("messages/datausageinfo.json").getFile()), - DataUsageInfo.class); - Assert.assertNotNull(info.lastUpdate()); - Assert.assertEquals( - 15, LocalDateTime.ofInstant(info.lastUpdate().toInstant(), ZoneOffset.UTC).getHour()); - - Assert.assertEquals(1, info.bucketsCount()); - Assert.assertTrue(info.bucketsUsageInfo().containsKey("tier-bucket")); - Assert.assertTrue(info.tierStats().tiers().containsKey("STANDARD")); - - Assert.assertEquals(7155L, (long) info.bucketsSizes().get("tier-bucket")); - } -} 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..e18755c63 --- /dev/null +++ b/api/src/main/java/io/minio/AbortMultipartUploadArgs.java @@ -0,0 +1,73 @@ +/* + * 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; + +/** Arguments of {@link BaseS3Client#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(); + } + + /** 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/AbortMultipartUploadResponse.java b/api/src/main/java/io/minio/AbortMultipartUploadResponse.java index ee36bb4c5..f08166391 100644 --- a/api/src/main/java/io/minio/AbortMultipartUploadResponse.java +++ b/api/src/main/java/io/minio/AbortMultipartUploadResponse.java @@ -18,7 +18,7 @@ import okhttp3.Headers; -/** Response class of {@link S3Base#abortMultipartUploadAsync}. */ +/** Response of {@link BaseS3Client#abortMultipartUpload}. */ public class AbortMultipartUploadResponse extends GenericResponse { private String uploadId; diff --git a/api/src/main/java/io/minio/AppendObjectArgs.java b/api/src/main/java/io/minio/AppendObjectArgs.java new file mode 100644 index 000000000..250af4b57 --- /dev/null +++ b/api/src/main/java/io/minio/AppendObjectArgs.java @@ -0,0 +1,140 @@ +/* + * 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.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Objects; + +/** + * Arguments of {@link MinioAsyncClient#appendObject(io.minio.AppendObjectArgs)} and {@link + * MinioClient#appendObject}. + */ +public class AppendObjectArgs extends ObjectArgs { + private String filename; + private InputStream stream; + private byte[] data; + private Long length; + private Long chunkSize; + + public String filename() { + return filename; + } + + public InputStream stream() { + return stream; + } + + public byte[] data() { + return data; + } + + public Long length() { + return length; + } + + public Long chunkSize() { + return chunkSize; + } + + public static Builder builder() { + return new Builder(); + } + + /** Builder of {@link AppendObjectArgs}. */ + public static final class Builder extends ObjectArgs.Builder { + @Override + protected void validate(AppendObjectArgs args) { + super.validate(args); + if (args.filename == null && args.stream == null && args.data == null) { + throw new IllegalArgumentException("either filename, stream or data must be provided"); + } + } + + private void validateFilename(String filename) { + Utils.validateNotEmptyString(filename, "filename"); + if (!Files.isRegularFile(Paths.get(filename))) { + throw new IllegalArgumentException(filename + " not a regular file"); + } + } + + private Builder setStream(String filename, InputStream stream, byte[] data, Long length) { + operations.add(args -> args.filename = filename); + operations.add(args -> args.stream = stream); + operations.add(args -> args.data = data); + operations.add(args -> args.length = length); + return this; + } + + public Builder filename(String filename) throws MinioException { + try { + validateFilename(filename); + long length = Files.size(Paths.get(filename)); + return setStream(filename, null, null, length); + } catch (IOException e) { + throw new MinioException(e); + } + } + + public Builder stream(InputStream stream, Long length) { + Utils.validateNotNull(stream, "stream"); + return setStream(null, stream, null, length); + } + + public Builder data(byte[] data, int length) { + if (data != null && length <= 0) { + throw new IllegalArgumentException("valid length must be provided"); + } + return setStream(null, null, data, data == null ? null : (long) length); + } + + public Builder chunkSize(Long chunkSize) { + if (chunkSize != null) { + if (chunkSize < ObjectWriteArgs.MIN_MULTIPART_SIZE) { + throw new IllegalArgumentException("chunk size must be minimum of 5 MiB"); + } + if (chunkSize > ObjectWriteArgs.MAX_PART_SIZE) { + throw new IllegalArgumentException("chunk size must be less than 5 GiB"); + } + } + operations.add(args -> args.chunkSize = chunkSize); + return this; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AppendObjectArgs)) return false; + if (!super.equals(o)) return false; + AppendObjectArgs that = (AppendObjectArgs) o; + return Objects.equals(filename, that.filename) + && Objects.equals(stream, that.stream) + && Arrays.equals(data, that.data) + && Objects.equals(length, that.length) + && Objects.equals(chunkSize, that.chunkSize); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), filename, stream, data, length, chunkSize); + } +} diff --git a/api/src/main/java/io/minio/BaseArgs.java b/api/src/main/java/io/minio/BaseArgs.java index c8857ec55..390980369 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,42 @@ import java.util.Map; import java.util.Objects; import java.util.function.Consumer; -import okhttp3.HttpUrl; -/** Base argument class. */ +/** + * Common arguments of {@link BucketArgs}, {@link ListBucketsArgs} and {@link PutObjectFanOutEntry}. + */ 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; + + 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 Multimap extraHeaders() { + public Http.Headers extraHeaders() { return extraHeaders; } - public Multimap extraQueryParams() { + public Http.QueryParameters extraQueryParams() { return extraQueryParams; } - protected void checkSse(ServerSideEncryption sse, HttpUrl url) { - if (sse == null) { - return; - } - - if (sse.tlsRequired() && !url.isHttps()) { + protected void checkSse(ServerSideEncryption sse, boolean isHttps) { + if (sse == null) return; + if (sse.tlsRequired() && !isHttps) { throw new IllegalArgumentException( sse + " operations must be performed over a secure connection."); } @@ -60,77 +70,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 @@ -155,7 +120,7 @@ private A newInstance() { } /** Creates derived Args class with each attribute populated. */ - public A build() throws IllegalArgumentException { + public A build() { A args = newInstance(); operations.forEach(operation -> operation.accept(args)); validate(args); 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..af01d2aff --- /dev/null +++ b/api/src/main/java/io/minio/BaseS3Client.java @@ -0,0 +1,1394 @@ +/* + * 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.function.Supplier; +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 static final Random RANDOM = new Random(new SecureRandom().nextLong()); + protected static final ObjectMapper OBJECT_MAPPER = + 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 final Map regionCache = new ConcurrentHashMap<>(); + 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; + } + + /** Closes underneath HTTP client. */ + @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 //////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + + protected CompletableFuture supplyAsync(Supplier supplier) { + return CompletableFuture.supplyAsync(supplier); + } + + protected CompletableFuture newCompleteableFuture() { + return new CompletableFuture<>(); + } + + 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) { + Credentials credentials = (provider == null) ? null : provider.fetch(); + Http.Request request = null; + try { + request = s3request.toRequest(baseUrl, region, credentials); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + + 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 = newCompleteableFuture(); + 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", + response.protocol().toString().toUpperCase(Locale.US), + response.code(), + response.message(), + response.headers().toString()); + if (!trace.endsWith("\n\n")) { + trace += trace.endsWith("\n") ? "\n" : "\n\n"; + } + 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")))) { + String responseBody = response.peekBody(1024 * 1024).string(); + traceStream.print(responseBody); + if (!responseBody.endsWith("\n")) traceStream.println(); + } + 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()); + } + + completableFuture.completeExceptionally( + new ErrorResponseException(errorResponse, response, traceBuilder.toString())); + } + }); + return completableFuture; + } + + /** Execute HTTP request asynchronously for given args and parameters. */ + protected CompletableFuture executeAsync(Http.S3Request s3request) { + return getRegion(s3request.bucket(), s3request.region()) + .thenCompose( + location -> { + s3request.args().setLocation(location); + return executeAsync(s3request, location); + }); + } + + /** Execute asynchronously GET HTTP request for given parameters. */ + protected CompletableFuture executeGetAsync( + BaseArgs args, Http.Headers headers, Http.QueryParameters queryParams) { + 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) { + Http.S3Request s3request = + Http.S3Request.builder() + .userAgent(userAgent) + .method(Http.Method.HEAD) + .headers(headers) + .queryParams(queryParams) + .args(args) + .build(); + return executeAsync(s3request) + .exceptionally( + e -> { + e = e.getCause(); + if (e instanceof ErrorResponseException + && ((ErrorResponseException) e).errorResponse().code().equals(RETRY_HEAD)) { + return null; + } + throw new CompletionException(e); + }) + .thenCompose( + response -> { + if (response != null) return CompletableFuture.completedFuture(response); + return executeAsync(s3request); + }); + } + + /** Execute asynchronously DELETE HTTP request for given parameters. */ + protected CompletableFuture executeDeleteAsync( + BaseArgs args, Http.Headers headers, Http.QueryParameters queryParams) { + 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) { + 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) { + 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) { + 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. + */ + public CompletableFuture abortMultipartUpload( + AbortMultipartUploadArgs args) { + 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. + */ + public CompletableFuture completeMultipartUpload( + CompleteMultipartUploadArgs args) { + checkArgs(args); + Http.Body body = null; + try { + body = new Http.Body(new CompleteMultipartUpload(args.parts()), null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return executePostAsync(args, null, new Http.QueryParameters(UPLOAD_ID, args.uploadId()), body) + .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(); + } + }); + } + + /** + * Do CopyObject S3 + * API asynchronously. + * + * @param args {@link CopyObjectArgs} object. + * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> object. + */ + public CompletableFuture copyObject(CopyObjectArgs args) { + 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()); + if (args.taggingDirective() == Directive.REPLACE && !headers.containsKey("x-amz-tagging")) { + headers.put("x-amz-tagging", ""); + } + } + + 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. + */ + public CompletableFuture createBucket(CreateBucketArgs args) { + 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()); + } + + Http.Body body = null; + try { + body = new Http.Body(config, null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + + return executeAsync( + Http.S3Request.builder() + .userAgent(userAgent) + .method(Http.Method.PUT) + .headers(headers) + .body(body) + .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. + */ + public CompletableFuture createMultipartUpload( + CreateMultipartUploadArgs args) { + 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. + */ + public CompletableFuture deleteObjects(DeleteObjectsArgs args) { + checkArgs(args); + Http.Body body = null; + try { + body = new Http.Body(new DeleteRequest(args.objects(), args.quiet()), null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return executePostAsync( + args, + args.bypassGovernanceMode() + ? new Http.Headers("x-amz-bypass-governance-retention", "true") + : null, + new Http.QueryParameters("delete", ""), + body) + .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. + */ + public CompletableFuture getBucketLocation(GetBucketLocationArgs args) { + 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. + */ + public CompletableFuture headObject(HeadObjectArgs args) { + 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. + */ + public CompletableFuture listBucketsAPI(ListBucketsArgs args) { + 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. + */ + public CompletableFuture listMultipartUploads( + ListMultipartUploadsArgs args) { + 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. + */ + public CompletableFuture listObjectsV1(ListObjectsV1Args args) { + 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. + */ + public CompletableFuture listObjectsV2(ListObjectsV2Args args) { + 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. + */ + public CompletableFuture listObjectVersions( + ListObjectVersionsArgs args) { + 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. + */ + public CompletableFuture listParts(ListPartsArgs args) { + 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. + */ + public CompletableFuture putObject(PutObjectAPIArgs args) { + checkArgs(args); + + Object[] result = null; + try { + result = createBody(args, args.contentType()); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + 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. + */ + public CompletableFuture uploadPart(UploadPartArgs args) { + checkArgs(args); + + Object[] result = null; + try { + result = createBody(args, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + 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. + */ + public CompletableFuture uploadPartCopy(UploadPartCopyArgs args) { + 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..2306ef94e 100644 --- a/api/src/main/java/io/minio/BucketArgs.java +++ b/api/src/main/java/io/minio/BucketArgs.java @@ -16,16 +16,38 @@ 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; -/** Base argument class holds bucket name and region. */ +/** + * Common arguments of {@link BucketExistsArgs}, {@link CreateBucketBaseArgs}, {@link + * DeleteBucketCorsArgs}, {@link DeleteBucketEncryptionArgs}, {@link DeleteBucketLifecycleArgs}, + * {@link DeleteBucketNotificationArgs}, {@link DeleteBucketPolicyArgs}, {@link + * DeleteBucketReplicationArgs}, {@link DeleteBucketTagsArgs}, {@link + * DeleteObjectLockConfigurationArgs}, {@link DeleteObjectsArgs}, {@link GetBucketCorsArgs}, {@link + * GetBucketEncryptionArgs}, {@link GetBucketLifecycleArgs}, {@link GetBucketLocationArgs}, {@link + * GetBucketNotificationArgs}, {@link GetBucketPolicyArgs}, {@link GetBucketReplicationArgs}, {@link + * GetBucketTagsArgs}, {@link GetBucketVersioningArgs}, {@link GetObjectLockConfigurationArgs}, + * {@link ListenBucketNotificationArgs}, {@link ListMultipartUploadsArgs}, {@link ListObjectsArgs}, + * {@link ListObjectsV1Args}, {@link ListObjectsV2Args}, {@link ListObjectVersionsArgs}, {@link + * ListPartsArgs}, {@link ObjectArgs}, {@link PutObjectFanOutArgs}, {@link RemoveBucketArgs}, {@link + * RemoveObjectsArgs}, {@link SetBucketCorsArgs}, {@link SetBucketEncryptionArgs}, {@link + * SetBucketLifecycleArgs}, {@link SetBucketNotificationArgs}, {@link SetBucketPolicyArgs}, {@link + * SetBucketReplicationArgs}, {@link SetBucketTagsArgs}, {@link SetBucketVersioningArgs} and {@link + * SetObjectLockConfigurationArgs}. + */ 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; } @@ -34,7 +56,7 @@ public String region() { return region; } - /** Base argument builder class for {@link BucketArgs}. */ + /** Builder of {@link BucketArgs}. */ public abstract static class Builder, A extends BucketArgs> extends BaseArgs.Builder { private static final Pattern BUCKET_NAME_REGEX = @@ -42,7 +64,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 +77,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 +89,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/BucketExistsArgs.java b/api/src/main/java/io/minio/BucketExistsArgs.java index 86b19eb32..2dd41a4ea 100644 --- a/api/src/main/java/io/minio/BucketExistsArgs.java +++ b/api/src/main/java/io/minio/BucketExistsArgs.java @@ -16,12 +16,12 @@ package io.minio; -/** Argument class of {@link MinioAsyncClient#bucketExists} and {@link MinioClient#bucketExists}. */ +/** Arguments of {@link MinioAsyncClient#bucketExists} and {@link MinioClient#bucketExists}. */ public class BucketExistsArgs extends BucketArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link BucketExistsArgs}. */ + /** Builder of {@link BucketExistsArgs}. */ public static final class Builder extends BucketArgs.Builder {} } 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..456ec9e60 --- /dev/null +++ b/api/src/main/java/io/minio/ByteBuffer.java @@ -0,0 +1,138 @@ +/* + * 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.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; + +/** {@link OutputStream} compatible byte buffer to store bytes to maximum 5GiB in size. */ +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 int CHUNK_SIZE = Integer.MAX_VALUE; + private final long size; + private final List buffers = new ArrayList<>(); + private int index = 0; + private long writtenBytes = 0; + private boolean isClosed = false; + + /** Creates ByteBuffer for given size. */ + public ByteBuffer(long size) { + if (size > ObjectWriteArgs.MAX_PART_SIZE) { + throw new IllegalArgumentException("Size cannot exceed 5GiB"); + } + this.size = size; + } + + 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()); + } + } + + /** Writes the specified byte to this buffer. */ + @Override + public void write(int b) throws IOException { + if (isClosed) throw new IOException("Stream is closed"); + if (writtenBytes >= size) throw new IOException("Exceeded total size limit"); + updateIndex(); + buffers.get(index).write(b); + writtenBytes++; + } + + /** Writes len bytes from the specified byte array starting at offset off to this buffer. */ + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (isClosed) throw new IOException("Stream is closed"); + if (len > (size - 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; + } + } + + /** Writes b.length bytes from the specified byte array to this buffer. */ + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + /** Returns the current length of bytes written to this buffer. */ + public long length() { + return writtenBytes; + } + + /** Returns the size of this buffer. */ + public long size() { + return size; + } + + /** Resets this buffer to freshen up for reuse. */ + public void reset() throws MinioException { + if (isClosed) throw new MinioException("Cannot reset a closed stream"); + writtenBytes = 0; + index = 0; + for (Buffer buffer : buffers) buffer.reset(); + } + + /** Closes this buffer and releases any system resources associated with the buffer. */ + public void close() throws IOException { + if (!isClosed) { + isClosed = true; + buffers.clear(); + } + } + + /** Returns this buffer as {@link InputStream}. */ + 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_BYTE_ARRAY); + 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..3c6aacd11 --- /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 io.minio.errors.MinioException; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +/** Pool of {@link ByteBuffer} to be used in parallel part uploads. */ +public class ByteBufferPool { + private final BlockingQueue pool; + long bufferSize; + + /** Creates given capacity pool of ByteBuffer with buffer size. */ + public ByteBufferPool(int capacity, long bufferSize) { + this.pool = new ArrayBlockingQueue<>(capacity); + this.bufferSize = bufferSize; + // Optionally pre-fill + for (int i = 0; i < capacity - 1; i++) { + if (!pool.offer(new ByteBuffer(bufferSize))) { + throw new RuntimeException("unable to allocate byte buffer; this should not happen"); + } + } + } + + /** + * Retrieves and removes the head of this pool, or returns new ByteBuffer if this pool is empty. + */ + public ByteBuffer take() { + ByteBuffer buffer = pool.poll(); // non-blocking + return (buffer != null) ? buffer : new ByteBuffer(bufferSize); + } + + /** Inserts the specified buffer into this pool, ignore if the pool is full. */ + public void put(ByteBuffer buffer) { + try { + buffer.reset(); + if (!pool.offer(buffer)) return; // ignore if pool is full + } catch (MinioException e) { + throw new RuntimeException(e); + } + } +} 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..75c048b96 --- /dev/null +++ b/api/src/main/java/io/minio/Checksum.java @@ -0,0 +1,533 @@ +/* + * 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.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; + +/** 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"; + + private Checksum() {} + + /** Encodes the specified bytes to Base64 string. */ + public static String base64String(byte[] sum) { + return Base64.getEncoder().encodeToString(sum); + } + + /** Decodes the specified base64 encoded string to bytes. */ + public static byte[] base64StringToSum(String sum) { + return Base64.getDecoder().decode(sum); + } + + /** Encodes the specified bytes to Base16 string. */ + public static String hexString(byte[] sum) { + StringBuilder builder = new StringBuilder(); + for (byte b : sum) builder.append(String.format("%02x", b)); + return builder.toString(); + } + + /** Decodes the specified Base16 encoded string to bytes. */ + 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; + } + + /** Creates hasher map for the specified algorithms. */ + 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; + } + + /** Updates each hasher using the specified array of bytes, ending at the specified length. */ + public static void update(Map hashers, byte[] data, int length) { + if (hashers == null || hashers.size() == 0) return; + for (Map.Entry entry : hashers.entrySet()) { + entry.getValue().update(data, 0, length); + } + } + + 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(); + } + + 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(hashers, buf16k, n); + } + } catch (IOException e) { + throw new MinioException(e); + } + } + } + + /** Updates each hasher using the specified file, ending at the specified size. */ + public static void update(Map hashers, RandomAccessFile file, long size) + throws MinioException { + update(hashers, null, file, size); + } + + /** Updates each hasher using the specified byte buffer. */ + public static void update(Map hashers, ByteBuffer buffer) + throws MinioException { + update(hashers, buffer, null, null); + } + + /** Makes checksum headers for given hashers. */ + public static Http.Headers makeHeaders( + Map hashers, boolean addContentSha256, boolean addSha256Checksum) { + 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 (addContentSha256) { + checksumHeaders.put(Http.Headers.X_AMZ_CONTENT_SHA256, Checksum.hexString(sum)); + } + if (!addSha256Checksum) 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; + + /** Converts this algorithm to string. */ + @Override + public String toString() { + return name().toLowerCase(Locale.US); + } + + /** Gets HTTP header key for this algorithm. */ + public String header() { + if (this == MD5) return Http.Headers.CONTENT_MD5; + return "x-amz-checksum-" + name().toLowerCase(Locale.US); + } + + /** Returns whether this algorithm supports full object checksum. */ + public boolean fullObjectSupport() { + return this == CRC32 || this == CRC32C || this == CRC64NVME; + } + + /** Returns whether this algorithm supports composite object checksum. */ + public boolean compositeSupport() { + return this == CRC32 || this == CRC32C || this == SHA1 || this == SHA256; + } + + /** Validates this algorithm for the specified type. */ + 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"); + } + } + + /** Gets hasher for this algorithm. */ + 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; + } + } + + /** Checksum hasher. */ + public static interface Hasher { + /** Updates len bytes from the specified byte array starting at offset off to this hasher. */ + public abstract void update(byte[] b, int off, int len); + + /** Completes the hash computation by performing final operations such as padding. */ + public abstract byte[] sum(); + + /** Resets the hasher for further use. */ + public abstract void reset(); + } + + /** CRC32 {@link Hasher}. */ + public static class CRC32 implements 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 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 {@link Hasher}. */ + public static class CRC32C implements java.util.zip.Checksum, Hasher { + 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 {@link Hasher} copied from https://github.com/minio/crc64nvme. */ + public static class CRC64NVME implements java.util.zip.Checksum, Hasher { + 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()) + "}"; + } + } + + /** SHA1 {@link Hasher}. */ + public static class SHA1 implements 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()) + "}"; + } + } + + /** SHA256 {@link Hasher}. */ + public static class SHA256 implements 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)); + } + } + + /** MD5 {@link Hasher}. */ + public static class MD5 implements 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..824ea42d1 --- /dev/null +++ b/api/src/main/java/io/minio/CompleteMultipartUploadArgs.java @@ -0,0 +1,90 @@ +/* + * 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; + +/** Arguments of {@link BaseS3Client#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(); + } + + /** 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..62a697903 100644 --- a/api/src/main/java/io/minio/ComposeObjectArgs.java +++ b/api/src/main/java/io/minio/ComposeObjectArgs.java @@ -16,36 +16,30 @@ 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}. + * Arguments of {@link MinioAsyncClient#composeObject(io.minio.ComposeObjectArgs)} 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 +48,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}. */ + /** 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 +69,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..d113ae80c 100644 --- a/api/src/main/java/io/minio/CopyObjectArgs.java +++ b/api/src/main/java/io/minio/CopyObjectArgs.java @@ -17,32 +17,31 @@ package io.minio; import java.util.Objects; -import okhttp3.HttpUrl; -/** Argument class of {@link MinioAsyncClient#copyObject} and {@link MinioClient#copyObject}. */ +/** + * Arguments of {@link BaseS3Client#copyObject}, {@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,31 +58,31 @@ 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}. */ + /** Builder of {@link CopyObjectArgs}. */ public static final class Builder extends ObjectWriteArgs.Builder { @Override protected void validate(CopyObjectArgs args) { super.validate(args); - validateNotNull(args.source, "copy source"); + Utils.validateNotNull(args.source, "source object"); if (args.source.offset() != null || args.source.length() != null) { - if (args.metadataDirective != null && args.metadataDirective == Directive.COPY) { + if (args.metadataDirective == Directive.COPY) { throw new IllegalArgumentException( "COPY metadata directive is not applicable to source object with range"); } - if (args.taggingDirective != null && args.taggingDirective == Directive.COPY) { + if (args.taggingDirective == Directive.COPY) { throw new IllegalArgumentException( "COPY tagging directive is not applicable to source object with range"); } } } - public Builder source(CopySource source) { - validateNotNull(source, "copy source"); + public Builder source(SourceObject source) { + Utils.validateNotNull(source, "source object"); operations.add(args -> 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/Version.java b/api/src/main/java/io/minio/CreateBucketArgs.java similarity index 57% rename from api/src/main/java/io/minio/messages/Version.java rename to api/src/main/java/io/minio/CreateBucketArgs.java index 65eb57d97..136cd5996 100644 --- a/api/src/main/java/io/minio/messages/Version.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,18 +14,21 @@ * limitations under the License. */ -package io.minio.messages; +package io.minio; -import org.simpleframework.xml.Root; +/** Arguments of {@link BaseS3Client#createBucket}. */ +public class CreateBucketArgs extends CreateBucketBaseArgs { + protected CreateBucketArgs() {} -/** 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 CreateBucketArgs(CreateBucketBaseArgs args) { + super(args); } - public Version(String prefix) { - super(prefix); + public static Builder builder() { + return new Builder(); } + + /** 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..bf4f7cb08 --- /dev/null +++ b/api/src/main/java/io/minio/CreateBucketBaseArgs.java @@ -0,0 +1,89 @@ +/* + * 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; + +/** Common arguments of {@link CreateBucketArgs} and {@link MakeBucketArgs}. */ +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..4c07885ec --- /dev/null +++ b/api/src/main/java/io/minio/CreateMultipartUploadArgs.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; + +import java.util.Objects; +import okhttp3.MediaType; + +/** Arguments of {@link BaseS3Client#createMultipartUpload}. */ +public class CreateMultipartUploadArgs extends ObjectArgs { + private Http.Headers headers; + + protected CreateMultipartUploadArgs() {} + + public CreateMultipartUploadArgs( + PutObjectBaseArgs args, MediaType contentType, Checksum.Algorithm algorithm) { + super(args); + this.headers = + args.makeHeaders( + contentType, + algorithm == null + ? null + : new Http.Headers("x-amz-checksum-algorithm", algorithm.toString())); + } + + public CreateMultipartUploadArgs(ComposeObjectArgs args) { + super(args); + this.headers = args.makeHeaders(); + } + + public Http.Headers headers() { + return headers; + } + + public static Builder builder() { + return new Builder(); + } + + /** 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/CreateMultipartUploadResponse.java b/api/src/main/java/io/minio/CreateMultipartUploadResponse.java index 977f1b6d4..117d0e701 100644 --- a/api/src/main/java/io/minio/CreateMultipartUploadResponse.java +++ b/api/src/main/java/io/minio/CreateMultipartUploadResponse.java @@ -19,7 +19,7 @@ import io.minio.messages.InitiateMultipartUploadResult; import okhttp3.Headers; -/** Response class of {@link S3Base#createMultipartUploadAsync}. */ +/** Response of {@link BaseS3Client#createMultipartUpload}. */ public class CreateMultipartUploadResponse extends GenericResponse { private InitiateMultipartUploadResult result; diff --git a/api/src/main/java/io/minio/DeleteBucketCorsArgs.java b/api/src/main/java/io/minio/DeleteBucketCorsArgs.java index b56679da8..fda545888 100644 --- a/api/src/main/java/io/minio/DeleteBucketCorsArgs.java +++ b/api/src/main/java/io/minio/DeleteBucketCorsArgs.java @@ -17,14 +17,13 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#deleteBucketCors} and {@link - * MinioClient#deleteBucketCors}. + * Arguments of {@link MinioAsyncClient#deleteBucketCors} and {@link MinioClient#deleteBucketCors}. */ public class DeleteBucketCorsArgs extends BucketArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DeleteBucketCorsArgs}. */ + /** Builder of {@link DeleteBucketCorsArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/DeleteBucketEncryptionArgs.java b/api/src/main/java/io/minio/DeleteBucketEncryptionArgs.java index 2109e2a75..fb31b1af9 100644 --- a/api/src/main/java/io/minio/DeleteBucketEncryptionArgs.java +++ b/api/src/main/java/io/minio/DeleteBucketEncryptionArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#deleteBucketEncryption} and {@link + * Arguments of {@link MinioAsyncClient#deleteBucketEncryption} and {@link * MinioClient#deleteBucketEncryption}. */ public class DeleteBucketEncryptionArgs extends BucketArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DeleteBucketEncryptionArgs}. */ + /** Builder of {@link DeleteBucketEncryptionArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/DeleteBucketLifecycleArgs.java b/api/src/main/java/io/minio/DeleteBucketLifecycleArgs.java index 3c790a8b6..141a0869d 100644 --- a/api/src/main/java/io/minio/DeleteBucketLifecycleArgs.java +++ b/api/src/main/java/io/minio/DeleteBucketLifecycleArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#deleteBucketLifecycle} and {@link + * Arguments of {@link MinioAsyncClient#deleteBucketLifecycle} and {@link * MinioClient#deleteBucketLifecycle}. */ public class DeleteBucketLifecycleArgs extends BucketArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DeleteBucketLifecycleArgs}. */ + /** Builder of {@link DeleteBucketLifecycleArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/DeleteBucketNotificationArgs.java b/api/src/main/java/io/minio/DeleteBucketNotificationArgs.java index 72a593d0a..e3f649f70 100644 --- a/api/src/main/java/io/minio/DeleteBucketNotificationArgs.java +++ b/api/src/main/java/io/minio/DeleteBucketNotificationArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#deleteBucketNotification} and {@link + * Arguments of {@link MinioAsyncClient#deleteBucketNotification} and {@link * MinioClient#deleteBucketNotification}. */ public class DeleteBucketNotificationArgs extends BucketArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DeleteBucketNotificationArgs}. */ + /** Builder of {@link DeleteBucketNotificationArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/DeleteBucketPolicyArgs.java b/api/src/main/java/io/minio/DeleteBucketPolicyArgs.java index 466811907..be189d51a 100644 --- a/api/src/main/java/io/minio/DeleteBucketPolicyArgs.java +++ b/api/src/main/java/io/minio/DeleteBucketPolicyArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#deleteBucketPolicy} and {@link + * Arguments of {@link MinioAsyncClient#deleteBucketPolicy} and {@link * MinioClient#deleteBucketPolicy}. */ public class DeleteBucketPolicyArgs extends BucketArgs { @@ -25,6 +25,6 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DeleteBucketPolicyArgs}. */ + /** Builder of {@link DeleteBucketPolicyArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/DeleteBucketReplicationArgs.java b/api/src/main/java/io/minio/DeleteBucketReplicationArgs.java index 1e0842dc2..ad489082d 100644 --- a/api/src/main/java/io/minio/DeleteBucketReplicationArgs.java +++ b/api/src/main/java/io/minio/DeleteBucketReplicationArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#deleteBucketReplication} and {@link + * Arguments of {@link MinioAsyncClient#deleteBucketReplication} and {@link * MinioClient#deleteBucketReplication}. */ public class DeleteBucketReplicationArgs extends BucketArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DeleteBucketReplicationArgs}. */ + /** Builder of {@link DeleteBucketReplicationArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/DeleteBucketTagsArgs.java b/api/src/main/java/io/minio/DeleteBucketTagsArgs.java index 0bff8b08c..91cbab136 100644 --- a/api/src/main/java/io/minio/DeleteBucketTagsArgs.java +++ b/api/src/main/java/io/minio/DeleteBucketTagsArgs.java @@ -17,14 +17,13 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#deleteBucketTags} and {@link - * MinioClient#deleteBucketTags}. + * Arguments of {@link MinioAsyncClient#deleteBucketTags} and {@link MinioClient#deleteBucketTags}. */ public class DeleteBucketTagsArgs extends BucketArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DeleteBucketTagsArgs}. */ + /** Builder of {@link DeleteBucketTagsArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/DeleteObjectLockConfigurationArgs.java b/api/src/main/java/io/minio/DeleteObjectLockConfigurationArgs.java index df12f8d65..bafebd5fc 100644 --- a/api/src/main/java/io/minio/DeleteObjectLockConfigurationArgs.java +++ b/api/src/main/java/io/minio/DeleteObjectLockConfigurationArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#deleteObjectLockConfiguration} and {@link + * Arguments of {@link MinioAsyncClient#deleteObjectLockConfiguration} and {@link * MinioClient#deleteObjectLockConfiguration}. */ public class DeleteObjectLockConfigurationArgs extends BucketArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DeleteObjectLockConfigurationArgs}. */ + /** Builder of {@link DeleteObjectLockConfigurationArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/DeleteObjectTagsArgs.java b/api/src/main/java/io/minio/DeleteObjectTagsArgs.java index daf65b613..13d9c5367 100644 --- a/api/src/main/java/io/minio/DeleteObjectTagsArgs.java +++ b/api/src/main/java/io/minio/DeleteObjectTagsArgs.java @@ -17,15 +17,14 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#deleteObjectTags} and {@link - * MinioClient#deleteObjectTags}. + * Arguments of {@link MinioAsyncClient#deleteObjectTags} and {@link MinioClient#deleteObjectTags}. */ public class DeleteObjectTagsArgs extends ObjectVersionArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DeleteObjectTagsArgs}. */ + /** Builder of {@link DeleteObjectTagsArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder {} } 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..5e59baa69 --- /dev/null +++ b/api/src/main/java/io/minio/DeleteObjectsArgs.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 io.minio.messages.DeleteRequest; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** Arguments of {@link BaseS3Client#deleteObjects}. */ +public class DeleteObjectsArgs extends BucketArgs { + private boolean quiet; + private boolean bypassGovernanceMode; + private List objects = new ArrayList<>(); + + public boolean quiet() { + return quiet; + } + + public boolean bypassGovernanceMode() { + return bypassGovernanceMode; + } + + public List objects() { + return objects; + } + + public static Builder builder() { + return new Builder(); + } + + /** 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/DeleteObjectsResponse.java b/api/src/main/java/io/minio/DeleteObjectsResponse.java index 745e27068..7a1ade135 100644 --- a/api/src/main/java/io/minio/DeleteObjectsResponse.java +++ b/api/src/main/java/io/minio/DeleteObjectsResponse.java @@ -19,7 +19,7 @@ import io.minio.messages.DeleteResult; import okhttp3.Headers; -/** Response class of {@link S3Base#deleteObjectsAsync}. */ +/** Response of {@link MinioAsyncClient#deleteObjects}. */ public class DeleteObjectsResponse extends GenericResponse { private DeleteResult result; 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/DisableObjectLegalHoldArgs.java b/api/src/main/java/io/minio/DisableObjectLegalHoldArgs.java index 9cae46502..6d3aae02d 100644 --- a/api/src/main/java/io/minio/DisableObjectLegalHoldArgs.java +++ b/api/src/main/java/io/minio/DisableObjectLegalHoldArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#disableObjectLegalHold} and {@link + * Arguments of {@link MinioAsyncClient#disableObjectLegalHold} and {@link * MinioClient#disableObjectLegalHold}. */ public class DisableObjectLegalHoldArgs extends ObjectVersionArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link DisableObjectLegalHoldArgs}. */ + /** Builder of {@link DisableObjectLegalHoldArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder {} } diff --git a/api/src/main/java/io/minio/DownloadObjectArgs.java b/api/src/main/java/io/minio/DownloadObjectArgs.java index eb9fa6daf..e4e96ebec 100644 --- a/api/src/main/java/io/minio/DownloadObjectArgs.java +++ b/api/src/main/java/io/minio/DownloadObjectArgs.java @@ -19,7 +19,8 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#downloadObject} and {@link MinioClient#downloadObject}. + * Arguments of {@link MinioAsyncClient#downloadObject(io.minio.DownloadObjectArgs)} and {@link + * MinioClient#downloadObject}. */ public class DownloadObjectArgs extends ObjectReadArgs { private String filename; @@ -37,10 +38,10 @@ public static Builder builder() { return new Builder(); } - /** Argument class of {@link DownloadObjectArgs}. */ + /** Arguments 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/EnableObjectLegalHoldArgs.java b/api/src/main/java/io/minio/EnableObjectLegalHoldArgs.java index 58807f45b..afeb50ef6 100644 --- a/api/src/main/java/io/minio/EnableObjectLegalHoldArgs.java +++ b/api/src/main/java/io/minio/EnableObjectLegalHoldArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#enableObjectLegalHold} and {@link + * Arguments of {@link MinioAsyncClient#enableObjectLegalHold} and {@link * MinioClient#enableObjectLegalHold}. */ public class EnableObjectLegalHoldArgs extends ObjectVersionArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link EnableObjectLegalHoldArgs}. */ + /** Builder of {@link EnableObjectLegalHoldArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GenericResponse.java b/api/src/main/java/io/minio/GenericResponse.java index ac330f656..16585c6fc 100644 --- a/api/src/main/java/io/minio/GenericResponse.java +++ b/api/src/main/java/io/minio/GenericResponse.java @@ -18,7 +18,7 @@ import okhttp3.Headers; -/** Generic response class of any APIs. */ +/** Generic response of any APIs. */ public class GenericResponse { private Headers headers; private String bucket; diff --git a/api/src/main/java/io/minio/GenericUploadResponse.java b/api/src/main/java/io/minio/GenericUploadResponse.java index c8b012138..d62c1d83d 100644 --- a/api/src/main/java/io/minio/GenericUploadResponse.java +++ b/api/src/main/java/io/minio/GenericUploadResponse.java @@ -20,7 +20,7 @@ import io.minio.messages.CopyObjectResult; import okhttp3.Headers; -/** Response class of any APIs doing object/part upload. */ +/** Common response of {@link ObjectWriteResponse} and {@link PutObjectFanOutResponse}. */ public class GenericUploadResponse extends GenericResponse { private String etag; private String checksumCRC32; diff --git a/api/src/main/java/io/minio/GetBucketCorsArgs.java b/api/src/main/java/io/minio/GetBucketCorsArgs.java index 477310038..df9043342 100644 --- a/api/src/main/java/io/minio/GetBucketCorsArgs.java +++ b/api/src/main/java/io/minio/GetBucketCorsArgs.java @@ -16,14 +16,12 @@ package io.minio; -/** - * Argument class of {@link MinioAsyncClient#getBucketCors} and {@link MinioClient#getBucketCors}. - */ +/** Arguments of {@link MinioAsyncClient#getBucketCors} and {@link MinioClient#getBucketCors}. */ public class GetBucketCorsArgs extends BucketArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetBucketCorsArgs}. */ + /** Builder of {@link GetBucketCorsArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetBucketEncryptionArgs.java b/api/src/main/java/io/minio/GetBucketEncryptionArgs.java index fe21b565f..c7e0d6ab6 100644 --- a/api/src/main/java/io/minio/GetBucketEncryptionArgs.java +++ b/api/src/main/java/io/minio/GetBucketEncryptionArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#getBucketEncryption} and {@link + * Arguments of {@link MinioAsyncClient#getBucketEncryption} and {@link * MinioClient#getBucketEncryption}. */ public class GetBucketEncryptionArgs extends BucketArgs { @@ -25,6 +25,6 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetBucketEncryptionArgs}. */ + /** Builder of {@link GetBucketEncryptionArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetBucketLifecycleArgs.java b/api/src/main/java/io/minio/GetBucketLifecycleArgs.java index d4c7585c4..0fc457d47 100644 --- a/api/src/main/java/io/minio/GetBucketLifecycleArgs.java +++ b/api/src/main/java/io/minio/GetBucketLifecycleArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#getBucketLifecycle} and {@link + * Arguments of {@link MinioAsyncClient#getBucketLifecycle} and {@link * MinioClient#getBucketLifecycle}. */ public class GetBucketLifecycleArgs extends BucketArgs { @@ -25,6 +25,6 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetBucketLifecycleArgs}. */ + /** Builder of {@link GetBucketLifecycleArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/messages/DeleteMarker.java b/api/src/main/java/io/minio/GetBucketLocationArgs.java similarity index 63% rename from api/src/main/java/io/minio/messages/DeleteMarker.java rename to api/src/main/java/io/minio/GetBucketLocationArgs.java index 0e95b74ed..4a61a0795 100644 --- a/api/src/main/java/io/minio/messages/DeleteMarker.java +++ b/api/src/main/java/io/minio/GetBucketLocationArgs.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,18 +14,14 @@ * limitations under the License. */ -package io.minio.messages; +package io.minio; -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(); +/** Arguments of {@link BaseS3Client#getBucketLocation}. */ +public class GetBucketLocationArgs extends BucketArgs { + public static Builder builder() { + return new Builder(); } - public DeleteMarker(String prefix) { - super(prefix); - } + /** Builder of {@link GetBucketLocationArgs}. */ + public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetBucketNotificationArgs.java b/api/src/main/java/io/minio/GetBucketNotificationArgs.java index 48502dabb..4471d8ba0 100644 --- a/api/src/main/java/io/minio/GetBucketNotificationArgs.java +++ b/api/src/main/java/io/minio/GetBucketNotificationArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#getBucketNotification} and {@link + * Arguments of {@link MinioAsyncClient#getBucketNotification} and {@link * MinioClient#getBucketNotification}. */ public class GetBucketNotificationArgs extends BucketArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetBucketNotificationArgs}. */ + /** Builder of {@link GetBucketNotificationArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetBucketPolicyArgs.java b/api/src/main/java/io/minio/GetBucketPolicyArgs.java index a896ee8fe..294ac04c2 100644 --- a/api/src/main/java/io/minio/GetBucketPolicyArgs.java +++ b/api/src/main/java/io/minio/GetBucketPolicyArgs.java @@ -17,14 +17,13 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#getBucketPolicy} and {@link - * MinioClient#getBucketPolicy}. + * Arguments of {@link MinioAsyncClient#getBucketPolicy} and {@link MinioClient#getBucketPolicy}. */ public class GetBucketPolicyArgs extends BucketArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetBucketPolicyArgs}. */ + /** Builder of {@link GetBucketPolicyArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetBucketReplicationArgs.java b/api/src/main/java/io/minio/GetBucketReplicationArgs.java index 6e1dc4335..9fd5ff179 100644 --- a/api/src/main/java/io/minio/GetBucketReplicationArgs.java +++ b/api/src/main/java/io/minio/GetBucketReplicationArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#getBucketReplication} and {@link + * Arguments of {@link MinioAsyncClient#getBucketReplication} and {@link * MinioClient#getBucketReplication}. */ public class GetBucketReplicationArgs extends BucketArgs { @@ -25,6 +25,6 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetBucketReplicationArgs}. */ + /** Builder of {@link GetBucketReplicationArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetBucketTagsArgs.java b/api/src/main/java/io/minio/GetBucketTagsArgs.java index 8e4c104d3..fa891330e 100644 --- a/api/src/main/java/io/minio/GetBucketTagsArgs.java +++ b/api/src/main/java/io/minio/GetBucketTagsArgs.java @@ -16,14 +16,12 @@ package io.minio; -/** - * Argument class of {@link MinioAsyncClient#getBucketTags} and {@link MinioClient#getBucketTags}. - */ +/** Arguments of {@link MinioAsyncClient#getBucketTags} and {@link MinioClient#getBucketTags}. */ public class GetBucketTagsArgs extends BucketArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetBucketTagsArgs}. */ + /** Builder of {@link GetBucketTagsArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetBucketVersioningArgs.java b/api/src/main/java/io/minio/GetBucketVersioningArgs.java index 72c436fdb..1a67d805d 100644 --- a/api/src/main/java/io/minio/GetBucketVersioningArgs.java +++ b/api/src/main/java/io/minio/GetBucketVersioningArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#getBucketVersioning} and {@link + * Arguments of {@link MinioAsyncClient#getBucketVersioning} and {@link * MinioClient#getBucketVersioning}. */ public class GetBucketVersioningArgs extends BucketArgs { @@ -25,6 +25,6 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetBucketVersioningArgs}. */ + /** Builder of {@link GetBucketVersioningArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetObjectAclArgs.java b/api/src/main/java/io/minio/GetObjectAclArgs.java index 840b25fc0..c0e56568a 100644 --- a/api/src/main/java/io/minio/GetObjectAclArgs.java +++ b/api/src/main/java/io/minio/GetObjectAclArgs.java @@ -16,12 +16,12 @@ package io.minio; -/** Argument class of {@link MinioAsyncClient#getObjectAcl} and {@link MinioClient#getObjectAcl}. */ +/** Arguments of {@link MinioAsyncClient#getObjectAcl} and {@link MinioClient#getObjectAcl}. */ public class GetObjectAclArgs extends ObjectVersionArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetObjectAclArgs}. */ + /** Builder of {@link GetObjectAclArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetObjectArgs.java b/api/src/main/java/io/minio/GetObjectArgs.java index 709d2c255..eecf41ca5 100644 --- a/api/src/main/java/io/minio/GetObjectArgs.java +++ b/api/src/main/java/io/minio/GetObjectArgs.java @@ -16,7 +16,7 @@ package io.minio; -/** Argument class of {@link MinioAsyncClient#getObject} and {@link MinioClient#getObject}. */ +/** Arguments of {@link MinioAsyncClient#getObject} and {@link MinioClient#getObject}. */ public class GetObjectArgs extends ObjectConditionalReadArgs { protected GetObjectArgs() {} @@ -34,7 +34,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetObjectArgs}. */ + /** Builder of {@link GetObjectArgs}. */ public static final class Builder extends ObjectConditionalReadArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetObjectAttributesArgs.java b/api/src/main/java/io/minio/GetObjectAttributesArgs.java index 19bd9bbba..b414bc64a 100644 --- a/api/src/main/java/io/minio/GetObjectAttributesArgs.java +++ b/api/src/main/java/io/minio/GetObjectAttributesArgs.java @@ -21,7 +21,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#getObjectAttributes} and {@link + * Arguments of {@link MinioAsyncClient#getObjectAttributes} and {@link * MinioClient#getObjectAttributes}. */ public class GetObjectAttributesArgs extends ObjectReadArgs { @@ -45,13 +45,13 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetObjectAttributesArgs}. */ + /** Builder of {@link GetObjectAttributesArgs}. */ public static final class Builder extends ObjectReadArgs.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/GetObjectAttributesResponse.java b/api/src/main/java/io/minio/GetObjectAttributesResponse.java index bbb4e95e6..ce98157d4 100644 --- a/api/src/main/java/io/minio/GetObjectAttributesResponse.java +++ b/api/src/main/java/io/minio/GetObjectAttributesResponse.java @@ -20,7 +20,7 @@ import okhttp3.Headers; /** - * Response class of {@link MinioAsyncClient#getObjectAttributes} and {@link + * Response of {@link MinioAsyncClient#getObjectAttributes} and {@link * MinioClient#getObjectAttributes}. */ public class GetObjectAttributesResponse extends GenericResponse { diff --git a/api/src/main/java/io/minio/GetObjectLockConfigurationArgs.java b/api/src/main/java/io/minio/GetObjectLockConfigurationArgs.java index 481c219a1..626a5eb64 100644 --- a/api/src/main/java/io/minio/GetObjectLockConfigurationArgs.java +++ b/api/src/main/java/io/minio/GetObjectLockConfigurationArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#getObjectLockConfiguration} and {@link + * Arguments of {@link MinioAsyncClient#getObjectLockConfiguration} and {@link * MinioClient#getObjectLockConfiguration}. */ public class GetObjectLockConfigurationArgs extends BucketArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetObjectLockConfigurationArgs}. */ + /** Builder of {@link GetObjectLockConfigurationArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetObjectResponse.java b/api/src/main/java/io/minio/GetObjectResponse.java index 23f6fc385..4d4b60033 100644 --- a/api/src/main/java/io/minio/GetObjectResponse.java +++ b/api/src/main/java/io/minio/GetObjectResponse.java @@ -21,9 +21,9 @@ import okhttp3.Headers; /** - * Response class of {@link MinioAsyncClient#getObject} and {@link MinioClient#getObject}. This - * class is {@link InputStream} interface compatible and it must be closed after use to release - * underneath network resources. + * Response of {@link MinioAsyncClient#getObject} and {@link MinioClient#getObject}. As it is {@link + * InputStream} interface compatible, it must be closed after use to release underneath network + * resources. */ public class GetObjectResponse extends FilterInputStream { private GenericResponse response; diff --git a/api/src/main/java/io/minio/GetObjectRetentionArgs.java b/api/src/main/java/io/minio/GetObjectRetentionArgs.java index 2bdd12af5..aef88774d 100644 --- a/api/src/main/java/io/minio/GetObjectRetentionArgs.java +++ b/api/src/main/java/io/minio/GetObjectRetentionArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#getObjectRetention} and {@link + * Arguments of {@link MinioAsyncClient#getObjectRetention} and {@link * MinioClient#getObjectRetention}. */ public class GetObjectRetentionArgs extends ObjectVersionArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetObjectRetentionArgs}. */ + /** Builder of {@link GetObjectRetentionArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetObjectTagsArgs.java b/api/src/main/java/io/minio/GetObjectTagsArgs.java index f71ec408d..4356d47f4 100644 --- a/api/src/main/java/io/minio/GetObjectTagsArgs.java +++ b/api/src/main/java/io/minio/GetObjectTagsArgs.java @@ -16,14 +16,12 @@ package io.minio; -/** - * Argument class of {@link MinioAsyncClient#getObjectTags} and {@link MinioClient#getObjectTags}. - */ +/** Arguments of {@link MinioAsyncClient#getObjectTags} and {@link MinioClient#getObjectTags}. */ public class GetObjectTagsArgs extends ObjectVersionArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetObjectTagsArgs}. */ + /** Builder of {@link GetObjectTagsArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder {} } diff --git a/api/src/main/java/io/minio/GetPresignedObjectUrlArgs.java b/api/src/main/java/io/minio/GetPresignedObjectUrlArgs.java index a6e3d2a23..ea221ea37 100644 --- a/api/src/main/java/io/minio/GetPresignedObjectUrlArgs.java +++ b/api/src/main/java/io/minio/GetPresignedObjectUrlArgs.java @@ -16,22 +16,21 @@ package io.minio; -import io.minio.http.Method; import java.util.Objects; import java.util.concurrent.TimeUnit; /** - * Argument class of {@link MinioAsyncClient#getPresignedObjectUrl} and {@link + * Arguments of {@link MinioAsyncClient#getPresignedObjectUrl} and {@link * MinioClient#getPresignedObjectUrl}. */ 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; } @@ -43,11 +42,11 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link GetPresignedObjectUrlArgs}. */ + /** 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 51% rename from api/src/main/java/io/minio/ServerSideEncryptionS3.java rename to api/src/main/java/io/minio/HeadObjectArgs.java index 4a2615ee7..703ade17b 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,30 @@ package io.minio; -import java.util.HashMap; -import java.util.Map; +/** Arguments of {@link BaseS3Client#headObject}. */ +public class HeadObjectArgs extends HeadObjectBaseArgs { + protected HeadObjectArgs() {} -/** S3 type of Server-side encryption. */ -public class ServerSideEncryptionS3 extends ServerSideEncryption { - private static final Map headers; + public HeadObjectArgs(SourceObject args) { + super(args); + } - static { - Map map = new HashMap<>(); - map.put("X-Amz-Server-Side-Encryption", "AES256"); - headers = Utils.unmodifiableMap(map); + public HeadObjectArgs(AppendObjectArgs 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(); } + + /** Builder of {@link HeadObjectArgs}. */ + public static final class Builder extends HeadObjectBaseArgs.Builder {} } diff --git a/api/src/main/java/io/minio/HeadObjectBaseArgs.java b/api/src/main/java/io/minio/HeadObjectBaseArgs.java new file mode 100644 index 000000000..69f20af53 --- /dev/null +++ b/api/src/main/java/io/minio/HeadObjectBaseArgs.java @@ -0,0 +1,42 @@ +/* + * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2025 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.minio; + +/** Common arguments of {@link HeadObjectArgs} and {@link StatObjectArgs}. */ +public abstract class HeadObjectBaseArgs extends ObjectConditionalReadArgs { + protected HeadObjectBaseArgs() {} + + protected HeadObjectBaseArgs(SourceObject args) { + super(args); + } + + protected HeadObjectBaseArgs(AppendObjectArgs args) { + super(args); + } + + protected HeadObjectBaseArgs(DownloadObjectArgs args) { + super(args); + } + + protected HeadObjectBaseArgs(HeadObjectBaseArgs args) { + super(args); + } + + /** Builder of {@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..c34bcd71b --- /dev/null +++ b/api/src/main/java/io/minio/HeadObjectResponse.java @@ -0,0 +1,157 @@ +/* + * 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.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** Response of {@link BaseS3Client#headObject}. */ +public class HeadObjectResponse extends GenericResponse { + private String etag; + private long size; + private ZonedDateTime lastModified; + private RetentionMode lockMode; + private ZonedDateTime lockRetainUntilDate; + private LegalHold lockLegalHold; + private boolean deleteMarker; + private Http.Headers userMetadata; + private Checksum.Type checksumType; + + 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.lockMode = (value != null ? RetentionMode.valueOf(value) : null); + + value = headers.get("x-amz-object-lock-retain-until-date"); + this.lockRetainUntilDate = + value == null ? null : Time.S3Time.fromString(value).toZonedDateTime(); + + this.lockLegalHold = 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.put( + lowerName.substring("x-amz-meta-".length(), lowerName.length()), entry.getValue()); + } + } + this.userMetadata = userMetadata; + + value = headers.get("x-amz-checksum-type"); + this.checksumType = value == null ? null : Checksum.Type.valueOf(value); + } + + public String etag() { + return etag; + } + + public long size() { + return size; + } + + public ZonedDateTime lastModified() { + return lastModified; + } + + public RetentionMode lockMode() { + return lockMode; + } + + public ZonedDateTime lockRetainUntilDate() { + return lockRetainUntilDate; + } + + public LegalHold lockLegalHold() { + return lockLegalHold; + } + + 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; + } + + public Checksum.Type checksumType() { + return checksumType; + } + + public List algorithms() { + okhttp3.Headers headers = headers(); + List algorithms = new ArrayList<>(); + String value; + + value = headers.get("x-amz-checksum-crc32"); + if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.CRC32); + + value = headers.get("x-amz-checksum-crc32c"); + if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.CRC32C); + + value = headers.get("x-amz-checksum-crc64nvme"); + if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.CRC64NVME); + + value = headers.get("x-amz-checksum-sha1"); + if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.SHA1); + + value = headers.get("x-amz-checksum-sha256"); + if (value != null && !value.isEmpty()) algorithms.add(Checksum.Algorithm.SHA256); + + return algorithms.size() == 0 ? null : algorithms; + } + + @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..5fa9bec24 --- /dev/null +++ b/api/src/main/java/io/minio/Http.java @@ -0,0 +1,1685 @@ +/* + * 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.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +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.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 java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.net.ssl.HostnameVerifier; +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 Body EMPTY_BODY = + new Body( + Utils.EMPTY_BYTE_ARRAY, + 0, + DEFAULT_MEDIA_TYPE, + Checksum.ZERO_SHA256_HASH, + Checksum.ZERO_MD5_HASH); + + /** Base URL of S3 endpoint. */ + public static class BaseUrl { + private okhttp3.HttpUrl url; + private String awsS3Prefix; + private String awsDomainSuffix; + private boolean awsDualstack; + private String region; + private boolean useVirtualStyle; + + /** Creates BaseUrl to the specified endpoint. */ + public BaseUrl(String endpoint) { + setUrl(parse(endpoint)); + } + + /** Creates BaseUrl to the specified endpoint, port and secure flag. */ + 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); + } + + /** Creates BaseUrl to the specified url. */ + public BaseUrl(okhttp3.HttpUrl url) { + Utils.validateNotNull(url, "url"); + Utils.validateUrl(url); + setUrl(url); + } + + /** Creates BaseUrl to the specified 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; + } + + /** Checks this base url is HTTPS scheme or not. */ + public boolean isHttps() { + return url.isHttps(); + } + + /** Gets AWS S3 prefix. */ + public String awsS3Prefix() { + return awsS3Prefix; + } + + /** Gets AWS domain suffix. */ + public String awsDomainSuffix() { + return awsDomainSuffix; + } + + /** Gets region if present in this base url. */ + public String region() { + return region; + } + + /** Sets region to this base url. */ + 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; + } + + /** Builds 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(); + } + } + + /** Gets media type of the specified string value. */ + 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 X509TrustManager createCompositeTrustManager( + List trustManagers) { + return new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + for (X509TrustManager tm : trustManagers) { + try { + tm.checkClientTrusted(chain, authType); + return; + } catch (CertificateException ignored) { + } + } + throw new CertificateException( + "None of the TrustManagers trust this client certificate chain"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + for (X509TrustManager tm : trustManagers) { + try { + tm.checkServerTrusted(chain, authType); + return; + } catch (CertificateException ignored) { + } + } + throw new CertificateException( + "None of the TrustManagers trust this server certificate chain"); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return trustManagers.stream() + .flatMap(tm -> Arrays.stream(tm.getAcceptedIssuers())) + .toArray(X509Certificate[]::new); + } + }; + } + + private static X509TrustManager buildTrustManagerFromKeyStore(KeyStore ks) + throws KeyStoreException, NoSuchAlgorithmException { + TrustManagerFactory factory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init(ks); + for (TrustManager tm : factory.getTrustManagers()) { + if (tm instanceof X509TrustManager) { + return (X509TrustManager) tm; + } + } + return null; + } + + private static int setCertificateEntry( + CertificateFactory cf, KeyStore ks, Path file, String namePrefix) + throws CertificateException, IOException, KeyStoreException { + try (InputStream in = Files.newInputStream(file)) { + int index = 0; + while (in.available() > 0) { + X509Certificate cert = (X509Certificate) cf.generateCertificate(in); + ks.setCertificateEntry(namePrefix + (index++), cert); + } + return index; + } + } + + private static X509TrustManager getTrustManagerFromFile(String filePath) + throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(null); + if (setCertificateEntry(cf, ks, Paths.get(filePath), "cert-file-") == 0) return null; + return buildTrustManagerFromKeyStore(ks); + } + + private static X509TrustManager getTrustManagerFromDir(String dirPath) + throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(null); + + int index = 0; + try (Stream paths = Files.walk(Paths.get(dirPath))) { + int number = 1; + for (Path file : (Iterable) paths.filter(Files::isRegularFile)::iterator) { + try { + index += setCertificateEntry(cf, ks, file, "cert-dir-file-" + number + "-"); + number++; + } catch (CertificateException | IOException | KeyStoreException e) { + // Ignore these errors. + } + } + } + + if (index == 0) return null; + + return buildTrustManagerFromKeyStore(ks); + } + + private static X509TrustManager getDefaultTrustManager() + throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException { + TrustManagerFactory factory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init((KeyStore) null); + for (TrustManager tm : factory.getTrustManagers()) { + if (tm instanceof X509TrustManager) return (X509TrustManager) tm; + } + return null; + } + + private static X509TrustManager getCompositeTrustManager(String filePath, String dirPath) + throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException { + List trustManagers = new ArrayList<>(); + + X509TrustManager defaultTm = getDefaultTrustManager(); + if (defaultTm != null) trustManagers.add(defaultTm); + + if (dirPath != null && !dirPath.isEmpty()) { + X509TrustManager dirTm = getTrustManagerFromDir(dirPath); + if (dirTm != null) trustManagers.add(dirTm); + } + + if (filePath != null && !filePath.isEmpty()) { + X509TrustManager fileTm = getTrustManagerFromFile(filePath); + if (fileTm != null) trustManagers.add(fileTm); + } + + if (trustManagers.isEmpty()) return null; + + return createCompositeTrustManager(trustManagers); + } + + 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 (InputStream trustInput = Files.newInputStream(Paths.get(trustStorePath)); + InputStream keyInput = Files.newInputStream(Paths.get(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); + } + } + + /** Enables JKS formatted TLS certificates to the specified HTTP client. */ + public static OkHttpClient enableJKSCertificates( + OkHttpClient httpClient, + String trustStorePath, + String trustStorePassword, + String keyStorePath, + String keyStorePassword) + throws MinioException { + return enableJKSPKCS12Certificates( + httpClient, trustStorePath, trustStorePassword, keyStorePath, keyStorePassword, "JKS"); + } + + /** Enables PKCS12 formatted TLS certificates to the specified HTTP client. */ + public static OkHttpClient enablePKCS12Certificates( + OkHttpClient httpClient, + String trustStorePath, + String trustStorePassword, + String keyStorePath, + String keyStorePassword) + throws MinioException { + return enableJKSPKCS12Certificates( + httpClient, trustStorePath, trustStorePassword, keyStorePath, keyStorePassword, "PKCS12"); + } + + /** Enable external TLS certificates from given file path and all valid files from dir path. */ + public static OkHttpClient enableExternalCertificates( + OkHttpClient client, String filePath, String dirPath) throws MinioException { + try { + X509TrustManager tm = getCompositeTrustManager(filePath, dirPath); + if (tm == null) return client; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[] {tm}, new SecureRandom()); + return client.newBuilder().sslSocketFactory(sslContext.getSocketFactory(), tm).build(); + } catch (CertificateException + | IOException + | KeyManagementException + | KeyStoreException + | NoSuchAlgorithmException e) { + throw new MinioException(e); + } + } + + /** + * Enables external TLS certificates from SSL_CERT_FILE and SSL_CERT_DIR environment variables if + * present. + */ + public static OkHttpClient enableExternalCertificatesFromEnv(OkHttpClient client) + throws MinioException { + return enableExternalCertificates( + client, System.getenv("SSL_CERT_FILE"), System.getenv("SSL_CERT_DIR")); + } + + /** + * Creates new HTTP client with default timeout with additional TLS certificates from + * SSL_CERT_FILE and SSL_CERT_DIR environment variables if present. + */ + public static OkHttpClient newDefaultClient() { + OkHttpClient client = + 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(); + try { + return enableExternalCertificatesFromEnv(client); + } catch (MinioException e) { + throw new RuntimeException(e); + } + } + + /** + * Disables TLS certificate check as a special case for self-signed certificate and testing to the + * specified HTTP client. + */ + @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); + } + } + + /** Sets connect, write and read timeout in milliseconds to the specified HTTP client. */ + 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(); + } + + /** HTTP body of {@link RandomAccessFile}, {@link ByteBuffer} or {@link byte} array. */ + 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; + + /** Creates Body for okhttp3 RequestBody. */ + public Body(okhttp3.RequestBody requestBody) { + this.requestBody = requestBody; + this.contentType = requestBody.contentType(); + } + + /** Creates Body for RandomAccessFile. */ + 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); + } + + /** Creates Body for byte array. */ + 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); + } + + /** Creates Body for ByteBuffer, string or XML encodable object. */ + 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; + } + + /** Gets content type of this body. */ + public MediaType contentType() { + return contentType; + } + + /** Gets SHA256 hash of this body. */ + public String sha256Hash() { + return sha256Hash; + } + + /** Gets SHA256 hash of this body. */ + public String md5Hash() { + return md5Hash; + } + + /** Checks whether this body is okhttp3 RequestBody. */ + public boolean isHttpRequestBody() { + return requestBody != null; + } + + /** Creates headers for this body. */ + 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; + } + + /** Creates HTTP RequestBody for this body. */ + 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) : "<<>>"; + } + } + + /** HTTP request body of {@link RandomAccessFile}, {@link ByteBuffer} or byte array. */ + 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; + + /** Creates RequestBody for byte array. */ + 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"); + } + + /** Creates RequestBody for RandomAccessFile. */ + 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); + } + } + + /** Creates RequestBody for ByteBuffer. */ + 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"); + } + + /** Creates RequestBody for okhttp3 RequestBody. */ + 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); + } + } + + /** Gets content type. */ + @Override + public MediaType contentType() { + return contentType; + } + + /** Gets content length. */ + @Override + public long contentLength() { + return length; + } + + /** Writes data to the specified sink. */ + @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; + } + + /** HTTP headers. */ + 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(); + } + + /** Creates empty headers. */ + public Headers() {} + + /** Creates headers as copy of the specified source. */ + public Headers(Headers source) { + if (source != null) putAll(source); + } + + /** Creates headers as copy of the specified source. */ + public Headers(Map source) { + if (source != null) putAll(source); + } + + /** Creates headers as copy of the specified source. */ + public Headers(Multimap source) { + if (source != null) putAll(source); + } + + /** Creates headers by alternating names and values. */ + 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]); + } + } + + /** Creates new headers by merging names and values from the specified headers list. */ + 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"); + } + + int index; + if ((index = value.indexOf('\r')) >= 0) { + throw new IllegalArgumentException("Unexpected char 0x0d at " + index + " in header value"); + } + if ((index = value.indexOf('\n')) >= 0) { + throw new IllegalArgumentException("Unexpected char 0x0a at " + index + " in header value"); + } + + return value; + } + + /** + * Adds the specified value to the name in this headers. If the name already exists, the value + * is appended uniquely. + */ + public void add(@Nonnull String name, @Nonnull String value) { + name = validateName(name); + value = validateValue(name, value); + headers.computeIfAbsent(name, k -> new HashSet<>()).add(value); + } + + /** Sets the specified name and value in this headers. */ + public void set(@Nonnull String name, @Nonnull String value) { + name = validateName(name); + value = validateValue(name, value); + headers.put(name, new HashSet<>(Collections.singletonList(value))); + } + + /** Gets the first value of the specified name in this headers. */ + public String getFirst(String name) { + if (name == null) return null; + Set values = get(name); + return values.isEmpty() ? null : values.iterator().next(); + } + + /** Checks whether the specified name exists in this headers. */ + public boolean contains(String name) { + if (name == null) return false; + return headers.containsKey(normalize(name)); + } + + /** Checks whether any name in this headers starts with the specified prefix or not. */ + public boolean namePrefixAny(String prefix) { + if (prefix == null) return false; + final String finalPrefix = normalize(prefix); + return names().stream().anyMatch(name -> name.startsWith(finalPrefix)); + } + + /** Gets all names in this headers. */ + public Set names() { + return Collections.unmodifiableSet(headers.keySet()); + } + + /** Gets iterator of this headers. */ + @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); + } + + /** Gets new okhttp3.Headers by populating this 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(); + } + + /** Clears all names and values in this headers. */ + public void clear() { + headers.clear(); + } + + /** Checks whether the specified name exists in this headers. */ + public boolean containsKey(String name) { + return contains(name); + } + + /** Gets Set of Map.Entry of this headers. */ + 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; + } + + /** Gets Set of Map.Entry of this headers. */ + public Set> entries() { + return entrySet(); + } + + /** Gets set of values of the specified name in this headers. */ + public Set get(String name) { + if (name == null) name = ""; + return headers.getOrDefault(normalize(name), Collections.emptySet()); + } + + /** Gets set of values of the specified name in this headers. */ + public Set keySet() { + return names(); + } + + /** + * Adds the specified value to the name in this headers. If the name already exists, the value + * is appended uniquely. + */ + public boolean put(@Nonnull String name, @Nonnull String value) { + add(name, value); + return true; + } + + /** Adds set of values for the specified name to this headers. */ + public boolean put(@Nonnull String name, @Nonnull Set values) { + Utils.validateNotNull(values, "value"); + for (String value : values) put(name, value); + return true; + } + + /** Adds list of values for the specified name to this headers. */ + public boolean put(@Nonnull String name, @Nonnull List values) { + Utils.validateNotNull(values, "value"); + for (String value : values) put(name, value); + return true; + } + + /** Adds all names and values from the specified source to this headers. */ + public Headers putAll(Map source) { + if (source == null) return this; + for (Map.Entry entry : source.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** Adds all names and values from the specified source to this headers. */ + public Headers putAll(Multimap source) { + if (source == null) return this; + for (Map.Entry entry : source.entries()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** Adds all names and values from the specified source to this headers. */ + public Headers putAll(Headers source) { + if (source == null) return this; + for (Map.Entry entry : source.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** Removes the specified name from this headers. */ + public Set remove(String name) { + if (name == null) name = ""; + return headers.remove(normalize(name)); + } + + /** Removes the specified value of the specified name from this headers. */ + public boolean remove(String name, String value) { + if (name == null) return false; + name = normalize(name); + boolean result = headers.containsKey(name) ? headers.get(name).remove(value) : false; + if (result && headers.get(name).size() == 0) headers.remove(name); + return result; + } + + /** Removes the specified name from this headers. */ + public boolean removeAll(String name) { + return remove(name) != null; + } + + /** Gets size of this headers. */ + public int size() { + return headers.size(); + } + } + + /** HTTP query parameters. */ + public static class QueryParameters implements Iterable> { + private static final long serialVersionUID = 5193347714796984439L; + + private final Map> parameters = new HashMap<>(); + + /** Creates empty query parameters. */ + public QueryParameters() {} + + /** Creates query parameters as copy of the specified source. */ + public QueryParameters(QueryParameters source) { + if (source != null) putAll(source); + } + + /** Creates query parameters as copy of the specified source. */ + public QueryParameters(Map source) { + if (source != null) putAll(source); + } + + /** Creates query parameters as copy of the specified source. */ + public QueryParameters(Multimap source) { + if (source != null) putAll(source); + } + + /** Creates query parameters by alternating keys and values. */ + 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]); + } + } + + /** + * Creates new query parameters by merging keys and values from the specified query parameters + * list. + */ + public static QueryParameters merge(QueryParameters... queryParamsList) { + QueryParameters queryParams = new QueryParameters(); + for (QueryParameters q : queryParamsList) queryParams.putAll(q); + return queryParams; + } + + /** Adds the specified value to the key in this query parameters. */ + 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); + } + + /** Sets the specified key and value in this query parameters. */ + 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))); + } + + /** Gets the first value of the specified key in this query parameters. */ + public String getFirst(String key) { + if (key == null) return null; + List values = get(key); + return values.isEmpty() ? null : values.get(0); + } + + /** Checks whether the specified key exists in this query parameters. */ + public boolean contains(String key) { + if (key == null) return false; + return parameters.containsKey(key); + } + + /** Checks whether any key in this query parameters starts with the specified prefix or not. */ + public boolean keyPrefixAny(String prefix) { + if (prefix == null) return false; + return keys().stream().anyMatch(key -> key.startsWith(prefix)); + } + + /** Gets all keys in this query parameters. */ + public Set keys() { + return Collections.unmodifiableSet(parameters.keySet()); + } + + /** Gets iterator of this query parameters. */ + @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); + } + + /** Clears all keys and values in this query parameters. */ + public void clear() { + parameters.clear(); + } + + /** Checks whether the specified key exists in this query parameters. */ + public boolean containsKey(String key) { + if (key == null) return false; + return parameters.containsKey(key); + } + + /** Gets Set of Map.Entry of this query parameters. */ + 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; + } + + /** Gets Set of Map.Entry of this query parameters. */ + public Set> entries() { + return entrySet(); + } + + /** Gets set of values of the specified key in this query parameters. */ + public List get(String key) { + if (key == null) key = ""; + return parameters.getOrDefault(key, Collections.emptyList()); + } + + /** Gets set of values of the specified key in this query parameters. */ + public Set keySet() { + return keys(); + } + + /** Adds the specified value to the key in this query parameters. */ + public boolean put(@Nonnull String key, @Nonnull String value) { + add(key, value); + return true; + } + + /** Adds list of values for the specified key to this query parameters. */ + public QueryParameters put(@Nonnull String key, @Nonnull List values) { + Utils.validateNotNull(values, "value"); + for (String value : values) put(key, value); + return this; + } + + /** Adds all keys and values from the specified source to this query parameters. */ + public QueryParameters putAll(Map source) { + if (source == null) return this; + for (Map.Entry entry : source.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** Adds all keys and values from the specified source to this query parameters. */ + public QueryParameters putAll(Multimap source) { + if (source == null) return this; + for (Map.Entry entry : source.entries()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** Adds all keys and values from the specified source to this query parameters. */ + public QueryParameters putAll(QueryParameters source) { + if (source == null) return this; + for (Map.Entry entry : source.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** Removes the specified key from this query parameters. */ + public List remove(String key) { + if (key == null) key = ""; + return parameters.remove(key); + } + + /** Removes the specified value of the specified key from this query parameters. */ + public boolean remove(String key, String value) { + if (key == null) return false; + return parameters.containsKey(key) ? parameters.get(key).remove(value) : false; + } + + /** Removes the specified key from this query parameters. */ + public boolean removeAll(String key) { + return remove(key) != null; + } + + /** Gets size of this query parameters. */ + public int size() { + return parameters.size(); + } + } + + /** HTTP request. */ + public static class Request { + private okhttp3.Request httpRequest; + private String httpTraces; + + /** Creates request with specified HTTP request and HTTP trace. */ + public Request(okhttp3.Request httpRequest, String httpTraces) { + this.httpRequest = httpRequest; + this.httpTraces = httpTraces; + } + + /** Gets HTTP request. */ + public okhttp3.Request httpRequest() { + return httpRequest; + } + + /** Gets HTTP trace. */ + public String httpTraces() { + return httpTraces; + } + } + + /** S3 request. */ + 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( + Utils.EMPTY_BYTE_ARRAY, + 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*")); + String lastTwoChars = traceBuilder.substring(traceBuilder.length() - 2); + if (lastTwoChars.charAt(1) != '\n') { + traceBuilder.append("\n\n"); + } else if (lastTwoChars.charAt(0) != '\n') { + traceBuilder.append("\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(); + } + + /** Builder of {@link S3Request}. */ + 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/IsObjectLegalHoldEnabledArgs.java b/api/src/main/java/io/minio/IsObjectLegalHoldEnabledArgs.java index 4b2a1adfd..6a435ef1f 100644 --- a/api/src/main/java/io/minio/IsObjectLegalHoldEnabledArgs.java +++ b/api/src/main/java/io/minio/IsObjectLegalHoldEnabledArgs.java @@ -17,7 +17,7 @@ package io.minio; /** - * Argument class of {@link MinioAsyncClient#isObjectLegalHoldEnabled} and {@link + * Arguments of {@link MinioAsyncClient#isObjectLegalHoldEnabled} and {@link * MinioClient#isObjectLegalHoldEnabled}. */ public class IsObjectLegalHoldEnabledArgs extends ObjectVersionArgs { @@ -25,7 +25,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link IsObjectLegalHoldEnabledArgs}. */ + /** Builder of {@link IsObjectLegalHoldEnabledArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder {} } diff --git a/api/src/main/java/io/minio/ListBucketsArgs.java b/api/src/main/java/io/minio/ListBucketsArgs.java index 539e4192f..a3fc9ad5b 100644 --- a/api/src/main/java/io/minio/ListBucketsArgs.java +++ b/api/src/main/java/io/minio/ListBucketsArgs.java @@ -18,7 +18,10 @@ import java.util.Objects; -/** Argument class of {@link MinioAsyncClient#listBuckets} and {@link MinioClient#listBuckets}. */ +/** + * Arguments of {@link BaseS3Client#listBucketsAPI}, {@link MinioAsyncClient#listBuckets} and {@link + * MinioClient#listBuckets}. + */ public class ListBucketsArgs extends BaseArgs { private String bucketRegion; private int maxBuckets = 10000; @@ -45,13 +48,13 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link ListBucketsArgs}. */ + /** Builder of {@link ListBucketsArgs}. */ public static final class Builder extends BaseArgs.Builder { @Override protected void validate(ListBucketsArgs args) {} public Builder bucketRegion(String region) { - validateNullOrNotEmptyString(region, "bucket region"); + Utils.validateNullOrNotEmptyString(region, "bucket region"); operations.add(args -> args.bucketRegion = region); return this; } @@ -66,13 +69,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/ListBucketsResponse.java b/api/src/main/java/io/minio/ListBucketsResponse.java index 1914a01a0..99ebd063b 100644 --- a/api/src/main/java/io/minio/ListBucketsResponse.java +++ b/api/src/main/java/io/minio/ListBucketsResponse.java @@ -19,7 +19,7 @@ import io.minio.messages.ListAllMyBucketsResult; import okhttp3.Headers; -/** Response class of {@link S3Base#listBucketsAsync}. */ +/** Response of {@link BaseS3Client#listBucketsAPI}. */ public class ListBucketsResponse extends GenericResponse { private ListAllMyBucketsResult result; 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..a059080a1 --- /dev/null +++ b/api/src/main/java/io/minio/ListMultipartUploadsArgs.java @@ -0,0 +1,114 @@ +/* + * 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; + +/** Arguments of {@link BaseS3Client#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(); + } + + /** 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/ListMultipartUploadsResponse.java b/api/src/main/java/io/minio/ListMultipartUploadsResponse.java index 1fa6d6830..531bbb21e 100644 --- a/api/src/main/java/io/minio/ListMultipartUploadsResponse.java +++ b/api/src/main/java/io/minio/ListMultipartUploadsResponse.java @@ -19,7 +19,7 @@ import io.minio.messages.ListMultipartUploadsResult; import okhttp3.Headers; -/** Response class of {@link S3Base#listMultipartUploadsAsync}. */ +/** Response of {@link BaseS3Client#listMultipartUploads}. */ public class ListMultipartUploadsResponse extends GenericResponse { private ListMultipartUploadsResult result; 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..3309a01d0 --- /dev/null +++ b/api/src/main/java/io/minio/ListObjectVersionsArgs.java @@ -0,0 +1,129 @@ +/* + * 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; + +/** Arguments of {@link BaseS3Client#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(); + } + + /** 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/ListObjectVersionsResponse.java b/api/src/main/java/io/minio/ListObjectVersionsResponse.java index 399d1b513..6aea62745 100644 --- a/api/src/main/java/io/minio/ListObjectVersionsResponse.java +++ b/api/src/main/java/io/minio/ListObjectVersionsResponse.java @@ -19,7 +19,7 @@ import io.minio.messages.ListVersionsResult; import okhttp3.Headers; -/** Response class of {@link S3Base#listObjectVersionsAsync}. */ +/** Response of {@link BaseS3Client#listObjectVersions}. */ public class ListObjectVersionsResponse extends GenericResponse { private ListVersionsResult result; diff --git a/api/src/main/java/io/minio/ListObjectsArgs.java b/api/src/main/java/io/minio/ListObjectsArgs.java index 40231b6c3..7f31fea81 100644 --- a/api/src/main/java/io/minio/ListObjectsArgs.java +++ b/api/src/main/java/io/minio/ListObjectsArgs.java @@ -18,7 +18,7 @@ import java.util.Objects; -/** Argument class of {@link MinioAsyncClient#listObjects} and {@link MinioClient#listObjects}. */ +/** Arguments of {@link MinioAsyncClient#listObjects} and {@link MinioClient#listObjects}. */ public class ListObjectsArgs extends BucketArgs { private String delimiter = ""; private boolean useUrlEncodingType = true; @@ -97,7 +97,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link ListObjectsArgs}. */ + /** Builder of {@link ListObjectsArgs}. */ public static final class Builder extends BucketArgs.Builder { @Override protected void validate(ListObjectsArgs args) { @@ -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..9e086a787 --- /dev/null +++ b/api/src/main/java/io/minio/ListObjectsV1Args.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.util.Objects; + +/** Arguments of {@link BaseS3Client#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(); + } + + /** 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/ListObjectsV1Response.java b/api/src/main/java/io/minio/ListObjectsV1Response.java index 201523ffa..34faf5d6b 100644 --- a/api/src/main/java/io/minio/ListObjectsV1Response.java +++ b/api/src/main/java/io/minio/ListObjectsV1Response.java @@ -19,7 +19,7 @@ import io.minio.messages.ListBucketResultV1; import okhttp3.Headers; -/** Response class of {@link S3Base#listObjectsV1Async}. */ +/** Response of {@link BaseS3Client#listObjectsV1}. */ public class ListObjectsV1Response extends GenericResponse { private ListBucketResultV1 result; 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..9806ce7ee --- /dev/null +++ b/api/src/main/java/io/minio/ListObjectsV2Args.java @@ -0,0 +1,161 @@ +/* + * 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; + +/** Arguments of {@link BaseS3Client#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(); + } + + /** 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/ListObjectsV2Response.java b/api/src/main/java/io/minio/ListObjectsV2Response.java index 4a0ca0f47..01f50f86a 100644 --- a/api/src/main/java/io/minio/ListObjectsV2Response.java +++ b/api/src/main/java/io/minio/ListObjectsV2Response.java @@ -19,7 +19,7 @@ import io.minio.messages.ListBucketResultV2; import okhttp3.Headers; -/** Response class of {@link S3Base#listObjectsV2Async}. */ +/** Response of {@link BaseS3Client#listObjectsV2}. */ public class ListObjectsV2Response extends GenericResponse { private ListBucketResultV2 result; 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..dff97394d --- /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; + +/** Arguments of {@link BaseS3Client#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(); + } + + /** 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/ListPartsResponse.java b/api/src/main/java/io/minio/ListPartsResponse.java index 8b45dfea5..165619ba3 100644 --- a/api/src/main/java/io/minio/ListPartsResponse.java +++ b/api/src/main/java/io/minio/ListPartsResponse.java @@ -19,7 +19,7 @@ import io.minio.messages.ListPartsResult; import okhttp3.Headers; -/** Response class of {@link S3Base#listPartsAsync}. */ +/** Response of {@link BaseS3Client#listParts}. */ public class ListPartsResponse extends GenericResponse { private ListPartsResult result; diff --git a/api/src/main/java/io/minio/ListenBucketNotificationArgs.java b/api/src/main/java/io/minio/ListenBucketNotificationArgs.java index a1dc359bc..a56e86a3f 100644 --- a/api/src/main/java/io/minio/ListenBucketNotificationArgs.java +++ b/api/src/main/java/io/minio/ListenBucketNotificationArgs.java @@ -20,7 +20,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#listenBucketNotification} and {@link + * Arguments of {@link MinioAsyncClient#listenBucketNotification} and {@link * MinioClient#listenBucketNotification}. */ public class ListenBucketNotificationArgs extends BucketArgs { @@ -44,11 +44,11 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link ListenBucketNotificationArgs}. */ + /** Builder of {@link ListenBucketNotificationArgs}. */ 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) { @@ -89,6 +89,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), prefix, suffix, events); + return Objects.hash(super.hashCode(), prefix, suffix, Arrays.hashCode(events)); } } diff --git a/api/src/main/java/io/minio/MakeBucketArgs.java b/api/src/main/java/io/minio/MakeBucketArgs.java index 81b9e0ac3..b96478b24 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; - } - +/** Arguments of {@link MinioAsyncClient#makeBucket} and {@link MinioClient#makeBucket}. */ +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); - } + /** Builder of {@link MakeBucketArgs}. */ + 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..b02effdd0 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,25 +72,31 @@ 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.Collections; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Scanner; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; -import java.util.regex.Matcher; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; 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 +147,102 @@ * .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); + } + } + + /** Creates new {@link MinioAsyncClient.Builder}. */ + 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 +289,11 @@ 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. * @see StatObjectResponse */ - public CompletableFuture statObject(StatObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return super.statObjectAsync(args); + public CompletableFuture statObject(StatObjectArgs args) { + return headObject(new HeadObjectArgs(args)) + .thenApply(response -> new StatObjectResponse(response)); } /** @@ -236,23 +312,17 @@ 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. * @see GetObjectResponse */ - public CompletableFuture getObject(GetObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture getObject(GetObjectArgs args) { 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 +337,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 +363,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,31 +390,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. */ - public CompletableFuture downloadObject(DownloadObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture downloadObject(DownloadObjectArgs args) { 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) { - throw new CompletionException(e); + } catch (MinioException e) { + return Utils.failedFuture(e); } }) .thenAccept(nullValue -> {}); @@ -355,7 +423,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 +436,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 +449,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 +463,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 +477,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 +491,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 +505,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 +516,230 @@ 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. */ - public CompletableFuture copyObject(CopyObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture copyObject(CopyObjectArgs args) { 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(); + if (size < ObjectWriteArgs.MAX_PART_SIZE + && copyArgs.source().offset() == null + && copyArgs.source().length() == null) { + return super.copyObject(copyArgs); + } + + 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"); } } - 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) { + if (copyArgs.taggingDirective() == Directive.COPY) { + throw new IllegalArgumentException( + "COPY tagging directive is not applicable to source object size greater than" + + " 5 GiB"); + } + + return composeObject(new ComposeObjectArgs(copyArgs)); + }); + } + + /** Calculates part count for given list of {@link SourceObject}. */ + protected CompletableFuture calculatePartCount(List sources) { + long[] objectSize = {0}; + + 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); + } + + long size = source.length() != null ? source.length() : source.objectSize(); + size -= source.offset() != null ? source.offset() : 0; + if (size < ObjectWriteArgs.MIN_MULTIPART_SIZE && interimPart) { throw new IllegalArgumentException( - "COPY metadata directive is not applicable to source object size greater than" - + " 5 GiB"); + "compose source " + + source.bucket() + + "/" + + source.object() + + ": size " + + size + + " must be greater than " + + ObjectWriteArgs.MIN_MULTIPART_SIZE); } - if (args.taggingDirective() != null && args.taggingDirective() == Directive.COPY) { + + objectSize[0] += size; + if (objectSize[0] > ObjectWriteArgs.MAX_OBJECT_SIZE) { throw new IllegalArgumentException( - "COPY tagging directive is not applicable to source object size greater than" - + " 5 GiB"); + "destination object size must be less than " + + ObjectWriteArgs.MAX_OBJECT_SIZE); } - try { - return composeObject(new ComposeObjectArgs(args)); - } catch (InsufficientDataException - | InternalException - | InvalidKeyException - | IOException - | NoSuchAlgorithmException - | XmlParserException e) { - throw new CompletionException(e); + 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++; } - } - return CompletableFuture.completedFuture(null); - }) - .thenCompose( - objectWriteResponse -> { - if (objectWriteResponse != null) { - return CompletableFuture.completedFuture(objectWriteResponse); - } - Multimap headers = args.genHeaders(); + 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; + } - if (args.metadataDirective() != null) { - headers.put("x-amz-metadata-directive", args.metadataDirective().name()); - } + private CompletableFuture uploadParts( + ComposeObjectArgs args, int partCount, String uploadId) { + Http.Headers ssecHeaders = + (args.sse() != null && args.sse() instanceof ServerSideEncryption.CustomerKey) + ? args.sse().headers() + : null; - if (args.taggingDirective() != null) { - headers.put("x-amz-tagging-directive", args.taggingDirective().name()); - } + int partNumber = 0; + CompletableFuture future = 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(); + Http.Headers sourceHeaders = null; + try { + sourceHeaders = source.headers(); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + final Http.Headers headers = Http.Headers.merge(sourceHeaders, 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; + } - headers.putAll(args.source().genCopyHeaders()); + 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; + } + } - 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) { - throw new CompletionException(e); - } - }); + return future; } - 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; + private CompletableFuture composeObject( + ComposeObjectArgs args, int partCount) { + String[] uploadId = {null}; + return createMultipartUpload(new CreateMultipartUploadArgs(args)) + .thenCompose( + response -> { + uploadId[0] = response.result().uploadId(); + return uploadParts(args, partCount, uploadId[0]); + }) + .thenCompose( + parts -> + completeMultipartUpload(new CompleteMultipartUploadArgs(args, uploadId[0], parts))) + .exceptionally( + e -> { + e = e.getCause(); + if (uploadId[0] != null) { + try { + abortMultipartUpload(new AbortMultipartUploadArgs(args, uploadId[0])).join(); + } catch (CompletionException ex) { + e.addSuppressed(ex.getCause()); + } + } + throw new CompletionException(e); }); } @@ -582,14 +747,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 +790,20 @@ 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.
    */
-  public CompletableFuture composeObject(ComposeObjectArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture composeObject(ComposeObjectArgs args) {
     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 {
-                  return copyObject(new CopyObjectArgs(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);
+            partCount -> {
+              if (partCount == 1
+                  && args.sources().get(0).offset() == null
+                  && args.sources().get(0).length() == null) {
+                return copyObject(new CopyObjectArgs(args));
               }
-
-              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;
+              return composeObject(args, partCount);
             });
   }
 
@@ -877,7 +816,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 +831,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 +844,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 +854,36 @@ 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();
-    } catch (InterruptedException e) {
-      throw new RuntimeException(e);
-    } catch (ExecutionException e) {
-      throwEncapsulatedException(e);
+      region = getRegion(args.bucket(), args.region()).join();
+    } catch (CompletionException e) {
+      throwMinioException(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 +933,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,11 +944,9 @@ public Map getPresignedPostFormData(PostPolicy policy)
 
     String region = null;
     try {
-      region = getRegionAsync(policy.bucket(), null).get();
-    } catch (InterruptedException e) {
-      throw new RuntimeException(e);
-    } catch (ExecutionException e) {
-      throwEncapsulatedException(e);
+      region = getRegion(policy.bucket(), null).join();
+    } catch (CompletionException e) {
+      throwMinioException(e);
     }
     return policy.formData(provider.fetch(), region);
   }
@@ -1073,26 +979,17 @@ 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.
    */
-  public CompletableFuture removeObject(RemoveObjectArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture removeObject(RemoveObjectArgs args) {
     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());
   }
 
@@ -1101,39 +998,40 @@ public CompletableFuture removeObject(RemoveObjectArgs args)
    * removal.
    *
    * 
Example:{@code
-   * List objects = new LinkedList<>();
+   * List objects = new ArrayList<>();
    * 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 +1045,7 @@ private synchronized void populate() { } try { - List objectList = new LinkedList<>(); + List objectList = new ArrayList<>(); while (objectIter.hasNext() && objectList.size() < 1000) { objectList.add(objectIter.next()); } @@ -1157,34 +1055,26 @@ private synchronized void populate() { DeleteObjectsResponse response = null; try { response = - deleteObjectsAsync( - args.bucket(), - args.region(), - objectList, - true, - args.bypassGovernanceMode(), - args.extraHeaders(), - args.extraQueryParams()) - .get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); - } - if (!response.result().errorList().isEmpty()) { - errorIterator = response.result().errorList().iterator(); + deleteObjects( + DeleteObjectsArgs.builder() + .extraHeaders(args.extraHeaders()) + .extraQueryParams(args.extraQueryParams()) + .bucket(args.bucket()) + .region(args.region()) + .objects(objectList) + .quiet(true) + .bypassGovernanceMode(args.bypassGovernanceMode()) + .build()) + .join(); + } catch (CompletionException e) { + throwMinioException(e); + } + 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 +1095,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 +1141,16 @@ 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. */ - public CompletableFuture restoreObject(RestoreObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture restoreObject(RestoreObjectArgs args) { checkArgs(args); - return executePostAsync(args, null, newMultimap("restore", ""), args.request()) + Http.Body body = null; + try { + body = new Http.Body(args.request(), null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return executePostAsync(args, null, new Http.QueryParameters("restore", ""), body) .thenAccept(response -> response.close()); } @@ -1307,39 +1195,31 @@ 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. */ - public CompletableFuture> listBuckets() - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - return listBucketsAsync(null, null, null, null, null, null) + public CompletableFuture> listBuckets() { + return listBucketsAPI(ListBucketsArgs.builder().build()) .thenApply( response -> { return response.result().buckets(); @@ -1350,58 +1230,52 @@ 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 = Collections.emptyIterator(); try { ListBucketsResponse response = - listBucketsAsync( - args.bucketRegion(), - args.maxBuckets(), - args.prefix(), - (result == null) - ? args.continuationToken() - : result.continuationToken(), - args.extraHeaders(), - args.extraQueryParams()) - .get(); + 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()) + .join(); this.result = response.result(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throwEncapsulatedException(e); + } catch (CompletionException e) { + throwMinioException(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 +1304,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 +1322,7 @@ public Result next() { return this.error; } - Bucket item = null; + ListAllMyBucketsResult.Bucket item = null; if (this.iterator.hasNext()) { item = this.iterator.next(); } @@ -1480,35 +1354,18 @@ 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. */ - public CompletableFuture bucketExists(BucketExistsArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture bucketExists(BucketExistsArgs args) { return executeHeadAsync(args, null, null) .exceptionally( e -> { - Throwable ex = e.getCause(); - - if (ex instanceof CompletionException) { - ex = ((CompletionException) ex).getCause(); - } - - if (ex instanceof ExecutionException) { - ex = ((ExecutionException) ex).getCause(); - } - - if (ex instanceof ErrorResponseException) { - if (((ErrorResponseException) ex).errorResponse().code().equals(NO_SUCH_BUCKET)) { + e = e.getCause(); + if (e instanceof ErrorResponseException) { + if (((ErrorResponseException) e).errorResponse().code().equals(NO_SUCH_BUCKET)) { return null; } } - throw new CompletionException(ex); + throw new CompletionException(e); }) .thenApply( response -> { @@ -1547,52 +1404,11 @@ 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. */ - public CompletableFuture makeBucket(MakeBucketArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture makeBucket(MakeBucketArgs args) { 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 +1421,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. */ - public CompletableFuture setBucketVersioning(SetBucketVersioningArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture setBucketVersioning(SetBucketVersioningArgs args) { checkArgs(args); - return executePutAsync(args, null, newMultimap("versioning", ""), args.config(), 0) + Http.Body body = null; + try { + body = new Http.Body(args.config(), null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return executePutAsync(args, null, new Http.QueryParameters("versioning", ""), body) .thenAccept(response -> response.close()); } @@ -1631,19 +1445,11 @@ 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. */ public CompletableFuture getBucketVersioning( - GetBucketVersioningArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + GetBucketVersioningArgs args) { checkArgs(args); - return executeGetAsync(args, null, newMultimap("versioning", "")) + return executeGetAsync(args, null, new Http.QueryParameters("versioning", "")) .thenApply( response -> { try { @@ -1668,18 +1474,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. */ - public CompletableFuture setObjectLockConfiguration(SetObjectLockConfigurationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture setObjectLockConfiguration(SetObjectLockConfigurationArgs args) { checkArgs(args); - return executePutAsync(args, null, newMultimap("object-lock", ""), args.config(), 0) + Http.Body body = null; + try { + body = new Http.Body(args.config(), null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return executePutAsync(args, null, new Http.QueryParameters("object-lock", ""), body) .thenAccept(response -> response.close()); } @@ -1693,20 +1497,17 @@ 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. */ public CompletableFuture deleteObjectLockConfiguration( - DeleteObjectLockConfigurationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + DeleteObjectLockConfigurationArgs args) { checkArgs(args); - return executePutAsync( - args, null, newMultimap("object-lock", ""), new ObjectLockConfiguration(), 0) + Http.Body body = null; + try { + body = new Http.Body(new ObjectLockConfiguration(), null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return executePutAsync(args, null, new Http.QueryParameters("object-lock", ""), body) .thenAccept(response -> response.close()); } @@ -1721,19 +1522,11 @@ 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. */ public CompletableFuture getObjectLockConfiguration( - GetObjectLockConfigurationArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + GetObjectLockConfigurationArgs args) { checkArgs(args); - return executeGetAsync(args, null, newMultimap("object-lock", "")) + return executeGetAsync(args, null, new Http.QueryParameters("object-lock", "")) .thenApply( response -> { try { @@ -1763,27 +1556,24 @@ 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. */ - public CompletableFuture setObjectRetention(SetObjectRetentionArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture setObjectRetention(SetObjectRetentionArgs args) { checkArgs(args); - Multimap queryParams = newMultimap("retention", ""); + Http.QueryParameters queryParams = new Http.QueryParameters("retention", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); + Http.Body body = null; + try { + body = new Http.Body(args.config(), null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } 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) + body) .thenAccept(response -> response.close()); } @@ -1801,41 +1591,24 @@ 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. */ - public CompletableFuture getObjectRetention(GetObjectRetentionArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture getObjectRetention(GetObjectRetentionArgs args) { 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( e -> { - Throwable ex = e.getCause(); - - if (ex instanceof CompletionException) { - ex = ((CompletionException) ex).getCause(); - } - - if (ex instanceof ExecutionException) { - ex = ((ExecutionException) ex).getCause(); - } - - if (ex instanceof ErrorResponseException) { - if (((ErrorResponseException) ex) + e = e.getCause(); + if (e instanceof ErrorResponseException) { + if (((ErrorResponseException) e) .errorResponse() .code() .equals(NO_SUCH_OBJECT_LOCK_CONFIGURATION)) { return null; } } - throw new CompletionException(ex); + throw new CompletionException(e); }) .thenApply( response -> { @@ -1864,21 +1637,18 @@ 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. */ - public CompletableFuture enableObjectLegalHold(EnableObjectLegalHoldArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture enableObjectLegalHold(EnableObjectLegalHoldArgs args) { 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) - .thenAccept(response -> response.close()); + Http.Body body = null; + try { + body = new Http.Body(new LegalHold(true), null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return executePutAsync(args, null, queryParams, body).thenAccept(response -> response.close()); } /** @@ -1895,21 +1665,18 @@ 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. */ - public CompletableFuture disableObjectLegalHold(DisableObjectLegalHoldArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture disableObjectLegalHold(DisableObjectLegalHoldArgs args) { 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) - .thenAccept(response -> response.close()); + Http.Body body = null; + try { + body = new Http.Body(new LegalHold(false), null, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return executePutAsync(args, null, queryParams, body).thenAccept(response -> response.close()); } /** @@ -1927,41 +1694,24 @@ 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. */ - public CompletableFuture isObjectLegalHoldEnabled(IsObjectLegalHoldEnabledArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture isObjectLegalHoldEnabled(IsObjectLegalHoldEnabledArgs args) { 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( e -> { - Throwable ex = e.getCause(); - - if (ex instanceof CompletionException) { - ex = ((CompletionException) ex).getCause(); - } - - if (ex instanceof ExecutionException) { - ex = ((ExecutionException) ex).getCause(); - } - - if (ex instanceof ErrorResponseException) { - if (((ErrorResponseException) ex) + e = e.getCause(); + if (e instanceof ErrorResponseException) { + if (((ErrorResponseException) e) .errorResponse() .code() .equals(NO_SUCH_OBJECT_LOCK_CONFIGURATION)) { return null; } } - throw new CompletionException(ex); + throw new CompletionException(e); }) .thenApply( response -> { @@ -1987,21 +1737,319 @@ 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. */ - public CompletableFuture removeBucket(RemoveBucketArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture removeBucket(RemoveBucketArgs args) { checkArgs(args); return executeDeleteAsync(args, null, null) .thenAccept(response -> regionCache.remove(args.bucket())); } + private CompletableFuture> uploadPartsSequentially( + PutObjectBaseArgs args, + String uploadId, + PartReader partReader, + boolean addContentSha256, + boolean addSha256Checksum, + ByteBuffer buffer, + long partSize, + List responses) { + return supplyAsync( + () -> + new UploadPartArgs( + args, + uploadId, + partReader.partNumber(), + buffer, + Checksum.makeHeaders( + partReader.hashers(), addContentSha256, addSha256Checksum))) + .thenCompose(partArgs -> uploadPart(partArgs)) + .thenCompose( + response -> { + responses.add(response); + try { + buffer.reset(); + if (partReader.partNumber() == partReader.partCount()) { + return CompletableFuture.completedFuture(responses); + } + partReader.read(buffer); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return uploadPartsSequentially( + args, + uploadId, + partReader, + addContentSha256, + addSha256Checksum, + buffer, + partSize, + responses); + }); + } + + private CompletableFuture> uploadPartsParallelly( + PutObjectBaseArgs args, + String uploadId, + PartReader partReader, + boolean addContentSha256, + boolean addSha256Checksum, + ByteBuffer buffer, + long partSize, + int parallelUploads) { + ByteBufferPool bufferPool = new ByteBufferPool(parallelUploads, partSize); + ExecutorService uploadExecutor = Executors.newFixedThreadPool(parallelUploads); + BlockingQueue queue = new ArrayBlockingQueue<>(parallelUploads); + CountDownLatch doneLatch = new CountDownLatch(parallelUploads); + List uploadResults = Collections.synchronizedList(new ArrayList<>()); + AtomicBoolean errorOccurred = new AtomicBoolean(false); + ConcurrentLinkedQueue exceptions = new ConcurrentLinkedQueue<>(); + + return supplyAsync( + () -> { + try { + // Start uploader workers + for (int i = 0; i < parallelUploads; i++) { + Future result = + uploadExecutor.submit( + () -> { + try { + while (!errorOccurred.get()) { + UploadPartArgs.Wrapper part = queue.take(); + if (part.args() == null) break; // poison pill + UploadPartResponse response = uploadPart(part.args()).join(); + bufferPool.put(part.args().buffer()); + uploadResults.add(response); + } + } catch (InterruptedException e) { + errorOccurred.set(true); // signal to all threads + exceptions.add(e); + } finally { + doneLatch.countDown(); + } + }); + if (result == null) { + throw new RuntimeException( + "uploadExecutor.submit() returns null; this should not happen"); + } + } + + // Reader: submit initial buffer + queue.put( + new UploadPartArgs.Wrapper( + new UploadPartArgs( + args, + uploadId, + partReader.partNumber(), + buffer, + Checksum.makeHeaders( + partReader.hashers(), addContentSha256, addSha256Checksum)))); + + // Reader: loop to submit remaining parts + while (partReader.partNumber() != partReader.partCount() && !errorOccurred.get()) { + ByteBuffer buf = bufferPool.take(); + partReader.read(buf); + queue.put( + new UploadPartArgs.Wrapper( + new UploadPartArgs( + args, + uploadId, + partReader.partNumber(), + buf, + Checksum.makeHeaders( + partReader.hashers(), addContentSha256, addSha256Checksum)))); + } + + // Signal all workers to stop with poison pills + for (int i = 0; i < parallelUploads; i++) { + queue.put(new UploadPartArgs.Wrapper(null)); + } + + doneLatch.await(); + uploadExecutor.shutdown(); + + if (!exceptions.isEmpty()) { + CompletionException combined = + new CompletionException("uploadPartsParallelly failed", exceptions.peek()); + exceptions.stream().skip(1).forEach(combined::addSuppressed); + throw combined; + } + + uploadResults.sort(Comparator.comparingInt(r -> r.part().partNumber())); + return uploadResults; + + } catch (InterruptedException | MinioException e) { + throw new CompletionException(e); + } finally { + uploadExecutor.shutdownNow(); // ensure executor exits on error + } + }); + } + + private CompletableFuture putObject( + PutObjectBaseArgs args, + Object fileStreamData, + MediaType contentType, + boolean addContentSha256) { + 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() != null ? args.checksum() : Checksum.Algorithm.CRC32C; + boolean addSha256Checksum = algorithm == Checksum.Algorithm.SHA256; + Checksum.Algorithm[] algorithms; + if (addContentSha256 && !addSha256Checksum) { + algorithms = new Checksum.Algorithm[] {algorithm, Checksum.Algorithm.SHA256}; + } else { + algorithms = new Checksum.Algorithm[] {algorithm}; + } + + PartReader partReader = null; + ByteBuffer buffer = null; + int partCount = args.partCount(); + + if (stream != null) { + try { + 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(); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + } + + if (partCount == 1) { + if (stream != null) { + return putObject( + new PutObjectAPIArgs( + args, + buffer, + contentType, + Checksum.makeHeaders(partReader.hashers(), addContentSha256, addSha256Checksum))); + } + + if (args.objectSize() == null) { + return Utils.failedFuture( + new MinioException("object size is null; this should not happen")); + } + long length = args.objectSize(); + + try { + Map hashers = Checksum.newHasherMap(algorithms); + + if (file != null) { + long position = file.getFilePointer(); + Checksum.update(hashers, file, length); + file.seek(position); + return putObject( + new PutObjectAPIArgs( + args, + file, + length, + contentType, + Checksum.makeHeaders(hashers, addContentSha256, addSha256Checksum))); + } + + Checksum.update(hashers, data, (int) length); + return putObject( + new PutObjectAPIArgs( + args, + data, + (int) length, + contentType, + Checksum.makeHeaders(hashers, addContentSha256, addSha256Checksum))); + } catch (MinioException e) { + return Utils.failedFuture(e); + } catch (IOException e) { + return Utils.failedFuture(new MinioException(e)); + } + } + + // Multipart upload starts here + + if (args.checksum() != null && !args.checksum().compositeSupport()) { + throw new IllegalArgumentException( + "unsupported checksum " + args.checksum() + " for multipart upload"); + } + + if (file != null) { + try { + partReader = + new PartReader(file, args.objectSize(), args.partSize(), args.partCount(), algorithms); + buffer = new ByteBuffer(args.partSize()); + partReader.read(buffer); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + } + + int parallelUploads = args.parallelUploads(); + if (parallelUploads <= 0) parallelUploads = 1; + if (partReader.partCount() > 0 && parallelUploads > partReader.partCount()) { + parallelUploads = partReader.partCount(); + } + + String[] uploadId = {null}; + final PartReader finalPartReader = partReader; + final ByteBuffer finalBuffer = buffer; + final int finalParallelUploads = parallelUploads; + return createMultipartUpload(new CreateMultipartUploadArgs(args, contentType, algorithm)) + .thenCompose( + response -> { + uploadId[0] = response.result().uploadId(); + // Do sequential multipart uploads + if (finalParallelUploads == 1) { + return uploadPartsSequentially( + args, + uploadId[0], + finalPartReader, + addContentSha256, + addSha256Checksum, + finalBuffer, + args.partSize(), + new ArrayList()); + } + + // Do sequential multipart uploads + return uploadPartsParallelly( + args, + uploadId[0], + finalPartReader, + addContentSha256, + addSha256Checksum, + finalBuffer, + args.partSize(), + finalParallelUploads); + }) + .thenCompose( + responses -> + completeMultipartUpload( + new CompleteMultipartUploadArgs( + args, + uploadId[0], + responses.stream() + .map(UploadPartResponse::part) + .toArray(io.minio.messages.Part[]::new)))) + .exceptionally( + e -> { + e = e.getCause(); + if (uploadId[0] != null) { + try { + abortMultipartUpload(new AbortMultipartUploadArgs(args, uploadId[0])).join(); + } catch (CompletionException ex) { + e.addSuppressed(ex.getCause()); + } + } + throw new CompletionException(e); + }); + } + /** * Uploads data from a stream to an object. * @@ -2048,25 +2096,19 @@ 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. */ - public CompletableFuture putObject(PutObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture putObject(PutObjectArgs args) { 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) { + return Utils.failedFuture(new MinioException(e)); + } } /** @@ -2090,50 +2132,35 @@ 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. */ - public CompletableFuture uploadObject(UploadObjectArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture uploadObject(UploadObjectArgs args) { 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); - } - - Throwable ex = e.getCause(); - - if (ex instanceof CompletionException) { - ex = ((CompletionException) ex).getCause(); - } - - if (ex instanceof ExecutionException) { - ex = ((ExecutionException) ex).getCause(); - } - - throw new CompletionException(ex); - }) - .thenApply( - objectWriteResponse -> { - try { - file.close(); - } catch (IOException e) { + 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 -> { + e = e.getCause(); + try { + file.close(); + } catch (IOException ex) { + e.addSuppressed(new MinioException(ex)); + } throw new CompletionException(e); - } - return objectWriteResponse; - }); + }) + .thenApply( + objectWriteResponse -> { + try { + file.close(); + } catch (IOException e) { + throw new CompletionException(new MinioException(e)); + } + return objectWriteResponse; + }); + } catch (IOException e) { + return Utils.failedFuture(new MinioException(e)); + } } /** @@ -2147,39 +2174,22 @@ 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. */ - public CompletableFuture getBucketPolicy(GetBucketPolicyArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture getBucketPolicy(GetBucketPolicyArgs args) { checkArgs(args); - return executeGetAsync(args, null, newMultimap("policy", "")) + return executeGetAsync(args, null, new Http.QueryParameters("policy", "")) .exceptionally( e -> { - Throwable ex = e.getCause(); - - if (ex instanceof CompletionException) { - ex = ((CompletionException) ex).getCause(); - } - - if (ex instanceof ExecutionException) { - ex = ((ExecutionException) ex).getCause(); - } - - if (ex instanceof ErrorResponseException) { - if (((ErrorResponseException) ex) + e = e.getCause(); + if (e instanceof ErrorResponseException) { + if (((ErrorResponseException) e) .errorResponse() .code() .equals(NO_SUCH_BUCKET_POLICY)) { return null; } } - throw new CompletionException(ex); + throw new CompletionException(e); }) .thenApply( response -> { @@ -2212,7 +2222,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 +2261,16 @@ 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. */ - public CompletableFuture setBucketPolicy(SetBucketPolicyArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture setBucketPolicy(SetBucketPolicyArgs args) { checkArgs(args); - return executePutAsync( - args, - newMultimap("Content-Type", "application/json"), - newMultimap("policy", ""), - args.config(), - 0) + Http.Body body = null; + try { + body = new Http.Body(args.config(), Http.JSON_MEDIA_TYPE, null, null); + } catch (MinioException e) { + return Utils.failedFuture(e); + } + return executePutAsync(args, null, new Http.QueryParameters("policy", ""), body) .thenAccept(response -> response.close()); } @@ -2282,39 +2285,22 @@ 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. */ - public CompletableFuture deleteBucketPolicy(DeleteBucketPolicyArgs args) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { + public CompletableFuture deleteBucketPolicy(DeleteBucketPolicyArgs args) { checkArgs(args); - return executeDeleteAsync(args, null, newMultimap("policy", "")) + return executeDeleteAsync(args, null, new Http.QueryParameters("policy", "")) .exceptionally( e -> { - Throwable ex = e.getCause(); - - if (ex instanceof CompletionException) { - ex = ((CompletionException) ex).getCause(); - } - - if (ex instanceof ExecutionException) { - ex = ((ExecutionException) ex).getCause(); - } - - if (ex instanceof ErrorResponseException) { - if (((ErrorResponseException) ex) + e = e.getCause(); + if (e instanceof ErrorResponseException) { + if (((ErrorResponseException) e) .errorResponse() .code() .equals(NO_SUCH_BUCKET_POLICY)) { return null; } } - throw new CompletionException(ex); + throw new CompletionException(e); }) .thenAccept( response -> { @@ -2326,7 +2312,7 @@ public CompletableFuture deleteBucketPolicy(DeleteBucketPolicyArgs args) * Sets lifecycle configuration to a bucket. * *
Example:{@code
-   * List rules = new LinkedList<>();
+   * List rules = new ArrayList<>();
    * rules.add(
    *     new LifecycleRule(
    *         Status.ENABLED,
@@ -2344,18 +2330,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.
    */
-  public CompletableFuture setBucketLifecycle(SetBucketLifecycleArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture setBucketLifecycle(SetBucketLifecycleArgs args) {
     checkArgs(args);
-    return executePutAsync(args, null, newMultimap("lifecycle", ""), args.config(), 0)
+    Http.Body body = null;
+    try {
+      body = new Http.Body(args.config(), null, null, null);
+    } catch (MinioException e) {
+      return Utils.failedFuture(e);
+    }
+    return executePutAsync(args, null, new Http.QueryParameters("lifecycle", ""), body)
         .thenAccept(response -> response.close());
   }
 
@@ -2369,18 +2353,10 @@ 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.
    */
-  public CompletableFuture deleteBucketLifecycle(DeleteBucketLifecycleArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture deleteBucketLifecycle(DeleteBucketLifecycleArgs args) {
     checkArgs(args);
-    return executeDeleteAsync(args, null, newMultimap("lifecycle", ""))
+    return executeDeleteAsync(args, null, new Http.QueryParameters("lifecycle", ""))
         .thenAccept(response -> response.close());
   }
 
@@ -2396,39 +2372,22 @@ 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.
    */
-  public CompletableFuture getBucketLifecycle(GetBucketLifecycleArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture getBucketLifecycle(GetBucketLifecycleArgs args) {
     checkArgs(args);
-    return executeGetAsync(args, null, newMultimap("lifecycle", ""))
+    return executeGetAsync(args, null, new Http.QueryParameters("lifecycle", ""))
         .exceptionally(
             e -> {
-              Throwable ex = e.getCause();
-
-              if (ex instanceof CompletionException) {
-                ex = ((CompletionException) ex).getCause();
-              }
-
-              if (ex instanceof ExecutionException) {
-                ex = ((ExecutionException) ex).getCause();
-              }
-
-              if (ex instanceof ErrorResponseException) {
-                if (((ErrorResponseException) ex)
+              e = e.getCause();
+              if (e instanceof ErrorResponseException) {
+                if (((ErrorResponseException) e)
                     .errorResponse()
                     .code()
                     .equals("NoSuchLifecycleConfiguration")) {
                   return null;
                 }
               }
-              throw new CompletionException(ex);
+              throw new CompletionException(e);
             })
         .thenApply(
             response -> {
@@ -2454,19 +2413,11 @@ 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.
    */
   public CompletableFuture getBucketNotification(
-      GetBucketNotificationArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+      GetBucketNotificationArgs args) {
     checkArgs(args);
-    return executeGetAsync(args, null, newMultimap("notification", ""))
+    return executeGetAsync(args, null, new Http.QueryParameters("notification", ""))
         .thenApply(
             response -> {
               try {
@@ -2483,7 +2434,7 @@ public CompletableFuture getBucketNotification(
    * Sets notification configuration to a bucket.
    *
    * 
Example:{@code
-   * List eventList = new LinkedList<>();
+   * List eventList = new ArrayList<>();
    * eventList.add(EventType.OBJECT_CREATED_PUT);
    * eventList.add(EventType.OBJECT_CREATED_COPY);
    *
@@ -2493,7 +2444,7 @@ public CompletableFuture getBucketNotification(
    * queueConfiguration.setPrefixRule("images");
    * queueConfiguration.setSuffixRule("pg");
    *
-   * List queueConfigurationList = new LinkedList<>();
+   * List queueConfigurationList = new ArrayList<>();
    * queueConfigurationList.add(queueConfiguration);
    *
    * NotificationConfiguration config = new NotificationConfiguration();
@@ -2505,18 +2456,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.
    */
-  public CompletableFuture setBucketNotification(SetBucketNotificationArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture setBucketNotification(SetBucketNotificationArgs args) {
     checkArgs(args);
-    return executePutAsync(args, null, newMultimap("notification", ""), args.config(), 0)
+    Http.Body body = null;
+    try {
+      body = new Http.Body(args.config(), null, null, null);
+    } catch (MinioException e) {
+      return Utils.failedFuture(e);
+    }
+    return executePutAsync(args, null, new Http.QueryParameters("notification", ""), body)
         .thenAccept(response -> response.close());
   }
 
@@ -2530,19 +2479,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.
    */
-  public CompletableFuture deleteBucketNotification(DeleteBucketNotificationArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture deleteBucketNotification(DeleteBucketNotificationArgs args) {
     checkArgs(args);
-    return executePutAsync(
-            args, null, newMultimap("notification", ""), new NotificationConfiguration(), 0)
+    Http.Body body = null;
+    try {
+      body = new Http.Body(new NotificationConfiguration(null, null, null, null), null, null, null);
+    } catch (MinioException e) {
+      return Utils.failedFuture(e);
+    }
+    return executePutAsync(args, null, new Http.QueryParameters("notification", ""), body)
         .thenAccept(response -> response.close());
   }
 
@@ -2557,40 +2503,23 @@ 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.
    */
   public CompletableFuture getBucketReplication(
-      GetBucketReplicationArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+      GetBucketReplicationArgs args) {
     checkArgs(args);
-    return executeGetAsync(args, null, newMultimap("replication", ""))
+    return executeGetAsync(args, null, new Http.QueryParameters("replication", ""))
         .exceptionally(
             e -> {
-              Throwable ex = e.getCause();
-
-              if (ex instanceof CompletionException) {
-                ex = ((CompletionException) ex).getCause();
-              }
-
-              if (ex instanceof ExecutionException) {
-                ex = ((ExecutionException) ex).getCause();
-              }
-
-              if (ex instanceof ErrorResponseException) {
-                if (((ErrorResponseException) ex)
+              e = e.getCause();
+              if (e instanceof ErrorResponseException) {
+                if (((ErrorResponseException) e)
                     .errorResponse()
                     .code()
                     .equals("ReplicationConfigurationNotFoundError")) {
                   return null;
                 }
               }
-              throw new CompletionException(ex);
+              throw new CompletionException(e);
             })
         .thenApply(
             response -> {
@@ -2626,7 +2555,7 @@ public CompletableFuture getBucketReplication(
    *         null,
    *         Status.ENABLED);
    *
-   * List rules = new LinkedList<>();
+   * List rules = new ArrayList<>();
    * rules.add(rule);
    *
    * ReplicationConfiguration config =
@@ -2638,25 +2567,22 @@ 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.
    */
-  public CompletableFuture setBucketReplication(SetBucketReplicationArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture setBucketReplication(SetBucketReplicationArgs args) {
     checkArgs(args);
+    Http.Body body = null;
+    try {
+      body = new Http.Body(args.config(), null, null, null);
+    } catch (MinioException e) {
+      return Utils.failedFuture(e);
+    }
     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", ""),
+            body)
         .thenAccept(response -> response.close());
   }
 
@@ -2670,18 +2596,10 @@ 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.
    */
-  public CompletableFuture deleteBucketReplication(DeleteBucketReplicationArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture deleteBucketReplication(DeleteBucketReplicationArgs args) {
     checkArgs(args);
-    return executeDeleteAsync(args, null, newMultimap("replication", ""))
+    return executeDeleteAsync(args, null, new Http.QueryParameters("replication", ""))
         .thenAccept(response -> response.close());
   }
 
@@ -2714,36 +2632,23 @@ 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);
     }
 
     Response response = null;
     try {
-      response = executeGetAsync(args, null, queryParams).get();
-    } catch (InterruptedException e) {
-      throw new RuntimeException(e);
-    } catch (ExecutionException e) {
-      throwEncapsulatedException(e);
+      response = executeGetAsync(args, null, queryParams).join();
+    } catch (CompletionException e) {
+      throwMinioException(e);
     }
     NotificationResultRecords result = new NotificationResultRecords(response);
     return result.closeableIterator();
@@ -2784,41 +2689,33 @@ 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()))
-              .get();
-    } catch (InterruptedException e) {
-      throw new RuntimeException(e);
-    } catch (ExecutionException e) {
-      throwEncapsulatedException(e);
+                  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))
+              .join();
+    } catch (CompletionException e) {
+      throwMinioException(e);
     }
     return new SelectResponseStream(response.body().byteStream());
   }
@@ -2833,18 +2730,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.
    */
-  public CompletableFuture setBucketEncryption(SetBucketEncryptionArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture setBucketEncryption(SetBucketEncryptionArgs args) {
     checkArgs(args);
-    return executePutAsync(args, null, newMultimap("encryption", ""), args.config(), 0)
+    Http.Body body = null;
+    try {
+      body = new Http.Body(args.config(), null, null, null);
+    } catch (MinioException e) {
+      return Utils.failedFuture(e);
+    }
+    return executePutAsync(args, null, new Http.QueryParameters("encryption", ""), body)
         .thenAccept(response -> response.close());
   }
 
@@ -2859,39 +2754,22 @@ 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.
    */
-  public CompletableFuture getBucketEncryption(GetBucketEncryptionArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture getBucketEncryption(GetBucketEncryptionArgs args) {
     checkArgs(args);
-    return executeGetAsync(args, null, newMultimap("encryption", ""))
+    return executeGetAsync(args, null, new Http.QueryParameters("encryption", ""))
         .exceptionally(
             e -> {
-              Throwable ex = e.getCause();
-
-              if (ex instanceof CompletionException) {
-                ex = ((CompletionException) ex).getCause();
-              }
-
-              if (ex instanceof ExecutionException) {
-                ex = ((ExecutionException) ex).getCause();
-              }
-
-              if (ex instanceof ErrorResponseException) {
-                if (((ErrorResponseException) ex)
+              e = e.getCause();
+              if (e instanceof ErrorResponseException) {
+                if (((ErrorResponseException) e)
                     .errorResponse()
                     .code()
                     .equals(SERVER_SIDE_ENCRYPTION_CONFIGURATION_NOT_FOUND_ERROR)) {
                   return null;
                 }
               }
-              throw new CompletionException(ex);
+              throw new CompletionException(e);
             })
         .thenApply(
             response -> {
@@ -2916,39 +2794,22 @@ 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.
    */
-  public CompletableFuture deleteBucketEncryption(DeleteBucketEncryptionArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture deleteBucketEncryption(DeleteBucketEncryptionArgs args) {
     checkArgs(args);
-    return executeDeleteAsync(args, null, newMultimap("encryption", ""))
+    return executeDeleteAsync(args, null, new Http.QueryParameters("encryption", ""))
         .exceptionally(
             e -> {
-              Throwable ex = e.getCause();
-
-              if (ex instanceof CompletionException) {
-                ex = ((CompletionException) ex).getCause();
-              }
-
-              if (ex instanceof ExecutionException) {
-                ex = ((ExecutionException) ex).getCause();
-              }
-
-              if (ex instanceof ErrorResponseException) {
-                if (((ErrorResponseException) ex)
+              e = e.getCause();
+              if (e instanceof ErrorResponseException) {
+                if (((ErrorResponseException) e)
                     .errorResponse()
                     .code()
                     .equals(SERVER_SIDE_ENCRYPTION_CONFIGURATION_NOT_FOUND_ERROR)) {
                   return null;
                 }
               }
-              throw new CompletionException(ex);
+              throw new CompletionException(e);
             })
         .thenAccept(
             response -> {
@@ -2966,36 +2827,19 @@ 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.
    */
-  public CompletableFuture getBucketTags(GetBucketTagsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture getBucketTags(GetBucketTagsArgs args) {
     checkArgs(args);
-    return executeGetAsync(args, null, newMultimap("tagging", ""))
+    return executeGetAsync(args, null, new Http.QueryParameters("tagging", ""))
         .exceptionally(
             e -> {
-              Throwable ex = e.getCause();
-
-              if (ex instanceof CompletionException) {
-                ex = ((CompletionException) ex).getCause();
-              }
-
-              if (ex instanceof ExecutionException) {
-                ex = ((ExecutionException) ex).getCause();
-              }
-
-              if (ex instanceof ErrorResponseException) {
-                if (((ErrorResponseException) ex).errorResponse().code().equals("NoSuchTagSet")) {
+              e = e.getCause();
+              if (e instanceof ErrorResponseException) {
+                if (((ErrorResponseException) e).errorResponse().code().equals("NoSuchTagSet")) {
                   return null;
                 }
               }
-              throw new CompletionException(ex);
+              throw new CompletionException(e);
             })
         .thenApply(
             response -> {
@@ -3023,18 +2867,16 @@ 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.
    */
-  public CompletableFuture setBucketTags(SetBucketTagsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture setBucketTags(SetBucketTagsArgs args) {
     checkArgs(args);
-    return executePutAsync(args, null, newMultimap("tagging", ""), args.tags(), 0)
+    Http.Body body = null;
+    try {
+      body = new Http.Body(args.tags(), null, null, null);
+    } catch (MinioException e) {
+      return Utils.failedFuture(e);
+    }
+    return executePutAsync(args, null, new Http.QueryParameters("tagging", ""), body)
         .thenAccept(response -> response.close());
   }
 
@@ -3048,18 +2890,10 @@ 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.
    */
-  public CompletableFuture deleteBucketTags(DeleteBucketTagsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture deleteBucketTags(DeleteBucketTagsArgs args) {
     checkArgs(args);
-    return executeDeleteAsync(args, null, newMultimap("tagging", ""))
+    return executeDeleteAsync(args, null, new Http.QueryParameters("tagging", ""))
         .thenAccept(response -> response.close());
   }
 
@@ -3074,18 +2908,10 @@ 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.
    */
-  public CompletableFuture getObjectTags(GetObjectTagsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture getObjectTags(GetObjectTagsArgs args) {
     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,21 +2943,18 @@ 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.
    */
-  public CompletableFuture setObjectTags(SetObjectTagsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture setObjectTags(SetObjectTagsArgs args) {
     checkArgs(args);
-    Multimap queryParams = newMultimap("tagging", "");
+    Http.Body body = null;
+    try {
+      body = new Http.Body(args.tags(), null, null, null);
+    } catch (MinioException e) {
+      return Utils.failedFuture(e);
+    }
+    Http.QueryParameters queryParams = new Http.QueryParameters("tagging", "");
     if (args.versionId() != null) queryParams.put("versionId", args.versionId());
-    return executePutAsync(args, null, queryParams, args.tags(), 0)
-        .thenAccept(response -> response.close());
+    return executePutAsync(args, null, queryParams, body).thenAccept(response -> response.close());
   }
 
   /**
@@ -3144,18 +2967,10 @@ 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.
    */
-  public CompletableFuture deleteObjectTags(DeleteObjectTagsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture deleteObjectTags(DeleteObjectTagsArgs args) {
     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,36 +2985,22 @@ 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.
    */
-  public CompletableFuture getBucketCors(GetBucketCorsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture getBucketCors(GetBucketCorsArgs args) {
     checkArgs(args);
-    return executeGetAsync(args, null, newMultimap("cors", ""))
+    return executeGetAsync(args, null, new Http.QueryParameters("cors", ""))
         .exceptionally(
             e -> {
-              Throwable ex = e.getCause();
-
-              if (ex instanceof CompletionException) {
-                ex = ((CompletionException) ex).getCause();
-              }
-
-              if (ex instanceof ExecutionException) {
-                ex = ((ExecutionException) ex).getCause();
-              }
-
-              if (ex instanceof ErrorResponseException) {
-                if (((ErrorResponseException) ex).errorResponse().code().equals("NoSuchTagSet")) {
+              e = e.getCause();
+              if (e instanceof ErrorResponseException) {
+                if (((ErrorResponseException) e)
+                    .errorResponse()
+                    .code()
+                    .equals("NoSuchCORSConfiguration")) {
                   return null;
                 }
               }
-              throw new CompletionException(ex);
+              throw new CompletionException(e);
             })
         .thenApply(
             response -> {
@@ -3247,18 +3048,16 @@ 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.
    */
-  public CompletableFuture setBucketCors(SetBucketCorsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture setBucketCors(SetBucketCorsArgs args) {
     checkArgs(args);
-    return executePutAsync(args, null, newMultimap("cors", ""), args.config(), 0)
+    Http.Body body = null;
+    try {
+      body = new Http.Body(args.config(), null, null, null);
+    } catch (MinioException e) {
+      return Utils.failedFuture(e);
+    }
+    return executePutAsync(args, null, new Http.QueryParameters("cors", ""), body)
         .thenAccept(response -> response.close());
   }
 
@@ -3272,18 +3071,10 @@ 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.
    */
-  public CompletableFuture deleteBucketCors(DeleteBucketCorsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture deleteBucketCors(DeleteBucketCorsArgs args) {
     checkArgs(args);
-    return executeDeleteAsync(args, null, newMultimap("cors", ""))
+    return executeDeleteAsync(args, null, new Http.QueryParameters("cors", ""))
         .thenAccept(response -> response.close());
   }
 
@@ -3298,18 +3089,10 @@ 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.
    */
-  public CompletableFuture getObjectAcl(GetObjectAclArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture getObjectAcl(GetObjectAclArgs args) {
     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 +3125,15 @@ 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.
    */
   public CompletableFuture getObjectAttributes(
-      GetObjectAttributesArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+      GetObjectAttributesArgs args) {
     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,20 +3192,12 @@ 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.
    */
   public CompletableFuture uploadSnowballObjects(
-      UploadSnowballObjectsArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+      UploadSnowballObjectsArgs args) {
     checkArgs(args);
 
-    return CompletableFuture.supplyAsync(
+    return supplyAsync(
             () -> {
               FileOutputStream fos = null;
               BufferedOutputStream bos = null;
@@ -3475,7 +3242,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,36 +3254,19 @@ 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) {
-                  throw new CompletionException(e);
-                }
+                return putObject(new PutObjectAPIArgs(args, data, data.length, headers));
               }
 
               long length = Paths.get(args.stagingFilename()).toFile().length();
@@ -3525,21 +3275,9 @@ 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) {
-                throw new CompletionException(e);
+                return putObject(new PutObjectAPIArgs(args, file, length, headers));
+              } catch (IOException e) {
+                throw new CompletionException(new MinioException(e));
               }
             });
   }
@@ -3566,26 +3304,36 @@ 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.
    */
-  public CompletableFuture putObjectFanOut(PutObjectFanOutArgs args)
-      throws ErrorResponseException, InsufficientDataException, InternalException,
-          InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture putObjectFanOut(PutObjectFanOutArgs args) {
     checkArgs(args);
-    args.validateSse(this.baseUrl);
+    args.validateSse(this.baseUrl.isHttps());
 
-    return CompletableFuture.supplyAsync(
+    return 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-"
-                      + new BigInteger(32, random).toString(32)
+                      + new BigInteger(32, RANDOM).toString(32)
                       + "-"
                       + System.currentTimeMillis();
               PostPolicy policy =
@@ -3617,50 +3365,30 @@ 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(e);
-              }
-            })
-        .thenCompose(
-            body -> {
-              try {
-                return executePostAsync(args, null, null, body);
-              } catch (InsufficientDataException
-                  | InternalException
-                  | InvalidKeyException
-                  | IOException
-                  | NoSuchAlgorithmException
-                  | XmlParserException e) {
+                throw new CompletionException(new MinioException(e));
+              } catch (MinioException e) {
                 throw new CompletionException(e);
               }
             })
+        .thenCompose(body -> executePostAsync(args, null, null, new Http.Body(body)))
         .thenApply(
             response -> {
               try {
                 JsonFactory jsonFactory = new JsonFactory();
                 Iterator iterator =
-                    objectMapper.readValues(
+                    OBJECT_MAPPER.readValues(
                         jsonFactory.createParser(response.body().byteStream()),
                         PutObjectFanOutResponse.Result.class);
-                List results = new LinkedList<>();
+                List results = new ArrayList<>();
                 iterator.forEachRemaining(results::add);
                 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 +3400,582 @@ 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.
    */
-  public CompletableFuture promptObject(PromptObjectArgs args)
-      throws InsufficientDataException, InternalException, InvalidKeyException, IOException,
-          NoSuchAlgorithmException, XmlParserException {
+  public CompletableFuture promptObject(PromptObjectArgs args) {
     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());
+    if (args.versionId() == null) queryParams.put("versionId", args.versionId());
+    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);
+    try {
+      byte[] data = OBJECT_MAPPER.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) {
+      return Utils.failedFuture(new MinioException(e));
+    }
+  }
 
-    return executePostAsync(args, headers, queryParams, data)
-        .thenApply(
+  private CompletableFuture appendObject(
+      AppendObjectArgs args,
+      long writeOffset,
+      PartReader partReader,
+      ByteBuffer buffer,
+      byte[] data,
+      Long length,
+      RandomAccessFile file,
+      Long partSize,
+      Map hashers,
+      boolean addContentSha256,
+      boolean addSha256Checksum) {
+    Http.Headers headers =
+        new Http.Headers("x-amz-write-offset-bytes", String.valueOf(writeOffset));
+
+    if (data != null) {
+      if (hashers != null) {
+        Checksum.update(hashers, data, length.intValue());
+        headers.putAll(Checksum.makeHeaders(hashers, addContentSha256, addSha256Checksum));
+      }
+      return putObject(new PutObjectAPIArgs(args, data, length.intValue(), headers));
+    }
+
+    if (partReader != null) {
+      if (partReader.hashers() != null) {
+        headers.putAll(
+            Checksum.makeHeaders(partReader.hashers(), addContentSha256, addSha256Checksum));
+      }
+      return putObject(new PutObjectAPIArgs(args, buffer, headers))
+          .thenCompose(
+              response -> {
+                long finalWriteOffset = writeOffset + buffer.length();
+                try {
+                  buffer.reset();
+                  if (partReader.partNumber() == partReader.partCount()) {
+                    return CompletableFuture.completedFuture(response);
+                  }
+                  partReader.read(buffer);
+                } catch (MinioException e) {
+                  return Utils.failedFuture(e);
+                }
+                return appendObject(
+                    args,
+                    finalWriteOffset,
+                    partReader,
+                    buffer,
+                    null,
+                    null,
+                    null,
+                    null,
+                    null,
+                    addContentSha256,
+                    addSha256Checksum);
+              });
+    }
+
+    long size = Math.min(length, partSize);
+    if (hashers != null) {
+      for (Map.Entry entry : hashers.entrySet()) {
+        entry.getValue().reset();
+      }
+
+      try {
+        long position = file.getFilePointer();
+        Checksum.update(hashers, file, size);
+        file.seek(position);
+      } catch (MinioException e) {
+        return Utils.failedFuture(e);
+      } catch (IOException e) {
+        return Utils.failedFuture(new MinioException(e));
+      }
+
+      headers.putAll(Checksum.makeHeaders(hashers, addContentSha256, addSha256Checksum));
+    }
+
+    return putObject(new PutObjectAPIArgs(args, file, size, headers))
+        .thenCompose(
             response -> {
-              return new PromptObjectResponse(
-                  response.headers(),
-                  args.bucket(),
-                  args.region(),
-                  args.object(),
-                  response.body().byteStream());
+              long finalWriteOffset = writeOffset + size;
+              long finalLength = length - size;
+              if (finalLength == 0) {
+                return CompletableFuture.completedFuture(response);
+              }
+              return appendObject(
+                  args,
+                  finalWriteOffset,
+                  null,
+                  null,
+                  null,
+                  finalLength,
+                  file,
+                  partSize,
+                  hashers,
+                  addContentSha256,
+                  addSha256Checksum);
             });
   }
 
-  public static Builder builder() {
-    return new Builder();
+  /**
+   * Appends from a file, stream or data to existing object in a bucket.
+   *
+   * @param args {@link AppendObjectArgs} object.
+   * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> object.
+   */
+  public CompletableFuture appendObject(AppendObjectArgs args) {
+    checkArgs(args);
+    return headObject(new HeadObjectArgs(args))
+        .thenCompose(
+            response -> {
+              Checksum.Algorithm algorithm = null;
+              if (response.checksumType() != null) {
+                if (response.checksumType() != Checksum.Type.FULL_OBJECT) {
+                  return Utils.failedFuture(
+                      new MinioException(
+                          "append object does not support checksum type "
+                              + response.checksumType()));
+                }
+                List algorithms = response.algorithms();
+                if (algorithms != null) algorithm = algorithms.get(0);
+              }
+
+              long writeOffset = response.size();
+
+              long partSize =
+                  args.chunkSize() != null ? args.chunkSize() : ObjectWriteArgs.MIN_MULTIPART_SIZE;
+
+              boolean addContentSha256 = !this.baseUrl.isHttps();
+              boolean addSha256Checksum = algorithm == Checksum.Algorithm.SHA256;
+              Checksum.Algorithm[] algorithms;
+              if (addContentSha256 && !addSha256Checksum) {
+                algorithms =
+                    (algorithm != null)
+                        ? new Checksum.Algorithm[] {algorithm, Checksum.Algorithm.SHA256}
+                        : new Checksum.Algorithm[] {Checksum.Algorithm.SHA256};
+              } else {
+                algorithms =
+                    (algorithm != null)
+                        ? new Checksum.Algorithm[] {algorithm}
+                        : new Checksum.Algorithm[0];
+              }
+
+              if (args.stream() != null) {
+                try {
+                  int partCount =
+                      args.length() == null
+                          ? -1
+                          : (int) Math.max((args.length() + partSize - 1) / partSize, 1);
+                  PartReader partReader =
+                      new PartReader(args.stream(), args.length(), partSize, partCount, algorithms);
+                  ByteBuffer buffer =
+                      new ByteBuffer(partReader.partCount() == 1 ? args.length() : partSize);
+                  partReader.read(buffer);
+                  return appendObject(
+                      args,
+                      writeOffset,
+                      partReader,
+                      buffer,
+                      null,
+                      null,
+                      null,
+                      null,
+                      null,
+                      addContentSha256,
+                      addSha256Checksum);
+                } catch (MinioException e) {
+                  return Utils.failedFuture(e);
+                }
+              }
+
+              try {
+                Map hashers =
+                    Checksum.newHasherMap(algorithms);
+                if (args.data() != null) {
+                  return appendObject(
+                      args,
+                      writeOffset,
+                      null,
+                      null,
+                      args.data(),
+                      args.length(),
+                      null,
+                      null,
+                      hashers,
+                      addContentSha256,
+                      addSha256Checksum);
+                }
+
+                RandomAccessFile file = new RandomAccessFile(args.filename(), "r");
+                return appendObject(
+                    args,
+                    writeOffset,
+                    null,
+                    null,
+                    null,
+                    args.length(),
+                    file,
+                    partSize,
+                    hashers,
+                    addContentSha256,
+                    addSha256Checksum);
+              } catch (MinioException e) {
+                return Utils.failedFuture(e);
+              } catch (IOException e) {
+                return Utils.failedFuture(new MinioException(e));
+              }
+            });
   }
 
-  /** 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;
+  /////////////////////////////////////////////////////////////////////////////////////////////////
+  ///////////////////////////// Higher level ListObjects implementation ///////////////////////////
+  /////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private String region;
-    private Provider provider;
-    private OkHttpClient httpClient;
-    private boolean closeHttpClient;
+  /** Throws encapsulated exception wrapped by {@link CompletionException}. */
+  public void throwMinioException(CompletionException e) throws MinioException {
+    if (e == null) return;
+    Throwable ex = e.getCause();
+    if (ex instanceof MinioException) throw (MinioException) ex;
+    Throwable exc = ex.getCause();
+    throw new RuntimeException(exc != null ? exc : ex);
+  }
+
+  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 void setAwsInfo(String host, boolean https) {
-      this.awsS3Prefix = null;
-      this.awsDomainSuffix = null;
-      this.awsDualstack = false;
+    protected abstract void populateResult() throws MinioException;
 
-      if (!HttpUtils.HOSTNAME_REGEX.matcher(host).find()) return;
+    protected synchronized void populate() {
+      try {
+        populateResult();
+      } catch (MinioException e) {
+        this.error = new Result<>(e);
+      }
 
-      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.listObjectsResult != null) {
+        this.itemIterator = this.listObjectsResult.contents().iterator();
+        this.deleteMarkerIterator = this.listObjectsResult.deleteMarkers().iterator();
+        this.prefixIterator = this.listObjectsResult.commonPrefixes().iterator();
+      } else {
+        this.itemIterator = Collections.emptyIterator();
+        this.deleteMarkerIterator = Collections.emptyIterator();
+        this.prefixIterator = Collections.emptyIterator();
       }
+    }
+
+    @Override
+    public boolean hasNext() {
+      if (this.completed) return false;
 
-      if (!HttpUtils.AWS_ENDPOINT_REGEX.matcher(host).find()) return;
+      if (this.error == null
+          && this.itemIterator == null
+          && this.deleteMarkerIterator == null
+          && this.prefixIterator == null) {
+        populate();
+      }
 
-      if (!HttpUtils.AWS_S3_ENDPOINT_REGEX.matcher(host).find()) {
-        throw new IllegalArgumentException("invalid Amazon AWS host " + host);
+      if (this.error == null
+          && !this.itemIterator.hasNext()
+          && !this.deleteMarkerIterator.hasNext()
+          && !this.prefixIterator.hasNext()
+          && this.listObjectsResult.isTruncated()) {
+        populate();
       }
 
-      Matcher matcher = HttpUtils.AWS_S3_PREFIX_REGEX.matcher(host);
-      matcher.lookingAt();
-      int end = matcher.end();
+      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;
+    }
 
-      this.awsS3Prefix = host.substring(0, end);
-      if (this.awsS3Prefix.contains("s3-accesspoint") && !https) {
-        throw new IllegalArgumentException("use HTTPS scheme for 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();
       }
 
-      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);
+      if (this.error == null
+          && !this.itemIterator.hasNext()
+          && !this.deleteMarkerIterator.hasNext()
+          && !this.prefixIterator.hasNext()
+          && this.listObjectsResult.isTruncated()) {
+        populate();
       }
-      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 (this.error != null) {
+        this.completed = true;
+        return this.error;
       }
 
-      if (regionInHost != null) this.region = regionInHost;
-    }
+      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);
+      }
 
-    private void setBaseUrl(HttpUrl url) {
-      this.baseUrl = url;
-      this.setAwsInfo(url.host(), url.isHttps());
-      this.useVirtualStyle = this.awsDomainSuffix != null || url.host().endsWith("aliyuncs.com");
+      this.completed = true;
+      throw new NoSuchElementException();
     }
 
-    public Builder endpoint(String endpoint) {
-      setBaseUrl(HttpUtils.getBaseUrl(endpoint));
-      return this;
+    @Override
+    public void remove() {
+      throw new UnsupportedOperationException();
     }
+  }
 
-    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");
+  /** Execute list objects v1. */
+  protected Iterable> objectV1Lister(ListObjectsV1Args args) {
+    return new Iterable>() {
+      @Override
+      public Iterator> iterator() {
+        return new ObjectIterator() {
+          private ListBucketResultV1 result = null;
+
+          @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())
+                      .join();
+              result = response.result();
+              this.listObjectsResult = response.result();
+            } catch (CompletionException e) {
+              throwMinioException(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())
+                      .join();
+              result = response.result();
+              this.listObjectsResult = response.result();
+            } catch (CompletionException e) {
+              throwMinioException(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())
+                      .join();
+              result = response.result();
+              this.listObjectsResult = response.result();
+            } catch (CompletionException e) {
+              throwMinioException(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 3b2bf5e19..d2413f87c 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,16 +33,11 @@
 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;
+import java.util.concurrent.CompletionException;
 import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
 
@@ -148,37 +136,24 @@ 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) {
-      throw new RuntimeException(e);
-    } catch (ExecutionException e) {
-      asyncClient.throwEncapsulatedException(e);
+      return asyncClient.statObject(args).join();
+    } catch (CompletionException e) {
+      asyncClient.throwMinioException(e);
       return null;
     }
   }
 
   /**
-   * 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,31 +163,19 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -231,26 +194,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.downloadObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -357,26 +307,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.copyObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -428,26 +365,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.composeObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -461,7 +385,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 +400,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 +413,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 +423,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 +476,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,26 +510,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.removeObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -637,24 +525,25 @@ public void removeObject(RemoveObjectArgs args) * removal. * *
Example:{@code
-   * List objects = new LinkedList<>();
+   * List objects = new ArrayList<>();
    * 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,26 +570,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.restoreObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -745,7 +621,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,33 +630,20 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.listBuckets().join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -790,16 +652,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,26 +681,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.bucketExists(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return false; } } @@ -869,26 +719,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.makeBucket(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -901,26 +738,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setBucketVersioning(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -935,26 +759,14 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getBucketVersioning(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -970,26 +782,14 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setObjectLockConfiguration(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1002,26 +802,14 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.deleteObjectLockConfiguration(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1039,26 +827,14 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getObjectLockConfiguration(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -1079,26 +855,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setObjectRetention(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1118,26 +881,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getObjectRetention(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -1155,26 +905,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.enableObjectLegalHold(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1191,26 +928,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.disableObjectLegalHold(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1235,26 +959,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.isObjectLegalHoldEnabled(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return false; } } @@ -1267,26 +978,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.removeBucket(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1336,26 +1034,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.putObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -1381,26 +1066,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.uploadObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -1415,27 +1087,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getBucketPolicy(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return ""; } } @@ -1471,26 +1129,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setBucketPolicy(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1502,26 +1147,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.deleteBucketPolicy(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1529,7 +1161,7 @@ public void deleteBucketPolicy(DeleteBucketPolicyArgs args) * Sets lifecycle configuration to a bucket. * *
Example:{@code
-   * List rules = new LinkedList<>();
+   * List rules = new ArrayList<>();
    * rules.add(
    *     new LifecycleRule(
    *         Status.ENABLED,
@@ -1546,26 +1178,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setBucketLifecycle(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1577,26 +1196,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.deleteBucketLifecycle(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1612,26 +1218,14 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getBucketLifecycle(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -1647,26 +1241,14 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getBucketNotification(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -1675,7 +1257,7 @@ public NotificationConfiguration getBucketNotification(GetBucketNotificationArgs * Sets notification configuration to a bucket. * *
Example:{@code
-   * List eventList = new LinkedList<>();
+   * List eventList = new ArrayList<>();
    * eventList.add(EventType.OBJECT_CREATED_PUT);
    * eventList.add(EventType.OBJECT_CREATED_COPY);
    *
@@ -1685,7 +1267,7 @@ public NotificationConfiguration getBucketNotification(GetBucketNotificationArgs
    * queueConfiguration.setPrefixRule("images");
    * queueConfiguration.setSuffixRule("pg");
    *
-   * List queueConfigurationList = new LinkedList<>();
+   * List queueConfigurationList = new ArrayList<>();
    * queueConfigurationList.add(queueConfiguration);
    *
    * NotificationConfiguration config = new NotificationConfiguration();
@@ -1696,26 +1278,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setBucketNotification(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1728,26 +1297,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.deleteBucketNotification(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1762,26 +1318,14 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getBucketReplication(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -1807,7 +1351,7 @@ public ReplicationConfiguration getBucketReplication(GetBucketReplicationArgs ar * null, * Status.ENABLED); * - * List rules = new LinkedList<>(); + * List rules = new ArrayList<>(); * rules.add(rule); * * ReplicationConfiguration config = @@ -1818,26 +1362,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setBucketReplication(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1850,26 +1381,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.deleteBucketReplication(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -1902,21 +1420,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 +1462,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,26 +1478,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setBucketEncryption(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -2015,26 +1499,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getBucketEncryption(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -2048,26 +1519,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.deleteBucketEncryption(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -2081,26 +1539,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getBucketTags(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -2117,26 +1562,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setBucketTags(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -2148,26 +1580,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.deleteBucketTags(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -2182,26 +1601,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getObjectTags(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -2222,26 +1628,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setObjectTags(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -2254,26 +1647,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.deleteObjectTags(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -2287,26 +1667,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getBucketCors(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -2343,26 +1710,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.setBucketCors(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -2374,26 +1728,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + asyncClient.deleteBucketCors(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); } } @@ -2408,26 +1749,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getObjectAcl(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -2450,26 +1778,14 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.getObjectAttributes(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -2498,26 +1814,14 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.uploadSnowballObjects(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -2544,26 +1848,13 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.putObjectFanOut(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -2573,26 +1864,29 @@ 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) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - asyncClient.throwEncapsulatedException(e); + return asyncClient.promptObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); + return null; + } + } + + /** + * Appends from a file, stream or data to existing object in a bucket. + * + * @param args {@link AppendObjectArgs} object. + * @return {@link ObjectWriteResponse} object. + * @throws MinioException thrown to indicate SDK exception. + */ + public ObjectWriteResponse appendObject(AppendObjectArgs args) throws MinioException { + try { + return asyncClient.appendObject(args).join(); + } catch (CompletionException e) { + asyncClient.throwMinioException(e); return null; } } @@ -2621,11 +1915,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,32 +1949,11 @@ 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(); } - /** - * 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(); @@ -2705,13 +1979,13 @@ public void setAwsS3Prefix(String awsS3Prefix) { asyncClient.setAwsS3Prefix(awsS3Prefix); } + /** Closes underneath async client. */ @Override public void close() throws Exception { - if (asyncClient != null) { - asyncClient.close(); - } + if (asyncClient != null) asyncClient.close(); } + /** Creates new {@link MinioClient.Builder}. */ public static Builder builder() { return new Builder(); } 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..8d33d44c6 100644 --- a/api/src/main/java/io/minio/ObjectArgs.java +++ b/api/src/main/java/io/minio/ObjectArgs.java @@ -18,19 +18,31 @@ import java.util.Objects; -/** Base argument class holds object name and version ID along with bucket information. */ +/** + * Common arguments of {@link AbortMultipartUploadArgs}, {@link CompleteMultipartUploadArgs}, {@link + * CreateMultipartUploadArgs}, {@link ListPartsArgs}, {@link ObjectVersionArgs}, {@link + * ObjectWriteArgs}, {@link PromptObjectArgs}, {@link PutObjectAPIBaseArgs} and {@link + * UploadPartCopyArgs}. + */ 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; } - /** Base argument builder class for {@link ObjectArgs}. */ + /** Builder of {@link ObjectArgs}. */ 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..e8ab88432 100644 --- a/api/src/main/java/io/minio/ObjectConditionalReadArgs.java +++ b/api/src/main/java/io/minio/ObjectConditionalReadArgs.java @@ -16,13 +16,12 @@ 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; -/** Base argument class holds condition properties for reading object. */ +/** + * Common arguments of {@link SourceObject}, {@link GetObjectArgs} and {@link HeadObjectBaseArgs}. + */ public abstract class ObjectConditionalReadArgs extends ObjectReadArgs { protected Long offset; protected Long length; @@ -30,6 +29,38 @@ public abstract class ObjectConditionalReadArgs extends ObjectReadArgs { protected String notMatchETag; protected ZonedDateTime modifiedSince; protected ZonedDateTime unmodifiedSince; + protected boolean fetchChecksum; + + protected ObjectConditionalReadArgs() {} + + protected ObjectConditionalReadArgs(SourceObject args) { + super(args); + } + + protected ObjectConditionalReadArgs(AppendObjectArgs args) { + super(args); + this.fetchChecksum = true; + } + + 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; + this.fetchChecksum = args.fetchChecksum; + } + + protected ObjectConditionalReadArgs(ObjectConditionalReadArgs args, String matchETag) { + this(args); + this.matchETag = args.matchETag; + } public Long offset() { return offset; @@ -55,7 +86,11 @@ public ZonedDateTime unmodifiedSince() { return unmodifiedSince; } - public Multimap getHeaders() { + public boolean fetchChecksum() { + return fetchChecksum; + } + + public Http.Headers makeHeaders() { Long offset = this.offset; Long length = this.length; if (length != null && offset == null) { @@ -70,7 +105,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 +119,20 @@ public Multimap getHeaders() { headers.put("if-unmodified-since", unmodifiedSince.format(Time.HTTP_HEADER_DATE_FORMAT)); } - if (ssec != null) headers.putAll(Multimaps.forMap(ssec.headers())); + if (fetchChecksum) headers.put("x-amz-checksum-mode", "ENABLED"); 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); @@ -118,7 +151,7 @@ public Multimap genCopyHeaders() { return headers; } - /** Base argument builder class for {@link ObjectConditionalReadArgs}. */ + /** Builder of {@link ObjectConditionalReadArgs}. */ @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public abstract static class Builder, A extends ObjectConditionalReadArgs> extends ObjectReadArgs.Builder { @@ -147,13 +180,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; } @@ -167,6 +200,11 @@ public B unmodifiedSince(ZonedDateTime unmodifiedTime) { operations.add(args -> args.unmodifiedSince = unmodifiedTime); return (B) this; } + + public B fetchChecksum(boolean flag) { + operations.add(args -> args.fetchChecksum = flag); + return (B) this; + } } @Override @@ -180,12 +218,20 @@ public boolean equals(Object o) { && Objects.equals(matchETag, that.matchETag) && Objects.equals(notMatchETag, that.notMatchETag) && Objects.equals(modifiedSince, that.modifiedSince) - && Objects.equals(unmodifiedSince, that.unmodifiedSince); + && Objects.equals(unmodifiedSince, that.unmodifiedSince) + && Objects.equals(fetchChecksum, that.fetchChecksum); } @Override public int hashCode() { return Objects.hash( - super.hashCode(), offset, length, matchETag, notMatchETag, modifiedSince, unmodifiedSince); + super.hashCode(), + offset, + length, + matchETag, + notMatchETag, + modifiedSince, + unmodifiedSince, + fetchChecksum); } } diff --git a/api/src/main/java/io/minio/ObjectReadArgs.java b/api/src/main/java/io/minio/ObjectReadArgs.java index 2c36048f1..991780dc6 100644 --- a/api/src/main/java/io/minio/ObjectReadArgs.java +++ b/api/src/main/java/io/minio/ObjectReadArgs.java @@ -17,25 +17,38 @@ package io.minio; import java.util.Objects; -import okhttp3.HttpUrl; -/** Base argument class for reading object. */ +/** + * Common arguments of {@link DownloadObjectArgs}, {@link GetObjectAttributesArgs}, {@link + * ObjectConditionalReadArgs} and {@link SelectObjectContentArgs}. + */ public abstract class ObjectReadArgs extends ObjectVersionArgs { - protected ServerSideEncryptionCustomerKey ssec; + protected ServerSideEncryption.CustomerKey ssec; + + protected ObjectReadArgs() {} + + protected ObjectReadArgs(ObjectReadArgs args) { + super(args); + this.ssec = args.ssec; + } + + protected ObjectReadArgs(AppendObjectArgs args) { + super(args); + } - public ServerSideEncryptionCustomerKey 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}. */ + /** Builder of {@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..7cec6189c 100644 --- a/api/src/main/java/io/minio/ObjectVersionArgs.java +++ b/api/src/main/java/io/minio/ObjectVersionArgs.java @@ -18,15 +18,32 @@ import java.util.Objects; -/** Base argument class holds object name and version ID along with bucket information. */ +/** + * Common arguments of {@link DeleteObjectTagsArgs}, {@link DisableObjectLegalHoldArgs}, {@link + * EnableObjectLegalHoldArgs}, {@link GetObjectAclArgs}, {@link GetObjectRetentionArgs}, {@link + * GetObjectTagsArgs}, {@link GetPresignedObjectUrlArgs}, {@link IsObjectLegalHoldEnabledArgs}, + * {@link ObjectReadArgs}, {@link ObjectVersionArgs}, {@link RemoveObjectArgs}, {@link + * RestoreObjectArgs}, {@link SetObjectRetentionArgs} and {@link SetObjectTagsArgs}. + */ public abstract class ObjectVersionArgs extends ObjectArgs { protected String versionId; + protected ObjectVersionArgs() {} + + protected ObjectVersionArgs(ObjectVersionArgs args) { + super(args); + this.versionId = args.versionId; + } + + protected ObjectVersionArgs(AppendObjectArgs args) { + super(args); + } + public String versionId() { return versionId; } - /** Base argument builder class for {@link ObjectVersionArgs}. */ + /** Builder of {@link ObjectVersionArgs}. */ public abstract static class Builder, A extends ObjectVersionArgs> extends ObjectArgs.Builder { @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class diff --git a/api/src/main/java/io/minio/ObjectWriteArgs.java b/api/src/main/java/io/minio/ObjectWriteArgs.java index b7d76613b..840c4dfee 100644 --- a/api/src/main/java/io/minio/ObjectWriteArgs.java +++ b/api/src/main/java/io/minio/ObjectWriteArgs.java @@ -16,18 +16,19 @@ 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. */ +/** + * Common arguments of {@link UploadSnowballObjectsArgs}, {@link UploadSnowballObjectsArgs}, {@link + * PutObjectBaseArgs}, {@link ComposeObjectArgs} and {@link CopyObjectArgs}. + */ public abstract class ObjectWriteArgs extends ObjectArgs { // allowed maximum object size is 5TiB. public static final long MAX_OBJECT_SIZE = 5L * 1024 * 1024 * 1024 * 1024; @@ -37,20 +38,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,74 +81,73 @@ 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, 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}. */ + /** Builder of {@link ObjectWriteArgs}. */ @SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class public abstract static class Builder, A extends ObjectWriteArgs> extends ObjectArgs.Builder { 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.put( (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/ObjectWriteResponse.java b/api/src/main/java/io/minio/ObjectWriteResponse.java index 43ece549d..0c6825f79 100644 --- a/api/src/main/java/io/minio/ObjectWriteResponse.java +++ b/api/src/main/java/io/minio/ObjectWriteResponse.java @@ -20,7 +20,12 @@ import io.minio.messages.CopyObjectResult; import okhttp3.Headers; -/** Response class of any APIs doing object creation. */ +/** + * Response of {@link MinioAsyncClient#completeMultipartUpload}, {@link + * MinioAsyncClient#composeObject(io.minio.ComposeObjectArgs)}, {@link MinioAsyncClient#copyObject}, + * {@link MinioAsyncClient#putObject(io.minio.PutObjectArgs)}, {@link MinioAsyncClient#uploadObject} + * and {@link MinioAsyncClient#uploadSnowballObjects}. + */ public class ObjectWriteResponse extends GenericUploadResponse { private String versionId; diff --git a/api/src/main/java/io/minio/PartReader.java b/api/src/main/java/io/minio/PartReader.java index 9209c4536..1eb03e208 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,159 @@ 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; +/** + * Multipart data reader for {@link RandomAccessFile} or {@link InputStream} to given {@link + * ByteBuffer}. + */ +public class PartReader { + 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; + if (hashers != null) { + 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); + } + if (hashers != null) 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); + } + if (hashers != null) 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 deleted file mode 100644 index b18581eb5..000000000 --- a/api/src/main/java/io/minio/PartSource.java +++ /dev/null @@ -1,115 +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 java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; -import java.io.SequenceInputStream; -import java.nio.channels.Channels; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -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 RandomAccessFile file; - private long position; - - private ByteBufferStream[] buffers; - - private InputStream inputStream; - - private PartSource(int partNumber, long size, String md5Hash, String sha256Hash) { - this.partNumber = partNumber; - this.size = size; - this.md5Hash = md5Hash; - this.sha256Hash = sha256Hash; - } - - public PartSource( - int partNumber, @Nonnull RandomAccessFile file, long size, String md5Hash, String sha256Hash) - throws IOException { - this(partNumber, size, md5Hash, sha256Hash); - this.file = Objects.requireNonNull(file, "file must not be null"); - this.position = this.file.getFilePointer(); - } - - public PartSource( - int partNumber, - @Nonnull ByteBufferStream[] buffers, - long size, - String md5Hash, - String sha256Hash) { - this(partNumber, size, md5Hash, sha256Hash); - 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; - } - - public long size() { - return this.size; - } - - public String md5Hash() { - return this.md5Hash; - } - - public String sha256Hash() { - return this.sha256Hash; - } - - public Source source() 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); - } - - InputStream stream = buffers[0].inputStream(); - if (buffers.length == 1) return Okio.source(stream); - - List streams = new ArrayList<>(); - streams.add(stream); - for (int i = 1; i < buffers.length; i++) { - 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))); - } -} diff --git a/api/src/main/java/io/minio/PostPolicy.java b/api/src/main/java/io/minio/PostPolicy.java index 204d06a30..ee0660f93 100644 --- a/api/src/main/java/io/minio/PostPolicy.java +++ b/api/src/main/java/io/minio/PostPolicy.java @@ -19,16 +19,16 @@ 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.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; 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,22 +173,17 @@ 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 { - if (creds == null) { - throw new IllegalArgumentException("credentials cannot be null"); - } - - if (region.isEmpty()) { - throw new IllegalArgumentException("region cannot be empty"); - } + throws MinioException { + if (creds == null) throw new IllegalArgumentException("credentials cannot be null"); + if (region.isEmpty()) throw new IllegalArgumentException("region cannot be empty"); if (!conditions.get(EQ).containsKey("key") && !conditions.get(STARTS_WITH).containsKey("key")) { throw new IllegalArgumentException("key condition must be set"); } Map policyMap = new HashMap<>(); - policyMap.put("expiration", expiration.format(Time.EXPIRATION_DATE_FORMAT)); - List> conditionList = new LinkedList<>(); + policyMap.put("expiration", expiration.format(Time.ISO8601UTC_FORMAT)); + List> conditionList = new ArrayList<>(); conditionList.add(Arrays.asList(new Object[] {"eq", "$bucket", bucketName})); for (Map.Entry> condition : conditions.entrySet()) { for (Map.Entry entry : condition.getValue().entrySet()) { @@ -228,14 +223,15 @@ 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; } + /** Get bucket name. */ public String bucket() { return this.bucketName; } diff --git a/api/src/main/java/io/minio/PromptObjectArgs.java b/api/src/main/java/io/minio/PromptObjectArgs.java index f092f4c65..90dbf9ad9 100644 --- a/api/src/main/java/io/minio/PromptObjectArgs.java +++ b/api/src/main/java/io/minio/PromptObjectArgs.java @@ -19,8 +19,8 @@ import java.util.Map; import java.util.Objects; -/** Argument class of {@link MinioAsyncClient#promptObject} and {@link MinioClient#promptObject}. */ -public class PromptObjectArgs extends ObjectArgs { +/** Arguments of {@link MinioAsyncClient#promptObject} and {@link MinioClient#promptObject}. */ +public class PromptObjectArgs extends ObjectVersionArgs { private String prompt; private String lambdaArn; private Map promptArgs; @@ -46,30 +46,30 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link PromptObjectArgs}. */ - public static final class Builder extends ObjectArgs.Builder { + /** Builder of {@link PromptObjectArgs}. */ + public static final class Builder extends ObjectVersionArgs.Builder { @Override protected void validate(PromptObjectArgs args) { super.validate(args); - validateNotEmptyString(args.prompt, "prompt"); - validateNotEmptyString(args.lambdaArn, "lambda ARN"); - validateNotNull(args.promptArgs, "prompt argument"); + Utils.validateNotEmptyString(args.prompt, "prompt"); + Utils.validateNotEmptyString(args.lambdaArn, "lambda ARN"); + Utils.validateNotNull(args.promptArgs, "prompt argument"); } public Builder offset(String prompt) { - validateNotEmptyString(prompt, "prompt"); + Utils.validateNotEmptyString(prompt, "prompt"); operations.add(args -> 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/PromptObjectResponse.java b/api/src/main/java/io/minio/PromptObjectResponse.java index 0d501850b..38234f2f8 100644 --- a/api/src/main/java/io/minio/PromptObjectResponse.java +++ b/api/src/main/java/io/minio/PromptObjectResponse.java @@ -21,9 +21,9 @@ import okhttp3.Headers; /** - * Response class of {@link MinioAsyncClient#promptObject} and {@link MinioClient#promptObject}. - * This class is {@link InputStream} interface compatible and it must be closed after use to release - * underneath network resources. + * Response of {@link MinioAsyncClient#promptObject} and {@link MinioClient#promptObject}. As it is + * {@link InputStream} interface compatible, it must be closed after use to release underneath + * network resources. */ public class PromptObjectResponse extends FilterInputStream { private GenericResponse response; 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..48095d1cb --- /dev/null +++ b/api/src/main/java/io/minio/PutObjectAPIArgs.java @@ -0,0 +1,119 @@ +/* + * 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; + +/** Arguments of {@link BaseS3Client#putObject}. */ +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(AppendObjectArgs args, ByteBuffer buffer, Http.Headers headers) { + super(args, buffer, headers); + } + + public PutObjectAPIArgs( + AppendObjectArgs args, RandomAccessFile file, long length, Http.Headers headers) { + super(args, file, length, headers); + } + + public PutObjectAPIArgs(AppendObjectArgs args, byte[] data, int length, Http.Headers headers) { + super(args, data, length, headers); + } + + 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(); + } + + /** 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..a1217b493 --- /dev/null +++ b/api/src/main/java/io/minio/PutObjectAPIBaseArgs.java @@ -0,0 +1,177 @@ +/* + * 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; + +/** Arguments of {@link BaseS3Client#putObject}. */ +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; + } + + private PutObjectAPIBaseArgs(AppendObjectArgs args, Http.Headers headers) { + super(args); + this.headers = headers; + } + + protected PutObjectAPIBaseArgs(AppendObjectArgs args, ByteBuffer buffer, Http.Headers headers) { + this(args, headers); + this.buffer = buffer; + } + + protected PutObjectAPIBaseArgs( + AppendObjectArgs args, RandomAccessFile file, long length, Http.Headers headers) { + this(args, headers); + this.file = file; + this.length = length; + } + + protected PutObjectAPIBaseArgs( + AppendObjectArgs 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..d870c8beb 100644 --- a/api/src/main/java/io/minio/PutObjectArgs.java +++ b/api/src/main/java/io/minio/PutObjectArgs.java @@ -16,79 +16,83 @@ package io.minio; -import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Objects; +import okhttp3.MediaType; -/** Argument class of {@link MinioAsyncClient#putObject} and {@link MinioClient#putObject}. */ +/** + * Arguments of {@link MinioAsyncClient#putObject(io.minio.PutObjectArgs)} 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() { return new Builder(); } - /** Argument builder of {@link PutObjectArgs}. */ + /** Builder of {@link PutObjectArgs}. */ public static final class Builder extends PutObjectBaseArgs.Builder { @Override protected void validate(PutObjectArgs args) { super.validate(args); - validateNotNull(args.stream, "stream"); + if (args.stream == null && args.data == null) { + throw new IllegalArgumentException("either stream or data must be provided"); + } + } + + private Builder setStream( + InputStream stream, byte[] data, Long objectSize, long partSize, int partCount) { + operations.add(args -> 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); + return setStream(stream, null, objectSize, partinfo[0], (int) partinfo[1]); } - 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; - } - - 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); } } @@ -98,11 +102,11 @@ public boolean equals(Object o) { if (!(o instanceof PutObjectArgs)) return false; if (!super.equals(o)) return false; PutObjectArgs that = (PutObjectArgs) o; - return Objects.equals(stream, that.stream); + return Objects.equals(stream, that.stream) && Arrays.equals(data, that.data); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), stream); + return Objects.hash(super.hashCode(), stream, data); } } diff --git a/api/src/main/java/io/minio/PutObjectBaseArgs.java b/api/src/main/java/io/minio/PutObjectBaseArgs.java index aaec5e1bb..7ec3c9dd9 100644 --- a/api/src/main/java/io/minio/PutObjectBaseArgs.java +++ b/api/src/main/java/io/minio/PutObjectBaseArgs.java @@ -20,15 +20,16 @@ import java.util.Objects; import okhttp3.MediaType; -/** Base argument class for {@link PutObjectArgs} and {@link UploadObjectArgs}. */ +/** Common arguments of {@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}. */ + /** Builder of {@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..976f518fb 100644 --- a/api/src/main/java/io/minio/PutObjectFanOutArgs.java +++ b/api/src/main/java/io/minio/PutObjectFanOutArgs.java @@ -25,11 +25,9 @@ import java.io.InputStream; import java.util.List; import java.util.Objects; -import okhttp3.HttpUrl; /** - * Argument class of {@link MinioAsyncClient#putObjectFanOut} and {@link - * MinioClient#putObjectFanOut}. + * Arguments of {@link MinioAsyncClient#putObjectFanOut} and {@link MinioClient#putObjectFanOut}. */ public class PutObjectFanOutArgs extends BucketArgs { private static final ObjectMapper objectMapper = @@ -74,25 +72,25 @@ 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() { return new Builder(); } - /** Argument builder of {@link PutObjectFanOutArgs}. */ + /** Builder of {@link PutObjectFanOutArgs}. */ public static final class Builder extends BucketArgs.Builder { @Override protected void validate(PutObjectFanOutArgs args) { super.validate(args); - validateNotNull(args.stream, "stream"); - validateNotNull(args.entries, "fan-out entries"); + Utils.validateNotNull(args.stream, "stream"); + Utils.validateNotNull(args.entries, "fan-out entries"); } public Builder stream(InputStream stream, long size) { - validateNotNull(stream, "stream"); + Utils.validateNotNull(stream, "stream"); if (size < 0) { throw new IllegalArgumentException("invalid stream size " + size); } diff --git a/api/src/main/java/io/minio/PutObjectFanOutEntry.java b/api/src/main/java/io/minio/PutObjectFanOutEntry.java index 9bf029b97..ac9614707 100644 --- a/api/src/main/java/io/minio/PutObjectFanOutEntry.java +++ b/api/src/main/java/io/minio/PutObjectFanOutEntry.java @@ -67,11 +67,11 @@ public static Builder builder() { public static final class Builder extends BaseArgs.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..91db7b07f 100644 --- a/api/src/main/java/io/minio/PutObjectFanOutResponse.java +++ b/api/src/main/java/io/minio/PutObjectFanOutResponse.java @@ -17,13 +17,11 @@ package io.minio; import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; import java.util.List; import okhttp3.Headers; -/** - * Response class of {@link MinioAsyncClient#putObjectFanOut} and {@link - * MinioClient#putObjectFanOut}. - */ +/** Response of {@link MinioAsyncClient#putObjectFanOut} and {@link MinioClient#putObjectFanOut}. */ public class PutObjectFanOutResponse extends GenericUploadResponse { private List results; @@ -37,6 +35,7 @@ public List results() { return results; } + /** Result of {@link PutObjectFanOutResponse}. */ public static class Result { @JsonProperty("key") private String key; @@ -48,8 +47,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 +66,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/RemoveBucketArgs.java b/api/src/main/java/io/minio/RemoveBucketArgs.java index 958c3d40a..d7be206ff 100644 --- a/api/src/main/java/io/minio/RemoveBucketArgs.java +++ b/api/src/main/java/io/minio/RemoveBucketArgs.java @@ -16,13 +16,13 @@ package io.minio; -/** Argument class of {@link MinioAsyncClient#removeBucket} and {@link MinioClient#removeBucket}. */ +/** Arguments of {@link MinioAsyncClient#removeBucket} and {@link MinioClient#removeBucket}. */ public class RemoveBucketArgs extends BucketArgs { public static Builder builder() { return new Builder(); } - /** Argument builder of {@link RemoveBucketArgs}. */ + /** Builder of {@link RemoveBucketArgs}. */ public static final class Builder extends BucketArgs.Builder {} } diff --git a/api/src/main/java/io/minio/RemoveObjectArgs.java b/api/src/main/java/io/minio/RemoveObjectArgs.java index b33376118..d62d8b503 100644 --- a/api/src/main/java/io/minio/RemoveObjectArgs.java +++ b/api/src/main/java/io/minio/RemoveObjectArgs.java @@ -18,7 +18,7 @@ import java.util.Objects; -/** Argument class of {@link MinioAsyncClient#removeObject} and {@link MinioClient#removeObject}. */ +/** Arguments of {@link MinioAsyncClient#removeObject} and {@link MinioClient#removeObject}. */ public class RemoveObjectArgs extends ObjectVersionArgs { private boolean bypassGovernanceMode; @@ -30,7 +30,7 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link RemoveObjectArgs}. */ + /** Builder of {@link RemoveObjectArgs}. */ public static final class Builder extends ObjectVersionArgs.Builder { public Builder bypassGovernanceMode(boolean flag) { operations.add(args -> args.bypassGovernanceMode = flag); diff --git a/api/src/main/java/io/minio/RemoveObjectsArgs.java b/api/src/main/java/io/minio/RemoveObjectsArgs.java index a3b678e65..e4715d264 100644 --- a/api/src/main/java/io/minio/RemoveObjectsArgs.java +++ b/api/src/main/java/io/minio/RemoveObjectsArgs.java @@ -16,22 +16,20 @@ package io.minio; -import io.minio.messages.DeleteObject; -import java.util.LinkedList; +import io.minio.messages.DeleteRequest; +import java.util.ArrayList; import java.util.Objects; -/** - * Argument class of {@link MinioAsyncClient#removeObjects} and {@link MinioClient#removeObjects}. - */ +/** Arguments of {@link MinioAsyncClient#removeObjects} and {@link MinioClient#removeObjects}. */ public class RemoveObjectsArgs extends BucketArgs { private boolean bypassGovernanceMode; - private Iterable objects = new LinkedList<>(); + private Iterable objects = new ArrayList<>(); public boolean bypassGovernanceMode() { return bypassGovernanceMode; } - public Iterable objects() { + public Iterable objects() { return objects; } @@ -39,15 +37,15 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link RemoveObjectsArgs}. */ + /** Builder of {@link RemoveObjectsArgs}. */ public static final class Builder extends BucketArgs.Builder { public Builder bypassGovernanceMode(boolean flag) { operations.add(args -> args.bypassGovernanceMode = 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..771b77aa2 100644 --- a/api/src/main/java/io/minio/RestoreObjectArgs.java +++ b/api/src/main/java/io/minio/RestoreObjectArgs.java @@ -19,7 +19,7 @@ import io.minio.messages.RestoreRequest; import java.util.Objects; -/** Argument class of {@link MinioClient#restoreObject}. */ +/** Arguments of {@link MinioClient#restoreObject}. */ public class RestoreObjectArgs extends ObjectVersionArgs { private RestoreRequest request; @@ -31,10 +31,10 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link RestoreObjectArgs}. */ + /** 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/Result.java b/api/src/main/java/io/minio/Result.java index 0b3b3cc50..bb1df0e74 100644 --- a/api/src/main/java/io/minio/Result.java +++ b/api/src/main/java/io/minio/Result.java @@ -16,91 +16,26 @@ package io.minio; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; -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 java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import io.minio.errors.MinioException; -/** A container class keeps any type or an exception. */ +/** A container class keeps any type or a MinioException. */ public class Result { private final T type; - private final Exception ex; + private final MinioException ex; public Result(T type) { this.type = type; this.ex = null; } - public Result(Exception ex) { + public Result(MinioException ex) { this.type = null; this.ex = ex; } - /** Returns given Type if exception is null, else respective exception is thrown. */ - public T get() - throws ErrorResponseException, IllegalArgumentException, InsufficientDataException, - InternalException, InvalidKeyException, InvalidResponseException, IOException, - JsonMappingException, JsonParseException, NoSuchAlgorithmException, ServerException, - XmlParserException { - if (ex == null) { - return type; - } - - if (ex instanceof ErrorResponseException) { - throw (ErrorResponseException) ex; - } - - if (ex instanceof IllegalArgumentException) { - throw (IllegalArgumentException) ex; - } - - if (ex instanceof InsufficientDataException) { - throw (InsufficientDataException) ex; - } - - if (ex instanceof InternalException) { - throw (InternalException) ex; - } - - if (ex instanceof InvalidKeyException) { - throw (InvalidKeyException) ex; - } - - if (ex instanceof InvalidResponseException) { - throw (InvalidResponseException) ex; - } - - if (ex instanceof IOException) { - throw (IOException) ex; - } - - if (ex instanceof JsonMappingException) { - throw (JsonMappingException) ex; - } - - if (ex instanceof JsonParseException) { - throw (JsonParseException) ex; - } - - if (ex instanceof NoSuchAlgorithmException) { - throw (NoSuchAlgorithmException) ex; - } - - if (ex instanceof ServerException) { - throw (ServerException) ex; - } - - if (ex instanceof XmlParserException) { - throw (XmlParserException) ex; - } - - throw new RuntimeException("Exception not handled", ex); + /** Returns given Type if exception is null, else the exception is thrown. */ + public T get() throws MinioException { + if (ex == null) return type; + throw ex; } } 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 324637ccb..000000000 --- a/api/src/main/java/io/minio/S3Base.java +++ /dev/null @@ -1,3909 +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; - - /** @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, - 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; - } - - /** @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; - 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); - } - }); - } - - /** - * 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, - 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; - }); - } - - /** - * 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) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - 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) - 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 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) - 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 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, - Multimap headers, - Multimap queryParams, - Object data) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - 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, - Multimap headers, - Multimap queryParams, - Object data, - int length) - throws InsufficientDataException, InternalException, InvalidKeyException, IOException, - NoSuchAlgorithmException, XmlParserException { - 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 { - 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; - } - - /** 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; - 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; - } - - /** - * 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; - 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 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; - } - - /** 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 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 - * 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 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 - * 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 CreateMultipartUpload - * S3 API. - * - * @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 CreateMultipartUploadResponse} 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 #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( - 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 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. - * - * @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. - * - * @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. - * - * @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 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 - * 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(); - } - }); - } - - /** - * 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, - 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 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 - * 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 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. - * - * @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 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. - * - * @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 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 - * 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..8c1e6ba7c 100644 --- a/api/src/main/java/io/minio/SelectObjectContentArgs.java +++ b/api/src/main/java/io/minio/SelectObjectContentArgs.java @@ -22,7 +22,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#selectObjectContent} and {@link + * Arguments of {@link MinioAsyncClient#selectObjectContent} and {@link * MinioClient#selectObjectContent}. */ public class SelectObjectContentArgs extends ObjectReadArgs { @@ -61,11 +61,11 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SelectObjectContentArgs}. */ + /** Builder of {@link SelectObjectContentArgs}. */ 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..95726ce97 100644 --- a/api/src/main/java/io/minio/ServerSideEncryption.java +++ b/api/src/main/java/io/minio/ServerSideEncryption.java @@ -16,19 +16,147 @@ package io.minio; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.minio.errors.MinioException; +import java.nio.charset.StandardCharsets; 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 MinioException { + 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) { + try { + this.headers.put( + "X-Amz-Server-Side-Encryption-Context", + Checksum.base64String( + objectMapper.writeValueAsString(context).getBytes(StandardCharsets.UTF_8))); + } catch (JsonProcessingException e) { + throw new MinioException(e); + } + } + } + + @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 MinioException { + 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(); + String customerKey = Checksum.base64String(keyBytes); + String customerKeyMd5 = Checksum.base64String(Checksum.MD5.sum(keyBytes)); + + 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..3c0d3e64a 100644 --- a/api/src/main/java/io/minio/SetBucketCorsArgs.java +++ b/api/src/main/java/io/minio/SetBucketCorsArgs.java @@ -19,9 +19,7 @@ import io.minio.messages.CORSConfiguration; import java.util.Objects; -/** - * Argument class of {@link MinioAsyncClient#setBucketCors} and {@link MinioClient#setBucketCors}. - */ +/** Arguments of {@link MinioAsyncClient#setBucketCors} and {@link MinioClient#setBucketCors}. */ public class SetBucketCorsArgs extends BucketArgs { private CORSConfiguration config; @@ -33,10 +31,10 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetBucketCorsArgs}. */ + /** 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..148f20538 100644 --- a/api/src/main/java/io/minio/SetBucketEncryptionArgs.java +++ b/api/src/main/java/io/minio/SetBucketEncryptionArgs.java @@ -20,7 +20,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#setBucketEncryption} and {@link + * Arguments of {@link MinioAsyncClient#setBucketEncryption} and {@link * MinioClient#setBucketEncryption}. */ public class SetBucketEncryptionArgs extends BucketArgs { @@ -34,10 +34,10 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetBucketEncryptionArgs}. */ + /** 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..7b43d172a 100644 --- a/api/src/main/java/io/minio/SetBucketLifecycleArgs.java +++ b/api/src/main/java/io/minio/SetBucketLifecycleArgs.java @@ -20,7 +20,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#setBucketLifecycle} and {@link + * Arguments of {@link MinioAsyncClient#setBucketLifecycle} and {@link * MinioClient#setBucketLifecycle}. */ public class SetBucketLifecycleArgs extends BucketArgs { @@ -34,10 +34,10 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetBucketLifecycleArgs}. */ + /** 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..40e9e066a 100644 --- a/api/src/main/java/io/minio/SetBucketNotificationArgs.java +++ b/api/src/main/java/io/minio/SetBucketNotificationArgs.java @@ -20,7 +20,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#setBucketNotification} and {@link + * Arguments of {@link MinioAsyncClient#setBucketNotification} and {@link * MinioClient#setBucketNotification}. */ public class SetBucketNotificationArgs extends BucketArgs { @@ -34,10 +34,10 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetBucketNotificationArgs}. */ + /** 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..3af76a918 100644 --- a/api/src/main/java/io/minio/SetBucketPolicyArgs.java +++ b/api/src/main/java/io/minio/SetBucketPolicyArgs.java @@ -19,8 +19,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#setBucketPolicy} and {@link - * MinioClient#setBucketPolicy}. + * Arguments of {@link MinioAsyncClient#setBucketPolicy} and {@link MinioClient#setBucketPolicy}. */ public class SetBucketPolicyArgs extends BucketArgs { private String config; @@ -33,10 +32,10 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetBucketPolicyArgs}. */ + /** 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..15fc250ff 100644 --- a/api/src/main/java/io/minio/SetBucketReplicationArgs.java +++ b/api/src/main/java/io/minio/SetBucketReplicationArgs.java @@ -20,7 +20,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#setBucketReplication} and {@link + * Arguments of {@link MinioAsyncClient#setBucketReplication} and {@link * MinioClient#setBucketReplication}. */ public class SetBucketReplicationArgs extends BucketArgs { @@ -39,14 +39,14 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetBucketReplicationArgs}. */ + /** 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..da4c371c8 100644 --- a/api/src/main/java/io/minio/SetBucketTagsArgs.java +++ b/api/src/main/java/io/minio/SetBucketTagsArgs.java @@ -20,9 +20,7 @@ import java.util.Map; import java.util.Objects; -/** - * Argument class of {@link MinioAsyncClient#setBucketTags} and {@link MinioClient#setBucketTags}. - */ +/** Arguments of {@link MinioAsyncClient#setBucketTags} and {@link MinioClient#setBucketTags}. */ public class SetBucketTagsArgs extends BucketArgs { private Tags tags; @@ -34,10 +32,10 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetBucketTagsArgs}. */ + /** 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 +44,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..9b9bbd3cb 100644 --- a/api/src/main/java/io/minio/SetBucketVersioningArgs.java +++ b/api/src/main/java/io/minio/SetBucketVersioningArgs.java @@ -20,7 +20,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#setBucketVersioning} and {@link + * Arguments of {@link MinioAsyncClient#setBucketVersioning} and {@link * MinioClient#setBucketVersioning}. */ public class SetBucketVersioningArgs extends BucketArgs { @@ -34,10 +34,10 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetBucketVersioningArgs}. */ + /** 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..71f1a9b1d 100644 --- a/api/src/main/java/io/minio/SetObjectLockConfigurationArgs.java +++ b/api/src/main/java/io/minio/SetObjectLockConfigurationArgs.java @@ -20,7 +20,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#setObjectLockConfiguration} and {@link + * Arguments of {@link MinioAsyncClient#setObjectLockConfiguration} and {@link * MinioClient#setObjectLockConfiguration}. */ public class SetObjectLockConfigurationArgs extends BucketArgs { @@ -34,11 +34,11 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetObjectLockConfigurationArgs}. */ + /** Builder of {@link SetObjectLockConfigurationArgs}. */ 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..6f5d52bad 100644 --- a/api/src/main/java/io/minio/SetObjectRetentionArgs.java +++ b/api/src/main/java/io/minio/SetObjectRetentionArgs.java @@ -20,7 +20,7 @@ import java.util.Objects; /** - * Argument class of {@link MinioAsyncClient#setObjectRetention} and {@link + * Arguments of {@link MinioAsyncClient#setObjectRetention} and {@link * MinioClient#setObjectRetention}. */ public class SetObjectRetentionArgs extends ObjectVersionArgs { @@ -39,11 +39,11 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetObjectRetentionArgs}. */ + /** Builder of {@link SetObjectRetentionArgs}. */ 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..1e20bab68 100644 --- a/api/src/main/java/io/minio/SetObjectTagsArgs.java +++ b/api/src/main/java/io/minio/SetObjectTagsArgs.java @@ -20,9 +20,7 @@ import java.util.Map; import java.util.Objects; -/** - * Argument class of {@link MinioAsyncClient#setObjectTags} and {@link MinioClient#setObjectTags}. - */ +/** Arguments of {@link MinioAsyncClient#setObjectTags} and {@link MinioClient#setObjectTags}. */ public class SetObjectTagsArgs extends ObjectVersionArgs { private Tags tags; @@ -34,10 +32,10 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link SetObjectTagsArgs}. */ + /** 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 +44,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..5d00d427e --- /dev/null +++ b/api/src/main/java/io/minio/SourceObject.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.errors.InternalException; +import java.util.Objects; + +/** A source object definition 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"); + long composeSize = (offset == null ? 0 : offset) + length; + if (composeSize > objectSize) throwException(objectSize, composeSize, "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..c8b277637 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. @@ -16,25 +16,14 @@ package io.minio; -/** Argument class of {@link MinioAsyncClient#statObject} and {@link MinioClient#statObject}. */ -public class StatObjectArgs extends ObjectConditionalReadArgs { +/** Arguments of {@link MinioAsyncClient#statObject} and {@link MinioClient#statObject}. */ +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 {} + /** Builder of {@link StatObjectArgs}. */ + 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..5bce3b0f2 100644 --- a/api/src/main/java/io/minio/UploadObjectArgs.java +++ b/api/src/main/java/io/minio/UploadObjectArgs.java @@ -16,12 +16,14 @@ package io.minio; +import io.minio.errors.MinioException; import java.io.IOException; 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}. */ +/** Arguments of {@link MinioAsyncClient#uploadObject} and {@link MinioClient#uploadObject}. */ public class UploadObjectArgs extends PutObjectBaseArgs { private String filename; @@ -29,27 +31,18 @@ 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() { return new Builder(); } - /** Argument builder of {@link UploadObjectArgs}. */ + /** Builder of {@link UploadObjectArgs}. */ public static final class Builder extends PutObjectBaseArgs.Builder { @Override protected void validate(UploadObjectArgs args) { @@ -58,36 +51,34 @@ 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"); } } - public Builder filename(String filename, long partSize) throws IOException { - validateFilename(filename); - final long objectSize = Files.size(Paths.get(filename)); - - long[] partinfo = getPartInfo(objectSize, partSize); - final long pSize = partinfo[0]; - final int partCount = (int) partinfo[1]; - - operations.add(args -> args.filename = filename); - operations.add(args -> args.objectSize = objectSize); - operations.add(args -> args.partSize = pSize); - operations.add(args -> args.partCount = partCount); - return this; + public Builder filename(String filename, long partSize) throws MinioException { + try { + validateFilename(filename); + final long objectSize = Files.size(Paths.get(filename)); + + long[] partinfo = getPartInfo(objectSize, partSize); + final long pSize = partinfo[0]; + final int partCount = (int) partinfo[1]; + + operations.add(args -> args.filename = filename); + operations.add(args -> args.objectSize = objectSize); + operations.add(args -> args.partSize = pSize); + operations.add(args -> args.partCount = partCount); + return this; + } catch (IOException e) { + throw new MinioException(e); + } } - public Builder filename(String filename) throws IOException { + public Builder filename(String filename) throws MinioException { 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..5c1933aa2 --- /dev/null +++ b/api/src/main/java/io/minio/UploadPartArgs.java @@ -0,0 +1,105 @@ +/* + * 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; + +/** Arguments of {@link BaseS3Client#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(); + } + + /** 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; + } + } + + /** Wrapper of {@link UploadPartArgs} to be used in parallel part uploads. */ + public static class Wrapper { + final UploadPartArgs args; + + public Wrapper(UploadPartArgs args) { + this.args = args; + } + + public UploadPartArgs args() { + return args; + } + } + + @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..8da88ec4c --- /dev/null +++ b/api/src/main/java/io/minio/UploadPartCopyArgs.java @@ -0,0 +1,99 @@ +/* + * 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; + +/** Arguments of {@link BaseS3Client#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(); + } + + /** 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..7e2cfe08d 100644 --- a/api/src/main/java/io/minio/UploadPartCopyResponse.java +++ b/api/src/main/java/io/minio/UploadPartCopyResponse.java @@ -17,9 +17,10 @@ package io.minio; import io.minio.messages.CopyPartResult; +import io.minio.messages.Part; import okhttp3.Headers; -/** Response class of {@link S3Base#uploadPartCopyAsync}. */ +/** Response of {@link BaseS3Client#uploadPartCopy}. */ public class UploadPartCopyResponse extends GenericResponse { private String uploadId; private int partNumber; @@ -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..cbcb01262 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}. */ +/** Response of {@link BaseS3Client#uploadPart}. */ 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..bf606f2ab 100644 --- a/api/src/main/java/io/minio/UploadSnowballObjectsArgs.java +++ b/api/src/main/java/io/minio/UploadSnowballObjectsArgs.java @@ -21,7 +21,7 @@ import java.util.Random; /** - * Argument class of {@link MinioAsyncClient#uploadSnowballObjects} and {@link + * Arguments of {@link MinioAsyncClient#uploadSnowballObjects} and {@link * MinioClient#uploadSnowballObjects}. */ public class UploadSnowballObjectsArgs extends ObjectWriteArgs { @@ -47,11 +47,11 @@ public static Builder builder() { return new Builder(); } - /** Argument builder of {@link UploadSnowballObjectsArgs}. */ + /** Builder of {@link UploadSnowballObjectsArgs}. */ 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..2de296ed9 100644 --- a/api/src/main/java/io/minio/Utils.java +++ b/api/src/main/java/io/minio/Utils.java @@ -16,23 +16,142 @@ 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.CompletableFuture; +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 { - public static final String UTF_8 = StandardCharsets.UTF_8.toString(); + private static final Escaper ESCAPER = UrlEscapers.urlPathSegmentEscaper(); + + 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 { - return value == null ? null : URLDecoder.decode(value, UTF_8); + return value == null ? null : URLDecoder.decode(value, StandardCharsets.UTF_8.toString()); } catch (UnsupportedEncodingException e) { // This never happens as 'enc' name comes from JDK's own StandardCharsets. throw new RuntimeException(e); @@ -40,10 +159,595 @@ public static String urlDecode(String value, String type) { } public static List unmodifiableList(List value) { - return Collections.unmodifiableList(value == null ? new LinkedList() : value); + return Collections.unmodifiableList(value == null ? new ArrayList() : 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 CompletableFuture failedFuture(Throwable throwable) { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(throwable); + return future; + } + + 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..44f8ec264 100644 --- a/api/src/main/java/io/minio/credentials/AssumeRoleProvider.java +++ b/api/src/main/java/io/minio/credentials/AssumeRoleProvider.java @@ -16,11 +16,12 @@ 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.Utils; +import io.minio.errors.MinioException; import java.security.ProviderException; import java.time.ZonedDateTime; import java.util.Objects; @@ -41,7 +42,7 @@ * href="https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html">AssumeRole * API. */ -public class AssumeRoleProvider extends AssumeRoleBaseProvider { +public class AssumeRoleProvider extends BaseIdentityProvider { private final String accessKey; private final String secretKey; private final String region; @@ -59,7 +60,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"); @@ -75,13 +76,6 @@ public AssumeRoleProvider( throw new IllegalArgumentException("Length of ExternalId must be in between 2 and 1224"); } - String host = url.host() + ":" + url.port(); - // 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)) { - host = url.host(); - } - HttpUrl.Builder urlBuilder = newUrlBuilder( url, @@ -95,11 +89,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, Utils.getHostHeader(url)) .method( "POST", RequestBody.create(data, MediaType.parse("application/x-www-form-urlencoded"))) @@ -112,26 +106,30 @@ 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); } } @Override - protected Class getResponseClass() { - return AssumeRoleResponse.class; + protected Class getResponseClass() { + return Response.class; } - /** Object representation of response XML of AssumeRole API. */ + /** + * Response XML of AssumeRole + * API. + */ @Root(name = "AssumeRoleResponse", strict = false) @Namespace(reference = "https://sts.amazonaws.com/doc/2011-06-15/") - public static class AssumeRoleResponse implements AssumeRoleBaseProvider.Response { + public static class Response implements BaseIdentityProvider.Response { @Path(value = "AssumeRoleResult") @Element(name = "Credentials") private Credentials credentials; diff --git a/api/src/main/java/io/minio/credentials/AwsConfigProvider.java b/api/src/main/java/io/minio/credentials/AwsConfigProvider.java index 2a2e594d3..0eb95c556 100644 --- a/api/src/main/java/io/minio/credentials/AwsConfigProvider.java +++ b/api/src/main/java/io/minio/credentials/AwsConfigProvider.java @@ -16,12 +16,12 @@ package io.minio.credentials; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Paths; import java.security.ProviderException; import java.util.HashMap; @@ -54,23 +54,17 @@ public AwsConfigProvider(@Nullable String filename, @Nullable String profile) { */ @Override public Credentials fetch() { - String filename = this.filename; - if (filename == null) { - filename = getProperty("AWS_SHARED_CREDENTIALS_FILE"); - } + String filename = + this.filename != null ? this.filename : getProperty("AWS_SHARED_CREDENTIALS_FILE"); if (filename == null) { filename = Paths.get(System.getProperty("user.home"), ".aws", "credentials").toString(); } String profile = this.profile; - if (profile == null) { - profile = getProperty("AWS_PROFILE"); - } - if (profile == null) { - profile = "default"; - } + if (profile == null) profile = getProperty("AWS_PROFILE"); + if (profile == null) profile = "default"; - try (InputStream is = new FileInputStream(filename)) { + try (InputStream is = Files.newInputStream(Paths.get(filename))) { Map result = unmarshal(new InputStreamReader(is, StandardCharsets.UTF_8)); Properties values = result.get(profile); if (values == null) { diff --git a/api/src/main/java/io/minio/credentials/AwsEnvironmentProvider.java b/api/src/main/java/io/minio/credentials/AwsEnvironmentProvider.java index 8e6a19450..006d21f9c 100644 --- a/api/src/main/java/io/minio/credentials/AwsEnvironmentProvider.java +++ b/api/src/main/java/io/minio/credentials/AwsEnvironmentProvider.java @@ -16,18 +16,41 @@ package io.minio.credentials; +import java.security.ProviderException; + /** Credential provider using Amazon AWS specific environment variables. */ public class AwsEnvironmentProvider extends EnvironmentProvider { public AwsEnvironmentProvider() {} + private final String getValue(String key, String name) { + String value = getProperty(key); + if (value != null && value.isEmpty()) { + throw new ProviderException("Empty " + name + " in " + key + " environment variable"); + } + return value; + } + + private final String getValue(String primaryKey, String secondaryKey, String name) { + String value = getValue(primaryKey, name); + return value != null ? value : getValue(secondaryKey, name); + } + private final String getAccessKey() { - String value = getProperty("AWS_ACCESS_KEY_ID"); - return (value != null) ? value : getProperty("AWS_ACCESS_KEY"); + String value = getValue("AWS_ACCESS_KEY_ID", "AWS_ACCESS_KEY", "access key"); + if (value == null) { + throw new ProviderException( + "Access key does not exist in AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY environment variable"); + } + return value; } private final String getSecretKey() { - String value = getProperty("AWS_SECRET_ACCESS_KEY"); - return (value != null) ? value : getProperty("AWS_SECRET_KEY"); + String value = getValue("AWS_SECRET_ACCESS_KEY", "AWS_SECRET_KEY", "secret key"); + if (value == null) { + throw new ProviderException( + "Secret key does not exist in AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY environment variable"); + } + return value; } @Override diff --git a/api/src/main/java/io/minio/credentials/AssumeRoleBaseProvider.java b/api/src/main/java/io/minio/credentials/BaseIdentityProvider.java similarity index 82% rename from api/src/main/java/io/minio/credentials/AssumeRoleBaseProvider.java rename to api/src/main/java/io/minio/credentials/BaseIdentityProvider.java index becc81b8e..75c98ce2d 100644 --- a/api/src/main/java/io/minio/credentials/AssumeRoleBaseProvider.java +++ b/api/src/main/java/io/minio/credentials/BaseIdentityProvider.java @@ -17,9 +17,9 @@ package io.minio.credentials; import io.minio.Xml; +import io.minio.errors.MinioException; import io.minio.errors.XmlParserException; import java.io.IOException; -import java.security.GeneralSecurityException; import java.security.ProviderException; import java.util.Arrays; import java.util.concurrent.TimeUnit; @@ -30,13 +30,16 @@ import okhttp3.Protocol; import okhttp3.Request; -/** Base class to AssumeRole based providers. */ -public abstract class AssumeRoleBaseProvider implements Provider { +/** + * Base provider of {@link LdapIdentityProvider}, {@link CertificateIdentityProvider}, {@link + * WebIdentityClientGrantsProvider} and {@link AssumeRoleProvider}. + */ +public abstract class BaseIdentityProvider implements Provider { public static final int DEFAULT_DURATION_SECONDS = (int) TimeUnit.HOURS.toSeconds(1); private final OkHttpClient httpClient; private Credentials credentials; - public AssumeRoleBaseProvider(OkHttpClient customHttpClient) { + public BaseIdentityProvider(OkHttpClient customHttpClient) { // HTTP/1.1 is only supported in default client because of HTTP/2 in OkHttpClient cause 5 // minutes timeout on program exit. this.httpClient = @@ -45,11 +48,11 @@ public AssumeRoleBaseProvider(OkHttpClient customHttpClient) { : new OkHttpClient().newBuilder().protocols(Arrays.asList(Protocol.HTTP_1_1)).build(); } - public AssumeRoleBaseProvider( + public BaseIdentityProvider( OkHttpClient customHttpClient, SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) - throws GeneralSecurityException, IOException { + throws MinioException { if (customHttpClient != null) { if (sslSocketFactory != null || trustManager != null) { throw new IllegalArgumentException( @@ -76,9 +79,7 @@ public AssumeRoleBaseProvider( @Override public synchronized Credentials fetch() { - if (credentials != null && !credentials.isExpired()) { - return credentials; - } + if (credentials != null && !credentials.isExpired()) return credentials; try (okhttp3.Response response = httpClient.newCall(getRequest()).execute()) { if (!response.isSuccessful()) { @@ -114,17 +115,9 @@ protected HttpUrl.Builder newUrlBuilder( urlBuilder.addQueryParameter("DurationSeconds", String.valueOf(durationSeconds)); } - if (policy != null) { - urlBuilder.addQueryParameter("Policy", policy); - } - - if (roleArn != null) { - urlBuilder.addQueryParameter("RoleArn", roleArn); - } - - if (roleSessionName != null) { - urlBuilder.addQueryParameter("RoleSessionName", roleSessionName); - } + if (policy != null) urlBuilder.addQueryParameter("Policy", policy); + if (roleArn != null) urlBuilder.addQueryParameter("RoleArn", roleArn); + if (roleSessionName != null) urlBuilder.addQueryParameter("RoleSessionName", roleSessionName); return urlBuilder; } @@ -137,8 +130,9 @@ protected Credentials parseResponse(okhttp3.Response response) protected abstract Request getRequest(); - protected abstract Class getResponseClass(); + protected abstract Class getResponseClass(); + /** Response to get credentials of {@link BaseIdentityProvider}. */ public static interface Response { public Credentials getCredentials(); } diff --git a/api/src/main/java/io/minio/credentials/CertificateIdentityProvider.java b/api/src/main/java/io/minio/credentials/CertificateIdentityProvider.java index 5fb1afebe..71fe13ef0 100644 --- a/api/src/main/java/io/minio/credentials/CertificateIdentityProvider.java +++ b/api/src/main/java/io/minio/credentials/CertificateIdentityProvider.java @@ -16,8 +16,7 @@ package io.minio.credentials; -import java.io.IOException; -import java.security.GeneralSecurityException; +import io.minio.errors.MinioException; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -38,7 +37,7 @@ * href="https://github.com/minio/minio/blob/master/docs/sts/tls.md">AssumeRoleWithCertificate * API. */ -public class CertificateIdentityProvider extends AssumeRoleBaseProvider { +public class CertificateIdentityProvider extends BaseIdentityProvider { private static final RequestBody EMPTY_BODY = RequestBody.create(new byte[] {}, MediaType.parse("application/octet-stream")); private final Request request; @@ -49,7 +48,7 @@ public CertificateIdentityProvider( @Nullable X509TrustManager trustManager, @Nullable Integer durationSeconds, @Nullable OkHttpClient customHttpClient) - throws GeneralSecurityException, IOException { + throws MinioException { super(customHttpClient, sslSocketFactory, trustManager); stsEndpoint = Objects.requireNonNull(stsEndpoint, "STS endpoint cannot be empty"); HttpUrl url = Objects.requireNonNull(HttpUrl.parse(stsEndpoint), "Invalid STS endpoint"); @@ -75,14 +74,18 @@ protected Request getRequest() { } @Override - protected Class getResponseClass() { - return CertificateIdentityResponse.class; + protected Class getResponseClass() { + return Response.class; } - /** Object representation of response XML of AssumeRoleWithCertificate API. */ + /** + * Response XML of AssumeRoleWithCertificate + * API. + */ @Root(name = "AssumeRoleWithCertificateResponse", strict = false) @Namespace(reference = "https://sts.amazonaws.com/doc/2011-06-15/") - public static class CertificateIdentityResponse implements AssumeRoleBaseProvider.Response { + public static class Response implements BaseIdentityProvider.Response { @Path(value = "AssumeRoleWithCertificateResult") @Element(name = "Credentials") private Credentials credentials; diff --git a/api/src/main/java/io/minio/credentials/ChainedProvider.java b/api/src/main/java/io/minio/credentials/ChainedProvider.java index 92af16550..46e2af325 100644 --- a/api/src/main/java/io/minio/credentials/ChainedProvider.java +++ b/api/src/main/java/io/minio/credentials/ChainedProvider.java @@ -33,9 +33,7 @@ public ChainedProvider(@Nonnull Provider... providers) { @Override public synchronized Credentials fetch() { - if (credentials != null && !credentials.isExpired()) { - return credentials; - } + if (credentials != null && !credentials.isExpired()) return credentials; if (currentProvider != null) { try { diff --git a/api/src/main/java/io/minio/credentials/ClientGrantsProvider.java b/api/src/main/java/io/minio/credentials/ClientGrantsProvider.java index 9e1c80747..2c808ee6a 100644 --- a/api/src/main/java/io/minio/credentials/ClientGrantsProvider.java +++ b/api/src/main/java/io/minio/credentials/ClientGrantsProvider.java @@ -55,14 +55,18 @@ protected HttpUrl.Builder newUrlBuilder(Jwt jwt) { } @Override - protected Class getResponseClass() { - return ClientGrantsResponse.class; + protected Class getResponseClass() { + return Response.class; } - /** Object representation of response XML of AssumeRoleWithClientGrants API. */ + /** + * Response XML of AssumeRoleWithClientGrants + * API. + */ @Root(name = "AssumeRoleWithClientGrantsResponse", strict = false) @Namespace(reference = "https://sts.amazonaws.com/doc/2011-06-15/") - public static class ClientGrantsResponse implements AssumeRoleBaseProvider.Response { + public static class Response implements BaseIdentityProvider.Response { @Path(value = "AssumeRoleWithClientGrantsResult") @Element(name = "Credentials") private Credentials credentials; diff --git a/api/src/main/java/io/minio/credentials/Credentials.java b/api/src/main/java/io/minio/credentials/Credentials.java index fb28da816..136f1f9fd 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; @@ -27,7 +27,7 @@ import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; -/** Object representation of credentials access key, secret key and session token. */ +/** Credentials contains access key, secret key and session token. */ @Root(name = "Credentials", strict = false) @JsonIgnoreProperties(ignoreUnknown = true) public class Credentials { @@ -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,12 @@ public String sessionToken() { return sessionToken; } - public boolean isExpired() { - if (expiration == null) { - return false; - } + public ZonedDateTime expiration() { + return expiration == null ? null : expiration.toZonedDateTime(); + } - return ZonedDateTime.now().plus(Duration.ofSeconds(10)).isAfter(expiration.zonedDateTime()); + public boolean isExpired() { + if (expiration == null) return false; + return ZonedDateTime.now().plus(Duration.ofSeconds(10)).isAfter(expiration.toZonedDateTime()); } } diff --git a/api/src/main/java/io/minio/credentials/EnvironmentProvider.java b/api/src/main/java/io/minio/credentials/EnvironmentProvider.java index 6b500f6c1..80ec3f79e 100644 --- a/api/src/main/java/io/minio/credentials/EnvironmentProvider.java +++ b/api/src/main/java/io/minio/credentials/EnvironmentProvider.java @@ -19,7 +19,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -/** Base class of credential providers using environment variables. */ +/** + * Base environment provider of {@link AwsConfigProvider}, {@link MinioClientConfigProvider}, {@link + * AwsEnvironmentProvider}, {@link MinioEnvironmentProvider} and {@link IamAwsProvider}. + */ public abstract class EnvironmentProvider implements Provider { /** Get value of a property from system property or environment variable. */ @Nullable diff --git a/api/src/main/java/io/minio/credentials/IamAwsProvider.java b/api/src/main/java/io/minio/credentials/IamAwsProvider.java index 08abb1050..74accf59e 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; @@ -200,9 +201,7 @@ private HttpUrl getIamRoleNamedUrl(String token) { @Override public synchronized Credentials fetch() { - if (credentials != null && !credentials.isExpired()) { - return credentials; - } + if (credentials != null && !credentials.isExpired()) return credentials; HttpUrl url = this.customEndpoint; String tokenFile = getProperty("AWS_WEB_IDENTITY_TOKEN_FILE"); @@ -211,7 +210,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) { @@ -223,9 +222,7 @@ public synchronized Credentials fetch() { .build(); } } else if (getProperty("AWS_CONTAINER_CREDENTIALS_FULL_URI") != null) { - if (url == null) { - url = HttpUrl.parse(getProperty("AWS_CONTAINER_CREDENTIALS_FULL_URI")); - } + if (url == null) url = HttpUrl.parse(getProperty("AWS_CONTAINER_CREDENTIALS_FULL_URI")); checkLoopbackHost(url); } else { token = fetchImdsToken(); @@ -237,6 +234,7 @@ public synchronized Credentials fetch() { return credentials; } + /** ECS Credentials of {@link IamAwsProvider}. */ public static class EcsCredentials { @JsonProperty("AccessKeyID") private String accessKey; @@ -248,7 +246,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/credentials/Jwt.java b/api/src/main/java/io/minio/credentials/Jwt.java index 86088059e..0475ebfcd 100644 --- a/api/src/main/java/io/minio/credentials/Jwt.java +++ b/api/src/main/java/io/minio/credentials/Jwt.java @@ -21,7 +21,7 @@ import java.util.Objects; import javax.annotation.Nonnull; -/** JSON web token used in WebIdentity and ClientGrants providers. */ +/** JSON web token used in {@link WebIdentityProvider} and {@link ClientGrantsProvider}. */ public class Jwt { @JsonProperty("access_token") private final String token; diff --git a/api/src/main/java/io/minio/credentials/LdapIdentityProvider.java b/api/src/main/java/io/minio/credentials/LdapIdentityProvider.java index 14b21cde7..b601e7d0b 100644 --- a/api/src/main/java/io/minio/credentials/LdapIdentityProvider.java +++ b/api/src/main/java/io/minio/credentials/LdapIdentityProvider.java @@ -34,7 +34,7 @@ * href="https://github.com/minio/minio/blob/master/docs/sts/ldap.md">AssumeRoleWithLDAPIdentity * API. */ -public class LdapIdentityProvider extends AssumeRoleBaseProvider { +public class LdapIdentityProvider extends BaseIdentityProvider { private static final RequestBody EMPTY_BODY = RequestBody.create(new byte[] {}, MediaType.parse("application/octet-stream")); private final Request request; @@ -84,14 +84,18 @@ protected Request getRequest() { } @Override - protected Class getResponseClass() { - return LdapIdentityResponse.class; + protected Class getResponseClass() { + return Response.class; } - /** Object representation of response XML of AssumeRoleWithLDAPIdentity API. */ + /** + * Response XML of AssumeRoleWithLDAPIdentity + * API. + */ @Root(name = "AssumeRoleWithLDAPIdentityResponse", strict = false) @Namespace(reference = "https://sts.amazonaws.com/doc/2011-06-15/") - public static class LdapIdentityResponse implements AssumeRoleBaseProvider.Response { + public static class Response implements BaseIdentityProvider.Response { @Path(value = "AssumeRoleWithLDAPIdentityResult") @Element(name = "Credentials") private Credentials credentials; diff --git a/api/src/main/java/io/minio/credentials/MinioClientConfigProvider.java b/api/src/main/java/io/minio/credentials/MinioClientConfigProvider.java index fee406209..c45c885d7 100644 --- a/api/src/main/java/io/minio/credentials/MinioClientConfigProvider.java +++ b/api/src/main/java/io/minio/credentials/MinioClientConfigProvider.java @@ -20,11 +20,11 @@ import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Paths; import java.security.ProviderException; import java.util.Locale; @@ -62,30 +62,26 @@ public MinioClientConfigProvider(@Nullable String filename, @Nullable String ali */ @Override public Credentials fetch() { - String filename = this.filename; + String filename = + this.filename != null ? this.filename : getProperty("MINIO_SHARED_CREDENTIALS_FILE"); if (filename == null) { - filename = getProperty("MINIO_SHARED_CREDENTIALS_FILE"); - } - if (filename == null) { - String mcDir = ".mc"; - if (System.getProperty("os.name").toLowerCase(Locale.US).contains("windows")) { - mcDir = "mc"; - } - - filename = Paths.get(System.getProperty("user.home"), mcDir, "config.json").toString(); + filename = + Paths.get( + System.getProperty("user.home"), + System.getProperty("os.name").toLowerCase(Locale.US).contains("windows") + ? "mc" + : ".mc", + "config.json") + .toString(); } String alias = this.alias; - if (alias == null) { - alias = getProperty("MINIO_ALIAS"); - } - if (alias == null) { - alias = "s3"; - } + if (alias == null) alias = getProperty("MINIO_ALIAS"); + if (alias == null) alias = "s3"; - try (InputStream is = new FileInputStream(filename)) { - McConfig config = - mapper.readValue(new InputStreamReader(is, StandardCharsets.UTF_8), McConfig.class); + try (InputStream is = Files.newInputStream(Paths.get(filename))) { + Config config = + mapper.readValue(new InputStreamReader(is, StandardCharsets.UTF_8), Config.class); Map values = config.get(alias); if (values == null) { throw new ProviderException( @@ -111,10 +107,11 @@ public Credentials fetch() { } } + /** Configuration of {@link MinioClientConfigProvider}. */ @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value = {"UwF", "UuF"}, justification = "All the fields are written at the time of JSON unmarshalling.") - public static class McConfig { + public static class Config { private Map> hosts; public Map get(String alias) { diff --git a/api/src/main/java/io/minio/credentials/Provider.java b/api/src/main/java/io/minio/credentials/Provider.java index 4540534d6..4aa459b5d 100644 --- a/api/src/main/java/io/minio/credentials/Provider.java +++ b/api/src/main/java/io/minio/credentials/Provider.java @@ -25,7 +25,7 @@ public interface Provider { /** * Returns a valid {@link Credentials} instance by retrieving from credential provider service if - * neccessary. + * necessary. */ Credentials fetch(); } diff --git a/api/src/main/java/io/minio/credentials/StaticProvider.java b/api/src/main/java/io/minio/credentials/StaticProvider.java index e0b227fce..66f8efdfe 100644 --- a/api/src/main/java/io/minio/credentials/StaticProvider.java +++ b/api/src/main/java/io/minio/credentials/StaticProvider.java @@ -19,7 +19,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -/** Fixed credential provider. */ +/** Provider contains constant credentials. */ public class StaticProvider implements Provider { private final Credentials credentials; diff --git a/api/src/main/java/io/minio/credentials/WebIdentityClientGrantsProvider.java b/api/src/main/java/io/minio/credentials/WebIdentityClientGrantsProvider.java index d3e3e3b11..7c4bd3486 100644 --- a/api/src/main/java/io/minio/credentials/WebIdentityClientGrantsProvider.java +++ b/api/src/main/java/io/minio/credentials/WebIdentityClientGrantsProvider.java @@ -27,8 +27,8 @@ import okhttp3.Request; import okhttp3.RequestBody; -/** Base class of WebIdentity and ClientGrants providers. */ -public abstract class WebIdentityClientGrantsProvider extends AssumeRoleBaseProvider { +/** Base provider of {@link WebIdentityProvider} and {@link ClientGrantsProvider}. */ +public abstract class WebIdentityClientGrantsProvider extends BaseIdentityProvider { public static final int MIN_DURATION_SECONDS = (int) TimeUnit.MINUTES.toSeconds(15); public static final int MAX_DURATION_SECONDS = (int) TimeUnit.DAYS.toSeconds(7); private static final RequestBody EMPTY_BODY = @@ -59,18 +59,9 @@ public WebIdentityClientGrantsProvider( } protected int getDurationSeconds(int expiry) { - if (durationSeconds != null && durationSeconds > 0) { - expiry = durationSeconds; - } - - if (expiry > MAX_DURATION_SECONDS) { - return MAX_DURATION_SECONDS; - } - - if (expiry <= 0) { - return expiry; - } - + if (durationSeconds != null && durationSeconds > 0) expiry = durationSeconds; + if (expiry > MAX_DURATION_SECONDS) return MAX_DURATION_SECONDS; + if (expiry <= 0) return expiry; return (expiry < MIN_DURATION_SECONDS) ? MIN_DURATION_SECONDS : expiry; } diff --git a/api/src/main/java/io/minio/credentials/WebIdentityProvider.java b/api/src/main/java/io/minio/credentials/WebIdentityProvider.java index 787be1b62..108af80bf 100644 --- a/api/src/main/java/io/minio/credentials/WebIdentityProvider.java +++ b/api/src/main/java/io/minio/credentials/WebIdentityProvider.java @@ -60,14 +60,18 @@ protected HttpUrl.Builder newUrlBuilder(Jwt jwt) { } @Override - protected Class getResponseClass() { - return WebIdentityResponse.class; + protected Class getResponseClass() { + return Response.class; } - /** Object representation of response XML of AssumeRoleWithWebIdentity API. */ + /** + * Response XML AssumeRoleWithWebIdentity + * API. + */ @Root(name = "AssumeRoleWithWebIdentityResponse", strict = false) @Namespace(reference = "https://sts.amazonaws.com/doc/2011-06-15/") - public static class WebIdentityResponse implements AssumeRoleBaseProvider.Response { + public static class Response implements BaseIdentityProvider.Response { @Path(value = "AssumeRoleWithWebIdentityResult") @Element(name = "Credentials") private Credentials credentials; diff --git a/api/src/main/java/io/minio/errors/ErrorResponseException.java b/api/src/main/java/io/minio/errors/ErrorResponseException.java index d2755932d..6f314cf1a 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; @@ -40,7 +38,7 @@ public ErrorResponseException(ErrorResponse errorResponse, Response response, St this.response = response; } - /** Returns ErrorResponse contains detail of what error occured. */ + /** Returns ErrorResponse contains detail of what error occurred. */ public ErrorResponse errorResponse() { return this.errorResponse; } @@ -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..5e0c1450b 100644 --- a/api/src/main/java/io/minio/errors/InternalException.java +++ b/api/src/main/java/io/minio/errors/InternalException.java @@ -17,11 +17,17 @@ package io.minio.errors; /** - * Thrown to indicate that unexpected internal library error occured while processing given request. + * Thrown to indicate that unexpected internal library error occurred while processing given + * request. */ 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..31f3be597 100644 --- a/api/src/main/java/io/minio/errors/MinioException.java +++ b/api/src/main/java/io/minio/errors/MinioException.java @@ -16,17 +16,25 @@ package io.minio.errors; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import java.io.EOFException; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + /** Base Exception class for all minio-java exceptions. */ public class MinioException extends Exception { private static final long serialVersionUID = -7241010318779326306L; String httpTrace = null; - /** Constructs a new MinioException. */ - public MinioException() { - super(); - } - /** Constructs a new MinioException with given error message. */ public MinioException(String message) { super(message); @@ -38,7 +46,55 @@ 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; } + + /** Throws encapsulated exception. */ + public void throwEncapsulatedException() + throws BucketPolicyTooLargeException, CertificateException, EOFException, + ErrorResponseException, FileNotFoundException, GeneralSecurityException, + InsufficientDataException, InternalException, InvalidKeyException, + InvalidResponseException, IOException, JsonMappingException, JsonParseException, + JsonProcessingException, KeyManagementException, KeyStoreException, MinioException, + NoSuchAlgorithmException, ServerException, XmlParserException { + Throwable e = getCause(); + + // Inherited by MinioException + if (e instanceof BucketPolicyTooLargeException) throw (BucketPolicyTooLargeException) e; + if (e instanceof ErrorResponseException) throw (ErrorResponseException) e; + if (e instanceof InsufficientDataException) throw (InsufficientDataException) e; + if (e instanceof InternalException) throw (InternalException) e; + if (e instanceof InvalidResponseException) throw (InvalidResponseException) e; + if (e instanceof ServerException) throw (ServerException) e; + if (e instanceof XmlParserException) throw (XmlParserException) e; + + // Inherited by IOException + if (e instanceof JsonMappingException) throw (JsonMappingException) e; + if (e instanceof JsonParseException) throw (JsonParseException) e; + if (e instanceof JsonProcessingException) throw (JsonProcessingException) e; + if (e instanceof EOFException) throw (EOFException) e; + if (e instanceof FileNotFoundException) throw (FileNotFoundException) e; + if (e instanceof IOException) throw (IOException) e; + + // Inherited by GeneralSecurityException + if (e instanceof CertificateException) throw (CertificateException) e; + if (e instanceof InvalidKeyException) throw (InvalidKeyException) e; + if (e instanceof KeyManagementException) throw (KeyManagementException) e; + if (e instanceof KeyStoreException) throw (KeyStoreException) e; + if (e instanceof NoSuchAlgorithmException) throw (NoSuchAlgorithmException) e; + if (e instanceof GeneralSecurityException) throw (GeneralSecurityException) e; + + throw this; + } } diff --git a/api/src/main/java/io/minio/errors/XmlParserException.java b/api/src/main/java/io/minio/errors/XmlParserException.java index 55de48554..5b2627596 100644 --- a/api/src/main/java/io/minio/errors/XmlParserException.java +++ b/api/src/main/java/io/minio/errors/XmlParserException.java @@ -21,6 +21,6 @@ public class XmlParserException extends MinioException { private static final long serialVersionUID = -3877568719271880309L; public XmlParserException(Exception e) { - super(e.toString()); + super(e); } } 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..bfa92289c 100644 --- a/api/src/main/java/io/minio/messages/AccessControlList.java +++ b/api/src/main/java/io/minio/messages/AccessControlList.java @@ -20,10 +20,18 @@ 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}. */ +/** 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 +50,186 @@ public AccessControlList( public List grants() { return Utils.unmodifiableList(grants); } + + @Override + public String toString() { + return String.format("AccessControlList{grants=%s}", Utils.stringify(grants)); + } + + /** 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)); + } + } + + /** 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 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..409a803c7 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 88% rename from api/src/main/java/io/minio/messages/SelectObjectContentRequestBase.java rename to api/src/main/java/io/minio/messages/BaseSelectParameters.java index 3bce8fc8b..8bdf67a2a 100644 --- a/api/src/main/java/io/minio/messages/SelectObjectContentRequestBase.java +++ b/api/src/main/java/io/minio/messages/BaseSelectParameters.java @@ -20,9 +20,12 @@ import javax.annotation.Nonnull; import org.simpleframework.xml.Element; -/** Base class for {@link SelectObjectContentRequest} and {@link SelectParameters}. */ +/** + * Base select parameters information 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 +38,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..c2b0f13dd 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)); + } + + /** 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..d8b09c429 100644 --- a/api/src/main/java/io/minio/messages/Checksum.java +++ b/api/src/main/java/io/minio/messages/Checksum.java @@ -16,13 +16,13 @@ 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 javax.annotation.Nullable; 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) @@ -43,7 +43,31 @@ public class Checksum { @Element(name = "ChecksumType", required = false) private String checksumType; - public Checksum() {} + protected Checksum() {} + + public Checksum( + @Nullable @Element(name = "ChecksumCRC32", required = false) String checksumCRC32, + @Nullable @Element(name = "ChecksumCRC32C", required = false) String checksumCRC32C, + @Nullable @Element(name = "ChecksumCRC64NVME", required = false) String checksumCRC64NVME, + @Nullable @Element(name = "ChecksumSHA1", required = false) String checksumSHA1, + @Nullable @Element(name = "ChecksumSHA256", required = false) String checksumSHA256, + @Nullable @Element(name = "ChecksumType", required = false) String checksumType) { + this.checksumCRC32 = checksumCRC32; + this.checksumCRC32C = checksumCRC32C; + this.checksumCRC64NVME = checksumCRC64NVME; + this.checksumSHA1 = checksumSHA1; + this.checksumSHA256 = checksumSHA256; + this.checksumType = checksumType; + } + + 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 String checksumCRC32() { return checksumCRC32; @@ -69,20 +93,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..eabd700ee 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 { + /** Constructs a new CompleteMultipartUpload object with given parts. */ + public CompleteMultipartUpload(@Nonnull Part[] parts) { 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..e5443d326 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..eba2dad7f 100644 --- a/api/src/main/java/io/minio/messages/Contents.java +++ b/api/src/main/java/io/minio/messages/Contents.java @@ -18,10 +18,7 @@ import org.simpleframework.xml.Root; -/** - * Helper class to denote Object information in {@link ListBucketResultV1} and {@link - * ListBucketResultV2} - */ +/** Object information in {@link ListBucketResultV1} and {@link ListBucketResultV2} */ @Root(name = "Contents", strict = false) public class Contents extends Item { public Contents() { @@ -31,4 +28,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..75fe941d2 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..71ba090b7 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..5d3063346 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; } + /** 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) { } } + /** 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/DateDays.java b/api/src/main/java/io/minio/messages/DateDays.java deleted file mode 100644 index 03eda56ad..000000000 --- a/api/src/main/java/io/minio/messages/DateDays.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.time.ZonedDateTime; -import org.simpleframework.xml.Element; - -/** 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 Integer days() { - return days; - } -} 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/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..41e65ea4e 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 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 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..a1218f14e 100644 --- a/api/src/main/java/io/minio/messages/DeleteResult.java +++ b/api/src/main/java/io/minio/messages/DeleteResult.java @@ -17,14 +17,15 @@ package io.minio.messages; import io.minio.Utils; -import java.util.LinkedList; +import java.util.ArrayList; 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 ArrayList(); + 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)); + } + + /** Deleted object of {@link DeleteResult}. */ + @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)); + } + } + + /** Error information of {@link DeleteResult}. */ + @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..861f425af 100644 --- a/api/src/main/java/io/minio/messages/ErrorResponse.java +++ b/api/src/main/java/io/minio/messages/ErrorResponse.java @@ -16,12 +16,15 @@ package io.minio.messages; +import io.minio.Utils; import java.io.Serializable; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; 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. */ +/** 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 { @@ -48,20 +51,20 @@ public class ErrorResponse implements Serializable { @Element(name = "HostId", required = false) protected String hostId; - public ErrorResponse() {} + protected ErrorResponse() {} /** * Constructs a new ErrorResponse object with error code, bucket name, object name, resource, * request ID and host ID. */ public ErrorResponse( - String code, - String message, - String bucketName, - String objectName, - String resource, - String requestId, - String hostId) { + @Nonnull @Element(name = "Code") String code, + @Nullable @Element(name = "Message", required = false) String message, + @Nullable @Element(name = "BucketName", required = false) String bucketName, + @Nullable @Element(name = "Key", required = false) String objectName, + @Nullable @Element(name = "Resource", required = false) String resource, + @Nullable @Element(name = "RequestId", required = false) String requestId, + @Nullable @Element(name = "HostId", required = false) String hostId) { this.code = code; this.message = message; this.bucketName = bucketName; @@ -106,28 +109,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..41f04258f 100644 --- a/api/src/main/java/io/minio/messages/EventType.java +++ b/api/src/main/java/io/minio/messages/EventType.java @@ -23,7 +23,7 @@ import org.simpleframework.xml.stream.InputNode; import org.simpleframework.xml.stream.OutputNode; -/** Amazon AWS S3 event types for notifications. */ +/** S3 event type. */ @Root(name = "Event") @Convert(EventType.EventTypeConverter.class) public enum EventType { @@ -64,15 +64,12 @@ public String toString() { public static EventType fromString(String eventTypeString) { String s3EventTypeString = "s3:" + eventTypeString; for (EventType et : EventType.values()) { - if (eventTypeString.equals(et.value) || s3EventTypeString.equals(et.value)) { - return et; - } + if (eventTypeString.equals(et.value) || s3EventTypeString.equals(et.value)) return et; } - - throw new IllegalArgumentException("unknown event '" + eventTypeString + "'"); + throw new IllegalArgumentException("Unknown event '" + eventTypeString + "'"); } - /** XML converter class. */ + /** XML converter 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..079ffa83f 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,177 @@ package io.minio.messages; import io.minio.Utils; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; +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.ElementList; 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}. + * 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(StringConverter.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(@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 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) { + 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"); } + this.objectSizeLessThan = objectSizeLessThan; + this.objectSizeGreaterThan = objectSizeGreaterThan; + } + + 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); + /** AND operator of {@link Filter}. */ + @Root(name = "And") + public static class And { + @Element(name = "Prefix", required = false) + @Convert(StringConverter.class) + private String prefix; + + @Element(name = "ObjectSizeLessThan", required = false) + private Long objectSizeLessThan; + + @Element(name = "ObjectSizeGreaterThan", required = false) + private Long objectSizeGreaterThan; + + @ElementList(entry = "Tag", inline = true, required = false) + private List tags; + + private void set( + String prefix, List tags, Long objectSizeLessThan, Long objectSizeGreaterThan) { + if (prefix == null && tags == null) { + throw new IllegalArgumentException("At least Prefix or Tags must be set"); + } + + this.prefix = prefix; + this.tags = Utils.unmodifiableList(tags); + this.objectSizeLessThan = objectSizeLessThan; + this.objectSizeGreaterThan = objectSizeGreaterThan; + } + + public And( + @Nullable @Element(name = "Prefix", required = false) String prefix, + @Nullable @ElementList(entry = "Tag", inline = true, required = false) List tags, + @Nullable @Element(name = "ObjectSizeLessThan", required = false) Long objectSizeLessThan, + @Nullable @Element(name = "ObjectSizeGreaterThan", required = false) + Long objectSizeGreaterThan) { + set(prefix, tags, objectSizeLessThan, objectSizeGreaterThan); + } + + public And( + @Nullable String prefix, + @Nullable Map tags, + @Nullable Long objectSizeLessThan, + @Nullable Long objectSizeGreaterThan) { + List tagList = null; + if (tags != null) { + this.tags = new ArrayList<>(); + for (Map.Entry entry : tags.entrySet()) { + this.tags.add(new Tag(entry.getKey(), entry.getValue())); + } + } + set(prefix, tagList, objectSizeLessThan, objectSizeGreaterThan); + } + + public String prefix() { + return this.prefix; + } + + public Long objectSizeLessThan() { + return this.objectSizeLessThan; + } + + public Long objectSizeGreaterThan() { + return this.objectSizeGreaterThan; + } + + public List 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)); + } } } 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..35da68b5e 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 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..d8470cef3 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..809937f49 100644 --- a/api/src/main/java/io/minio/messages/Initiator.java +++ b/api/src/main/java/io/minio/messages/Initiator.java @@ -16,13 +16,11 @@ 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}. - */ +/** Initiator information of {@link ListMultipartUploadsResult} and {@link ListPartsResult}. */ @Root(name = "Initiator", strict = false) public class Initiator { @Element(name = "ID", required = false) @@ -42,4 +40,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..c2e6a9a69 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}. */ +/** 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()); + } + + /** 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; + } + } + + /** 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; + } + } + + /** 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 type of {@link JSON}. */ + 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..4e956013a 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 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)); + } + + /** 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,66 @@ 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)); + } + } + + /** 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 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..6af65684d 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..e5694db3e 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,348 @@ @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)); + } + + /** Lifecycle rule information of {@link LifecycleConfiguration}. */ + @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, + @Nullable @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 = filter; + 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)); + } + } + + /** Abort incomplete multipart upload information of {@link LifecycleConfiguration.Rule}. */ + @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); + } + } + + /** + * Date and days information of {@link LifecycleConfiguration.Expiration} and {@link + * LifecycleConfiguration.Transition}. + */ + 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)); + } + } + + /** Expiration information of {@link LifecycleConfiguration.Rule}. */ + @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, + @Element(name = "ExpiredObjectAllVersions", required = false) + Boolean expiredObjectAllVersions) { + 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; + this.expiredObjectAllVersions = expiredObjectAllVersions; + } + + public Expiration( + ZonedDateTime date, + Integer days, + Boolean expiredObjectDeleteMarker, + Boolean expiredObjectAllVersions) { + this( + date == null ? null : new Time.S3Time(date), + days, + expiredObjectDeleteMarker, + 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)); + } + } + + /** Non-current version expiration information of {@link LifecycleConfiguration.Rule}. */ + @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, + @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()); + } + } + + /** Non-current version transition information of {@link LifecycleConfiguration.Rule}. */ + @Root(name = "NoncurrentVersionTransition") + public static class NoncurrentVersionTransition extends NoncurrentVersionExpiration { + @Element(name = "StorageClass") + private String storageClass; + + public NoncurrentVersionTransition( + @Element(name = "NoncurrentDays", required = false) int noncurrentDays, + @Element(name = "NewerNoncurrentVersions", required = false) + Integer newerNoncurrentVersions, + @Nullable @Element(name = "StorageClass", required = false) String storageClass) { + super(noncurrentDays, newerNoncurrentVersions); + this.storageClass = storageClass; + } + + public String storageClass() { + return storageClass; + } + + @Override + public String toString() { + return String.format( + "NoncurrentVersionTransition{%s, storageClass=%s}", super.stringify(), storageClass); + } + } + + /** Transition information of {@link LifecycleConfiguration.Rule}. */ + @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, + @Nullable @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..53a4a168a 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) @@ -36,7 +38,7 @@ public class ListAllMyBucketsResult { @ElementList(name = "Buckets") private List buckets; - @Element(name = "prefix", required = false) + @Element(name = "Prefix", required = false) private String prefix; @Element(name = "ContinuationToken", required = false) @@ -61,4 +63,50 @@ 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)); + } + + /** Bucket information of {@link ListAllMyBucketsResult}. */ + @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..6fb9cc918 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..75b6b4d49 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..84c7912eb 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,123 @@ 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)); + } + + /** Upload information of {@link ListMultipartUploadsResult}. */ + @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..ed085e0ad 100644 --- a/api/src/main/java/io/minio/messages/ListObjectsResult.java +++ b/api/src/main/java/io/minio/messages/ListObjectsResult.java @@ -20,9 +20,10 @@ 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 + * Base object information of {@link ListBucketResultV1}, {@link ListBucketResultV2} and {@link * ListVersionsResult}. */ public abstract class ListObjectsResult { @@ -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,41 @@ 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)); + } + + /** Common prefix informaton. */ + @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..5aa708496 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() { @@ -86,7 +71,7 @@ public String storageClass() { return storageClass; } - /** Returns initator information. */ + /** Returns initiator information. */ public Initiator initiator() { return initiator; } @@ -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..c9e85c0a3 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,52 @@ 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)); + } + + /** Object with version information. */ + @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()); + } + } + + /** Delete marker information. */ + @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..220203e06 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..0bc557cce 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.ArrayList; 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,281 @@ @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)); } + /** Event bridge configuration of {@link NotificationConfiguration}. */ @Root(name = "EventBridgeConfiguration") - public static class EventBridgeConfiguration {} + public static class EventBridgeConfiguration { + @Override + public String toString() { + return String.format("EventBridgeConfiguration{}"); + } + } + + /** + * Common configuration of {@link CloudFunctionConfiguration}, {@link QueueConfiguration} and + * {@link TopicConfiguration}. + */ + 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)); + } + } + + /** Filter configuration of {@link BaseConfiguration}. */ + @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 ArrayList<>(); + 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)); + } + } + + /** Filter rule configuration of {@link Filter}. */ + @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)); + } + } + + /** Cloud function configuration of {@link NotificationConfiguration}. */ + @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()); + } + } + + /** Queue configuration of {@link NotificationConfiguration}. */ + @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()); + } + } + + /** Topic configuration of {@link NotificationConfiguration}. */ + @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..944901e9f 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. + * JSON format of Notification + * Content Structure. */ @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value = "UwF", @@ -36,4 +39,240 @@ public class NotificationRecords { public List events() { return Utils.unmodifiableList(events); } + + @Override + public String toString() { + return String.format("NotificationRecords{events=%s}", Utils.stringify(events)); + } + + /** Event information of {@link NotificationRecords}. */ + @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)); + } + + /** Identity information of {@link Event}. */ + @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)); + } + } + + /** Bucket information of {@link Event}. */ + @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)); + } + } + + /** Object information of {@link Event}. */ + 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)); + } + } + + /** S3 information of {@link Event}. */ + @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)); + } + } + + /** Source information of {@link Event}. */ + /** 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..dd0ff43e8 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 @@ -40,8 +44,7 @@ public class ObjectLockConfiguration { public ObjectLockConfiguration() {} /** Constructs a new ObjectLockConfiguration object with given retention. */ - public ObjectLockConfiguration(RetentionMode mode, RetentionDuration duration) - throws IllegalArgumentException { + public ObjectLockConfiguration(RetentionMode mode, RetentionDuration duration) { this.rule = new Rule(mode, duration); } @@ -54,4 +57,121 @@ 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)); + } + + /** Rule information of {@link ObjectLockConfiguration}. */ + @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)); + } + } + + /** Interface represents retention duration of {@link Rule}. */ + public static interface RetentionDuration { + public RetentionDurationUnit unit(); + + public int duration(); + } + + /** Retention duration unit. */ + public static enum RetentionDurationUnit { + DAYS, + YEARS; + } + + /** Retention duration days of {@link Rule}. */ + @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)); + } + } + + /** Retention duration years of {@link Rule}. */ + @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..2405eb71a 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}. - */ +/** 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)); + } + + /** 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; + } + } + + /** 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; + } + } + + /** Quotation field type of {@link CSV}. */ + 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..18e29a1b9 100644 --- a/api/src/main/java/io/minio/messages/Owner.java +++ b/api/src/main/java/io/minio/messages/Owner.java @@ -16,13 +16,14 @@ 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 - * ListBucketResultV1}, {@link ListBucketResultV2}, {@link ListVersionsResult}, {@link - * ListMultipartUploadsResult} and {@link ListPartsResult}. + * Owner information for {@link ListAllMyBucketsResult}, {@link ListBucketResultV1}, {@link + * ListBucketResultV2}, {@link ListVersionsResult}, {@link ListMultipartUploadsResult} and {@link + * ListPartsResult}. */ @Root(name = "Owner", strict = false) public class Owner { @@ -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/ParquetInputSerialization.java b/api/src/main/java/io/minio/messages/ParquetInputSerialization.java deleted file mode 100644 index d40e85960..000000000 --- a/api/src/main/java/io/minio/messages/ParquetInputSerialization.java +++ /dev/null @@ -1,26 +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.Root; - -/** - * Helper class to denote Parquet input serialization request XML as per {@link - * SelectObjectContentRequest}. - */ -@Root(name = "Parquet") -public class ParquetInputSerialization {} diff --git a/api/src/main/java/io/minio/messages/Part.java b/api/src/main/java/io/minio/messages/Part.java index d8aac178e..c6b7e841b 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 - * CompleteMultipartUpload} and {@link ListPartsResult}. + * Part information of {@link CompleteMultipartUpload}, {@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..ef1668139 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. */ +/** 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/QueueConfiguration.java b/api/src/main/java/io/minio/messages/QueueConfiguration.java deleted file mode 100644 index 2123b6505..000000000 --- a/api/src/main/java/io/minio/messages/QueueConfiguration.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 Queue configuration of {@link NotificationConfiguration}. */ -@Root(name = "QueueConfiguration", strict = false) -public class QueueConfiguration extends NotificationCommonConfiguration { - @Element(name = "Queue") - private String queue; - - public QueueConfiguration() { - super(); - } - - /** Returns queue. */ - public String queue() { - return queue; - } - - /** Sets queue. */ - public void setQueue(String queue) { - this.queue = queue; - } -} 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..066aa177c 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,494 @@ 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)); + } + + /** Rule information of {@link ReplicationConfiguration}. */ + @Root(name = "Rule") + public static class Rule { + @Element(name = "Status") + private Status status; + + @Element(name = "Destination") + private Destination destination; + + @Element(name = "DeleteMarkerReplication", required = false) + private DeleteMarkerReplication deleteMarkerReplication; + + @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(StringConverter.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. + + public Rule( + @Nonnull @Element(name = "Status") Status status, + @Nonnull @Element(name = "Destination") Destination destination, + @Nullable @Element(name = "DeleteMarkerReplication", required = false) + DeleteMarkerReplication deleteMarkerReplication, + @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) { + 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.status = Objects.requireNonNull(status, "Status must not be null"); + this.destination = Objects.requireNonNull(destination, "Destination must not be null"); + this.deleteMarkerReplication = deleteMarkerReplication; + this.existingObjectReplication = existingObjectReplication; + this.filter = filter; + this.id = id; + this.prefix = prefix; + this.priority = priority; + this.sourceSelectionCriteria = sourceSelectionCriteria; + this.deleteReplication = deleteReplication; + } + + 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)); + } + } + + /** Delete marker replication information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** Destination information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** Access control translation information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** Encryption configuration information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** Metrics information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** Replication time information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** Replication time value information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** Existing object replication information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** Source selection criteria information of {@link ReplicationConfiguration.Rule}. */ + @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 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)); + } + } + + /** Replica modification information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** SSE KMS encrypted objects information of {@link ReplicationConfiguration.Rule}. */ + @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)); + } + } + + /** Delete replication (MinIO extension) information of {@link ReplicationConfiguration.Rule}. */ + @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..8e364528a 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,227 @@ public RestoreRequest( this.selectParameters = selectParameters; this.outputLocation = outputLocation; } + + /** Tier type of {@link RestoreRequest}. */ + @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 of {@link Tier}. */ + 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()); + } + } + } + + /** Glacier job parameters information of {@link RestoreRequest}. */ + @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"); + } + } + + /** Select parameters information of {@link RestoreRequest}. */ + @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); + } + } + + /** Output location information of {@link RestoreRequest}. */ + @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"); + } + } + + /** S3 information of {@link RestoreRequest.OutputLocation}. */ + @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; + } + } + + /** Canned ACL type of {@link RestoreRequest.S3}. */ + @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 of {@link CannedAcl}. */ + 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()); + } + } + } + + /** Encryption information of {@link RestoreRequest.S3}. */ + @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; + } + } + + /** User metadata information of {@link RestoreRequest.S3}. */ + @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..f5d2fe296 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,22 +37,19 @@ public class Retention { private RetentionMode mode; @Element(name = "RetainUntilDate", required = false) - private ResponseDate retainUntilDate; + private Time.S3Time retainUntilDate; public Retention() {} /** Constructs a new Retention object with given retention until date and mode. */ public Retention(RetentionMode mode, ZonedDateTime retainUntilDate) { - if (mode == null) { - throw new IllegalArgumentException("null mode is not allowed"); - } - + if (mode == null) throw new IllegalArgumentException("null mode is not allowed"); if (retainUntilDate == null) { throw new IllegalArgumentException("null retainUntilDate is not allowed"); } this.mode = mode; - this.retainUntilDate = new ResponseDate(retainUntilDate); + this.retainUntilDate = new Time.S3Time(retainUntilDate); } /** Returns mode. */ @@ -60,10 +59,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..fa0f1134a 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,30 @@ public SelectObjectContentRequest( this.scanRange = new ScanRange(scanStartRange, scanEndRange); } } + + /** Request progress information of {@link SelectObjectContentRequest}. */ + @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() {} + } + + /** Scan range information of {@link SelectObjectContentRequest}. */ + @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/SseAlgorithm.java b/api/src/main/java/io/minio/messages/SseAlgorithm.java index 811bbbd8e..19207b698 100644 --- a/api/src/main/java/io/minio/messages/SseAlgorithm.java +++ b/api/src/main/java/io/minio/messages/SseAlgorithm.java @@ -23,7 +23,10 @@ import org.simpleframework.xml.stream.InputNode; import org.simpleframework.xml.stream.OutputNode; -/** Server-side encryption algorithm. */ +/** + * Server-side encryption algorithm type of {@link RestoreRequest.Encryption} and {@link + * SseConfiguration.Rule}. + */ @Root(name = "SSEAlgorithm") @Convert(SseAlgorithm.SseAlgorithmConverter.class) public enum SseAlgorithm { @@ -44,15 +47,12 @@ public String toString() { @JsonCreator public static SseAlgorithm fromString(String sseAlgorithmString) { for (SseAlgorithm sa : SseAlgorithm.values()) { - if (sseAlgorithmString.equals(sa.value)) { - return sa; - } + if (sseAlgorithmString.equals(sa.value)) return sa; } - - throw new IllegalArgumentException("unknown SSE algorithm '" + sseAlgorithmString + "'"); + throw new IllegalArgumentException("Unknown SSE algorithm '" + sseAlgorithmString + "'"); } - /** XML converter class. */ + /** XML converter of {@link SseAlgorithm}. */ public static class SseAlgorithmConverter implements Converter { @Override public SseAlgorithm read(InputNode node) throws Exception { diff --git a/api/src/main/java/io/minio/messages/SseConfiguration.java b/api/src/main/java/io/minio/messages/SseConfiguration.java index 1107be67d..0ab00032c 100644 --- a/api/src/main/java/io/minio/messages/SseConfiguration.java +++ b/api/src/main/java/io/minio/messages/SseConfiguration.java @@ -16,39 +16,84 @@ 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)); + } + + /** Rule information of {@link SseConfiguration}. */ + @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..d747ab9d4 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. */ +/** 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/Status.java b/api/src/main/java/io/minio/messages/Status.java index 1d1918788..902a4b254 100644 --- a/api/src/main/java/io/minio/messages/Status.java +++ b/api/src/main/java/io/minio/messages/Status.java @@ -23,7 +23,7 @@ import org.simpleframework.xml.stream.InputNode; import org.simpleframework.xml.stream.OutputNode; -/** Status representing Disabled/Enabled. */ +/** Status type of {@link LifecycleConfiguration.Rule} and {@link ReplicationConfiguration.Rule}. */ @Root(name = "Status") @Convert(Status.StatusConverter.class) public enum Status { @@ -52,7 +52,7 @@ public static Status fromString(String statusString) { throw new IllegalArgumentException("Unknown status '" + statusString + "'"); } - /** XML converter class. */ + /** XML converter of {@link Status}. */ public static class StatusConverter implements Converter { @Override public Status read(InputNode node) throws Exception { diff --git a/api/src/main/java/io/minio/messages/PrefixConverter.java b/api/src/main/java/io/minio/messages/StringConverter.java similarity index 77% rename from api/src/main/java/io/minio/messages/PrefixConverter.java rename to api/src/main/java/io/minio/messages/StringConverter.java index 2b7e51560..e7f39cd5e 100644 --- a/api/src/main/java/io/minio/messages/PrefixConverter.java +++ b/api/src/main/java/io/minio/messages/StringConverter.java @@ -21,10 +21,10 @@ import org.simpleframework.xml.stream.OutputNode; /** - * XML converter class for prefix due to SimpleXML limitation in converting empty tag - * to empty string. + * XML converter for string due to SimpleXML limitation in converting empty element like + * to empty string. */ -public class PrefixConverter implements Converter { +public class StringConverter implements Converter { @Override public String read(InputNode node) throws Exception { String value = node.getValue(); @@ -32,7 +32,7 @@ public String read(InputNode node) throws Exception { } @Override - public void write(OutputNode node, String prefix) throws Exception { - node.setValue(prefix); + public void write(OutputNode node, String value) throws Exception { + node.setValue(value); } } diff --git a/api/src/main/java/io/minio/messages/Tag.java b/api/src/main/java/io/minio/messages/Tag.java index 841e38ae9..f1c35dd8c 100644 --- a/api/src/main/java/io/minio/messages/Tag.java +++ b/api/src/main/java/io/minio/messages/Tag.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,29 +16,31 @@ 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.Root; +import org.simpleframework.xml.convert.Convert; -/** Helper class to denote tag information for {@link RuleFilter}. */ +/** Single tag information of {@link Tags} and {@link Filter}. */ @Root(name = "Tag") public class Tag { @Element(name = "Key") private String key; - @Element(name = "Value") + @Element(name = "Value", required = false) + @Convert(StringConverter.class) private String value; public Tag( - @Nonnull @Element(name = "Key") String key, @Nonnull @Element(name = "Value") String value) { + @Nonnull @Element(name = "Key") String key, + @Nullable @Element(name = "Value", required = false) String value) { Objects.requireNonNull(key, "Key must not be null"); - if (key.isEmpty()) { - throw new IllegalArgumentException("Key must not be empty"); - } - + if (key.isEmpty()) throw new IllegalArgumentException("Key must not be empty"); this.key = key; - this.value = Objects.requireNonNull(value, "Value must not be null"); + this.value = value; } public String key() { @@ -48,4 +50,9 @@ public String 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/Tags.java b/api/src/main/java/io/minio/messages/Tags.java index 1d191c109..acad7a39c 100644 --- a/api/src/main/java/io/minio/messages/Tags.java +++ b/api/src/main/java/io/minio/messages/Tags.java @@ -17,18 +17,21 @@ package io.minio.messages; import io.minio.Utils; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; -import org.simpleframework.xml.ElementMap; +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 PutBucketTagging - * API and , PutObjectTagging - * API response XML of , GetBucketTagging * API and GetObjectTagging @@ -47,21 +50,13 @@ public class Tags { private static final int MAX_TAG_COUNT = 50; @Path(value = "TagSet") - @ElementMap( - attribute = false, - entry = "Tag", - inline = true, - key = "Key", - value = "Value", - required = false) - Map tags; + @ElementList(entry = "Tag", inline = true, required = false) + private List tags; public Tags() {} - private Tags(Map tags, boolean isObject) throws IllegalArgumentException { - if (tags == null) { - return; - } + private Tags(Map tags, boolean isObject) { + if (tags == null) return; int limit = isObject ? MAX_OBJECT_TAG_COUNT : MAX_TAG_COUNT; if (tags.size() > limit) { @@ -74,6 +69,7 @@ private Tags(Map tags, boolean isObject) throws IllegalArgumentE + tags.size()); } + this.tags = new ArrayList<>(); for (Map.Entry entry : tags.entrySet()) { String key = entry.getKey(); if (key.length() == 0 || key.length() > MAX_KEY_LENGTH || key.contains("&")) { @@ -84,23 +80,33 @@ private Tags(Map tags, boolean isObject) throws IllegalArgumentE if (value.length() > MAX_VALUE_LENGTH || value.contains("&")) { throw new IllegalArgumentException("invalid tag value '" + value + "'"); } - } - this.tags = Utils.unmodifiableMap(tags); + this.tags.add(new Tag(key, value)); + } } /** Creates new bucket tags. */ - public static Tags newBucketTags(Map tags) throws IllegalArgumentException { + public static Tags newBucketTags(Map tags) { return new Tags(tags, false); } /** Creates new object tags. */ - public static Tags newObjectTags(Map tags) throws IllegalArgumentException { + public static Tags newObjectTags(Map tags) { return new Tags(tags, true); } - /** Returns tags. */ public Map get() { - return Utils.unmodifiableMap(tags); + Map map = new HashMap<>(); + if (tags != null) { + for (Tag tag : tags) { + map.put(tag.key(), tag.value()); + } + } + return map; + } + + @Override + public String toString() { + return String.format("Tags{%s}", Utils.stringify(get())); } } 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/VersioningConfiguration.java b/api/src/main/java/io/minio/messages/VersioningConfiguration.java index d67536586..8358b9834 100644 --- a/api/src/main/java/io/minio/messages/VersioningConfiguration.java +++ b/api/src/main/java/io/minio/messages/VersioningConfiguration.java @@ -16,15 +16,22 @@ package io.minio.messages; +import io.minio.Utils; +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; +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 PutBucketVersioning * API and response XML of GetBucketVersioning @@ -34,35 +41,71 @@ @Namespace(reference = "http://s3.amazonaws.com/doc/2006-03-01/") public class VersioningConfiguration { @Element(name = "Status", required = false) - private String status; + private Status status; @Element(name = "MfaDelete", required = false) - private String mfaDelete; + private Status mfaDelete; + + @ElementList(name = "ExcludedPrefixes", inline = true, required = false) + private List excludedPrefixes; + + @Element(name = "ExcludeFolders", required = false) + private Boolean excludeFolders; public VersioningConfiguration() {} - /** Constructs a new VersioningConfiguration object with given status. */ - public VersioningConfiguration(@Nonnull Status status, @Nullable Boolean mfaDelete) { + /** Constructs a new VersioningConfiguration object with given parameters. */ + public VersioningConfiguration( + @Nonnull Status status, + @Nullable Status mfaDelete, + @Nullable List excludedPrefixes, + @Nullable Boolean excludeFolders) { Objects.requireNonNull(status, "Status must not be null"); if (status == Status.OFF) { throw new IllegalArgumentException("Status must be ENABLED or SUSPENDED"); } - this.status = status.toString(); - - if (mfaDelete != null) { - this.mfaDelete = mfaDelete ? "Enabled" : "Disabled"; + if (mfaDelete == Status.OFF) { + throw new IllegalArgumentException("Status must be ENABLED or SUSPENDED"); } + + this.status = status; + this.mfaDelete = mfaDelete; + this.excludedPrefixes = excludedPrefixes; + this.excludeFolders = excludeFolders; } public Status status() { - return Status.fromString(status); + return status == null ? Status.OFF : status; + } + + public Status mfaDelete() { + return mfaDelete; } public Boolean isMfaDeleteEnabled() { - Boolean flag = (mfaDelete != null) ? Boolean.valueOf("Enabled".equals(mfaDelete)) : null; - return flag; + return mfaDelete == Status.ENABLED; } + public List excludedPrefixes() { + return excludedPrefixes; + } + + public Boolean excludeFolders() { + return excludeFolders; + } + + @Override + public String toString() { + return String.format( + "VersioningConfiguration{status=%s, mfaDelete=%s, excludedPrefixes=%s, excludeFolders=%s}", + Utils.stringify(status), + Utils.stringify(mfaDelete), + Utils.stringify(excludedPrefixes), + Utils.stringify(excludeFolders)); + } + + @Root(name = "Status") + @Convert(Status.StatusConverter.class) public static enum Status { OFF(""), ENABLED("Enabled"), @@ -89,5 +132,37 @@ public static Status fromString(String statusString) { return OFF; } + + /** XML converter of {@link Status}. */ + public static class StatusConverter implements Converter { + @Override + public Status read(InputNode node) throws Exception { + return Status.fromString(node.getValue()); + } + + @Override + public void write(OutputNode node, Status status) throws Exception { + node.setValue(status.toString()); + } + } + } + + @Root(name = "ExcludedPrefixes") + public static class Prefix { + @Element(name = "Prefix") + private String prefix; + + public Prefix(@Nonnull @Element(name = "Prefix") String prefix) { + this.prefix = Objects.requireNonNull(prefix, "prefix must not be null"); + } + + public String get() { + return prefix; + } + + @Override + public String toString() { + return String.format("Prefix{%s}", Utils.stringify(prefix)); + } } } 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..74740981f 100644 --- a/api/src/test/java/io/minio/StatObjectArgsTest.java +++ b/api/src/test/java/io/minio/StatObjectArgsTest.java @@ -17,6 +17,7 @@ package io.minio; +import io.minio.errors.MinioException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.KeyGenerator; @@ -24,25 +25,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 +55,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"); @@ -67,11 +68,11 @@ public void testEmptyObjectBuild2() { } @Test - public void testBuild() throws NoSuchAlgorithmException, InvalidKeyException { + public void testBuild() throws InvalidKeyException, MinioException, NoSuchAlgorithmException { 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/api/src/main/java/io/minio/ByteBufferStream.java b/api/src/test/java/io/minio/TimeLocaleTest.java similarity index 52% rename from api/src/main/java/io/minio/ByteBufferStream.java rename to api/src/test/java/io/minio/TimeLocaleTest.java index 8dd8ca1f1..e414a77bf 100644 --- a/api/src/main/java/io/minio/ByteBufferStream.java +++ b/api/src/test/java/io/minio/TimeLocaleTest.java @@ -1,6 +1,6 @@ /* * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, - * (C) 2021 MinIO, Inc. + * (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,17 +17,17 @@ package io.minio; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; +import java.time.ZonedDateTime; +import org.junit.Assert; +import org.junit.Test; -/** ByteArrayOutputStream exposes underneath buffer as input stream. */ -class ByteBufferStream extends ByteArrayOutputStream { - public ByteBufferStream() { - super(); - } - - public InputStream inputStream() { - return new ByteArrayInputStream(this.buf, 0, this.count); +public class TimeLocaleTest { + @Test + public void testHttpHeaderDateFormat() { + ZonedDateTime time = ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]"); + Assert.assertEquals(time.format(Time.HTTP_HEADER_DATE_FORMAT), "Mon, 03 Dec 2007 09:15:30 GMT"); + ZonedDateTime parsedTime = + ZonedDateTime.parse("Mon, 03 Dec 2007 09:15:30 GMT", Time.HTTP_HEADER_DATE_FORMAT); + Assert.assertEquals(time.format(Time.AMZ_DATE_FORMAT), parsedTime.format(Time.AMZ_DATE_FORMAT)); } } diff --git a/build.gradle b/build.gradle index 5314a1424..20f8f74df 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ plugins { allprojects { group = 'io.minio' - version = '8.6.1' + version = '9.0.0' if (!project.hasProperty('release')) { version += '-DEV' } @@ -47,19 +47,19 @@ subprojects { dependencies { api 'com.carrotsearch.thirdparty:simple-xml-safe:2.7.1' - api 'com.google.guava:guava:33.4.8-jre' - api 'com.squareup.okhttp3:okhttp:5.1.0' - api 'com.fasterxml.jackson.core:jackson-annotations:2.19.1' - api 'com.fasterxml.jackson.core:jackson-core:2.19.1' - api 'com.fasterxml.jackson.core:jackson-databind:2.19.1' - api 'org.bouncycastle:bcprov-jdk18on:1.81' + api 'com.google.guava:guava:33.5.0-jre' + api 'com.squareup.okhttp3:okhttp:5.2.1' + api 'com.fasterxml.jackson.core:jackson-annotations:2.20' + api 'com.fasterxml.jackson.core:jackson-core:2.20.0' + api 'com.fasterxml.jackson.core:jackson-databind:2.20.0' + api 'org.bouncycastle:bcprov-jdk18on:1.82' api 'org.apache.commons:commons-compress:1.28.0' - api 'commons-codec:commons-codec:1.18.0' - api 'org.xerial.snappy:snappy-java:1.1.10.7' - compileOnly 'com.github.spotbugs:spotbugs-annotations:4.8.6' + api 'commons-codec:commons-codec:1.19.0' + api 'org.xerial.snappy:snappy-java:1.1.10.8' + compileOnly 'com.github.spotbugs:spotbugs-annotations:4.9.8' - testImplementation 'com.squareup.okhttp3:mockwebserver:5.1.0' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.13.3' + testImplementation 'com.squareup.okhttp3:mockwebserver:5.2.1' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.14.0' } [compileJava, compileTestJava].each() { @@ -94,13 +94,10 @@ subprojects { } } - task localeTest(type: Test) { - description = 'Runs tests with locale de.DE' - System.setProperty('user.language', 'de') - System.setProperty('user.country', 'DE') - systemProperties = System.properties - classpath = testing.suites.test.sources.runtimeClasspath - dependsOn test + tasks.register('localeTest', Test) { + description = 'Runs tests with locale de-DE' + systemProperty 'user.language', 'de' + systemProperty 'user.country', 'DE' } check.dependsOn localeTest @@ -167,14 +164,15 @@ project(':api') { } } - artifacts { - archives javadocJar, sourcesJar, shadowJar + tasks.named('assemble') { + dependsOn tasks.named('shadowJar') } - tasks.withType(Jar) { task -> - task.doLast { - ant.checksum algorithm: 'md5', file: it.archivePath - ant.checksum algorithm: 'sha1', file: it.archivePath + tasks.withType(Jar).configureEach { + doLast { + def jarFile = archiveFile.get().asFile + ant.checksum algorithm: 'md5', file: jarFile + ant.checksum algorithm: 'sha1', file: jarFile } } @@ -228,10 +226,10 @@ project(':api') { } signing { - setRequired { + required { gradle.taskGraph.allTasks.any { it.name.contains('LocalMavenWithChecksums') } - sign publishing.publications.minioJava } + sign publishing.publications.minioJava } } @@ -248,7 +246,7 @@ project(':adminapi') { dependencies { api project(':api') - api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.19.1' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.20.0' testImplementation project(':api') } @@ -286,14 +284,15 @@ project(':adminapi') { } } - artifacts { - archives javadocJar, sourcesJar, shadowJar + tasks.named('assemble') { + dependsOn tasks.named('shadowJar') } - tasks.withType(Jar) { task -> - task.doLast { - ant.checksum algorithm: 'md5', file: it.archivePath - ant.checksum algorithm: 'sha1', file: it.archivePath + tasks.withType(Jar).configureEach { + doLast { + def jarFile = archiveFile.get().asFile + ant.checksum algorithm: 'md5', file: jarFile + ant.checksum algorithm: 'sha1', file: jarFile } } @@ -347,10 +346,10 @@ project(':adminapi') { } signing { - setRequired { - gradle.taskGraph.allTasks.any { it.name.contains('LocalMavenWithChecksumsRepository') } - sign publishing.publications.minioJava + required { + gradle.taskGraph.allTasks.any { it.name.contains('LocalMavenWithChecksums') } } + sign publishing.publications.minioJava } } @@ -372,11 +371,9 @@ project(':examples') { } } -import org.gradle.internal.os.OperatingSystem; - project(':functional') { dependencies { - implementation 'org.junit.jupiter:junit-jupiter-api:5.13.3' + implementation 'org.junit.jupiter:junit-jupiter-api:5.14.0' implementation project(':api') implementation project(':adminapi') } @@ -394,7 +391,7 @@ project(':functional') { task runFunctionalTest(type:JavaExec) { mainClass = 'FunctionalTest' - classpath = testing.suites.test.sources.runtimeClasspath + classpath = sourceSets.main.runtimeClasspath ext.endpoint = 'https://play.min.io:9000' if (rootProject.hasProperty('endpoint')) { diff --git a/docs/API.md b/docs/API.md index 334d3c385..ee72ac8f1 100644 --- a/docs/API.md +++ b/docs/API.md @@ -196,7 +196,7 @@ MinioClient s3Client = MinioClient s3Client = MinioClient.builder() .endpoint("s3.amazonaws.com", 443, true) - .credentials("YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY"). + .credentials("YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY") .region("eu-west-2") .build(); @@ -1257,9 +1257,10 @@ Enables legal hold on an object. __Parameters__ -| Parameter | Type | Description | -|:---------------|:------------------------------|:-------------| -| ``args`` | _[EnableObjectLegalHoldArgs]_ | Argumments. | +| Parameter | Type | Description | +|:----------|:------------------------------|:------------| +| ``args`` | _[EnableObjectLegalHoldArgs]_ | Arguments. | + __Example__ ```java @@ -1376,9 +1377,9 @@ __Parameters__ | ``args`` | _[GetObjectAttributesArgs]_ | Arguments. | -| Returns | -|:-------------------------------------------| -| _[GetObjectAttributesResponse]_ - Respone. | +| Returns | +|:--------------------------------------------| +| _[GetObjectAttributesResponse]_ - Response. | __Example__ ```java @@ -1988,7 +1989,7 @@ minioClient.setObjectTags( ### statObject(StatObjectArgs args) -`public ObjectStat statObject(StatObjectArgs args)` _[[Javadoc]](http://minio.github.io/minio-java/io/minio/MinioClient.html#statObject-io.minio.StatObjectArgs-)_ +`public StatObjectResponse statObject(StatObjectArgs args)` _[[Javadoc]](http://minio.github.io/minio-java/io/minio/MinioClient.html#statObject-io.minio.StatObjectArgs-)_ Gets object information and metadata of an object. @@ -1997,19 +1998,19 @@ __Parameters__ |:----------|:-------------------|:------------| | ``args`` | _[StatObjectArgs]_ | Arguments. | -| Returns | -|:------------------------------------------------------------| -| _[ObjectStat]_ - Populated object information and metadata. | +| Returns | +|:--------------------------------------------------------------------| +| _[StatObjectResponse]_ - Populated object information and metadata. | __Example__ ```java // Get information of an object. -ObjectStat objectStat = +StatObjectResponse response = minioClient.statObject( StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); // Get information of SSE-C encrypted object. -ObjectStat objectStat = +StatObjectResponse response = minioClient.statObject( StatObjectArgs.builder() .bucket("my-bucketname") @@ -2018,7 +2019,7 @@ ObjectStat objectStat = .build()); // Get information of a versioned object. -ObjectStat objectStat = +StatObjectResponse response = minioClient.statObject( StatObjectArgs.builder() .bucket("my-bucketname") @@ -2027,7 +2028,7 @@ ObjectStat objectStat = .build()); // Get information of a SSE-C encrypted versioned object. -ObjectStat objectStat = +StatObjectResponse response = minioClient.statObject( StatObjectArgs.builder() .bucket("my-bucketname") @@ -2070,7 +2071,7 @@ ObjectStat objectStat = [InputSerialization]: http://minio.github.io/minio-java/io/minio/messages/InputSerialization.html [OutputSerialization]: http://minio.github.io/minio-java/io/minio/messages/OutputSerialization.html [Retention]: http://minio.github.io/minio-java/io/minio/messages/Retention.html -[ObjectStat]: http://minio.github.io/minio-java/io/minio/ObjectStat.html +[StatObjectResponse]: http://minio.github.io/minio-java/io/minio/StatObjectResponse.html [DeleteError]: http://minio.github.io/minio-java/io/minio/messages/DeleteError.html [SelectResponseStream]: http://minio.github.io/minio-java/io/minio/SelectResponseStream.html [MakeBucketArgs]: http://minio.github.io/minio-java/io/minio/MakeBucketArgs.html diff --git a/docs/zh_CN/API.md b/docs/zh_CN/API.md deleted file mode 100644 index 5cbaa6f14..000000000 --- a/docs/zh_CN/API.md +++ /dev/null @@ -1,1446 +0,0 @@ -# Java Client API参考文档 [![Slack](https://slack.min.io/slack?type=svg)](https://slack.min.io) - -## 初始化Minio Client object。 - -## MinIO - -```java -MinioClient minioClient = new MinioClient("https://play.min.io", "Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); -``` - -## AWS S3 - - -```java -MinioClient s3Client = new MinioClient("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY"); -``` - -| 存储桶操作 | 文件对象操作 | Presigned操作 | 存储桶策略 -|:--- |:--- |:--- |:--- | -| [`makeBucket`](#makeBucket) |[`getObject`](#getObject) |[`presignedGetObject`](#presignedGetObject) | [`getBucketPolicy`](#getBucketPolicy) | -| [`listBuckets`](#listBuckets) | [`putObject`](#putObject) | [`presignedPutObject`](#presignedPutObject) | [`setBucketPolicy`](#setBucketPolicy) | -| [`bucketExists`](#bucketExists) | [`copyObject`](#copyObject) | [`presignedPostPolicy`](#presignedPostPolicy) | | -| [`removeBucket`](#removeBucket) | [`statObject`](#statObject) | | | -| [`listObjects`](#listObjects) | [`removeObject`](#removeObject) | | | -| [`listIncompleteUploads`](#listIncompleteUploads) | [`removeIncompleteUpload`](#removeIncompleteUpload) | | | - - -## 1. 构造函数 - - - -| | -|---| -|`public MinioClient(String endpoint) throws NullPointerException, InvalidEndpointException, InvalidPortException` | -| 使用给定的endpoint以及匿名方式创建一个Minio client对象。| -| [查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#MinioClient-java.lang.String-) | - - -| | -|---| -|`public MinioClient(URL url) throws NullPointerException, InvalidEndpointException, InvalidPortException` | -| 使用给定的url以及匿名方式创建一个Minio client对象。 | -| [查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#MinioClient-java.net.URL-) | - - -| | -|---| -| `public MinioClient(com.squareup.okhttp.HttpUrl url) throws NullPointerException, InvalidEndpointException, InvalidPortException` | -|使用给定的HttpUrl以及匿名方式创建一个Minio client对象。 | -| [查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#MinioClient-com.squareup.okhttp.HttpUrl-) | - -| | -|---| -| `public MinioClient(String endpoint, String accessKey, String secretKey) throws NullPointerException, InvalidEndpointException, InvalidPortException` | -| 使用给定的endpoint、access key和secret key创建一个Minio client对象。 | -| [查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#MinioClient-java.lang.String-java.lang.String-java.lang.String-)| - -| | -|---| -| `public MinioClient(String endpoint, int port, String accessKey, String secretKey) throws NullPointerException, InvalidEndpointException, InvalidPortException` | -| 使用给定的endpoint、port、access key和secret key创建一个Minio client对象。 | -| [查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#MinioClient-java.lang.String-int-java.lang.String-java.lang.String-) | - - -| | -|---| -| `public MinioClient(String endpoint, String accessKey, String secretKey, boolean secure) throws NullPointerException, InvalidEndpointException, InvalidPortException` | -| 使用给定的endpoint、access key、secret key和一个secure选项(是否使用https)创建一个Minio client对象。 | -| [查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#MinioClient-java.lang.String-java.lang.String-java.lang.String-boolean-) | - - -| | -|---| -| `public MinioClient(String endpoint, int port, String accessKey, String secretKey, boolean secure) throws NullPointerException, InvalidEndpointException, InvalidPortException` | -| 使用给定的endpoint、port、access key、secret key和一个secure选项(是否使用https)创建一个Minio client对象。 | -| [查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#MinioClient-java.lang.String-int-java.lang.String-java.lang.String-boolean-) | - -| | -|---| -| `public MinioClient(com.squareup.okhttp.HttpUrl url, String accessKey, String secretKey) throws NullPointerException, InvalidEndpointException, InvalidPortException` | -| 使用给定的HttpUrl对象、access key、secret key创建一个Minio client对象。 | -| [查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#MinioClient-com.squareup.okhttp.HttpUrl-java.lang.String-java.lang.String-) | - - -| | -|---| -| `public MinioClient(URL url, String accessKey, String secretKey) throws NullPointerException, InvalidEndpointException, InvalidPortException` | -| 使用给定的URL对象、access key、secret key创建一个Minio client对象。 | -| [查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#MinioClient-java.net.URL-java.lang.String-java.lang.String-) | - - -__参数__ - -| 参数 | 类型 | 描述 | -|---|---|---| -| `endpoint` | _string_ | endPoint是一个URL,域名,IPv4或者IPv6地址。以下是合法的endpoints: | -| | |https://s3.amazonaws.com | -| | |https://play.min.io | -| | |localhost | -| | |play.min.io| -| `port` | _int_ | TCP/IP端口号。可选,默认值是,如果是http,则默认80端口,如果是https,则默认是443端口。| -| `accessKey` | _string_ |accessKey类似于用户ID,用于唯一标识你的账户。 | -|`secretKey` | _string_ | secretKey是你账户的密码。| -|`secure` | _boolean_ |如果是true,则用的是https而不是http,默认值是true。 | -|`url` | _URL_ |Endpoint URL对象。| -|`url` | _HttpURL_ |Endpoint HttpUrl对象。 | - - -__示例__ - - -### MinIO - - -```java -// 1. public MinioClient(String endpoint) -MinioClient minioClient = new MinioClient("https://play.min.io"); - -// 2. public MinioClient(URL url) -MinioClient minioClient = new MinioClient(new URL("https://play.min.io")); - -// 3. public MinioClient(com.squareup.okhttp.HttpUrl url) - MinioClient minioClient = new MinioClient(new HttpUrl.parse("https://play.min.io")); - -// 4. public MinioClient(String endpoint, String accessKey, String secretKey) -MinioClient minioClient = new MinioClient("https://play.min.io", "Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); - -// 5. public MinioClient(String endpoint, int port, String accessKey, String secretKey) -MinioClient minioClient = new MinioClient("https://play.min.io", 9000, "Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); - -// 6. public MinioClient(String endpoint, String accessKey, String secretKey, boolean insecure) -MinioClient minioClient = new MinioClient("https://play.min.io", "Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", true); - -// 7. public MinioClient(String endpoint, int port, String accessKey, String secretKey, boolean insecure) -MinioClient minioClient = new MinioClient("https://play.min.io", 9000, "Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", true); - -// 8. public MinioClient(com.squareup.okhttp.HttpUrl url, String accessKey, String secretKey) - MinioClient minioClient = new MinioClient(new URL("https://play.min.io"), "Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); - -// 9. public MinioClient(URL url, String accessKey, String secretKey) -MinioClient minioClient = new MinioClient(HttpUrl.parse("https://play.min.io"), "Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); -``` - - -### AWS S3 - - -```java -// 1. public MinioClient(String endpoint) -MinioClient s3Client = new MinioClient("https://s3.amazonaws.com"); - -// 2. public MinioClient(URL url) -MinioClient minioClient = new MinioClient(new URL("https://s3.amazonaws.com")); - -// 3. public MinioClient(com.squareup.okhttp.HttpUrl url) - MinioClient s3Client = new MinioClient(new HttpUrl.parse("https://s3.amazonaws.com")); - -// 4. public MinioClient(String endpoint, String accessKey, String secretKey) -MinioClient s3Client = new MinioClient("s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY"); - -// 5. public MinioClient(String endpoint, int port, String accessKey, String secretKey) -MinioClient s3Client = new MinioClient("s3.amazonaws.com", 80, "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY"); - -// 6. public MinioClient(String endpoint, String accessKey, String secretKey, boolean insecure) -MinioClient s3Client = new MinioClient("s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY", false); - -// 7. public MinioClient(String endpoint, int port, String accessKey, String secretKey, boolean insecure) -MinioClient s3Client = new MinioClient("s3.amazonaws.com", 80, "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY",false); - -// 8. public MinioClient(com.squareup.okhttp.HttpUrl url, String accessKey, String secretKey) - MinioClient s3Client = new MinioClient(new URL("s3.amazonaws.com"), "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY"); - -// 9. public MinioClient(URL url, String accessKey, String secretKey) -MinioClient s3Client = new MinioClient(HttpUrl.parse("s3.amazonaws.com"), "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY"); -``` - -## 2. 存储桶操作 - - -### makeBucket(String bucketName) -`public void makeBucket(String bucketName)` - -创建一个新的存储桶 - -[查看Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#makeBucket-java.lang.String-) - -__参数__ - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ |存储桶名称 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| ``None`` | 异常列表: | -| | ``InvalidBucketNameException`` : 非法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常 | -| | ``ErrorResponseException`` : 执行失败 | -| | ``InternalException`` : 内部异常 | - - -__示例__ - - -```java -try { - // 如存储桶不存在,创建之。 - boolean found = minioClient.bucketExists("mybucket"); - if (found) { - System.out.println("mybucket already exists"); - } else { - // 创建名为'my-bucketname'的存储桶。 - minioClient.makeBucket("mybucket"); - System.out.println("mybucket is created successfully"); - } -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### listBuckets() - -`public List listBuckets()` - -列出所有存储桶。 - -[查看Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#listBuckets--) - -|返回值类型 | 异常 | -|:--- |:--- | -| ``List Bucket`` : List of bucket type. | 异常列表: | -| | ``NoResponseException`` : 服务端无响应 | -| | ``IOException`` : 连接异常 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常 | -| | ``ErrorResponseException`` :执行失败异溃| -| | ``InternalException`` : 内部错误| - - -__示例__ - - -```java -try { - // 列出所有存储桶 - List bucketList = minioClient.listBuckets(); - for (Bucket bucket : bucketList) { - System.out.println(bucket.creationDate() + ", " + bucket.name()); - } -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### bucketExists(String bucketName) - -`public boolean bucketExists(String bucketName)` - -检查存储桶是否存在。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#bucketExists-java.lang.String-) - - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称 | - - -| 返回值值类型 | 异常 | -|:--- |:--- | -| ``boolean``: true if the bucket exists | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - - - -__示例__ - - -```java -try { - // 检查'my-bucketname'是否存在。 - boolean found = minioClient.bucketExists("mybucket"); - if (found) { - System.out.println("mybucket exists"); - } else { - System.out.println("mybucket does not exist"); - } -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - - -### removeBucket(String bucketName) - -`public void removeBucket(String bucketName)` - -删除一个存储桶。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#removeBucket-java.lang.String-) - -注意: - removeBucket不会删除存储桶里的对象,你需要通过removeObject API来删除它们。 - - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - - -__示例__ - - -```java -try { - // 删除之前先检查`my-bucket`是否存在。 - boolean found = minioClient.bucketExists("mybucket"); - if (found) { - // 删除`my-bucketname`存储桶,注意,只有存储桶为空时才能删除成功。 - minioClient.removeBucket("mybucket"); - System.out.println("mybucket is removed successfully"); - } else { - System.out.println("mybucket does not exist"); - } -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### listObjects(String bucketName, String prefix, boolean recursive, boolean useVersion1) - -`public Iterable> listObjects(String bucketName, String prefix, boolean recursive, boolean useVersion1)` - -列出某个存储桶中的所有对象。 - -[查看Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#listObjects-java.lang.String-java.lang.String-boolean-) - - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``prefix`` | _String_ | 对象名称的前缀 | -| ``recursive`` | _boolean_ | 是否递归查找,如果是false,就模拟文件夹结构查找。 | -| ``useVersion1`` | _boolean_ | 如果是true, 使用版本1 REST API | - - -|返回值类型 | 异常 | -|:--- |:--- | -| ``Iterable>``:an iterator of Result Items. | _None_ | - - -__示例__ - - -```java -try { - // 检查'mybucket'是否存在。 - boolean found = minioClient.bucketExists("mybucket"); - if (found) { - // 列出'my-bucketname'里的对象 - Iterable> myObjects = minioClient.listObjects("mybucket"); - for (Result result : myObjects) { - Item item = result.get(); - System.out.println(item.lastModified() + ", " + item.size() + ", " + item.objectName()); - } - } else { - System.out.println("mybucket does not exist"); - } -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - - -### listIncompleteUploads(String bucketName, String prefix, boolean recursive) - -`public Iterable> listIncompleteUploads(String bucketName, String prefix, boolean recursive)` - -列出存储桶中被部分上传的对象。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#listIncompleteUploads-java.lang.String-java.lang.String-boolean-) - - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``prefix`` | _String_ | 对象名称的前缀,列出有该前缀的对象 | -| ``recursive`` | _boolean_ | 是否递归查找,如果是false,就模拟文件夹结构查找。 | - - -|返回值类型 | 异常 | -|:--- |:--- | -| ``Iterable>``: an iterator of Upload. | _None_ | - - -__示例__ - - -```java -try { - // 检查'mybucket'是否存在。 - boolean found = minioClient.bucketExists("mybucket"); - if (found) { - // 列出'mybucket'中所有未完成的multipart上传的的对象。 - Iterable> myObjects = minioClient.listIncompleteUploads("mybucket"); - for (Result result : myObjects) { - Upload upload = result.get(); - System.out.println(upload.uploadId() + ", " + upload.objectName()); - } - } else { - System.out.println("mybucket does not exist"); - } -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### getBucketPolicy(String bucketName, String objectPrefix) -`public PolicyType getBucketPolicy(String bucketName, String objectPrefix)` - -获得指定对象前缀的存储桶策略。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#getBucketPolicy-java.lang.String-java.lang.String-) - -__参数__ - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectPrefix`` | _String_ | 策略适用的对象的前缀 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| ``PolicyType``: The current bucket policy type for a given bucket and objectPrefix. | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | -| | ``InvalidBucketNameException `` : 不合法的存储桶名称。 | -| | ``InvalidObjectPrefixException`` : 不合法的对象前缀 | -| | ``NoSuchAlgorithmException`` : 找不到相应的签名算法。 | -| | ``InsufficientDataException`` : 在读到相应length之前就得到一个EOFException。 | - - -__示例__ - - -```java -try { - System.out.println("Current policy: " + minioClient.getBucketPolicy("myBucket", "downloads")); -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### setBucketPolicy(String bucketName, String objectPrefix, PolicyType policy) -`public void setBucketPolicy(String bucketName, String objectPrefix, PolicyType policy)` - -给一个存储桶+对象前缀设置策略。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#setBucketPolicy-java.lang.String-java.lang.String-io.minio.BucketPolicy-) - -__参数__ - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectPrefix`` | _String_ | 对象前缀。 | -| ``policy`` | _PolicyType_ | 要赋予的策略,可选值有[PolicyType.NONE, PolicyType.READ_ONLY, PolicyType.READ_WRITE, PolicyType.WRITE_ONLY]. | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | -| | ``InvalidBucketNameException `` : 不合法的存储桶名称。 | -| | ``InvalidObjectPrefixException`` : 不合法的对象前缀 | -| | ``NoSuchAlgorithmException`` : 找不到相应的签名算法。 | -| | ``InsufficientDataException`` : 在读到相应length之前就得到一个EOFException。 | - - - -__示例__ - - -```java -try { - minioClient.setBucketPolicy("myBucket", "uploads", PolicyType.READ_ONLY); -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - -## 3. Object operations - - -### getObject(String bucketName, String objectName) - -`public InputStream getObject(String bucketName, String objectName, long offset)` - -以流的形式下载一个对象。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#getObject-java.lang.String-java.lang.String-long-) - - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| ``InputStream``: InputStream containing the object data. | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - - -__示例__ - - -```java -try { - // 调用statObject()来判断对象是否存在。 - // 如果不存在, statObject()抛出异常, - // 否则则代表对象存在。 - minioClient.statObject("mybucket", "myobject"); - - // 获取"myobject"的输入流。 - InputStream stream = minioClient.getObject("mybucket", "myobject"); - - // 读取输入流直到EOF并打印到控制台。 - byte[] buf = new byte[16384]; - int bytesRead; - while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) { - System.out.println(new String(buf, 0, bytesRead)); - } - - // 关闭流,此处为示例,流关闭最好放在finally块。 - stream.close(); -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### getObject(String bucketName, String objectName, long offset, Long length) - -`public InputStream getObject(String bucketName, String objectName, long offset, Long length)` - -下载对象指定区域的字节数组做为流。(断点下载) - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#getObject-java.lang.String-java.lang.String-long-java.lang.Long-) - - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``offset`` | _Long_ | ``offset`` 是起始字节的位置 | -| ``length`` | _Long_ | ``length``是要读取的长度 (可选,如果无值则代表读到文件结尾)。 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| ``InputStream`` : InputStream containing the object's data. | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - - -__示例__ - - -```java -try { - - // 调用statObject()来判断对象是否存在。 - // 如果不存在, statObject()抛出异常, - // 否则则代表对象存在。 - minioClient.statObject("mybucket", "myobject"); - - // 获取指定offset和length的"myobject"的输入流。 - InputStream stream = minioClient.getObject("mybucket", "myobject", 1024L, 4096L); - - // 读取输入流直到EOF并打印到控制台。 - byte[] buf = new byte[16384]; - int bytesRead; - while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) { - System.out.println(new String(buf, 0, bytesRead)); - } - - // 关闭流。 - stream.close(); -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### getObject(String bucketName, String objectName, String fileName) - -`public void getObject(String bucketName, String objectName, String fileName)` - -下载并将文件保存到本地。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#getObject-java.lang.String-java.lang.String-java.lang.String-) - - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``fileName`` | _String_ | File name. | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - -__示例__ - -```java -try { - // 调用statObject()来判断对象是否存在。 - // 如果不存在, statObject()抛出异常, - // 否则则代表对象存在。 - minioClient.statObject("mybucket", "myobject"); - - // 获取myobject的流并保存到photo.jpg文件中。 - minioClient.getObject("mybucket", "myobject", "photo.jpg"); - -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### getObject(String bucketName, String objectName, SecretKey key) - -`public CipherInputStream getObject(String bucketName, String objectName, SecretKey key)` - -在给定的存储桶中获取整个加密对象的数据作为InputStream,然后用传入的master key解密和加密对象关联的content key。然后创建一个含有InputStream和Cipher的CipherInputStream。这个Cipher被初始为用于使用content key进行解密,所以CipherInputStream会在返回数据前,尝试读取数据并进行解密。所以read()方法返回的是处理过的原始对象数据。 - -CipherInputStream必须用完关闭,否则连接不会被释放。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#getObject-java.lang.String-java.lang.String-javax.crypto.SecretKey-) - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``key`` | _SecretKey_ | [SecretKey](https://docs.oracle.com/javase/7/docs/api/javax/crypto/SecretKey.html)类型的数据。| - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | -| | ``InvalidEncryptionMetadataException`` : 加密秘钥错误。 | -| | ``BadPaddingException`` : 错误的padding | -| | ``IllegalBlockSizeException`` : 不正确的block size | -| | ``NoSuchPaddingException`` : 错误的pading类型 | -| | ``InvalidAlgorithmParameterException`` : 该算法不存在 | - -__示例__ - -```java -try { - // 调用statObject()来判断对象是否存在。 - // 如果不存在, statObject()抛出异常, - // 否则则代表对象存在。 - minioClient.statObject("mybucket", "myobject"); - - //生成256位AES key。 - KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES"); - symKeyGenerator.init(256); - SecretKey symKey = symKeyGenerator.generateKey(); - - // 获取对象数据并保存到photo.jpg - InputStream stream = minioClient.getObject("testbucket", "my-objectname", symKey); - - // 读流到EOF,并输出到控制台。 - byte[] buf = new byte[16384]; - int bytesRead; - while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) { - System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8)); - } - - // 关闭流。 - stream.close(); - -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### getObject(String bucketName, String objectName, KeyPair key) - -`public InputStream getObject(String bucketName, String objectName, KeyPair key)` - -在给定的存储桶中获取整个加密对象的数据作为InputStream,然后用传入的master keyPair解密和加密对象关联的content key。然后创建一个含有InputStream和Cipher的CipherInputStream。这个Cipher被初始为用于使用content key进行解密,所以CipherInputStream会在返回数据前,尝试读取数据并进行解密。所以read()方法返回的是处理过的原始对象数据。 - -CipherInputStream必须用完关闭,否则连接不会被释放。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#getObject-java.lang.String-java.lang.String-java.security.KeyPair-) - -__参数__ - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``key`` | _KeyPair_ | RSA [KeyPair](https://docs.oracle.com/javase/7/docs/api/java/security/KeyPair.html)类型的对象。 | - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | -| | ``InvalidEncryptionMetadataException`` : 加密秘钥错误。 | -| | ``BadPaddingException`` : 错误的padding | -| | ``IllegalBlockSizeException`` : 不正确的block size | -| | ``NoSuchPaddingException`` : 错误的pading类型 | -| | ``InvalidAlgorithmParameterException`` : 该算法不存在 | - -__示例__ - -```java -try { - // 调用statObject()来判断对象是否存在。 - // 如果不存在, statObject()抛出异常, - // 否则则代表对象存在。 - minioClient.statObject("mybucket", "myobject"); - - KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA"); - keyGenerator.initialize(1024, new SecureRandom()); - KeyPair keypair = keyGenerator.generateKeyPair(); - - // 获取对象数据并保存到photo.jpg - InputStream stream = minioClient.getObject("testbucket", "my-objectname", keypair); - - // 读流到EOF,并输出到控制台。 - byte[] buf = new byte[16384]; - int bytesRead; - while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) { - System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8)); - } - - // 关闭流。 - stream.close(); - -} catch (MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### putObject(String bucketName, String objectName, InputStream stream, long size, String contentType) - -`public void putObject(String bucketName, String objectName, InputStream stream, long size, String contentType)` - -通过InputStream上传对象。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#putObject-java.lang.String-java.lang.String-java.io.InputStream-long-java.lang.String-) - - -__参数__ - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``stream`` | _InputStream_ | 要上传的流。 | -| ``size`` | _long_ | 要上传的`stream`的size | -| ``contentType`` | _String_ | Content type。 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - - -__示例__ - - -单个对象的最大大小限制在5TB。putObject在对象大于5MiB时,自动使用multiple parts方式上传。这样,当上传失败时,客户端只需要上传未成功的部分即可(类似断点上传)。上传的对象使用MD5SUM签名进行完整性验证。 - -```java -try { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - builder.append("Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); - builder.append("(29 letters)\n"); - builder.append("Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); - builder.append("(31 letters)\n"); - builder.append("Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); - builder.append("NASAs Space Shuttle. (32 letters)\n"); - builder.append("The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. "); - builder.append("(39 letters)\n"); - builder.append("How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity "); - builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); - builder.append("Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac "); - builder.append("computers after System 7.\n"); - builder.append("A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my "); - builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); - builder.append("- --\n"); - } - ByteArrayInputStream bais = new - ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - // 创建对象 - minioClient.putObject("mybucket", "myobject", bais, bais.available(), "application/octet-stream"); - bais.close(); - System.out.println("myobject is uploaded successfully"); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### putObject(String bucketName, String objectName, String fileName) - -`public void putObject(String bucketName, String objectName, String fileName)` - -通过文件上传到对象中。 -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#putObject-java.lang.String-java.lang.String-java.lang.String-) - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``fileName`` | _String_ | File name. | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - -__示例__ - - -```java -try { - minioClient.putObject("mybucket", "island.jpg", "/mnt/photos/island.jpg") - System.out.println("island.jpg is uploaded successfully"); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - -### putObject(String bucketName, String objectName, InputStream stream, long size, String contentType, SecretKey key) - -`public void putObject(String bucketName, String objectName, InputStream stream, long size, String contentType, - SecretKey key)` - -拿到流的数据,使用随机生成的content key进行加密,并上传到指定存储桶中。同时将加密后的content key和iv做为加密对象有header也上传到存储桶中。content key使用传入到该方法的master key进行加密。 - -如果对象大于5MB,客户端会自动进行multi part上传。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#putObject-java.lang.String-java.lang.String-java.io.InputStream-long-java.lang.String-javax.crypto.SecretKey-) - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``stream`` | _InputStream_ | 要上传的流。 | -| ``size`` | _long_ | 要上传的流的大小。| -| ``contentType`` | _String_ | Content type。| -| ``key`` | _SecretKey_ | 用AES初使化的对象[SecretKey](https://docs.oracle.com/javase/7/docs/api/javax/crypto/SecretKey.html)。 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | -| | ``InvalidAlgorithmParameterException`` : 错误的加密算法。 | -| | ``BadPaddingException`` : 不正确的padding. | -| | ``IllegalBlockSizeException`` : 不正确的block。 | -| | ``NoSuchPaddingException`` : 错误的padding类型。 | - -__示例__ - -对象使用随机生成的key进行加密,然后这个用于加密数据的key又被由仅被client知道的master key(封装在encryptionMaterials对象里)进行加密。这个被加密后的key和IV做为对象的header和加密后的对象一起被上传到存储服务上。 - -```java -try { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - builder.append("Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); - builder.append("(29 letters)\n"); - builder.append("Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); - builder.append("(31 letters)\n"); - builder.append("Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); - builder.append("NASAs Space Shuttle. (32 letters)\n"); - builder.append("The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. "); - builder.append("(39 letters)\n"); - builder.append("How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity "); - builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); - builder.append("Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac "); - builder.append("computers after System 7.\n"); - builder.append("A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my "); - builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); - builder.append("- --\n"); - } - ByteArrayInputStream bais = new - ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - - //生成256位AES key. - KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES"); - symKeyGenerator.init(256); - SecretKey symKey = symKeyGenerator.generateKey(); - - // 创建一个对象 - minioClient.putObject("mybucket", "myobject", bais, bais.available(), "application/octet-stream", symKey); - bais.close(); - System.out.println("myobject is uploaded successfully"); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### putObject(String bucketName, String objectName, InputStream stream, long size, String contentType, KeyPair key) - -`public void putObject(String bucketName, String objectName, InputStream stream, long size, String contentType, - KeyPair key)` - - -拿到流的数据,使用随机生成的content key进行加密,并上传到指定存储桶中。同时将加密后的content key和iv做为加密对象有header也上传到存储桶中。content key使用传入到该方法的master key进行加密。 - -如果对象大于5MB,客户端会自动进行multi part上传。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#putObject-java.lang.String-java.lang.String-java.io.InputStream-long-java.lang.String-java.security.KeyPair-) - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``stream`` | _InputStream_ | 要上传的流。 | -| ``size`` | _long_ | 要上传的流的大小。 | -| ``contentType`` | _String_ | Content type。 | -| ``key`` | _KeyPair_ | 一个RSA [KeyPair](https://docs.oracle.com/javase/7/docs/api/java/security/KeyPair.html)的对象。 | - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | -| | ``InvalidAlgorithmParameterException`` : 错误的加密算法。 | -| | ``BadPaddingException`` : 不正确的padding。 | -| | ``IllegalBlockSizeException`` : 不正确的block。 | -| | ``NoSuchPaddingException`` : 错误的pading类型。 | - -__示例__ - -对象使用随机生成的key进行加密,然后这个用于加密数据的key又被由仅被client知道的master key(封装在encryptionMaterials对象里)进行加密。这个被加密后的key和IV做为对象的header和加密后的对象一起被上传到存储服务上。 - -```java -try { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - builder.append("Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); - builder.append("(29 letters)\n"); - builder.append("Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); - builder.append("(31 letters)\n"); - builder.append("Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); - builder.append("NASAs Space Shuttle. (32 letters)\n"); - builder.append("The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. "); - builder.append("(39 letters)\n"); - builder.append("How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity "); - builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); - builder.append("Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac "); - builder.append("computers after System 7.\n"); - builder.append("A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my "); - builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); - builder.append("- --\n"); - } - ByteArrayInputStream bais = new - ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - - KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA"); - keyGenerator.initialize(1024, new SecureRandom()); - KeyPair keypair = keyGenerator.generateKeyPair(); - - // Create an object - minioClient.putObject("mybucket", "myobject", bais, bais.available(), "application/octet-stream", keypair); - bais.close(); - System.out.println("myobject is uploaded successfully"); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### statObject(String bucketName, String objectName) - -*`public ObjectStat statObject(String bucketName, String objectName)`* - -获取对象的元数据。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#statObject-java.lang.String-java.lang.String-) - - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| ``ObjectStat``: Populated object meta data. | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - -__示例__ - - -```java -try { - // 获得对象的元数据。 - ObjectStat objectStat = minioClient.statObject("mybucket", "myobject"); - System.out.println(objectStat); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### copyObject(String bucketName, String objectName, String destBucketName, String destObjectName, CopyConditions cpConds, Map metadata) - -*`public void copyObject(String bucketName, String objectName, String destBucketName, String destObjectName, CopyConditions cpConds, Map metadata)`* - -从objectName指定的对象中将数据拷贝到destObjectName指定的对象。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#copyObject-java.lang.String-java.lang.String-java.lang.String-java.lang.String-io.minio.CopyConditions-) - -__参数__ - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 源存储桶名称。 | -| ``objectName`` | _String_ | 源存储桶中的源对象名称。 | -| ``destBucketName`` | _String_ | 目标存储桶名称。 | -| ``destObjectName`` | _String_ | 要创建的目标对象名称,如果为空,默认为源对象名称。| -| ``copyConditions`` | _CopyConditions_ | 拷贝操作的一些条件Map。| -| ``metadata`` | _Map_ | 给目标对象的元数据Map。| - - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - -__示例__ - -本API执行了一个服务端的拷贝操作。 - -```java -try { - CopyConditions copyConditions = new CopyConditions(); - copyConditions.setMatchETagNone("TestETag"); - - minioClient.copyObject("mybucket", "island.jpg", "mydestbucket", "processed.png", copyConditions); - System.out.println("island.jpg is uploaded successfully"); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### removeObject(String bucketName, String objectName) - -`public void removeObject(String bucketName, String objectName)` - -删除一个对象。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#removeObject-java.lang.String-java.lang.String-) - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - - - -__示例__ - - -```java -try { - // 从mybucket中删除myobject。 - minioClient.removeObject("mybucket", "myobject"); - System.out.println("successfully removed mybucket/myobject"); -} catch (MinioException e) { - System.out.println("Error: " + e); -} -``` - - -### removeObject(String bucketName, Iterable objectNames) - -`public Iterable> removeObject(String bucketName, Iterable objectNames)` - -删除多个对象。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#removeObject-java.lang.String-java.lang.String-) - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectNames`` | _Iterable_ | 含有要删除的多个object名称的迭代器对象。 | - -|返回值类型 | 异常 | -|:--- |:--- | -| ``Iterable>``:an iterator of Result DeleteError. | _None_ | - - - -__示例__ - - -```java -List objectNames = new LinkedList(); -objectNames.add("my-objectname1"); -objectNames.add("my-objectname2"); -objectNames.add("my-objectname3"); -try { - // 删除my-bucketname里的多个对象 - for (Result errorResult: minioClient.removeObject("my-bucketname", objectNames)) { - DeleteError error = errorResult.get(); - System.out.println("Failed to remove '" + error.objectName() + "'. Error:" + error.message()); - } -} catch (MinioException e) { - System.out.println("Error: " + e); -} -``` - - -### removeIncompleteUpload(String bucketName, String objectName) - -`public void removeIncompleteUpload(String bucketName, String objectName)` - -删除一个未完整上传的对象。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#removeIncompleteUpload-java.lang.String-java.lang.String-) - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| None | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``NoResponseException`` : 服务器无响应。 | -| | ``IOException`` : 连接异常。 | -| | ``org.xmlpull.v1.XmlPullParserException`` : 解析返回的XML异常。 | -| | ``ErrorResponseException`` : 执行失败异常。 | -| | ``InternalException`` : 内部错误。 | - - -__示例__ - - -```java -try { - // 从存储桶中删除名为myobject的未完整上传的对象。 - minioClient.removeIncompleteUpload("mybucket", "myobject"); - System.out.println("successfully removed all incomplete upload session of my-bucketname/my-objectname"); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - -## 4. Presigned操作 - - -### presignedGetObject(String bucketName, String objectName, Integer expires) -`public String presignedGetObject(String bucketName, String objectName, Integer expires)` - -生成一个给HTTP GET请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#presignedGetObject-java.lang.String-java.lang.String-java.lang.Integer-) - - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``expiry`` | _Integer_ | 失效时间(以秒为单位),默认是7天,不得大于七天。 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| ``String`` : string contains URL to download the object. | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``InvalidKeyException`` : 不合法的access key或者secret key。 | -| | ``IOException`` : 连接异常。 | -| | ``NoSuchAlgorithmException`` : 找不到相应的签名算法。 | -| | ``InvalidExpiresRangeException`` : presigned URL已经过期了。 | - - -__示例__ - - -```java -try { - String url = minioClient.presignedGetObject("mybucket", "myobject", 60 * 60 * 24); - System.out.println(url); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### presignedPutObject(String bucketName, String objectName, Integer expires) - -`public String presignedPutObject(String bucketName, String objectName, Integer expires)` - -生成一个给HTTP PUT请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#presignedPutObject-java.lang.String-java.lang.String-java.lang.Integer-) - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``bucketName`` | _String_ | 存储桶名称。 | -| ``objectName`` | _String_ | 存储桶里的对象名称。 | -| ``expiry`` | _Integer_ | 失效时间(以秒为单位),默认是7天,不得大于七天。 | - -| 返回值类型 | 异常 | -|:--- |:--- | -| ``String`` : string contains URL to download the object. | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``InvalidKeyException`` : 不合法的access key或者secret key。 | -| | ``IOException`` : 连接异常。 | -| | ``NoSuchAlgorithmException`` : 找不到相应的签名算法。 | -| | ``InvalidExpiresRangeException`` : presigned URL已经过期了。 | - - -__示例__ - -```java -try { - String url = minioClient.presignedPutObject("mybucket", "myobject", 60 * 60 * 24); - System.out.println(url); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -} -``` - - -### presignedPostPolicy(PostPolicy policy) - -`public Map presignedPostPolicy(PostPolicy policy)` - -允许给POST请求的presigned URL设置策略,比如接收对象上传的存储桶名称的策略,key名称前缀,过期策略。 - -[查看 Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#presignedPostPolicy-io.minio.PostPolicy-) - -__参数__ - - -|参数 | 类型 | 描述 | -|:--- |:--- |:--- | -| ``policy`` | _PostPolicy_ | 对象的post策略 | - - -| 返回值类型 | 异常 | -|:--- |:--- | -| ``Map``: Map of strings to construct form-data. | 异常列表: | -| | ``InvalidBucketNameException`` : 不合法的存储桶名称。 | -| | ``InvalidKeyException`` : 不合法的access key或者secret key。 | -| | ``IOException`` : 连接异常。 | -| | ``NoSuchAlgorithmException`` : 找不到相应的签名算法。 | - - - -__示例__ - -```java -try { - PostPolicy policy = new PostPolicy("mybucket", "myobject", - DateTime.now().plusDays(7)); - policy.setContentType("image/png"); - Map formData = minioClient.presignedPostPolicy(policy); - System.out.print("curl -X POST "); - for (Map.Entry entry : formData.entrySet()) { - System.out.print(" -F " + entry.getKey() + "=" + entry.getValue()); - } - System.out.println(" -F file=@/tmp/userpic.png https://play.min.io/mybucket"); -} catch(MinioException e) { - System.out.println("Error occurred: " + e); -``` - -## 5. 了解更多 - -- [创建属于你的照片API服务示例](https://github.com/minio/minio-java-rest-example) -- [完整的JavaDoc](http://minio.github.io/minio-java/) diff --git a/docs/zh_CN/CONTRIBUTING.md b/docs/zh_CN/CONTRIBUTING.md deleted file mode 100644 index 93f856b7d..000000000 --- a/docs/zh_CN/CONTRIBUTING.md +++ /dev/null @@ -1,9 +0,0 @@ -# 贡献者指南 -* Fork minio-java. -* 创建你的分支 (`$ git checkout -b my-new-feature`)。 -* 敲代码,敲代码... -* Commit你的修改(`$ git commit -am 'Add some feature'`)。 -* 构建测试包 (`$ ./gradlew build`)。 -* 功能测试 (`$ ./gradlew runFunctionalTest`)。 -* Push到你的分支 (`$ git push origin my-new-feature`)。 -* 创建一个Pull Request。 diff --git a/examples/BucketExists.java b/examples/BucketExists.java index aec612a1e..c49b187e9 100644 --- a/examples/BucketExists.java +++ b/examples/BucketExists.java @@ -17,39 +17,31 @@ import io.minio.BucketExistsArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class BucketExists { /** MinioClient.bucketExists() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Check whether 'my-bucketname' exist or not. - boolean found = - minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucketname").build()); - if (found) { - System.out.println("my-bucketname exists"); - } else { - System.out.println("my-bucketname does not exist"); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + // Check whether 'my-bucket' exist or not. + boolean found = + minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucket").build()); + if (found) { + System.out.println("my-bucket exists"); + } else { + System.out.println("my-bucket does not exist"); } } } diff --git a/examples/ComposeObject.java b/examples/ComposeObject.java index 3cad7faa2..f84a4c770 100644 --- a/examples/ComposeObject.java +++ b/examples/ComposeObject.java @@ -15,98 +15,72 @@ */ 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; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import javax.crypto.spec.SecretKeySpec; public class ComposeObject { /** MinioClient.composeObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - { - // Create a ComposeSource to compose Object. - List sources = new ArrayList(); - sources.add( - ComposeSource.builder() - .bucket("my-bucketname-one") - .object("my-objectname-one") - .build()); - sources.add( - ComposeSource.builder() - .bucket("my-bucketname-two") - .object("my-objectname-two") - .build()); + { + // Create a SourceObject to compose Object. + List sources = new ArrayList(); + sources.add(SourceObject.builder().bucket("my-bucket-one").object("my-object-one").build()); + sources.add(SourceObject.builder().bucket("my-bucket-two").object("my-object-two").build()); - minioClient.composeObject( - ComposeObjectArgs.builder() - .bucket("my-destination-bucket") - .object("my-destination-object") - .sources(sources) - .build()); - System.out.println("Object Composed successfully"); - } - - { - ServerSideEncryptionCustomerKey srcSsec = - new ServerSideEncryptionCustomerKey( - new SecretKeySpec( - "01234567890123456789012345678901".getBytes(StandardCharsets.UTF_8), "AES")); + minioClient.composeObject( + ComposeObjectArgs.builder() + .bucket("my-destination-bucket") + .object("my-destination-object") + .sources(sources) + .build()); + System.out.println("Object Composed successfully"); + } - ServerSideEncryption sse = - new ServerSideEncryptionCustomerKey( - new SecretKeySpec( - "12345678912345678912345678912345".getBytes(StandardCharsets.UTF_8), "AES")); + { + ServerSideEncryption.CustomerKey srcSsec = + new ServerSideEncryption.CustomerKey( + new SecretKeySpec( + "01234567890123456789012345678901".getBytes(StandardCharsets.UTF_8), "AES")); - List sources = new ArrayList(); - sources.add( - ComposeSource.builder() - .bucket("my-bucketname") - .object("my-objectname-one") - .ssec(srcSsec) - .build()); - sources.add( - ComposeSource.builder() - .bucket("my-bucketname") - .object("my-objectname-two") - .ssec(srcSsec) - .build()); + ServerSideEncryption sse = + new ServerSideEncryption.CustomerKey( + new SecretKeySpec( + "12345678912345678912345678912345".getBytes(StandardCharsets.UTF_8), "AES")); - minioClient.composeObject( - ComposeObjectArgs.builder() - .bucket("my-destination-bucket") - .object("my-destination-object") - .sources(sources) - .sse(sse) - .build()); - System.out.println("Object Composed successfully"); - } + List sources = new ArrayList(); + sources.add( + SourceObject.builder().bucket("my-bucket").object("my-object-one").ssec(srcSsec).build()); + sources.add( + SourceObject.builder().bucket("my-bucket").object("my-object-two").ssec(srcSsec).build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + minioClient.composeObject( + ComposeObjectArgs.builder() + .bucket("my-destination-bucket") + .object("my-destination-object") + .sources(sources) + .sse(sse) + .build()); + System.out.println("Object Composed successfully"); } } } diff --git a/examples/CopyObject.java b/examples/CopyObject.java index 6b6508f2a..8ede00fc8 100644 --- a/examples/CopyObject.java +++ b/examples/CopyObject.java @@ -16,14 +16,10 @@ */ 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; import java.security.NoSuchAlgorithmException; import java.util.HashMap; @@ -33,172 +29,151 @@ public class CopyObject { /** MinioClient.copyObject() example. */ public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + throws InvalidKeyException, MinioException, NoSuchAlgorithmException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - KeyGenerator keyGen = KeyGenerator.getInstance("AES"); - keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); - Map myContext = new HashMap<>(); - myContext.put("key1", "value1"); - ServerSideEncryption sseKms = new ServerSideEncryptionKms("Key-Id", myContext); + Map myContext = new HashMap<>(); + myContext.put("key1", "value1"); + 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"); - headers.put("x-amz-meta-my-project", "Project One"); + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + headers.put("x-amz-meta-my-project", "Project One"); - String etag = "9855d05ab7a1cfd5ea304f0547c24496"; + String etag = "9855d05ab7a1cfd5ea304f0547c24496"; - { - // Create object "my-objectname" in bucket "my-bucketname" by copying from object - // "my-objectname" in bucket "my-source-bucketname". - minioClient.copyObject( - CopyObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .source( - CopySource.builder() - .bucket("my-source-bucketname") - .object("my-objectname") - .build()) - .build()); - System.out.println( - "my-source-bucketname/my-objectname copied " - + "to my-bucketname/my-objectname successfully"); - } + { + // Create object "my-object" in bucket "my-bucket" by copying from object + // "my-object" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .source( + SourceObject.builder().bucket("my-source-bucketname").object("my-object").build()) + .build()); + System.out.println( + "my-source-bucketname/my-object copied " + "to my-bucket/my-object successfully"); + } - { - // Create object "my-objectname" in bucket "my-bucketname" by copying from object - // "my-source-objectname" in bucket "my-source-bucketname". - minioClient.copyObject( - CopyObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .source( - CopySource.builder() - .bucket("my-source-bucketname") - .object("my-source-objectname") - .build()) - .build()); - System.out.println( - "my-source-bucketname/my-source-objectname copied " - + "to my-bucketname/my-objectname successfully"); - } + { + // Create object "my-object" in bucket "my-bucket" by copying from object + // "my-source-objectname" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .source( + SourceObject.builder() + .bucket("my-source-bucketname") + .object("my-source-objectname") + .build()) + .build()); + System.out.println( + "my-source-bucketname/my-source-objectname copied " + + "to my-bucket/my-object successfully"); + } - { - // Create object "my-objectname" in bucket "my-bucketname" with SSE-KMS server-side - // encryption by copying from object "my-objectname" in bucket "my-source-bucketname". - minioClient.copyObject( - CopyObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .source( - CopySource.builder() - .bucket("my-source-bucketname") - .object("my-objectname") - .build()) - .sse(sseKms) // Replace with actual key. - .build()); - System.out.println( - "my-source-bucketname/my-objectname copied " - + "to my-bucketname/my-objectname successfully"); - } + { + // Create object "my-object" in bucket "my-bucket" with SSE-KMS server-side + // encryption by copying from object "my-object" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .source( + SourceObject.builder().bucket("my-source-bucketname").object("my-object").build()) + .sse(sseKms) // Replace with actual key. + .build()); + System.out.println( + "my-source-bucketname/my-object copied " + "to my-bucket/my-object successfully"); + } - { - // Create object "my-objectname" in bucket "my-bucketname" with SSE-S3 server-side - // encryption by copying from object "my-objectname" in bucket "my-source-bucketname". - minioClient.copyObject( - CopyObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .source( - CopySource.builder() - .bucket("my-source-bucketname") - .object("my-objectname") - .build()) - .sse(sseS3) // Replace with actual key. - .build()); - System.out.println( - "my-source-bucketname/my-objectname copied " - + "to my-bucketname/my-objectname successfully"); - } + { + // Create object "my-object" in bucket "my-bucket" with SSE-S3 server-side + // encryption by copying from object "my-object" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .source( + SourceObject.builder().bucket("my-source-bucketname").object("my-object").build()) + .sse(sseS3) // Replace with actual key. + .build()); + System.out.println( + "my-source-bucketname/my-object copied " + "to my-bucket/my-object successfully"); + } - { - // Create object "my-objectname" in bucket "my-bucketname" with SSE-C server-side encryption - // by copying from object "my-objectname" in bucket "my-source-bucketname". - minioClient.copyObject( - CopyObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .source( - CopySource.builder() - .bucket("my-source-bucketname") - .object("my-objectname") - .build()) - .sse(ssec) // Replace with actual key. - .build()); - System.out.println( - "my-source-bucketname/my-objectname copied " - + "to my-bucketname/my-objectname successfully"); - } + { + // Create object "my-object" in bucket "my-bucket" with SSE-C server-side encryption + // by copying from object "my-object" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .source( + SourceObject.builder().bucket("my-source-bucketname").object("my-object").build()) + .sse(ssec) // Replace with actual key. + .build()); + System.out.println( + "my-source-bucketname/my-object copied " + "to my-bucket/my-object successfully"); + } - { - // Create object "my-objectname" in bucket "my-bucketname" by copying from SSE-C encrypted - // object "my-source-objectname" in bucket "my-source-bucketname". - minioClient.copyObject( - CopyObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .source( - CopySource.builder() - .bucket("my-source-bucketname") - .object("my-source-objectname") - .ssec(ssec) // Replace with actual key. - .build()) - .build()); - System.out.println( - "my-source-bucketname/my-source-objectname copied " - + "to my-bucketname/my-objectname successfully"); - } + { + // Create object "my-object" in bucket "my-bucket" by copying from SSE-C encrypted + // object "my-source-objectname" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .source( + SourceObject.builder() + .bucket("my-source-bucketname") + .object("my-source-objectname") + .ssec(ssec) // Replace with actual key. + .build()) + .build()); + System.out.println( + "my-source-bucketname/my-source-objectname copied " + + "to my-bucket/my-object successfully"); + } - { - // Create object "my-objectname" in bucket "my-bucketname" with custom headers conditionally - // by copying from object "my-objectname" in bucket "my-source-bucketname". - minioClient.copyObject( - CopyObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .source( - CopySource.builder() - .bucket("my-source-bucketname") - .object("my-objectname") - .matchETag(etag) // Replace with actual etag. - .build()) - .headers(headers) // Replace with actual headers. - .build()); - System.out.println( - "my-source-bucketname/my-objectname copied " - + "to my-bucketname/my-objectname successfully"); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + { + // Create object "my-object" in bucket "my-bucket" with custom headers conditionally + // by copying from object "my-object" in bucket "my-source-bucketname". + minioClient.copyObject( + CopyObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .source( + SourceObject.builder() + .bucket("my-source-bucketname") + .object("my-object") + .matchETag(etag) // Replace with actual etag. + .build()) + .headers(headers) // Replace with actual headers. + .build()); + System.out.println( + "my-source-bucketname/my-object copied " + "to my-bucket/my-object successfully"); } } } diff --git a/examples/DeleteBucketCors.java b/examples/DeleteBucketCors.java index 86990ab43..62d0fdb50 100644 --- a/examples/DeleteBucketCors.java +++ b/examples/DeleteBucketCors.java @@ -17,33 +17,25 @@ import io.minio.DeleteBucketCorsArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DeleteBucketCors { /** MinioClient.deleteBucketCors() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - minioClient.deleteBucketCors(DeleteBucketCorsArgs.builder().bucket("my-bucketname").build()); - System.out.println("Bucket CORS configuration deleted successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.deleteBucketCors(DeleteBucketCorsArgs.builder().bucket("my-bucket").build()); + System.out.println("Bucket CORS configuration deleted successfully"); } } diff --git a/examples/DeleteBucketEncryption.java b/examples/DeleteBucketEncryption.java index 004b7a3ff..d23667962 100644 --- a/examples/DeleteBucketEncryption.java +++ b/examples/DeleteBucketEncryption.java @@ -17,34 +17,26 @@ import io.minio.DeleteBucketEncryptionArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DeleteBucketEncryption { /** MinioClient.deleteBucketEncryption() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - minioClient.deleteBucketEncryption( - DeleteBucketEncryptionArgs.builder().bucket("my-bucketname").build()); - System.out.println("Encryption configuration of my-bucketname is removed successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.deleteBucketEncryption( + DeleteBucketEncryptionArgs.builder().bucket("my-bucket").build()); + System.out.println("Encryption configuration of my-bucket is removed successfully"); } } diff --git a/examples/DeleteBucketLifecycle.java b/examples/DeleteBucketLifecycle.java index d9c234e01..2aacd91a1 100644 --- a/examples/DeleteBucketLifecycle.java +++ b/examples/DeleteBucketLifecycle.java @@ -17,34 +17,26 @@ import io.minio.DeleteBucketLifecycleArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DeleteBucketLifecycle { /** MinioClient.DeleteBucketLifecycle() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Pass blank as life cycle in setBucketLifecycle method. - minioClient.deleteBucketLifecycle( - DeleteBucketLifecycleArgs.builder().bucket("my-bucketName").build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Pass blank as life cycle in setBucketLifecycle method. + minioClient.deleteBucketLifecycle( + DeleteBucketLifecycleArgs.builder().bucket("my-bucketName").build()); } } diff --git a/examples/DeleteBucketNotification.java b/examples/DeleteBucketNotification.java index d8d0ca2ee..8b754941b 100644 --- a/examples/DeleteBucketNotification.java +++ b/examples/DeleteBucketNotification.java @@ -17,34 +17,26 @@ import io.minio.DeleteBucketNotificationArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DeleteBucketNotification { /** MinioClient.removeAllBucketNotification() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - minioClient.deleteBucketNotification( - DeleteBucketNotificationArgs.builder().bucket("my-bucketname").build()); - System.out.println("Removed all bucket notification"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.deleteBucketNotification( + DeleteBucketNotificationArgs.builder().bucket("my-bucket").build()); + System.out.println("Removed all bucket notification"); } } diff --git a/examples/DeleteBucketPolicy.java b/examples/DeleteBucketPolicy.java index 25137db09..e037db756 100644 --- a/examples/DeleteBucketPolicy.java +++ b/examples/DeleteBucketPolicy.java @@ -17,34 +17,25 @@ import io.minio.DeleteBucketPolicyArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DeleteBucketPolicy { /** MinioClient.deleteBucketPolicy() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - minioClient.deleteBucketPolicy( - DeleteBucketPolicyArgs.builder().bucket("my-bucketname").build()); - System.out.println("Bucket policy is deleted successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.deleteBucketPolicy(DeleteBucketPolicyArgs.builder().bucket("my-bucket").build()); + System.out.println("Bucket policy is deleted successfully"); } } diff --git a/examples/DeleteBucketReplication.java b/examples/DeleteBucketReplication.java index 685858772..1ccc9187d 100644 --- a/examples/DeleteBucketReplication.java +++ b/examples/DeleteBucketReplication.java @@ -17,33 +17,25 @@ import io.minio.DeleteBucketReplicationArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DeleteBucketReplication { /** MinioClient.deleteBucketReplication() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - minioClient.deleteBucketReplication( - DeleteBucketReplicationArgs.builder().bucket("my-bucketname").build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.deleteBucketReplication( + DeleteBucketReplicationArgs.builder().bucket("my-bucket").build()); } } diff --git a/examples/DeleteBucketTags.java b/examples/DeleteBucketTags.java index 106475f5f..ed7e57d68 100644 --- a/examples/DeleteBucketTags.java +++ b/examples/DeleteBucketTags.java @@ -17,33 +17,25 @@ import io.minio.DeleteBucketTagsArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DeleteBucketTags { /** MinioClient.deleteBucketTags() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - minioClient.deleteBucketTags(DeleteBucketTagsArgs.builder().bucket("my-bucketname").build()); - System.out.println("Bucket tags deleted successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.deleteBucketTags(DeleteBucketTagsArgs.builder().bucket("my-bucket").build()); + System.out.println("Bucket tags deleted successfully"); } } diff --git a/examples/DeleteObjectLockConfiguration.java b/examples/DeleteObjectLockConfiguration.java index bb0fe6c87..2ec7fd4a6 100644 --- a/examples/DeleteObjectLockConfiguration.java +++ b/examples/DeleteObjectLockConfiguration.java @@ -17,35 +17,27 @@ import io.minio.DeleteObjectLockConfigurationArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DeleteObjectLockConfiguration { /** MinioClient.deleteObjectLockConfiguration() exanple. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - minioClient.deleteObjectLockConfiguration( - DeleteObjectLockConfigurationArgs.builder().bucket("my-lock-enabled-bucketname").build()); + minioClient.deleteObjectLockConfiguration( + DeleteObjectLockConfigurationArgs.builder().bucket("my-lock-enabled-bucketname").build()); - System.out.println("Object-lock configuration is deleted successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + System.out.println("Object-lock configuration is deleted successfully"); } } diff --git a/examples/DeleteObjectTags.java b/examples/DeleteObjectTags.java index 9a0c342b3..272a57190 100644 --- a/examples/DeleteObjectTags.java +++ b/examples/DeleteObjectTags.java @@ -17,34 +17,26 @@ import io.minio.DeleteObjectTagsArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DeleteObjectTags { /** MinioClient.deleteObjectTags() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - minioClient.deleteObjectTags( - DeleteObjectTagsArgs.builder().bucket("my-bucketname").object("my-objectname").build()); - System.out.println("Object tags deleted successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.deleteObjectTags( + DeleteObjectTagsArgs.builder().bucket("my-bucket").object("my-object").build()); + System.out.println("Object tags deleted successfully"); } } diff --git a/examples/DisableObjectLegalHold.java b/examples/DisableObjectLegalHold.java index 668ca8a6c..292797239 100644 --- a/examples/DisableObjectLegalHold.java +++ b/examples/DisableObjectLegalHold.java @@ -16,39 +16,27 @@ import io.minio.DisableObjectLegalHoldArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class DisableObjectLegalHold { /** MinioClient.disableObjectLegalHold() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException, IllegalArgumentException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Disable object legal hold. - minioClient.disableObjectLegalHold( - DisableObjectLegalHoldArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .build()); - System.out.println("Legal hold disabled on object successfully "); - - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Disable object legal hold. + minioClient.disableObjectLegalHold( + DisableObjectLegalHoldArgs.builder().bucket("my-bucket").object("my-object").build()); + System.out.println("Legal hold disabled on object successfully "); } } diff --git a/examples/DownloadObject.java b/examples/DownloadObject.java index f357842d0..cf6f4e683 100644 --- a/examples/DownloadObject.java +++ b/examples/DownloadObject.java @@ -16,9 +16,8 @@ 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; import java.security.NoSuchAlgorithmException; import javax.crypto.KeyGenerator; @@ -26,51 +25,47 @@ public class DownloadObject { /** MinioClient.getObject() example. */ public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + throws InvalidKeyException, MinioException, NoSuchAlgorithmException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - { - // Download 'my-objectname' from 'my-bucketname' to 'my-filename' - minioClient.downloadObject( - DownloadObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .filename("my-filename") - .build()); - System.out.println("my-objectname is successfully downloaded to my-filename"); - } + { + // Download 'my-object' from 'my-bucket' to 'my-filename' + minioClient.downloadObject( + DownloadObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .filename("my-filename") + .build()); + System.out.println("my-object is successfully downloaded to my-filename"); + } - { - KeyGenerator keyGen = KeyGenerator.getInstance("AES"); - keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); + { + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); - // Download SSE-C encrypted 'my-objectname' from 'my-bucketname' to 'my-filename' - minioClient.downloadObject( - DownloadObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .filename("my-filename") - .ssec(ssec) // Replace with same SSE-C used at the time of upload. - .build()); - System.out.println("my-objectname is successfully downloaded to my-filename"); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + // Download SSE-C encrypted 'my-object' from 'my-bucket' to 'my-filename' + minioClient.downloadObject( + DownloadObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .filename("my-filename") + .ssec(ssec) // Replace with same SSE-C used at the time of upload. + .build()); + System.out.println("my-object is successfully downloaded to my-filename"); } } } diff --git a/examples/EnableObjectLegalHold.java b/examples/EnableObjectLegalHold.java index c96f50fce..0bc1c11cf 100644 --- a/examples/EnableObjectLegalHold.java +++ b/examples/EnableObjectLegalHold.java @@ -16,40 +16,32 @@ import io.minio.EnableObjectLegalHoldArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class EnableObjectLegalHold { /** MinioClient.enableObjectLegalHold() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException, IllegalArgumentException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Enable object legal hold. - minioClient.enableObjectLegalHold( - EnableObjectLegalHoldArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .versionId("object-versionId") - .build()); + // Enable object legal hold. + minioClient.enableObjectLegalHold( + EnableObjectLegalHoldArgs.builder() + .bucket("my-bucket") + .object("my-object") + .versionId("object-versionId") + .build()); - System.out.println("Legal hold enabled on object successfully "); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + System.out.println("Legal hold enabled on object successfully "); } } diff --git a/examples/GetBucketCors.java b/examples/GetBucketCors.java index 713f8fc07..0ad2f5952 100644 --- a/examples/GetBucketCors.java +++ b/examples/GetBucketCors.java @@ -18,34 +18,26 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.CORSConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetBucketCors { /** MinioClient.getBucketCors() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - CORSConfiguration config = - minioClient.getBucketCors(GetBucketCorsArgs.builder().bucket("my-bucketname").build()); - System.out.println("Bucket CORS configuration rules: " + config.rules()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + CORSConfiguration config = + minioClient.getBucketCors(GetBucketCorsArgs.builder().bucket("my-bucket").build()); + System.out.println("Bucket CORS configuration rules: " + config.rules()); } } diff --git a/examples/GetBucketEncryption.java b/examples/GetBucketEncryption.java index a00b2c110..ab46b9c66 100644 --- a/examples/GetBucketEncryption.java +++ b/examples/GetBucketEncryption.java @@ -19,42 +19,34 @@ import io.minio.errors.MinioException; import io.minio.messages.SseAlgorithm; import io.minio.messages.SseConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetBucketEncryption { /** MinioClient.getBucketEncryption() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - SseConfiguration config = - minioClient.getBucketEncryption( - GetBucketEncryptionArgs.builder().bucket("my-bucketname").build()); - if (config.rule() != null) { - System.out.println("Rule SSE algorithm: " + config.rule().sseAlgorithm()); - if (config.rule().sseAlgorithm() == SseAlgorithm.AWS_KMS) { - System.out.println("Rule KMS master key ID: " + config.rule().kmsMasterKeyId()); - } - } else { - System.out.println("No rule is set in SSE configuration."); + SseConfiguration config = + minioClient.getBucketEncryption( + GetBucketEncryptionArgs.builder().bucket("my-bucket").build()); + if (config.rule() != null) { + System.out.println("Rule SSE algorithm: " + config.rule().sseAlgorithm()); + if (config.rule().sseAlgorithm() == SseAlgorithm.AWS_KMS) { + System.out.println("Rule KMS master key ID: " + config.rule().kmsMasterKeyId()); } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + } else { + System.out.println("No rule is set in SSE configuration."); } } } diff --git a/examples/GetBucketLifecycle.java b/examples/GetBucketLifecycle.java index 5e6cd0d49..f5ab5106f 100644 --- a/examples/GetBucketLifecycle.java +++ b/examples/GetBucketLifecycle.java @@ -18,35 +18,27 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.LifecycleConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetBucketLifecycle { /** MinioClient.getBucketLifecycle() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - LifecycleConfiguration config = - minioClient.getBucketLifecycle( - GetBucketLifecycleArgs.builder().bucket("my-bucketName").build()); - System.out.println("Lifecycle configuration is " + config); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + LifecycleConfiguration config = + minioClient.getBucketLifecycle( + GetBucketLifecycleArgs.builder().bucket("my-bucketName").build()); + System.out.println("Lifecycle configuration is " + config); } } diff --git a/examples/GetBucketNotification.java b/examples/GetBucketNotification.java index 3755ab80e..03d29ce8a 100644 --- a/examples/GetBucketNotification.java +++ b/examples/GetBucketNotification.java @@ -18,35 +18,27 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.NotificationConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetBucketNotification { /** MinioClient.getBucketNotification() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - NotificationConfiguration config = - minioClient.getBucketNotification( - GetBucketNotificationArgs.builder().bucket("my-bucketname").build()); - System.out.println(config); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + NotificationConfiguration config = + minioClient.getBucketNotification( + GetBucketNotificationArgs.builder().bucket("my-bucket").build()); + System.out.println(config); } } diff --git a/examples/GetBucketPolicy.java b/examples/GetBucketPolicy.java index d049a0804..6601bbfbb 100644 --- a/examples/GetBucketPolicy.java +++ b/examples/GetBucketPolicy.java @@ -17,35 +17,26 @@ import io.minio.GetBucketPolicyArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetBucketPolicy { /** MinioClient.getBucketPolicy() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - String policy = - minioClient.getBucketPolicy( - GetBucketPolicyArgs.builder().bucket("my-bucketname").build()); - System.out.println("Current policy: " + policy); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + String policy = + minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket("my-bucket").build()); + System.out.println("Current policy: " + policy); } } diff --git a/examples/GetBucketReplication.java b/examples/GetBucketReplication.java index 36e506734..47571052b 100644 --- a/examples/GetBucketReplication.java +++ b/examples/GetBucketReplication.java @@ -18,35 +18,27 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.ReplicationConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetBucketReplication { /** MinioClient.getBucketReplication() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - ReplicationConfiguration config = - minioClient.getBucketReplication( - GetBucketReplicationArgs.builder().bucket("my-bucketname").build()); - System.out.println(" Replication configuration is: " + config); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + ReplicationConfiguration config = + minioClient.getBucketReplication( + GetBucketReplicationArgs.builder().bucket("my-bucket").build()); + System.out.println(" Replication configuration is: " + config); } } diff --git a/examples/GetBucketTags.java b/examples/GetBucketTags.java index 93b77cb76..a2168a970 100644 --- a/examples/GetBucketTags.java +++ b/examples/GetBucketTags.java @@ -18,34 +18,25 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.Tags; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetBucketTags { /** MinioClient.getBucketTags() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - Tags tags = - minioClient.getBucketTags(GetBucketTagsArgs.builder().bucket("my-bucketname").build()); - System.out.println("Bucket tags: " + tags.get()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + Tags tags = minioClient.getBucketTags(GetBucketTagsArgs.builder().bucket("my-bucket").build()); + System.out.println("Bucket tags: " + tags.get()); } } diff --git a/examples/GetBucketVersioning.java b/examples/GetBucketVersioning.java index e34682957..8155d77e8 100644 --- a/examples/GetBucketVersioning.java +++ b/examples/GetBucketVersioning.java @@ -18,35 +18,27 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.VersioningConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetBucketVersioning { /** MinioClient.getBucketVersioning() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - VersioningConfiguration config = - minioClient.getBucketVersioning( - GetBucketVersioningArgs.builder().bucket("my-bucketname").build()); - System.out.println("Versioning on bucket 'my-bucketname' is " + config.status()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + VersioningConfiguration config = + minioClient.getBucketVersioning( + GetBucketVersioningArgs.builder().bucket("my-bucket").build()); + System.out.println("Versioning on bucket 'my-bucket' is " + config.status()); } } diff --git a/examples/GetObject.java b/examples/GetObject.java index d052219b5..11da0f2fa 100644 --- a/examples/GetObject.java +++ b/examples/GetObject.java @@ -20,44 +20,37 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetObject { /** MinioClient.getObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws IOException, MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Get input stream to have content of 'my-objectname' from 'my-bucketname' - InputStream stream = - minioClient.getObject( - GetObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + // Get input stream to have content of 'my-object' from 'my-bucket' + InputStream stream = + minioClient.getObject( + GetObjectArgs.builder().bucket("my-bucket").object("my-object").build()); - // Read the input stream and print to the console till EOF. - byte[] buf = new byte[16384]; - int bytesRead; - while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) { - System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8)); - } - - // Close the input stream. - stream.close(); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + // Read the input stream and print to the console till EOF. + byte[] buf = new byte[16384]; + int bytesRead; + while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) { + System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8)); } + + // Close the input stream. + stream.close(); } } diff --git a/examples/GetObjectAcl.java b/examples/GetObjectAcl.java index 97a481649..22eaa3fd3 100644 --- a/examples/GetObjectAcl.java +++ b/examples/GetObjectAcl.java @@ -18,35 +18,27 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.AccessControlPolicy; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetObjectAcl { /** MinioClient.getObjectAcl() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - AccessControlPolicy policy = - minioClient.getObjectAcl( - GetObjectAclArgs.builder().bucket("my-bucketname").object("my-objectname").build()); - System.out.println("Access control policy: " + policy); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + AccessControlPolicy policy = + minioClient.getObjectAcl( + GetObjectAclArgs.builder().bucket("my-bucket").object("my-object").build()); + System.out.println("Access control policy: " + policy); } } diff --git a/examples/GetObjectAttributes.java b/examples/GetObjectAttributes.java index b2a03e426..eb8a7b8f3 100644 --- a/examples/GetObjectAttributes.java +++ b/examples/GetObjectAttributes.java @@ -18,42 +18,32 @@ import io.minio.GetObjectAttributesResponse; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetObjectAttributes { /** MinioClient.getObjectAttributes() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - GetObjectAttributesResponse response = - minioClient.getObjectAttributes( - GetObjectAttributesArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .objectAttributes( - new String[] { - "ETag", "Checksum", "ObjectParts", "StorageClass", "ObjectSize" - }) - .build()); - System.out.println("Response: " + response); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + GetObjectAttributesResponse response = + minioClient.getObjectAttributes( + GetObjectAttributesArgs.builder() + .bucket("my-bucket") + .object("my-object") + .objectAttributes( + new String[] {"ETag", "Checksum", "ObjectParts", "StorageClass", "ObjectSize"}) + .build()); + System.out.println("Response: " + response); } } diff --git a/examples/GetObjectLockConfiguration.java b/examples/GetObjectLockConfiguration.java index 165b25026..39c83f6dd 100644 --- a/examples/GetObjectLockConfiguration.java +++ b/examples/GetObjectLockConfiguration.java @@ -18,40 +18,30 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.ObjectLockConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetObjectLockConfiguration { /** MinioClient.getObjectLockConfiguration() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - ObjectLockConfiguration config = - minioClient.getObjectLockConfiguration( - GetObjectLockConfigurationArgs.builder() - .bucket("my-lock-enabled-bucketname") - .build()); + ObjectLockConfiguration config = + minioClient.getObjectLockConfiguration( + GetObjectLockConfigurationArgs.builder().bucket("my-lock-enabled-bucketname").build()); - System.out.println("Object-lock configuration of bucket"); - System.out.println("Mode: " + config.mode()); - System.out.println("Duration: " + config.duration()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + System.out.println("Object-lock configuration of bucket"); + System.out.println("Mode: " + config.mode()); + System.out.println("Duration: " + config.duration()); } } diff --git a/examples/GetObjectProgressBar.java b/examples/GetObjectProgressBar.java index 33ef9041b..eeeb8636d 100644 --- a/examples/GetObjectProgressBar.java +++ b/examples/GetObjectProgressBar.java @@ -27,66 +27,55 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetObjectProgressBar { /** MinioClient.getObjectProgressBar() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws IOException, MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Check whether the object exists using statObject(). If the object is not found, - // statObject() throws an exception. It means that the object exists when statObject() - // execution is successful. + // Check whether the object exists using statObject(). If the object is not found, + // statObject() throws an exception. It means that the object exists when statObject() + // execution is successful. - // Get object stat information. - StatObjectResponse stat = - minioClient.statObject( - StatObjectArgs.builder() - .bucket("testbucket") - .object("resumes/4.original.pdf") - .build()); + // Get object stat information. + StatObjectResponse stat = + minioClient.statObject( + StatObjectArgs.builder().bucket("testbucket").object("resumes/4.original.pdf").build()); - // Get input stream to have content of 'my-objectname' from 'my-bucketname' - InputStream is = - new ProgressStream( - "Downloading .. ", - stat.size(), - minioClient.getObject( - GetObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build())); + // Get input stream to have content of 'my-object' from 'my-bucket' + InputStream is = + new ProgressStream( + "Downloading .. ", + stat.size(), + minioClient.getObject( + GetObjectArgs.builder().bucket("my-bucket").object("my-object").build())); - Path path = Paths.get("my-filename"); - OutputStream os = Files.newOutputStream(path, StandardOpenOption.CREATE); + Path path = Paths.get("my-filename"); + OutputStream os = Files.newOutputStream(path, StandardOpenOption.CREATE); - long bytesWritten = ByteStreams.copy(is, os); - is.close(); - os.close(); + long bytesWritten = ByteStreams.copy(is, os); + is.close(); + os.close(); - if (bytesWritten != stat.size()) { - throw new IOException( - path - + ": unexpected data written. expected = " - + stat.size() - + ", written = " - + bytesWritten); - } - - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + if (bytesWritten != stat.size()) { + throw new IOException( + path + + ": unexpected data written. expected = " + + stat.size() + + ", written = " + + bytesWritten); } } } diff --git a/examples/GetObjectResume.java b/examples/GetObjectResume.java index ff9984cc6..3b857f5db 100644 --- a/examples/GetObjectResume.java +++ b/examples/GetObjectResume.java @@ -3,6 +3,8 @@ import io.minio.MinioClient; import io.minio.StatObjectArgs; import io.minio.StatObjectResponse; +import io.minio.errors.MinioException; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; @@ -11,21 +13,21 @@ import java.nio.file.StandardOpenOption; public class GetObjectResume { - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws IOException, MinioException { MinioClient minioClient = MinioClient.builder() .endpoint("https://play.min.io") .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") .build(); - String filename = "my-objectname"; + String filename = "my-object"; Path path = Paths.get(filename); long fileSize = 0; if (Files.exists(path)) fileSize = Files.size(path); StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); if (fileSize == stat.size()) { // Already fully downloaded. @@ -33,14 +35,14 @@ public static void main(String[] args) throws Exception { } if (fileSize > stat.size()) { - throw new Exception("stored file size is greater than object size"); + throw new IOException("stored file size is greater than object size"); } InputStream stream = minioClient.getObject( GetObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") + .bucket("my-bucket") + .object("my-object") .offset(fileSize) .build()); diff --git a/examples/GetObjectRetention.java b/examples/GetObjectRetention.java index 8c2a3a249..c7e624d07 100644 --- a/examples/GetObjectRetention.java +++ b/examples/GetObjectRetention.java @@ -18,41 +18,30 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.Retention; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetObjectRetention { /** MinioClient.getObjectRetention() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException, IllegalArgumentException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Get object lock retention - Retention retention = - minioClient.getObjectRetention( - GetObjectRetentionArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .build()); + // Get object lock retention + Retention retention = + minioClient.getObjectRetention( + GetObjectRetentionArgs.builder().bucket("my-bucket").object("my-object").build()); - System.out.println("Mode: " + retention.mode()); - System.out.println("Retainuntil Date: " + retention.retainUntilDate()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + System.out.println("Mode: " + retention.mode()); + System.out.println("Retain until date: " + retention.retainUntilDate()); } } diff --git a/examples/GetObjectTags.java b/examples/GetObjectTags.java index c951ea824..213c15317 100644 --- a/examples/GetObjectTags.java +++ b/examples/GetObjectTags.java @@ -18,35 +18,27 @@ import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.messages.Tags; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetObjectTags { /** MinioClient.getObjectTags() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - Tags tags = - minioClient.getObjectTags( - GetObjectTagsArgs.builder().bucket("my-bucketname").object("my-objectname").build()); - System.out.println("Object tags: " + tags.get()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + Tags tags = + minioClient.getObjectTags( + GetObjectTagsArgs.builder().bucket("my-bucket").object("my-object").build()); + System.out.println("Object tags: " + tags.get()); } } diff --git a/examples/GetPartialObject.java b/examples/GetPartialObject.java index f37cfdb40..65555f973 100644 --- a/examples/GetPartialObject.java +++ b/examples/GetPartialObject.java @@ -20,50 +20,43 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class GetPartialObject { /** MinioClient.getObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws IOException, MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Get input stream to have content of 'my-objectname' from 'my-bucketname' starts from - // byte position 1024 and length 4096. - InputStream stream = - minioClient.getObject( - GetObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .offset(1024L) - .length(4096L) - .build()); + // Get input stream to have content of 'my-object' from 'my-bucket' starts from + // byte position 1024 and length 4096. + InputStream stream = + minioClient.getObject( + GetObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .offset(1024L) + .length(4096L) + .build()); - // Read the input stream and print to the console till EOF. - byte[] buf = new byte[16384]; - int bytesRead; - while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) { - System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8)); - } - - // Close the input stream. - stream.close(); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + // Read the input stream and print to the console till EOF. + byte[] buf = new byte[16384]; + int bytesRead; + while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) { + System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8)); } + + // Close the input stream. + stream.close(); } } diff --git a/examples/GetPresignedObjectUrl.java b/examples/GetPresignedObjectUrl.java index e154b88cb..74d9104f4 100644 --- a/examples/GetPresignedObjectUrl.java +++ b/examples/GetPresignedObjectUrl.java @@ -15,45 +15,37 @@ */ 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; public class GetPresignedObjectUrl { /** MinioClient.getPresignedObjectUrl() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Get presigned URL string to delete 'my-objectname' in 'my-bucketname' and its life time - // is one day. - String url = - minioClient.getPresignedObjectUrl( - GetPresignedObjectUrlArgs.builder() - .method(Method.DELETE) - .bucket("my-bucketname") - .object("my-objectname") - .expiry(60 * 60 * 24) - .build()); - System.out.println(url); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Get presigned URL string to delete 'my-object' in 'my-bucket' and its life time + // is one day. + String url = + minioClient.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Http.Method.DELETE) + .bucket("my-bucket") + .object("my-object") + .expiry(60 * 60 * 24) + .build()); + System.out.println(url); } } diff --git a/examples/GetPresignedPostFormData.java b/examples/GetPresignedPostFormData.java index 305873577..2665c1230 100644 --- a/examples/GetPresignedPostFormData.java +++ b/examples/GetPresignedPostFormData.java @@ -19,8 +19,6 @@ import io.minio.errors.MinioException; import java.io.File; import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; import java.util.Map; import okhttp3.MultipartBody; @@ -31,72 +29,67 @@ public class GetPresignedPostFormData { /** MinioClient.presignedPostPolicy() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws IOException, MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Create new post policy for 'my-bucketname' with 7 days expiry from now. - PostPolicy policy = new PostPolicy("my-bucketname", ZonedDateTime.now().plusDays(7)); + // Create new post policy for 'my-bucket' with 7 days expiry from now. + PostPolicy policy = new PostPolicy("my-bucket", ZonedDateTime.now().plusDays(7)); - // Add condition that 'key' (object name) equals to 'my-objectname'. - policy.addEqualsCondition("key", "my-objectname"); + // Add condition that 'key' (object name) equals to 'my-object'. + policy.addEqualsCondition("key", "my-object"); - // Add condition that 'Content-Type' starts with 'image/'. - policy.addStartsWithCondition("Content-Type", "image/"); + // Add condition that 'Content-Type' starts with 'image/'. + policy.addStartsWithCondition("Content-Type", "image/"); - // Add condition that 'content-length-range' is between 64kiB to 10MiB. - policy.addContentLengthRangeCondition(64 * 1024, 10 * 1024 * 1024); + // Add condition that 'content-length-range' is between 64kiB to 10MiB. + policy.addContentLengthRangeCondition(64 * 1024, 10 * 1024 * 1024); - Map formData = minioClient.getPresignedPostFormData(policy); + Map formData = minioClient.getPresignedPostFormData(policy); - // Upload an image using POST object with form-data. - MultipartBody.Builder multipartBuilder = new MultipartBody.Builder(); - multipartBuilder.setType(MultipartBody.FORM); - for (Map.Entry entry : formData.entrySet()) { - multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue()); - } - multipartBuilder.addFormDataPart("key", "my-objectname"); - multipartBuilder.addFormDataPart("Content-Type", "image/png"); + // Upload an image using POST object with form-data. + MultipartBody.Builder multipartBuilder = new MultipartBody.Builder(); + multipartBuilder.setType(MultipartBody.FORM); + for (Map.Entry entry : formData.entrySet()) { + multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue()); + } + multipartBuilder.addFormDataPart("key", "my-object"); + multipartBuilder.addFormDataPart("Content-Type", "image/png"); - // "file" must be added at last. - multipartBuilder.addFormDataPart( - "file", "my-objectname", RequestBody.create(new File("Pictures/avatar.png"), null)); + // "file" must be added at last. + multipartBuilder.addFormDataPart( + "file", "my-object", RequestBody.create(new File("Pictures/avatar.png"), null)); - Request request = - new Request.Builder() - .url("https://play.min.io/my-bucketname") - .post(multipartBuilder.build()) - .build(); - OkHttpClient httpClient = new OkHttpClient().newBuilder().build(); - Response response = httpClient.newCall(request).execute(); - if (response.isSuccessful()) { - System.out.println("Pictures/avatar.png is uploaded successfully using POST object"); - } else { - System.out.println("Failed to upload Pictures/avatar.png"); - } + Request request = + new Request.Builder() + .url("https://play.min.io/my-bucket") + .post(multipartBuilder.build()) + .build(); + OkHttpClient httpClient = new OkHttpClient().newBuilder().build(); + Response response = httpClient.newCall(request).execute(); + if (response.isSuccessful()) { + System.out.println("Pictures/avatar.png is uploaded successfully using POST object"); + } else { + System.out.println("Failed to upload Pictures/avatar.png"); + } - // Print curl command usage to upload file /tmp/userpic.jpg. - System.out.print("curl -X POST "); - for (Map.Entry entry : formData.entrySet()) { - System.out.print(" -F " + entry.getKey() + "=" + entry.getValue()); - } - System.out.print(" -F key=my-objectname -F Content-Type=image/jpg"); - System.out.println(" -F file=@/tmp/userpic.jpg https://play.min.io/my-bucketname"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + // Print curl command usage to upload file /tmp/userpic.jpg. + System.out.print("curl -X POST "); + for (Map.Entry entry : formData.entrySet()) { + System.out.print(" -F " + entry.getKey() + "=" + entry.getValue()); } + System.out.print(" -F key=my-object -F Content-Type=image/jpg"); + System.out.println(" -F file=@/tmp/userpic.jpg https://play.min.io/my-bucket"); } } diff --git a/examples/IsObjectLegalHoldEnabled.java b/examples/IsObjectLegalHoldEnabled.java index fe760df2d..1a7ef1b16 100644 --- a/examples/IsObjectLegalHoldEnabled.java +++ b/examples/IsObjectLegalHoldEnabled.java @@ -18,74 +18,56 @@ import io.minio.IsObjectLegalHoldEnabledArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class IsObjectLegalHoldEnabled { /** MinioClient.isObjectLegalHoldEnabled() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException, IllegalArgumentException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Enable object legal hold. - minioClient.enableObjectLegalHold( - EnableObjectLegalHoldArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .versionId("object-versionId") - .build()); - System.out.println("Legal hold enabled on object successfully "); + // Enable object legal hold. + minioClient.enableObjectLegalHold( + EnableObjectLegalHoldArgs.builder() + .bucket("my-bucket") + .object("my-object") + .versionId("object-versionId") + .build()); + System.out.println("Legal hold enabled on object successfully "); - boolean status = - minioClient.isObjectLegalHoldEnabled( - IsObjectLegalHoldEnabledArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .build()); + boolean status = + minioClient.isObjectLegalHoldEnabled( + IsObjectLegalHoldEnabledArgs.builder().bucket("my-bucket").object("my-object").build()); - if (status) { - System.out.println("Legal hold is on"); - } else { - System.out.println("Legal hold is off"); - } - - // Disable object legal hold. - minioClient.disableObjectLegalHold( - DisableObjectLegalHoldArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .build()); + if (status) { + System.out.println("Legal hold is on"); + } else { + System.out.println("Legal hold is off"); + } - status = - minioClient.isObjectLegalHoldEnabled( - IsObjectLegalHoldEnabledArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .build()); - System.out.println("Legal hold disabled on object successfully "); + // Disable object legal hold. + minioClient.disableObjectLegalHold( + DisableObjectLegalHoldArgs.builder().bucket("my-bucket").object("my-object").build()); - if (status) { - System.out.println("Legal hold is on"); - } else { - System.out.println("Legal hold is off"); - } + status = + minioClient.isObjectLegalHoldEnabled( + IsObjectLegalHoldEnabledArgs.builder().bucket("my-bucket").object("my-object").build()); + System.out.println("Legal hold disabled on object successfully "); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + if (status) { + System.out.println("Legal hold is on"); + } else { + System.out.println("Legal hold is off"); } } } diff --git a/examples/ListBuckets.java b/examples/ListBuckets.java index f0e15263d..62e3a5b6c 100644 --- a/examples/ListBuckets.java +++ b/examples/ListBuckets.java @@ -18,41 +18,34 @@ import io.minio.MinioClient; import io.minio.Result; import io.minio.errors.MinioException; -import io.minio.messages.Bucket; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import io.minio.messages.ListAllMyBucketsResult; public class ListBuckets { /** MinioClient.listBuckets() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // List buckets we have atleast read access. - 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())); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + // List buckets we have at least read access. + 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", + bucket.name(), bucket.bucketRegion(), bucket.creationDate())); } } } diff --git a/examples/ListObjects.java b/examples/ListObjects.java index 3d4b03fb8..ae3abfa64 100644 --- a/examples/ListObjects.java +++ b/examples/ListObjects.java @@ -19,98 +19,90 @@ import io.minio.Result; import io.minio.errors.MinioException; import io.minio.messages.Item; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class ListObjects { /** MinioClient.listObjects() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - { - // Lists objects information. - Iterable> results = - minioClient.listObjects(ListObjectsArgs.builder().bucket("my-bucketname").build()); + { + // Lists objects information. + Iterable> results = + minioClient.listObjects(ListObjectsArgs.builder().bucket("my-bucket").build()); - for (Result result : results) { - Item item = result.get(); - System.out.println(item.lastModified() + "\t" + item.size() + "\t" + item.objectName()); - } + for (Result result : results) { + Item item = result.get(); + System.out.println(item.lastModified() + "\t" + item.size() + "\t" + item.objectName()); } + } - { - // Lists objects information recursively. - Iterable> results = - minioClient.listObjects( - ListObjectsArgs.builder().bucket("my-bucketname").recursive(true).build()); + { + // Lists objects information recursively. + Iterable> results = + minioClient.listObjects( + ListObjectsArgs.builder().bucket("my-bucket").recursive(true).build()); - for (Result result : results) { - Item item = result.get(); - System.out.println(item.lastModified() + "\t" + item.size() + "\t" + item.objectName()); - } + for (Result result : results) { + Item item = result.get(); + System.out.println(item.lastModified() + "\t" + item.size() + "\t" + item.objectName()); } + } - { - // Lists maximum 100 objects information those names starts with 'E' and after - // 'ExampleGuide.pdf'. - Iterable> results = - minioClient.listObjects( - ListObjectsArgs.builder() - .bucket("my-bucketname") - .startAfter("ExampleGuide.pdf") - .prefix("E") - .maxKeys(100) - .build()); + { + // Lists maximum 100 objects information those names starts with 'E' and after + // 'ExampleGuide.pdf'. + Iterable> results = + minioClient.listObjects( + ListObjectsArgs.builder() + .bucket("my-bucket") + .startAfter("ExampleGuide.pdf") + .prefix("E") + .maxKeys(100) + .build()); - for (Result result : results) { - Item item = result.get(); - System.out.println(item.lastModified() + "\t" + item.size() + "\t" + item.objectName()); - } + for (Result result : results) { + Item item = result.get(); + System.out.println(item.lastModified() + "\t" + item.size() + "\t" + item.objectName()); } + } - { - // Lists maximum 100 objects information with version those names starts with 'E' and after - // 'ExampleGuide.pdf'. - Iterable> results = - minioClient.listObjects( - ListObjectsArgs.builder() - .bucket("my-bucketname") - .startAfter("ExampleGuide.pdf") - .prefix("E") - .maxKeys(100) - .includeVersions(true) - .build()); + { + // Lists maximum 100 objects information with version those names starts with 'E' and after + // 'ExampleGuide.pdf'. + Iterable> results = + minioClient.listObjects( + ListObjectsArgs.builder() + .bucket("my-bucket") + .startAfter("ExampleGuide.pdf") + .prefix("E") + .maxKeys(100) + .includeVersions(true) + .build()); - for (Result result : results) { - Item item = result.get(); - System.out.println( - item.lastModified() - + "\t" - + item.size() - + "\t" - + item.objectName() - + " [" - + item.versionId() - + "]"); - } + for (Result result : results) { + Item item = result.get(); + System.out.println( + item.lastModified() + + "\t" + + item.size() + + "\t" + + item.objectName() + + " [" + + item.versionId() + + "]"); } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); } } } diff --git a/examples/ListenBucketNotification.java b/examples/ListenBucketNotification.java index d0b5bc80b..d7e138bfe 100644 --- a/examples/ListenBucketNotification.java +++ b/examples/ListenBucketNotification.java @@ -19,49 +19,42 @@ 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; -import java.security.NoSuchAlgorithmException; public class ListenBucketNotification { /** MinioClient.listenBucketNotification() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - String[] events = {"s3:ObjectCreated:*", "s3:ObjectAccessed:*"}; - try (CloseableIterator> ci = - minioClient.listenBucketNotification( - ListenBucketNotificationArgs.builder() - .bucket("bucketName") - .prefix("") - .suffix("") - .events(events) - .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"); - } - } catch (IOException e) { - System.out.println("Error occurred: " + e); + String[] events = {"s3:ObjectCreated:*", "s3:ObjectAccessed:*"}; + try (CloseableIterator> ci = + minioClient.listenBucketNotification( + ListenBucketNotificationArgs.builder() + .bucket("bucketName") + .prefix("") + .suffix("") + .events(events) + .build())) { + while (ci.hasNext()) { + NotificationRecords records = ci.next().get(); + NotificationRecords.Event event = records.events().get(0); + System.out.println( + event.bucket().name() + "/" + event.object().key() + " has been created"); } - } catch (MinioException e) { + } catch (IOException e) { System.out.println("Error occurred: " + e); } } diff --git a/examples/MakeBucket.java b/examples/MakeBucket.java index 967dc730f..9ae49cf56 100644 --- a/examples/MakeBucket.java +++ b/examples/MakeBucket.java @@ -18,57 +18,48 @@ import io.minio.MakeBucketArgs; import io.minio.MinioClient; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class MakeBucket { /** MinioClient.makeBucket() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Create bucket 'my-bucketname' if it doesn`t exist. - if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucketname").build())) { - minioClient.makeBucket(MakeBucketArgs.builder().bucket("my-bucketname").build()); - System.out.println("my-bucketname is created successfully"); - } + // Create bucket 'my-bucket' if it does not exist. + if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucket").build())) { + minioClient.makeBucket(MakeBucketArgs.builder().bucket("my-bucket").build()); + System.out.println("my-bucket is created successfully"); + } - // Create bucket 'my-bucketname-in-eu' in 'eu-west-1' region if it doesn't exist. - if (!minioClient.bucketExists( - BucketExistsArgs.builder().bucket("my-bucketname-in-eu").build())) { - minioClient.makeBucket( - MakeBucketArgs.builder().bucket("my-bucketname-in-eu").region("eu-west-1").build()); - System.out.println("my-bucketname-in-eu is created successfully"); - } + // Create bucket 'my-bucket-in-eu' in 'eu-west-1' region if it does not exist. + if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucket-in-eu").build())) { + minioClient.makeBucket( + MakeBucketArgs.builder().bucket("my-bucket-in-eu").region("eu-west-1").build()); + System.out.println("my-bucket-in-eu is created successfully"); + } - // Create bucket 'my-bucketname-in-eu-with-object-lock' in 'eu-west-1' with object lock - // functionality enabled. - if (!minioClient.bucketExists( - BucketExistsArgs.builder().bucket("my-bucketname-in-eu-with-object-lock").build())) { - minioClient.makeBucket( - MakeBucketArgs.builder() - .bucket("my-bucketname-in-eu-with-object-lock") - .region("eu-west-1") - .objectLock(true) - .build()); - System.out.println("my-bucketname-in-eu-with-object-lock is created successfully"); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + // Create bucket 'my-bucket-in-eu-with-object-lock' in 'eu-west-1' with object lock + // functionality enabled. + if (!minioClient.bucketExists( + BucketExistsArgs.builder().bucket("my-bucket-in-eu-with-object-lock").build())) { + minioClient.makeBucket( + MakeBucketArgs.builder() + .bucket("my-bucket-in-eu-with-object-lock") + .region("eu-west-1") + .objectLock(true) + .build()); + System.out.println("my-bucket-in-eu-with-object-lock is created successfully"); } } } diff --git a/examples/MinioClientWithAssumeRoleProvider.java b/examples/MinioClientWithAssumeRoleProvider.java index fa2b30edc..45a1c4aa6 100644 --- a/examples/MinioClientWithAssumeRoleProvider.java +++ b/examples/MinioClientWithAssumeRoleProvider.java @@ -19,9 +19,10 @@ import io.minio.StatObjectResponse; import io.minio.credentials.AssumeRoleProvider; import io.minio.credentials.Provider; +import io.minio.errors.MinioException; public class MinioClientWithAssumeRoleProvider { - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws MinioException { Provider provider = new AssumeRoleProvider( "https://play.minio.io:9000/", // STS endpoint usually point to MinIO server. @@ -44,7 +45,7 @@ public static void main(String[] args) throws Exception { // Get information of an object. StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); System.out.println(stat); } } diff --git a/examples/MinioClientWithAwsConfigProvider.java b/examples/MinioClientWithAwsConfigProvider.java index 1040a8f5b..87fa1b1f3 100644 --- a/examples/MinioClientWithAwsConfigProvider.java +++ b/examples/MinioClientWithAwsConfigProvider.java @@ -19,9 +19,10 @@ import io.minio.StatObjectResponse; import io.minio.credentials.AwsConfigProvider; import io.minio.credentials.Provider; +import io.minio.errors.MinioException; public class MinioClientWithAwsConfigProvider { - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws MinioException { Provider provider = new AwsConfigProvider(null, null); MinioClient minioClient = @@ -33,7 +34,7 @@ public static void main(String[] args) throws Exception { // Get information of an object. StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); System.out.println(stat); } } diff --git a/examples/MinioClientWithCertificateIdentityProvider.java b/examples/MinioClientWithCertificateIdentityProvider.java index 82429317a..1954c5882 100644 --- a/examples/MinioClientWithCertificateIdentityProvider.java +++ b/examples/MinioClientWithCertificateIdentityProvider.java @@ -19,11 +19,12 @@ import io.minio.StatObjectResponse; import io.minio.credentials.CertificateIdentityProvider; import io.minio.credentials.Provider; +import io.minio.errors.MinioException; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; public class MinioClientWithCertificateIdentityProvider { - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws MinioException { // STS endpoint usually point to MinIO server. String stsEndpoint = "https://STS-HOST:STS-PORT/"; @@ -39,7 +40,8 @@ public static void main(String[] args) throws Exception { // CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); // // Certificate serverCertificate = null; - // try (FileInputStream fis = new FileInputStream("/home/bala/.minio/certs/public.crt")) { + // try (FileInputStream fis = + // Files.newInputStream(Paths.get("/home/bala/.minio/certs/public.crt"))) { // serverCertificate = certificateFactory.generateCertificate(fis); // } // @@ -61,7 +63,8 @@ public static void main(String[] args) throws Exception { // PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey); // // Certificate certificateChain = null; - // try (FileInputStream fis = new FileInputStream("/home/bala/.minio/certs/CAs/client1.crt")) { + // try (FileInputStream fis = + // Files.newInputStream(Paths.get("/home/bala/.minio/certs/CAs/client1.crt"))) { // certificateChain = certificateFactory.generateCertificate(fis); // } // @@ -102,7 +105,7 @@ public static void main(String[] args) throws Exception { // Get information of an object. StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); System.out.println(stat); } } diff --git a/examples/MinioClientWithChainedProvider.java b/examples/MinioClientWithChainedProvider.java index 84af8c095..64ae532b4 100644 --- a/examples/MinioClientWithChainedProvider.java +++ b/examples/MinioClientWithChainedProvider.java @@ -21,9 +21,10 @@ import io.minio.credentials.ChainedProvider; import io.minio.credentials.MinioEnvironmentProvider; import io.minio.credentials.Provider; +import io.minio.errors.MinioException; public class MinioClientWithChainedProvider { - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws MinioException { Provider provider = new ChainedProvider(new AwsEnvironmentProvider(), new MinioEnvironmentProvider()); @@ -36,7 +37,7 @@ public static void main(String[] args) throws Exception { // Get information of an object. StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); System.out.println(stat); } } diff --git a/examples/MinioClientWithClientGrantsProvider.java b/examples/MinioClientWithClientGrantsProvider.java index 43ec46152..8636dc3be 100644 --- a/examples/MinioClientWithClientGrantsProvider.java +++ b/examples/MinioClientWithClientGrantsProvider.java @@ -24,6 +24,7 @@ import io.minio.credentials.ClientGrantsProvider; import io.minio.credentials.Jwt; import io.minio.credentials.Provider; +import io.minio.errors.MinioException; import java.io.IOException; import java.security.ProviderException; import java.util.Objects; @@ -62,7 +63,7 @@ static Jwt getJwt( } } - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws MinioException { // IDP endpoint. String idpEndpoint = "https://IDP-HOST:IDP-PORT/auth/realms/master/protocol/openid-connect/token"; @@ -112,7 +113,7 @@ public static void main(String[] args) throws Exception { // Get information of an object. StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); System.out.println(stat); } } diff --git a/examples/MinioClientWithIamAwsProvider.java b/examples/MinioClientWithIamAwsProvider.java index ade161a4a..508281bd9 100644 --- a/examples/MinioClientWithIamAwsProvider.java +++ b/examples/MinioClientWithIamAwsProvider.java @@ -19,9 +19,10 @@ import io.minio.StatObjectResponse; import io.minio.credentials.IamAwsProvider; import io.minio.credentials.Provider; +import io.minio.errors.MinioException; public class MinioClientWithIamAwsProvider { - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws MinioException { Provider provider = new IamAwsProvider(null, null); MinioClient minioClient = @@ -33,7 +34,7 @@ public static void main(String[] args) throws Exception { // Get information of an object. StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); System.out.println(stat); } } diff --git a/examples/MinioClientWithLdapIdentityProvider.java b/examples/MinioClientWithLdapIdentityProvider.java index b07e9f279..397cb5c03 100644 --- a/examples/MinioClientWithLdapIdentityProvider.java +++ b/examples/MinioClientWithLdapIdentityProvider.java @@ -19,9 +19,10 @@ import io.minio.StatObjectResponse; import io.minio.credentials.LdapIdentityProvider; import io.minio.credentials.Provider; +import io.minio.errors.MinioException; public class MinioClientWithLdapIdentityProvider { - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws MinioException { // STS endpoint usually point to MinIO server. String stsEndpoint = "http://STS-HOST:STS-PORT/"; @@ -42,7 +43,7 @@ public static void main(String[] args) throws Exception { // Get information of an object. StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); System.out.println(stat); } } diff --git a/examples/MinioClientWithMinioClientConfigProvider.java b/examples/MinioClientWithMinioClientConfigProvider.java index 5bc58730a..ea0214ec0 100644 --- a/examples/MinioClientWithMinioClientConfigProvider.java +++ b/examples/MinioClientWithMinioClientConfigProvider.java @@ -19,9 +19,10 @@ import io.minio.StatObjectResponse; import io.minio.credentials.MinioClientConfigProvider; import io.minio.credentials.Provider; +import io.minio.errors.MinioException; public class MinioClientWithMinioClientConfigProvider { - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws MinioException { Provider provider = new MinioClientConfigProvider(null, null); MinioClient minioClient = @@ -33,7 +34,7 @@ public static void main(String[] args) throws Exception { // Get information of an object. StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); System.out.println(stat); } } diff --git a/examples/MinioClientWithWebIdentityProvider.java b/examples/MinioClientWithWebIdentityProvider.java index 80550ee68..10ed3983a 100644 --- a/examples/MinioClientWithWebIdentityProvider.java +++ b/examples/MinioClientWithWebIdentityProvider.java @@ -24,6 +24,7 @@ import io.minio.credentials.Jwt; import io.minio.credentials.Provider; import io.minio.credentials.WebIdentityProvider; +import io.minio.errors.MinioException; import java.io.IOException; import java.security.ProviderException; import java.util.Objects; @@ -66,7 +67,7 @@ static Jwt getJwt( } } - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws MinioException { // IDP endpoint. String idpEndpoint = "https://IDP-HOST:IDP-PORT/auth/realms/master/protocol/openid-connect/token"; @@ -108,7 +109,7 @@ public static void main(String[] args) throws Exception { // Get information of an object. StatObjectResponse stat = minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); System.out.println(stat); } } diff --git a/examples/PresignedGetObject.java b/examples/PresignedGetObject.java index cf9c889d2..af31143ab 100644 --- a/examples/PresignedGetObject.java +++ b/examples/PresignedGetObject.java @@ -15,45 +15,37 @@ */ 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; public class PresignedGetObject { /** MinioClient.presignedGetObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Get presigned URL string to download 'my-objectname' in 'my-bucketname' and its life time - // is one day. - String url = - minioClient.getPresignedObjectUrl( - GetPresignedObjectUrlArgs.builder() - .method(Method.GET) - .bucket("my-bucketname") - .object("my-objectname") - .expiry(60 * 60 * 24) - .build()); - System.out.println(url); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Get presigned URL string to download 'my-object' in 'my-bucket' and its life time + // is one day. + String url = + minioClient.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Http.Method.GET) + .bucket("my-bucket") + .object("my-object") + .expiry(60 * 60 * 24) + .build()); + System.out.println(url); } } diff --git a/examples/PresignedPutObject.java b/examples/PresignedPutObject.java index e231de21e..716f0f0b6 100644 --- a/examples/PresignedPutObject.java +++ b/examples/PresignedPutObject.java @@ -15,52 +15,44 @@ */ 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; import java.util.HashMap; import java.util.Map; public class PresignedPutObject { /** MinioClient.presignedPutObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Get presigned URL string to upload 'my-objectname' in 'my-bucketname' - // with response-content-type as application/json and its life time is - // one day. - Map reqParams = new HashMap(); - reqParams.put("response-content-type", "application/json"); + // Get presigned URL string to upload 'my-object' in 'my-bucket' + // with response-content-type as application/json and its life time is + // one day. + Map reqParams = new HashMap(); + reqParams.put("response-content-type", "application/json"); - String url = - minioClient.getPresignedObjectUrl( - GetPresignedObjectUrlArgs.builder() - .method(Method.PUT) - .bucket("my-bucketname") - .object("my-objectname") - .expiry(60 * 60 * 24) - .extraQueryParams(reqParams) - .build()); - System.out.println(url); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + String url = + minioClient.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Http.Method.PUT) + .bucket("my-bucket") + .object("my-object") + .expiry(60 * 60 * 24) + .extraQueryParams(reqParams) + .build()); + System.out.println(url); } } diff --git a/examples/PutObject.java b/examples/PutObject.java index 06fdd75ef..21324bc81 100644 --- a/examples/PutObject.java +++ b/examples/PutObject.java @@ -17,12 +17,9 @@ 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.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.HashMap; @@ -32,157 +29,159 @@ public class PutObject { /** MinioClient.putObject() example. */ public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); - - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); - - // Create some content for the object. - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - builder.append( - "Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); - builder.append("(29 letters)\n"); - builder.append( - "Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); - builder.append("(31 letters)\n"); - builder.append( - "Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); - builder.append("NASAs Space Shuttle. (32 letters)\n"); - builder.append( - "The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. "); - builder.append("(39 letters)\n"); - builder.append( - "How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity "); - builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); - builder.append( - "Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac "); - builder.append("computers after System 7.\n"); - builder.append( - "A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my "); - builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); - builder.append("---\n"); - } - - { - // Create a InputStream for object upload. - ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8")); - - // Create object 'my-objectname' in 'my-bucketname' with content from the input stream. - minioClient.putObject( - PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream( - bais, bais.available(), -1) - .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()); - - // 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) - .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); - - // 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) - .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(); - - // 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) - .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 - headers.put("Content-Type", "application/octet-stream"); - // Add storage class - headers.put("X-Amz-Storage-Class", "REDUCED_REDUNDANCY"); - - // Add custom/user metadata - Map userMetadata = new HashMap<>(); - userMetadata.put("My-Project", "Project One"); - - // Create object 'my-objectname' with user metadata and other properties in 'my-bucketname' - // with content - // from the input stream. - minioClient.putObject( - PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream( - bais, bais.available(), -1) - .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) - .build()); - System.out.println("path/to/ is created successfully"); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + throws InvalidKeyException, MinioException, NoSuchAlgorithmException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); + + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); + + // Create some content for the object. + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + builder.append( + "Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. "); + builder.append("(29 letters)\n"); + builder.append( + "Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. "); + builder.append("(31 letters)\n"); + builder.append( + "Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on "); + builder.append("NASAs Space Shuttle. (32 letters)\n"); + builder.append( + "The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic" + + " Card. "); + builder.append("(39 letters)\n"); + builder.append( + "How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity" + + " "); + builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n"); + builder.append( + "Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for" + + " Mac "); + builder.append("computers after System 7.\n"); + builder.append( + "A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack!" + + " my "); + builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n"); + builder.append("---\n"); + } + byte[] data = builder.toString().getBytes(StandardCharsets.UTF_8); + + { + // Create object 'my-object' in 'my-bucket' with content from byte array. + minioClient.putObject( + PutObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .data(data, data.length) + .build()); + System.out.println("my-object is uploaded successfully"); + } + + { + // Create object 'my-object' in 'my-bucket' with content from input stream in parallel. + minioClient.putObject( + PutObjectArgs.builder().bucket("my-bucket").object("my-object").stream( + new ByteArrayInputStream(data), (long) data.length, null) + .parallelUploads(4) + .build()); + System.out.println("my-object is uploaded successfully"); + } + + { + // Generate a new 256 bit AES key - This key must be remembered by the client. + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); + + // Create encrypted object 'my-object' using SSE-C in 'my-bucket' with content from + // byte array. + minioClient.putObject( + PutObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .data(data, data.length) + .sse(ssec) + .build()); + System.out.println("my-object is uploaded successfully"); + } + + { + Map myContext = new HashMap<>(); + myContext.put("key1", "value1"); + ServerSideEncryption sseKms = new ServerSideEncryption.KMS("Key-Id", myContext); + + // Create encrypted object 'my-object' using SSE-KMS in 'my-bucket' with content + // from byte array. + minioClient.putObject( + PutObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .data(data, data.length) + .sse(sseKms) + .build()); + System.out.println("my-object is uploaded successfully"); + } + + { + ServerSideEncryption sseS3 = new ServerSideEncryption.S3(); + + // Create encrypted object 'my-object' using SSE-S3 in 'my-bucket' with content + // from byte array. + minioClient.putObject( + PutObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .data(data, data.length) + .sse(sseS3) + .build()); + System.out.println("my-object is uploaded successfully"); + } + + { + // Create headers + Map headers = new HashMap<>(); + // Add custom content type + headers.put("Content-Type", "application/octet-stream"); + // Add storage class + headers.put("X-Amz-Storage-Class", "REDUCED_REDUNDANCY"); + + // Add custom/user metadata + Map userMetadata = new HashMap<>(); + userMetadata.put("My-Project", "Project One"); + + // Create object 'my-object' with user metadata and other properties in 'my-bucket' + // with content from byte array. + minioClient.putObject( + PutObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .data(data, data.length) + .headers(headers) + .userMetadata(userMetadata) + .build()); + System.out.println("my-object is uploaded successfully"); + } + + { + // Create object name ending with '/' (mostly called folder or directory). + minioClient.putObject( + PutObjectArgs.builder() + .bucket("my-bucket") + .object("path/to/") + .data(new byte[0], 0) + .build()); + System.out.println("path/to/ is created successfully"); } } } diff --git a/examples/PutObjectFanOut.java b/examples/PutObjectFanOut.java index ca9c2dfea..e807ee664 100644 --- a/examples/PutObjectFanOut.java +++ b/examples/PutObjectFanOut.java @@ -20,50 +20,42 @@ import io.minio.PutObjectFanOutResponse; import io.minio.errors.MinioException; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; public class PutObjectFanOut { /** MinioClient.putObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - Map map = new HashMap<>(); - map.put("Project", "Project One"); - map.put("User", "jsmith"); - PutObjectFanOutResponse response = - minioClient.putObjectFanOut( - PutObjectFanOutArgs.builder().bucket("my-bucketname").stream( - new ByteArrayInputStream("somedata".getBytes(StandardCharsets.UTF_8)), 8) - .entries( - Arrays.asList( - new PutObjectFanOutEntry[] { - PutObjectFanOutEntry.builder().key("fan-out.0").build(), - PutObjectFanOutEntry.builder().key("fan-out.1").tags(map).build() - })) - .build()); - System.out.println("response: " + response); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + Map map = new HashMap<>(); + map.put("Project", "Project One"); + map.put("User", "jsmith"); + PutObjectFanOutResponse response = + minioClient.putObjectFanOut( + PutObjectFanOutArgs.builder().bucket("my-bucket").stream( + new ByteArrayInputStream("somedata".getBytes(StandardCharsets.UTF_8)), 8) + .entries( + Arrays.asList( + new PutObjectFanOutEntry[] { + PutObjectFanOutEntry.builder().key("fan-out.0").build(), + PutObjectFanOutEntry.builder().key("fan-out.1").tags(map).build() + })) + .build()); + System.out.println("response: " + response); } } diff --git a/examples/PutObjectProgressBar.java b/examples/PutObjectProgressBar.java index 557a20c15..4101a7ff7 100644 --- a/examples/PutObjectProgressBar.java +++ b/examples/PutObjectProgressBar.java @@ -18,17 +18,14 @@ import io.minio.PutObjectArgs; import io.minio.errors.MinioException; import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import java.nio.file.Files; +import java.nio.file.Paths; public class PutObjectProgressBar { /** MinioClient.putObjectProgressBar() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException, MinioException { + public static void main(String[] args) throws IOException, MinioException { /* play.min.io for test and development. */ MinioClient minioClient = MinioClient.builder() @@ -43,17 +40,17 @@ public static void main(String[] args) // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") // .build(); - String objectName = "my-objectname"; - String bucketName = "my-bucketname"; + String objectName = "my-object"; + String bucketName = "my-bucket"; - File file = new File("my-filename"); InputStream pis = - new BufferedInputStream(new ProgressStream("Uploading... ", new FileInputStream(file))); + new BufferedInputStream( + new ProgressStream("Uploading... ", Files.newInputStream(Paths.get("my-filename")))); 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"); + System.out.println("my-object is uploaded successfully"); } } diff --git a/examples/PutObjectUiProgressBar.java b/examples/PutObjectUiProgressBar.java index 655eae64d..ecb3bf546 100644 --- a/examples/PutObjectUiProgressBar.java +++ b/examples/PutObjectUiProgressBar.java @@ -21,11 +21,10 @@ import java.awt.event.ActionListener; import java.io.BufferedInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import java.nio.file.Files; +import java.nio.file.Paths; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.ProgressMonitorInputStream; @@ -52,12 +51,11 @@ public void go() { } /** - * uploadFile(fileName) uploads to configured object storage upon reading a local file, while + * uploadFile(filename) uploads to configured object storage upon reading a local file, while * asynchronously updating the progress bar UI as well. This function is involed when user clicks * on the UI */ - private void uploadFile(String fileName) - throws IOException, NoSuchAlgorithmException, InvalidKeyException, MinioException { + private void uploadFile(String filename) throws MinioException { /* play.min.io for test and development. */ MinioClient minioClient = MinioClient.builder() @@ -72,17 +70,18 @@ private void uploadFile(String fileName) // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") // .build(); - File file = new File(fileName); - try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) { + File file = new File(filename); + try (BufferedInputStream bis = + new BufferedInputStream(Files.newInputStream(Paths.get(filename)))) { ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(this, "Uploading... " + file.getAbsolutePath(), bis); pmis.getProgressMonitor().setMillisToPopup(10); minioClient.putObject( - PutObjectArgs.builder().bucket("bank").object("my-objectname").stream( - pmis, pmis.available(), -1) + PutObjectArgs.builder().bucket("bank").object("my-object").stream( + pmis, (long) pmis.available(), null) .build()); - System.out.println("my-objectname is uploaded successfully"); + System.out.println("my-object is uploaded successfully"); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/examples/RemoveBucket.java b/examples/RemoveBucket.java index 3b08c3064..2eadd7d6f 100644 --- a/examples/RemoveBucket.java +++ b/examples/RemoveBucket.java @@ -18,41 +18,33 @@ import io.minio.MinioClient; import io.minio.RemoveBucketArgs; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class RemoveBucket { /** MinioClient.removeBucket() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Remove bucket 'my-bucketname' if it exists. - // This operation will only work if your bucket is empty. - boolean found = - minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucketname").build()); - if (found) { - minioClient.removeBucket(RemoveBucketArgs.builder().bucket("my-bucketname").build()); - System.out.println("my-bucketname is removed successfully"); - } else { - System.out.println("my-bucketname does not exist"); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + // Remove bucket 'my-bucket' if it exists. + // This operation will only work if your bucket is empty. + boolean found = + minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucket").build()); + if (found) { + minioClient.removeBucket(RemoveBucketArgs.builder().bucket("my-bucket").build()); + System.out.println("my-bucket is removed successfully"); + } else { + System.out.println("my-bucket does not exist"); } } } diff --git a/examples/RemoveObject.java b/examples/RemoveObject.java index 3cc804509..c2cd327a5 100644 --- a/examples/RemoveObject.java +++ b/examples/RemoveObject.java @@ -17,51 +17,43 @@ import io.minio.MinioClient; import io.minio.RemoveObjectArgs; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class RemoveObject { /** MinioClient.removeObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Remove object. - minioClient.removeObject( - RemoveObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); + // Remove object. + minioClient.removeObject( + RemoveObjectArgs.builder().bucket("my-bucket").object("my-object").build()); - // Remove versioned object. - minioClient.removeObject( - RemoveObjectArgs.builder() - .bucket("my-bucketname") - .object("my-versioned-objectname") - .versionId("my-versionid") - .build()); + // Remove versioned object. + minioClient.removeObject( + RemoveObjectArgs.builder() + .bucket("my-bucket") + .object("my-versioned-objectname") + .versionId("my-versionid") + .build()); - // Remove versioned object bypassing Governance mode. - minioClient.removeObject( - RemoveObjectArgs.builder() - .bucket("my-bucketname") - .object("my-versioned-objectname") - .versionId("my-versionid") - .bypassGovernanceMode(true) - .build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Remove versioned object bypassing Governance mode. + minioClient.removeObject( + RemoveObjectArgs.builder() + .bucket("my-bucket") + .object("my-versioned-objectname") + .versionId("my-versionid") + .bypassGovernanceMode(true) + .build()); } } diff --git a/examples/RemoveObjects.java b/examples/RemoveObjects.java index 11827c3a7..d8c5f3202 100644 --- a/examples/RemoveObjects.java +++ b/examples/RemoveObjects.java @@ -18,47 +18,38 @@ import io.minio.RemoveObjectsArgs; import io.minio.Result; import io.minio.errors.MinioException; -import io.minio.messages.DeleteError; -import io.minio.messages.DeleteObject; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.LinkedList; +import io.minio.messages.DeleteRequest; +import io.minio.messages.DeleteResult; +import java.util.ArrayList; import java.util.List; public class RemoveObjects { /** MinioClient.removeObject() example removing multiple objects. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .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 = - minioClient.removeObjects( - RemoveObjectsArgs.builder().bucket("my-bucketname").objects(objects).build()); - for (Result result : results) { - DeleteError error = result.get(); - System.out.println( - "Error in deleting object " + error.objectName() + "; " + error.message()); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + List objects = new ArrayList<>(); + objects.add(new DeleteRequest.Object("my-object1")); + objects.add(new DeleteRequest.Object("my-object2")); + objects.add(new DeleteRequest.Object("my-object3")); + Iterable> results = + minioClient.removeObjects( + RemoveObjectsArgs.builder().bucket("my-bucket").objects(objects).build()); + for (Result result : results) { + DeleteResult.Error error = result.get(); + System.out.println("Error in deleting object " + error.objectName() + "; " + error.message()); } } } diff --git a/examples/RestoreObject.java b/examples/RestoreObject.java index 44589e788..5eceff4f9 100644 --- a/examples/RestoreObject.java +++ b/examples/RestoreObject.java @@ -18,47 +18,39 @@ import io.minio.RestoreObjectArgs; import io.minio.errors.MinioException; import io.minio.messages.RestoreRequest; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class RestoreObject { /** MinioClient.restoreObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Restore object. - minioClient.restoreObject( - RestoreObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .request(new RestoreRequest(null, null, null, null, null, null)) - .build()); + // Restore object. + minioClient.restoreObject( + RestoreObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .request(new RestoreRequest(null, null, null, null, null, null)) + .build()); - // Restore versioned object. - minioClient.restoreObject( - RestoreObjectArgs.builder() - .bucket("my-bucketname") - .object("my-versioned-objectname") - .versionId("my-versionid") - .request(new RestoreRequest(null, null, null, null, null, null)) - .build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Restore versioned object. + minioClient.restoreObject( + RestoreObjectArgs.builder() + .bucket("my-bucket") + .object("my-versioned-objectname") + .versionId("my-versionid") + .request(new RestoreRequest(null, null, null, null, null, null)) + .build()); } } diff --git a/examples/SelectObjectContent.java b/examples/SelectObjectContent.java index 247a08a1a..ab4a4ebdb 100644 --- a/examples/SelectObjectContent.java +++ b/examples/SelectObjectContent.java @@ -19,77 +19,70 @@ 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; -import java.security.NoSuchAlgorithmException; public class SelectObjectContent { /** MinioClient.getObject() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws IOException, MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - byte[] data = - ("Year,Make,Model,Description,Price\n" - + "1997,Ford,E350,\"ac, abs, moon\",3000.00\n" - + "1999,Chevy,\"Venture \"\"Extended Edition\"\"\",\"\",4900.00\n" - + "1999,Chevy,\"Venture \"\"Extended Edition, Very Large\"\"\",,5000.00\n" - + "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) - .build()); + byte[] data = + ("Year,Make,Model,Description,Price\n" + + "1997,Ford,E350,\"ac, abs, moon\",3000.00\n" + + "1999,Chevy,\"Venture \"\"Extended Edition\"\"\",\"\",4900.00\n" + + "1999,Chevy,\"Venture \"\"Extended Edition, Very Large\"\"\",,5000.00\n" + + "1996,Jeep,Grand Cherokee,\"MUST SELL!\n" + + "air, moon roof, loaded\",4799.00\n") + .getBytes(StandardCharsets.UTF_8); + minioClient.putObject( + PutObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .data(data, data.length) + .build()); - String sqlExpression = "select * from S3Object"; - InputSerialization is = - new InputSerialization(null, false, null, null, FileHeaderInfo.USE, null, null, null); - OutputSerialization os = - new OutputSerialization(null, null, null, QuoteFields.ASNEEDED, null); + String sqlExpression = "select * from S3Object"; + InputSerialization is = + InputSerialization.newCSV( + null, false, null, null, InputSerialization.FileHeaderInfo.USE, null, null, null); + OutputSerialization os = + OutputSerialization.newCSV( + null, null, null, OutputSerialization.QuoteFields.ASNEEDED, null); - SelectResponseStream stream = - minioClient.selectObjectContent( - SelectObjectContentArgs.builder() - .bucket("my-bucketname") - .object("my-objectName") - .sqlExpression(sqlExpression) - .inputSerialization(is) - .outputSerialization(os) - .requestProgress(true) - .build()); + SelectResponseStream stream = + minioClient.selectObjectContent( + SelectObjectContentArgs.builder() + .bucket("my-bucket") + .object("my-objectName") + .sqlExpression(sqlExpression) + .inputSerialization(is) + .outputSerialization(os) + .requestProgress(true) + .build()); - byte[] buf = new byte[512]; - int bytesRead = stream.read(buf, 0, buf.length); - System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8)); - Stats stats = stream.stats(); - System.out.println("bytes scanned: " + stats.bytesScanned()); - System.out.println("bytes processed: " + stats.bytesProcessed()); - System.out.println("bytes returned: " + stats.bytesReturned()); - stream.close(); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + byte[] buf = new byte[512]; + int bytesRead = stream.read(buf, 0, buf.length); + System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8)); + Stats stats = stream.stats(); + System.out.println("bytes scanned: " + stats.bytesScanned()); + System.out.println("bytes processed: " + stats.bytesProcessed()); + System.out.println("bytes returned: " + stats.bytesReturned()); + stream.close(); } } diff --git a/examples/SetBucketCors.java b/examples/SetBucketCors.java index 37ec1fcfc..ddba8d64e 100644 --- a/examples/SetBucketCors.java +++ b/examples/SetBucketCors.java @@ -18,58 +18,50 @@ import io.minio.SetBucketCorsArgs; import io.minio.errors.MinioException; import io.minio.messages.CORSConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.util.Arrays; public class SetBucketCors { /** MinioClient.setBucketCors() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - CORSConfiguration config = - new CORSConfiguration( - Arrays.asList( - new CORSConfiguration.CORSRule[] { - // Rule 1 - new CORSConfiguration.CORSRule( - Arrays.asList(new String[] {"*"}), // Allowed headers - Arrays.asList(new String[] {"PUT", "POST", "DELETE"}), // Allowed methods - Arrays.asList(new String[] {"http://www.example.com"}), // Allowed origins - Arrays.asList( - new String[] {"x-amz-server-side-encryption"}), // Expose headers - null, // ID - 3000), // Maximum age seconds - // Rule 2 - new CORSConfiguration.CORSRule( - null, // Allowed headers - Arrays.asList(new String[] {"GET"}), // Allowed methods - Arrays.asList(new String[] {"*"}), // Allowed origins - null, // Expose headers - null, // ID - null // Maximum age seconds - ) - })); + CORSConfiguration config = + new CORSConfiguration( + Arrays.asList( + new CORSConfiguration.CORSRule[] { + // Rule 1 + new CORSConfiguration.CORSRule( + Arrays.asList(new String[] {"*"}), // Allowed headers + Arrays.asList(new String[] {"PUT", "POST", "DELETE"}), // Allowed methods + Arrays.asList(new String[] {"http://www.example.com"}), // Allowed origins + Arrays.asList( + new String[] {"x-amz-server-side-encryption"}), // Expose headers + null, // ID + 3000), // Maximum age seconds + // Rule 2 + new CORSConfiguration.CORSRule( + null, // Allowed headers + Arrays.asList(new String[] {"GET"}), // Allowed methods + Arrays.asList(new String[] {"*"}), // Allowed origins + null, // Expose headers + null, // ID + null // Maximum age seconds + ) + })); - minioClient.setBucketCors( - SetBucketCorsArgs.builder().bucket("my-bucketname").config(config).build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.setBucketCors( + SetBucketCorsArgs.builder().bucket("my-bucket").config(config).build()); } } diff --git a/examples/SetBucketEncryption.java b/examples/SetBucketEncryption.java index 0956a0b05..8628227f1 100644 --- a/examples/SetBucketEncryption.java +++ b/examples/SetBucketEncryption.java @@ -18,37 +18,29 @@ import io.minio.SetBucketEncryptionArgs; import io.minio.errors.MinioException; import io.minio.messages.SseConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class SetBucketEncryption { /** MinioClient.setBucketEncryption() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - minioClient.setBucketEncryption( - SetBucketEncryptionArgs.builder() - .bucket("my-bucketname") - .config(SseConfiguration.newConfigWithSseS3Rule()) - .build()); - System.out.println("Encryption configuration of my-bucketname is set successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.setBucketEncryption( + SetBucketEncryptionArgs.builder() + .bucket("my-bucket") + .config(SseConfiguration.newConfigWithSseS3Rule()) + .build()); + System.out.println("Encryption configuration of my-bucket is set successfully"); } } diff --git a/examples/SetBucketLifecycle.java b/examples/SetBucketLifecycle.java index 2b512a5c1..fadb40d3f 100644 --- a/examples/SetBucketLifecycle.java +++ b/examples/SetBucketLifecycle.java @@ -17,54 +17,44 @@ 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; -import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; public class SetBucketLifecycle { /** MinioClient.SetBucketLifecycle() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - List rules = new LinkedList<>(); - rules.add( - new LifecycleRule( - Status.ENABLED, - null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter("logs/"), - "rule2", - null, - null, - null)); - LifecycleConfiguration config = new LifecycleConfiguration(rules); + List rules = new ArrayList<>(); + rules.add( + new LifecycleConfiguration.Rule( + Status.ENABLED, + null, + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null, null), + new Filter("logs/"), + "rule2", + null, + null, + null)); + LifecycleConfiguration config = new LifecycleConfiguration(rules); - minioClient.setBucketLifecycle( - SetBucketLifecycleArgs.builder().bucket("my-bucketname").config(config).build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.setBucketLifecycle( + SetBucketLifecycleArgs.builder().bucket("my-bucket").config(config).build()); } } diff --git a/examples/SetBucketNotification.java b/examples/SetBucketNotification.java index a3f1cf496..bc3ef744a 100644 --- a/examples/SetBucketNotification.java +++ b/examples/SetBucketNotification.java @@ -19,55 +19,47 @@ 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. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - NotificationConfiguration config = new NotificationConfiguration(); + 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); - // 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); - - // Set updated notification configuration. - minioClient.setBucketNotification( - SetBucketNotificationArgs.builder().bucket("my-bucketname").config(config).build()); - System.out.println("Bucket notification is set successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Set updated notification configuration. + minioClient.setBucketNotification( + SetBucketNotificationArgs.builder().bucket("my-bucket").config(config).build()); + System.out.println("Bucket notification is set successfully"); } } diff --git a/examples/SetBucketPolicy.java b/examples/SetBucketPolicy.java index 07f7b89ba..59e53a711 100644 --- a/examples/SetBucketPolicy.java +++ b/examples/SetBucketPolicy.java @@ -17,54 +17,46 @@ import io.minio.MinioClient; import io.minio.SetBucketPolicyArgs; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class SetBucketPolicy { /** MinioClient.setBucketPolicy() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - StringBuilder builder = new StringBuilder(); - builder.append("{\n"); - builder.append(" \"Statement\": [\n"); - builder.append(" {\n"); - builder.append(" \"Action\": [\n"); - builder.append(" \"s3:GetBucketLocation\",\n"); - builder.append(" \"s3:ListBucket\"\n"); - builder.append(" ],\n"); - builder.append(" \"Effect\": \"Allow\",\n"); - builder.append(" \"Principal\": \"*\",\n"); - builder.append(" \"Resource\": \"arn:aws:s3:::my-bucketname\"\n"); - builder.append(" },\n"); - builder.append(" {\n"); - builder.append(" \"Action\": \"s3:GetObject\",\n"); - builder.append(" \"Effect\": \"Allow\",\n"); - builder.append(" \"Principal\": \"*\",\n"); - builder.append(" \"Resource\": \"arn:aws:s3:::my-bucketname/myobject*\"\n"); - builder.append(" }\n"); - builder.append(" ],\n"); - builder.append(" \"Version\": \"2012-10-17\"\n"); - builder.append("}\n"); - minioClient.setBucketPolicy( - SetBucketPolicyArgs.builder().bucket("my-bucketname").config(builder.toString()).build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + StringBuilder builder = new StringBuilder(); + builder.append("{\n"); + builder.append(" \"Statement\": [\n"); + builder.append(" {\n"); + builder.append(" \"Action\": [\n"); + builder.append(" \"s3:GetBucketLocation\",\n"); + builder.append(" \"s3:ListBucket\"\n"); + builder.append(" ],\n"); + builder.append(" \"Effect\": \"Allow\",\n"); + builder.append(" \"Principal\": \"*\",\n"); + builder.append(" \"Resource\": \"arn:aws:s3:::my-bucket\"\n"); + builder.append(" },\n"); + builder.append(" {\n"); + builder.append(" \"Action\": \"s3:GetObject\",\n"); + builder.append(" \"Effect\": \"Allow\",\n"); + builder.append(" \"Principal\": \"*\",\n"); + builder.append(" \"Resource\": \"arn:aws:s3:::my-bucket/myobject*\"\n"); + builder.append(" }\n"); + builder.append(" ],\n"); + builder.append(" \"Version\": \"2012-10-17\"\n"); + builder.append("}\n"); + minioClient.setBucketPolicy( + SetBucketPolicyArgs.builder().bucket("my-bucket").config(builder.toString()).build()); } } diff --git a/examples/SetBucketReplication.java b/examples/SetBucketReplication.java index 5f35de2ce..10e6ba23d 100644 --- a/examples/SetBucketReplication.java +++ b/examples/SetBucketReplication.java @@ -17,67 +17,56 @@ 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; -import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; public class SetBucketReplication { /** MinioClient.setBucketReplication() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - Map tags = new HashMap<>(); - tags.put("key1", "value1"); - tags.put("key2", "value2"); + Map tags = new HashMap<>(); + tags.put("key1", "value1"); + tags.put("key2", "value2"); - ReplicationRule rule = - new ReplicationRule( - new DeleteMarkerReplication(Status.DISABLED), - new ReplicationDestination( - null, null, "REPLACE-WITH-ACTUAL-DESTINATION-BUCKET-ARN", null, null, null, null), - null, - new RuleFilter(new AndOperator("TaxDocs", tags)), - "rule1", - null, - 1, - null, - Status.ENABLED); + ReplicationConfiguration.Rule rule = + new ReplicationConfiguration.Rule( + Status.ENABLED, + new ReplicationConfiguration.Destination( + null, null, "REPLACE-WITH-ACTUAL-DESTINATION-BUCKET-ARN", null, null, null, null), + new ReplicationConfiguration.DeleteMarkerReplication(Status.DISABLED), + null, + new Filter(new Filter.And("TaxDocs", tags, null, null)), + "rule1", + null, + 1, + null, + null); - List rules = new LinkedList<>(); - rules.add(rule); + List rules = new ArrayList<>(); + rules.add(rule); - ReplicationConfiguration config = - new ReplicationConfiguration("REPLACE-WITH-ACTUAL-ROLE", rules); + ReplicationConfiguration config = + new ReplicationConfiguration("REPLACE-WITH-ACTUAL-ROLE", rules); - minioClient.setBucketReplication( - SetBucketReplicationArgs.builder().bucket("my-bucketname").config(config).build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + minioClient.setBucketReplication( + SetBucketReplicationArgs.builder().bucket("my-bucket").config(config).build()); } } diff --git a/examples/SetBucketTags.java b/examples/SetBucketTags.java index 49245d9ca..0fb6f40aa 100644 --- a/examples/SetBucketTags.java +++ b/examples/SetBucketTags.java @@ -17,38 +17,29 @@ import io.minio.MinioClient; import io.minio.SetBucketTagsArgs; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; public class SetBucketTags { /** MinioClient.setBucketTags() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - Map map = new HashMap<>(); - map.put("Project", "Project One"); - map.put("User", "jsmith"); - minioClient.setBucketTags( - SetBucketTagsArgs.builder().bucket("my-bucketname").tags(map).build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + Map map = new HashMap<>(); + map.put("Project", "Project One"); + map.put("User", "jsmith"); + minioClient.setBucketTags(SetBucketTagsArgs.builder().bucket("my-bucket").tags(map).build()); } } diff --git a/examples/SetBucketVersioning.java b/examples/SetBucketVersioning.java index 94298543b..69db9c810 100644 --- a/examples/SetBucketVersioning.java +++ b/examples/SetBucketVersioning.java @@ -18,46 +18,42 @@ import io.minio.SetBucketVersioningArgs; import io.minio.errors.MinioException; import io.minio.messages.VersioningConfiguration; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; public class SetBucketVersioning { /** MinioClient.setBucketVersioning() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Enable versioning on 'my-bucketname'. - minioClient.setBucketVersioning( - SetBucketVersioningArgs.builder() - .bucket("my-bucketname") - .config(new VersioningConfiguration(VersioningConfiguration.Status.ENABLED, null)) - .build()); - System.out.println("Bucket versioning is enabled successfully"); + // Enable versioning on 'my-bucket'. + minioClient.setBucketVersioning( + SetBucketVersioningArgs.builder() + .bucket("my-bucket") + .config( + new VersioningConfiguration( + VersioningConfiguration.Status.ENABLED, null, null, null)) + .build()); + System.out.println("Bucket versioning is enabled successfully"); - // Suspend versioning on 'my-bucketname'. - minioClient.setBucketVersioning( - SetBucketVersioningArgs.builder() - .bucket("my-bucketname") - .config(new VersioningConfiguration(VersioningConfiguration.Status.SUSPENDED, null)) - .build()); - System.out.println("Bucket versioning is suspended successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Suspend versioning on 'my-bucket'. + minioClient.setBucketVersioning( + SetBucketVersioningArgs.builder() + .bucket("my-bucket") + .config( + new VersioningConfiguration( + VersioningConfiguration.Status.SUSPENDED, null, null, null)) + .build()); + System.out.println("Bucket versioning is suspended successfully"); } } diff --git a/examples/SetObjectLockConfiguration.java b/examples/SetObjectLockConfiguration.java index 716518c9f..c2ac3f10a 100644 --- a/examples/SetObjectLockConfiguration.java +++ b/examples/SetObjectLockConfiguration.java @@ -18,44 +18,36 @@ 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; -import java.security.NoSuchAlgorithmException; public class SetObjectLockConfiguration { /** MinioClient.setObjectLockConfiguration() exanple. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Declaring config with Retention mode as Compliance and duration as 100 days - ObjectLockConfiguration config = - new ObjectLockConfiguration(RetentionMode.COMPLIANCE, new RetentionDurationDays(100)); + // Declaring config with Retention mode as Compliance and duration as 100 days + ObjectLockConfiguration config = + new ObjectLockConfiguration( + RetentionMode.COMPLIANCE, new ObjectLockConfiguration.RetentionDurationDays(100)); - minioClient.setObjectLockConfiguration( - SetObjectLockConfigurationArgs.builder() - .bucket("my-lock-enabled-bucketname") - .config(config) - .build()); + minioClient.setObjectLockConfiguration( + SetObjectLockConfigurationArgs.builder() + .bucket("my-lock-enabled-bucketname") + .config(config) + .build()); - System.out.println("object-lock configuration is set successfully"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + System.out.println("object-lock configuration is set successfully"); } } diff --git a/examples/SetObjectRetention.java b/examples/SetObjectRetention.java index 11303899e..7588e8abd 100644 --- a/examples/SetObjectRetention.java +++ b/examples/SetObjectRetention.java @@ -20,46 +20,37 @@ import io.minio.errors.MinioException; import io.minio.messages.Retention; import io.minio.messages.RetentionMode; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; public class SetObjectRetention { /** MinioClient.setObjectRetention() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException, IllegalArgumentException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Declaring config with Retention mode as Compliance and - // retain until 3 days from current date. - ZonedDateTime retentionUntil = ZonedDateTime.now(Time.UTC).plusDays(3).withNano(0); - Retention config = new Retention(RetentionMode.COMPLIANCE, retentionUntil); + // Declaring config with Retention mode as Compliance and + // retain until 3 days from current date. + ZonedDateTime retentionUntil = ZonedDateTime.now(Time.UTC).plusDays(3).withNano(0); + Retention config = new Retention(RetentionMode.COMPLIANCE, retentionUntil); - // Set object retention - minioClient.setObjectRetention( - SetObjectRetentionArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .config(config) - .bypassGovernanceMode(true) - .build()); - - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Set object retention + minioClient.setObjectRetention( + SetObjectRetentionArgs.builder() + .bucket("my-bucket") + .object("my-object") + .config(config) + .bypassGovernanceMode(true) + .build()); } } diff --git a/examples/SetObjectTags.java b/examples/SetObjectTags.java index 5075e0229..6fa185800 100644 --- a/examples/SetObjectTags.java +++ b/examples/SetObjectTags.java @@ -17,42 +17,30 @@ import io.minio.MinioClient; import io.minio.SetObjectTagsArgs; import io.minio.errors.MinioException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; public class SetObjectTags { /** MinioClient.setObjectTags() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - Map map = new HashMap<>(); - map.put("Project", "Project One"); - map.put("User", "jsmith"); - minioClient.setObjectTags( - SetObjectTagsArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .tags(map) - .build()); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + Map map = new HashMap<>(); + map.put("Project", "Project One"); + map.put("User", "jsmith"); + minioClient.setObjectTags( + SetObjectTagsArgs.builder().bucket("my-bucket").object("my-object").tags(map).build()); } } diff --git a/examples/StatObject.java b/examples/StatObject.java index f6a3e13d1..16f7004c9 100644 --- a/examples/StatObject.java +++ b/examples/StatObject.java @@ -15,11 +15,10 @@ */ 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; -import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.HashMap; @@ -28,92 +27,87 @@ public class StatObject { /** MinioClient.statObject() example. */ public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + throws InvalidKeyException, MinioException, NoSuchAlgorithmException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - KeyGenerator keyGen = KeyGenerator.getInstance("AES"); - keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); - String versionId = "ac38316c-fe14-4f96-9f76-8f675ae5a79e"; + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); + String versionId = "ac38316c-fe14-4f96-9f76-8f675ae5a79e"; - { - // Get information of an object. - StatObjectResponse stat = - minioClient.statObject( - StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build()); - System.out.println(stat); - } - - { - // Get information of SSE-C encrypted object. - StatObjectResponse stat = - minioClient.statObject( - StatObjectArgs.builder() - .bucket("my-bucketname") - .object("my-encrypted-objectname") - .ssec(ssec) // Replace with actual key. - .build()); - System.out.println(stat); - } + { + // Get information of an object. + StatObjectResponse stat = + minioClient.statObject( + StatObjectArgs.builder().bucket("my-bucket").object("my-object").build()); + System.out.println(stat); + } - { - // Get information of a versioned object. - StatObjectResponse stat = - minioClient.statObject( - StatObjectArgs.builder() - .bucket("my-bucketname") - .object("my-versioned-objectname") - .versionId(versionId) // Replace with actual version ID. - .build()); - System.out.println(stat); - } + { + // Get information of SSE-C encrypted object. + StatObjectResponse stat = + minioClient.statObject( + StatObjectArgs.builder() + .bucket("my-bucket") + .object("my-encrypted-objectname") + .ssec(ssec) // Replace with actual key. + .build()); + System.out.println(stat); + } - { - // Get information of a SSE-C encrypted versioned object. - StatObjectResponse stat = - minioClient.statObject( - StatObjectArgs.builder() - .bucket("my-bucketname") - .object("my-encrypted-versioned-objectname") - .versionId(versionId) // Replace with actual version ID. - .ssec(ssec) // Replace with actual key. - .build()); - System.out.println(stat); - } + { + // Get information of a versioned object. + StatObjectResponse stat = + minioClient.statObject( + StatObjectArgs.builder() + .bucket("my-bucket") + .object("my-versioned-objectname") + .versionId(versionId) // Replace with actual version ID. + .build()); + System.out.println(stat); + } - { - // Get information of an object with extra headers and query parameters. - HashMap headers = new HashMap<>(); - headers.put("x-amz-request-payer", "requester"); - HashMap queryParams = new HashMap<>(); - queryParams.put("partNumber", "1"); + { + // Get information of a SSE-C encrypted versioned object. + StatObjectResponse stat = + minioClient.statObject( + StatObjectArgs.builder() + .bucket("my-bucket") + .object("my-encrypted-versioned-objectname") + .versionId(versionId) // Replace with actual version ID. + .ssec(ssec) // Replace with actual key. + .build()); + System.out.println(stat); + } - StatObjectResponse stat = - minioClient.statObject( - StatObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .extraHeaders(headers) // Replace with actual headers. - .extraQueryParams(queryParams) // Replace with actual query parameters. - .build()); - System.out.println(stat); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); + { + // Get information of an object with extra headers and query parameters. + HashMap headers = new HashMap<>(); + headers.put("x-amz-request-payer", "requester"); + HashMap queryParams = new HashMap<>(); + queryParams.put("partNumber", "1"); + StatObjectResponse stat = + minioClient.statObject( + StatObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .extraHeaders(headers) // Replace with actual headers. + .extraQueryParams(queryParams) // Replace with actual query parameters. + .build()); + System.out.println(stat); } } } diff --git a/examples/UploadObject.java b/examples/UploadObject.java index 35abedc48..f03792f1f 100644 --- a/examples/UploadObject.java +++ b/examples/UploadObject.java @@ -15,62 +15,52 @@ */ 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; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.KeyGenerator; public class UploadObject { - /** MinioClient.putObject() example. */ + /** MinioClient.uploadObject() example. */ public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + throws InvalidKeyException, MinioException, NoSuchAlgorithmException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - { - // Upload 'my-filename' as object 'my-objectname' in 'my-bucketname'. - minioClient.uploadObject( - UploadObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .filename("my-filename") - .build()); - System.out.println("my-filename is uploaded to my-objectname successfully"); - } + // Upload 'my-filename' as object 'my-object' in 'my-bucket'. + minioClient.uploadObject( + UploadObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .filename("my-filename") + .build()); + System.out.println("my-filename is uploaded to my-object successfully"); - { - KeyGenerator keyGen = KeyGenerator.getInstance("AES"); - keyGen.init(256); - ServerSideEncryptionCustomerKey ssec = - new ServerSideEncryptionCustomerKey(keyGen.generateKey()); - - // Upload 'my-filename' as object encrypted 'my-objectname' in 'my-bucketname'. - minioClient.uploadObject( - UploadObjectArgs.builder() - .bucket("my-bucketname") - .object("my-objectname") - .filename("my-filename") - .sse(ssec) - .build()); - System.out.println("my-filename is uploaded to my-objectname successfully"); - } - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Upload 'my-filename' as object encrypted 'my-object' in 'my-bucket'. + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + ServerSideEncryption.CustomerKey ssec = + new ServerSideEncryption.CustomerKey(keyGen.generateKey()); + minioClient.uploadObject( + UploadObjectArgs.builder() + .bucket("my-bucket") + .object("my-object") + .filename("my-filename") + .sse(ssec) + .build()); + System.out.println("my-filename is uploaded to my-object successfully"); } } diff --git a/examples/UploadSnowballObjects.java b/examples/UploadSnowballObjects.java index b292f2b3e..5de2ba925 100644 --- a/examples/UploadSnowballObjects.java +++ b/examples/UploadSnowballObjects.java @@ -19,51 +19,43 @@ import io.minio.UploadSnowballObjectsArgs; import io.minio.errors.MinioException; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; public class UploadSnowballObjects { /** MinioClient.uploadSnowballObjects() example. */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { - try { - /* play.min.io for test and development. */ - MinioClient minioClient = - MinioClient.builder() - .endpoint("https://play.min.io") - .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") - .build(); + public static void main(String[] args) throws MinioException { + /* play.min.io for test and development. */ + MinioClient minioClient = + MinioClient.builder() + .endpoint("https://play.min.io") + .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") + .build(); - /* Amazon S3: */ - // MinioClient minioClient = - // MinioClient.builder() - // .endpoint("https://s3.amazonaws.com") - // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") - // .build(); + /* Amazon S3: */ + // MinioClient minioClient = + // MinioClient.builder() + // .endpoint("https://s3.amazonaws.com") + // .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY") + // .build(); - // Upload snowball objects. - List objects = new ArrayList(); - objects.add( - new SnowballObject( - "my-object-one", - new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)), - 5, - null)); - objects.add( - new SnowballObject( - "my-object-two", - new ByteArrayInputStream("java".getBytes(StandardCharsets.UTF_8)), - 4, - null)); - minioClient.uploadSnowballObjects( - UploadSnowballObjectsArgs.builder().bucket("my-bucketname").objects(objects).build()); - System.out.println("my-object-one and my-object-two are successfully uploaded"); - } catch (MinioException e) { - System.out.println("Error occurred: " + e); - } + // Upload snowball objects. + List objects = new ArrayList(); + objects.add( + new SnowballObject( + "my-object-one", + new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)), + 5, + null)); + objects.add( + new SnowballObject( + "my-object-two", + new ByteArrayInputStream("java".getBytes(StandardCharsets.UTF_8)), + 4, + null)); + minioClient.uploadSnowballObjects( + UploadSnowballObjectsArgs.builder().bucket("my-bucket").objects(objects).build()); + System.out.println("my-object-one and my-object-two are successfully uploaded"); } } diff --git a/functional/FunctionalTest.java b/functional/FunctionalTest.java index 1d66b5d2b..d87e38d69 100644 --- a/functional/FunctionalTest.java +++ b/functional/FunctionalTest.java @@ -1,4250 +1,117 @@ -/* - * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, - * (C) 2015-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. - */ - -import static java.nio.file.StandardOpenOption.APPEND; -import static java.nio.file.StandardOpenOption.CREATE; - -import com.google.common.io.BaseEncoding; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -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; -import io.minio.DeleteBucketNotificationArgs; -import io.minio.DeleteBucketPolicyArgs; -import io.minio.DeleteBucketReplicationArgs; -import io.minio.DeleteBucketTagsArgs; -import io.minio.DeleteObjectLockConfigurationArgs; -import io.minio.DeleteObjectTagsArgs; -import io.minio.Directive; -import io.minio.DisableObjectLegalHoldArgs; -import io.minio.DownloadObjectArgs; -import io.minio.EnableObjectLegalHoldArgs; -import io.minio.GetBucketCorsArgs; -import io.minio.GetBucketEncryptionArgs; -import io.minio.GetBucketLifecycleArgs; -import io.minio.GetBucketNotificationArgs; -import io.minio.GetBucketPolicyArgs; -import io.minio.GetBucketReplicationArgs; -import io.minio.GetBucketTagsArgs; -import io.minio.GetBucketVersioningArgs; -import io.minio.GetObjectAclArgs; -import io.minio.GetObjectArgs; -import io.minio.GetObjectAttributesArgs; -import io.minio.GetObjectAttributesResponse; -import io.minio.GetObjectLockConfigurationArgs; -import io.minio.GetObjectRetentionArgs; -import io.minio.GetObjectTagsArgs; -import io.minio.GetPresignedObjectUrlArgs; -import io.minio.IsObjectLegalHoldEnabledArgs; -import io.minio.ListBucketsArgs; -import io.minio.ListObjectsArgs; -import io.minio.ListenBucketNotificationArgs; -import io.minio.MakeBucketArgs; -import io.minio.MinioClient; -import io.minio.ObjectWriteResponse; -import io.minio.PostPolicy; -import io.minio.PutObjectArgs; -import io.minio.PutObjectFanOutArgs; -import io.minio.PutObjectFanOutEntry; -import io.minio.RemoveBucketArgs; -import io.minio.RemoveObjectArgs; -import io.minio.RemoveObjectsArgs; -import io.minio.Result; -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; -import io.minio.SetBucketNotificationArgs; -import io.minio.SetBucketPolicyArgs; -import io.minio.SetBucketReplicationArgs; -import io.minio.SetBucketTagsArgs; -import io.minio.SetBucketVersioningArgs; -import io.minio.SetObjectLockConfigurationArgs; -import io.minio.SetObjectRetentionArgs; -import io.minio.SetObjectTagsArgs; -import io.minio.SnowballObject; -import io.minio.StatObjectArgs; -import io.minio.StatObjectResponse; -import io.minio.Time; -import io.minio.UploadObjectArgs; -import io.minio.UploadSnowballObjectsArgs; -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.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.EventType; -import io.minio.messages.Expiration; -import io.minio.messages.FileHeaderInfo; -import io.minio.messages.GranteeType; -import io.minio.messages.InputSerialization; -import io.minio.messages.LifecycleConfiguration; -import io.minio.messages.LifecycleRule; -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; -import io.minio.messages.Status; -import io.minio.messages.Tags; -import io.minio.messages.VersioningConfiguration; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -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; -import java.time.ZonedDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import javax.crypto.KeyGenerator; -import okhttp3.Headers; -import okhttp3.HttpUrl; -import okhttp3.MultipartBody; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okio.BufferedSink; -import okio.Okio; -import org.junit.jupiter.api.Assertions; - -@SuppressFBWarnings( - value = "REC", - justification = "Allow catching super class Exception since it's tests") -public class FunctionalTest { - private static final String OS = System.getProperty("os.name").toLowerCase(Locale.US); - private static final String MINIO_BINARY; - private static final String PASS = "PASS"; - private static final String FAILED = "FAIL"; - private static final String IGNORED = "NA"; - private static final int KB = 1024; - private static final int MB = 1024 * 1024; - private static final Random random = new Random(new SecureRandom().nextLong()); - private static final String customContentType = "application/javascript"; - private static String bucketName = getRandomName(); - private static String bucketNameWithLock = getRandomName(); - private static boolean mintEnv = false; - private static boolean isQuickTest = false; - private static boolean isRunOnFail = false; - private static Path dataFile1Kb; - private static Path dataFile6Mb; - private static String endpoint; - private static String endpointTLS; - private static String accessKey; - private static String secretKey; - private static String region; - private static boolean isSecureEndpoint = false; - private static String sqsArn = null; - private static String replicationSrcBucket = null; - private static String replicationRole = null; - private static String replicationBucketArn = null; - private static MinioClient client = null; - private static TestMinioAdminClient adminClientTests; - - private static ServerSideEncryptionCustomerKey ssec = null; - private static ServerSideEncryption sseS3 = new ServerSideEncryptionS3(); - private static ServerSideEncryption sseKms = null; - - static { - String binaryName = "minio"; - if (OS.contains("windows")) { - binaryName = "minio.exe"; - } - - MINIO_BINARY = binaryName; - - try { - KeyGenerator keyGen = KeyGenerator.getInstance("AES"); - keyGen.init(256); - ssec = new ServerSideEncryptionCustomerKey(keyGen.generateKey()); - } catch (InvalidKeyException | NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - 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) { - throw new RuntimeException(e); - } - } - - /** Do no-op. */ - public static void ignore(Object... args) {} - - /** Create given sized file and returns its name. */ - public static String createFile(int size) throws IOException { - String filename = getRandomName(); - - try (OutputStream os = Files.newOutputStream(Paths.get(filename), CREATE, APPEND)) { - int totalBytesWritten = 0; - int bytesToWrite = 0; - byte[] buf = new byte[1 * MB]; - while (totalBytesWritten < size) { - random.nextBytes(buf); - bytesToWrite = size - totalBytesWritten; - if (bytesToWrite > buf.length) { - bytesToWrite = buf.length; - } - - os.write(buf, 0, bytesToWrite); - totalBytesWritten += bytesToWrite; - } - } - - return filename; - } - - /** Create 1 KB temporary file. */ - public static String createFile1Kb() throws IOException { - if (mintEnv) { - String filename = getRandomName(); - Files.createSymbolicLink(Paths.get(filename).toAbsolutePath(), dataFile1Kb); - return filename; - } - - return createFile(1 * KB); - } - - /** Create 6 MB temporary file. */ - public static String createFile6Mb() throws IOException { - if (mintEnv) { - String filename = getRandomName(); - Files.createSymbolicLink(Paths.get(filename).toAbsolutePath(), dataFile6Mb); - return filename; - } - - return createFile(6 * MB); - } - - /** Generate random name. */ - public static String getRandomName() { - return "minio-java-test-" + new BigInteger(32, random).toString(32); - } - - /** Returns byte array contains all data in given InputStream. */ - public static byte[] readAllBytes(InputStream is) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - int nRead; - byte[] data = new byte[16384]; - while ((nRead = is.read(data, 0, data.length)) != -1) { - buffer.write(data, 0, nRead); - } - return buffer.toByteArray(); - } - - /** Prints a success log entry in JSON format. */ - public static void mintSuccessLog(String function, String args, long startTime) { - if (mintEnv) { - System.out.println( - new MintLogger( - function, args, System.currentTimeMillis() - startTime, PASS, null, null, null)); - } - } - - /** Prints a failure log entry in JSON format. */ - public static void mintFailedLog( - String function, String args, long startTime, String message, String error) { - if (mintEnv) { - System.out.println( - new MintLogger( - function, - args, - System.currentTimeMillis() - startTime, - FAILED, - null, - message, - error)); - } - } - - /** Prints a ignore log entry in JSON format. */ - public static void mintIgnoredLog(String function, String args, long startTime) { - if (mintEnv) { - System.out.println( - new MintLogger( - function, args, System.currentTimeMillis() - startTime, IGNORED, null, null, null)); - } - } - - /** Read object content of the given url. */ - public static byte[] readObject(String urlString) throws Exception { - Request.Builder requestBuilder = new Request.Builder(); - Request request = requestBuilder.url(HttpUrl.parse(urlString)).method("GET", null).build(); - OkHttpClient transport = newHttpClient(); - Response response = transport.newCall(request).execute(); - - try { - if (response.isSuccessful()) { - return response.body().bytes(); - } - - String errorXml = response.body().string(); - throw new Exception( - "failed to create object. Response: " + response + ", Response body: " + errorXml); - } finally { - response.close(); - } - } - - /** Write data to given object url. */ - public static void writeObject(String urlString, byte[] dataBytes) throws Exception { - Request.Builder requestBuilder = new Request.Builder(); - // Set header 'x-amz-acl' to 'bucket-owner-full-control', so objects created - // anonymously, can be downloaded by bucket owner in AWS S3. - Request request = - requestBuilder - .url(HttpUrl.parse(urlString)) - .method("PUT", RequestBody.create(dataBytes, null)) - .addHeader("x-amz-acl", "bucket-owner-full-control") - .build(); - OkHttpClient transport = newHttpClient(); - Response response = transport.newCall(request).execute(); - - try { - if (!response.isSuccessful()) { - String errorXml = response.body().string(); - throw new Exception( - "failed to create object. Response: " + response + ", Response body: " + errorXml); - } - } finally { - response.close(); - } - } - - public static String getSha256Sum(InputStream stream, int len) throws Exception { - MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256"); - - // 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; - } - - bytesRead = stream.read(buf, 0, bytesToRead); - Assertions.assertFalse( - bytesRead < 0, "data length mismatch. expected: " + len + ", got: " + totalBytesRead); - - if (bytesRead > 0) { - sha256Digest.update(buf, 0, bytesRead); - totalBytesRead += bytesRead; - } - } - - return BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase(Locale.US); - } - - public static void skipStream(InputStream stream, int len) throws Exception { - // 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; - } - - bytesRead = stream.read(buf, 0, bytesToRead); - Assertions.assertFalse( - bytesRead < 0, "insufficient data. expected: " + len + ", got: " + totalBytesRead); - if (bytesRead > 0) { - totalBytesRead += bytesRead; - } - } - } - - public static void handleException(String methodName, String args, long startTime, Exception e) - throws Exception { - if (e instanceof ErrorResponseException) { - if (((ErrorResponseException) e).errorResponse().code().equals("NotImplemented")) { - mintIgnoredLog(methodName, args, startTime); - return; - } - } - - if (mintEnv) { - mintFailedLog( - methodName, - args, - startTime, - null, - e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); - if (isRunOnFail) { - return; - } - } else { - System.out.println(" " + methodName + " " + ((args == null) ? "" : args)); - } - - throw e; - } - - public static void testBucketApi( - String methodName, - String testTags, - MakeBucketArgs args, - boolean existCheck, - boolean removeCheck) - throws Exception { - long startTime = System.currentTimeMillis(); - try { - client.makeBucket(args); - try { - Assertions.assertFalse( - existCheck - && !client.bucketExists( - BucketExistsArgs.builder().bucket(args.bucket()).region(args.region()).build()), - methodName + " failed after bucket creation"); - if (removeCheck) { - client.removeBucket( - RemoveBucketArgs.builder().bucket(args.bucket()).region(args.region()).build()); - } - mintSuccessLog(methodName, null, startTime); - } finally { - if (!removeCheck) { - client.removeBucket( - RemoveBucketArgs.builder().bucket(args.bucket()).region(args.region()).build()); - } - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void testBucketApiCases(String methodName, boolean existCheck, boolean removeCheck) - throws Exception { - testBucketApi( - methodName, - "[basic check]", - MakeBucketArgs.builder().bucket(getRandomName()).build(), - existCheck, - removeCheck); - - if (isQuickTest) { - return; - } - - testBucketApi( - methodName, - "[object lock]", - MakeBucketArgs.builder().bucket(getRandomName()).objectLock(true).build(), - existCheck, - removeCheck); - testBucketApi( - methodName, - "[name contains period]", - MakeBucketArgs.builder().bucket(getRandomName() + ".withperiod").build(), - existCheck, - removeCheck); - } - - public static void makeBucket() throws Exception { - String methodName = "makeBucket()"; - if (!mintEnv) { - System.out.println(methodName); - } - - testBucketApiCases(methodName, false, false); - - if (isQuickTest) { - return; - } - - if (!endpoint.contains(".amazonaws.com")) { - mintIgnoredLog(methodName, "[region]", System.currentTimeMillis()); - mintIgnoredLog(methodName, "[region, object lock]", System.currentTimeMillis()); - return; - } - - testBucketApi( - methodName, - "[region]", - MakeBucketArgs.builder().bucket(getRandomName()).region("eu-west-1").build(), - false, - false); - testBucketApi( - methodName, - "[region, object lock]", - MakeBucketArgs.builder() - .bucket(getRandomName()) - .region("eu-central-1") - .objectLock(true) - .build(), - false, - false); - } - - public static void listBuckets() throws Exception { - String methodName = "listBuckets()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - List expectedBucketNames = new LinkedList<>(); - try { - try { - String bucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - expectedBucketNames.add(bucketName); - - bucketName = getRandomName(); - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); - expectedBucketNames.add(bucketName); - - bucketName = getRandomName() + ".withperiod"; - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - expectedBucketNames.add(bucketName); - - List bucketNames = new LinkedList<>(); - for (Result result : - client.listBuckets(ListBucketsArgs.builder().maxBuckets(1).build())) { - Bucket bucket = result.get(); - if (expectedBucketNames.contains(bucket.name())) { - bucketNames.add(bucket.name()); - } - } - - Assertions.assertTrue( - expectedBucketNames.containsAll(bucketNames), - "bucket names differ; expected = " + expectedBucketNames + ", got = " + bucketNames); - - mintSuccessLog(methodName, null, startTime); - } finally { - for (String bucketName : expectedBucketNames) { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void bucketExists() throws Exception { - String methodName = "bucketExists()"; - if (!mintEnv) { - System.out.println(methodName); - } - - testBucketApiCases(methodName, true, false); - } - - public static void removeBucket() throws Exception { - String methodName = "removeBucket()"; - if (!mintEnv) { - System.out.println(methodName); - } - - testBucketApiCases(methodName, false, true); - } - - public static void setBucketVersioning() throws Exception { - String methodName = "setBucketVersioning()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String name = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(name).build()); - try { - client.setBucketVersioning( - SetBucketVersioningArgs.builder() - .bucket(name) - .config(new VersioningConfiguration(VersioningConfiguration.Status.ENABLED, null)) - .build()); - client.setBucketVersioning( - SetBucketVersioningArgs.builder() - .bucket(name) - .config(new VersioningConfiguration(VersioningConfiguration.Status.SUSPENDED, null)) - .build()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(name).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getBucketVersioning() throws Exception { - String methodName = "getBucketVersioning()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String name = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(name).build()); - try { - VersioningConfiguration config = - client.getBucketVersioning(GetBucketVersioningArgs.builder().bucket(name).build()); - Assertions.assertEquals( - config.status(), - VersioningConfiguration.Status.OFF, - "getBucketVersioning(); expected = \"\", got = " + config.status()); - client.setBucketVersioning( - SetBucketVersioningArgs.builder() - .bucket(name) - .config(new VersioningConfiguration(VersioningConfiguration.Status.ENABLED, null)) - .build()); - config = client.getBucketVersioning(GetBucketVersioningArgs.builder().bucket(name).build()); - Assertions.assertEquals( - config.status(), - VersioningConfiguration.Status.ENABLED, - "getBucketVersioning(); expected = " - + VersioningConfiguration.Status.ENABLED - + ", got = " - + config.status()); - - client.setBucketVersioning( - SetBucketVersioningArgs.builder() - .bucket(name) - .config(new VersioningConfiguration(VersioningConfiguration.Status.SUSPENDED, null)) - .build()); - config = client.getBucketVersioning(GetBucketVersioningArgs.builder().bucket(name).build()); - Assertions.assertEquals( - config.status(), - VersioningConfiguration.Status.SUSPENDED, - "getBucketVersioning(); expected = " - + VersioningConfiguration.Status.SUSPENDED - + ", got = " - + config.status()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(name).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void setup() throws Exception { - long startTime = System.currentTimeMillis(); - - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - } catch (Exception e) { - handleException("makeBucket()", null, startTime, e); - } - - try { - client.makeBucket( - MakeBucketArgs.builder().bucket(bucketNameWithLock).objectLock(true).build()); - } catch (Exception e) { - if (e instanceof ErrorResponseException) { - if (((ErrorResponseException) e).errorResponse().code().equals("NotImplemented")) { - bucketNameWithLock = null; - return; - } - } - - handleException("makeBucket()", "[object lock]", startTime, e); - } - } - - public static void teardown() throws Exception { - long startTime = System.currentTimeMillis(); - try { - if (bucketName != null) { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - - if (bucketNameWithLock != null) { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketNameWithLock).build()); - } - } catch (Exception e) { - handleException("removeBucket()", null, startTime, e); - } - } - - public static void testUploadObject(String testTags, String filename, String contentType) - throws Exception { - String methodName = "uploadObject()"; - long startTime = System.currentTimeMillis(); - try { - try { - UploadObjectArgs.Builder builder = - UploadObjectArgs.builder().bucket(bucketName).object(filename).filename(filename); - if (contentType != null) { - builder.contentType(contentType); - } - client.uploadObject(builder.build()); - mintSuccessLog(methodName, testTags, startTime); - } finally { - Files.delete(Paths.get(filename)); - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(filename).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void uploadObject() throws Exception { - String methodName = "uploadObject()"; - if (!mintEnv) { - System.out.println(methodName); - } - - testUploadObject("[single upload]", createFile1Kb(), null); - - if (isQuickTest) { - return; - } - - testUploadObject("[multi-part upload]", createFile6Mb(), null); - testUploadObject("[custom content-type]", createFile1Kb(), customContentType); - } - - public static void testPutObject(String testTags, PutObjectArgs args, String errorCode) - throws Exception { - String methodName = "putObject()"; - long startTime = System.currentTimeMillis(); - try { - ObjectWriteResponse objectInfo = null; - try { - objectInfo = client.putObject(args); - } catch (ErrorResponseException e) { - if (errorCode == null || !e.errorResponse().code().equals(errorCode)) { - throw e; - } - } - if (args.retention() != null) { - client.setObjectRetention( - SetObjectRetentionArgs.builder() - .bucket(args.bucket()) - .object(args.object()) - .config(new Retention()) - .bypassGovernanceMode(true) - .build()); - } - client.removeObject( - RemoveObjectArgs.builder() - .bucket(args.bucket()) - .object(args.object()) - .versionId(objectInfo != null ? objectInfo.versionId() : null) - .build()); - - mintSuccessLog(methodName, testTags, startTime); - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void testThreadedPutObject() throws Exception { - String methodName = "putObject()"; - String testTags = "[threaded]"; - long startTime = System.currentTimeMillis(); - try { - int count = 7; - Thread[] threads = new Thread[count]; - - for (int i = 0; i < count; i++) { - threads[i] = new Thread(new PutObjectRunnable(client, bucketName, createFile6Mb())); - } - - for (int i = 0; i < count; i++) { - threads[i].start(); - } - - // Waiting for threads to complete. - for (int i = 0; i < count; i++) { - threads[i].join(); - } - - // All threads are completed. - mintSuccessLog(methodName, testTags, startTime); - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void putObject() throws Exception { - String methodName = "putObject()"; - if (!mintEnv) { - System.out.println(methodName); - } - - testPutObject( - "[single upload]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .contentType(customContentType) - .build(), - null); - - if (isQuickTest) { - return; - } - - testPutObject( - "[multi-part upload]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(11 * MB), 11 * MB, -1) - .contentType(customContentType) - .build(), - null); - - testPutObject( - "[object name with path segments]", - PutObjectArgs.builder().bucket(bucketName).object("path/to/" + getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .contentType(customContentType) - .build(), - null); - - testPutObject( - "[zero sized object]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(0), 0, -1) - .build(), - null); - - testPutObject( - "[object name ends with '/']", - PutObjectArgs.builder().bucket(bucketName).object("path/to/" + getRandomName() + "/") - .stream(new ContentInputStream(0), 0, -1) - .contentType(customContentType) - .build(), - null); - - testPutObject( - "[unknown stream size, single upload]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), -1, PutObjectArgs.MIN_MULTIPART_SIZE) - .contentType(customContentType) - .build(), - null); - - testPutObject( - "[unknown stream size, multi-part upload]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(11 * MB), -1, PutObjectArgs.MIN_MULTIPART_SIZE) - .contentType(customContentType) - .build(), - null); - - Map userMetadata = new HashMap<>(); - userMetadata.put("My-Project", "Project One"); - userMetadata.put("My-header1", " a b c "); - userMetadata.put("My-Header2", "\"a b c\""); - userMetadata.put("My-Unicode-Tag", "商品"); - - testPutObject( - "[user metadata]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .userMetadata(userMetadata) - .build(), - null); - - Map headers = new HashMap<>(); - - headers.put("X-Amz-Storage-Class", "REDUCED_REDUNDANCY"); - testPutObject( - "[storage-class=REDUCED_REDUNDANCY]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .headers(headers) - .build(), - null); - - headers.put("X-Amz-Storage-Class", "STANDARD"); - testPutObject( - "[storage-class=STANDARD]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .headers(headers) - .build(), - null); - - headers.put("X-Amz-Storage-Class", "INVALID"); - testPutObject( - "[storage-class=INVALID negative case]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .headers(headers) - .build(), - "InvalidStorageClass"); - - testPutObject( - "[SSE-S3]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .contentType(customContentType) - .sse(sseS3) - .build(), - null); - - if (bucketNameWithLock != null) { - testPutObject( - "[with retention]", - PutObjectArgs.builder().bucket(bucketNameWithLock).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .retention( - new Retention(RetentionMode.GOVERNANCE, ZonedDateTime.now(Time.UTC).plusDays(1))) - .build(), - null); - } - - testThreadedPutObject(); - - if (!isSecureEndpoint) { - return; - } - - testPutObject( - "[SSE-C single upload]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .contentType(customContentType) - .sse(ssec) - .build(), - null); - - testPutObject( - "[SSE-C multi-part upload]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(11 * MB), 11 * MB, -1) - .contentType(customContentType) - .sse(ssec) - .build(), - null); - - if (sseKms == null) { - mintIgnoredLog(methodName, null, System.currentTimeMillis()); - return; - } - - testPutObject( - "[SSE-KMS]", - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .contentType(customContentType) - .sse(sseKms) - .build(), - null); - } - - public static void testStatObject( - String testTags, PutObjectArgs args, StatObjectResponse expectedStat) throws Exception { - String methodName = "statObject()"; - long startTime = System.currentTimeMillis(); - try { - client.putObject(args); - try { - ServerSideEncryptionCustomerKey ssec = null; - if (args.sse() instanceof ServerSideEncryptionCustomerKey) { - ssec = (ServerSideEncryptionCustomerKey) args.sse(); - } - StatObjectResponse stat = - client.statObject( - StatObjectArgs.builder() - .bucket(args.bucket()) - .object(args.object()) - .ssec(ssec) - .build()); - - Assertions.assertEquals( - expectedStat.bucket(), - stat.bucket(), - "bucket name: expected = " + expectedStat.bucket() + ", got = " + stat.bucket()); - - Assertions.assertEquals( - expectedStat.object(), - stat.object(), - "object name: expected = " + expectedStat.object() + ", got = " + stat.object()); - - Assertions.assertEquals( - expectedStat.size(), - stat.size(), - "length: expected = " + expectedStat.size() + ", got = " + stat.size()); - - Assertions.assertEquals( - expectedStat.contentType(), - stat.contentType(), - "content-type: expected = " - + expectedStat.contentType() - + ", got = " - + stat.contentType()); - - for (String key : expectedStat.userMetadata().keySet()) { - Assertions.assertTrue( - stat.userMetadata().containsKey(key), "metadata " + key + " not found"); - Assertions.assertEquals( - expectedStat.userMetadata().get(key), - stat.userMetadata().get(key), - "metadata " - + key - + " value: expected: " - + expectedStat.userMetadata().get(key) - + ", got: " - + stat.userMetadata().get(key)); - } - - mintSuccessLog(methodName, testTags, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void statObject() throws Exception { - String methodName = "statObject()"; - if (!mintEnv) { - System.out.println(methodName); - } - - String objectName = getRandomName(); - - PutObjectArgs.Builder builder = - PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1024), 1024, -1); - Headers.Builder headersBuilder = - new Headers.Builder() - .add("Content-Type: application/octet-stream") - .add("Content-Length: 1024") - .add("Last-Modified", ZonedDateTime.now().format(Time.HTTP_HEADER_DATE_FORMAT)); - - testStatObject( - "[basic check]", - builder.build(), - new StatObjectResponse(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); - - StatObjectResponse stat = - new StatObjectResponse( - headersBuilder - .set("Content-Type", customContentType) - .add("X-Amz-Meta-My-Project: Project One") - .build(), - bucketName, - null, - objectName); - - testStatObject("[user metadata]", builder.build(), stat); - - if (isQuickTest) { - return; - } - - builder = builder.stream(new ContentInputStream(1024), 1024, -1); - testStatObject("[SSE-S3]", builder.sse(sseS3).build(), stat); - - if (!isSecureEndpoint) { - mintIgnoredLog(methodName, "[SSE-C]", System.currentTimeMillis()); - return; - } - - builder = builder.stream(new ContentInputStream(1024), 1024, -1); - testStatObject("[SSE-C]", builder.sse(ssec).build(), stat); - - if (sseKms == null) { - mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); - return; - } - - builder = builder.stream(new ContentInputStream(1024), 1024, -1); - testStatObject("[SSE-KMS]", builder.sse(sseKms).build(), stat); - } - - public static void testGetObject( - String testTags, - long objectSize, - ServerSideEncryption sse, - GetObjectArgs args, - int length, - String sha256sum) - throws Exception { - String methodName = "getObject()"; - long startTime = System.currentTimeMillis(); - try { - PutObjectArgs.Builder builder = - PutObjectArgs.builder().bucket(args.bucket()).object(args.object()).stream( - new ContentInputStream(objectSize), objectSize, -1); - if (sse != null) { - builder.sse(sse); - } - client.putObject(builder.build()); - - try (InputStream is = client.getObject(args)) { - String checksum = getSha256Sum(is, length); - Assertions.assertEquals( - checksum, - sha256sum, - "checksum mismatch. expected: " + sha256sum + ", got: " + checksum); - } - mintSuccessLog(methodName, testTags, startTime); - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); - } - } - - public static void getObject() throws Exception { - String methodName = "getObject()"; - if (!mintEnv) { - System.out.println(methodName); - } - - testGetObject( - "[single upload]", - 1 * KB, - null, - GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).build(), - 1 * KB, - getSha256Sum(new ContentInputStream(1 * KB), 1 * KB)); - - if (isQuickTest) { - return; - } - - InputStream cis = new ContentInputStream(1 * KB); - skipStream(cis, 1000); - testGetObject( - "[single upload, offset]", - 1 * KB, - null, - GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).offset(1000L).build(), - 1 * KB - 1000, - getSha256Sum(cis, 1 * KB - 1000)); - - testGetObject( - "[single upload, length]", - 1 * KB, - null, - GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).length(256L).build(), - 256, - getSha256Sum(new ContentInputStream(1 * KB), 256)); - - cis = new ContentInputStream(1 * KB); - skipStream(cis, 1000); - testGetObject( - "[single upload, offset, length]", - 1 * KB, - null, - GetObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .offset(1000L) - .length(24L) - .build(), - 24, - getSha256Sum(cis, 24)); - - cis = new ContentInputStream(1 * KB); - skipStream(cis, 1000); - testGetObject( - "[single upload, offset, length beyond available]", - 1 * KB, - null, - GetObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .offset(1000L) - .length(30L) - .build(), - 24, - getSha256Sum(cis, 24)); - - testGetObject( - "[multi-part upload]", - 6 * MB, - null, - GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).build(), - 6 * MB, - getSha256Sum(new ContentInputStream(6 * MB), 6 * MB)); - - cis = new ContentInputStream(6 * MB); - skipStream(cis, 1000); - testGetObject( - "[multi-part upload, offset, length]", - 6 * MB, - null, - GetObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .offset(1000L) - .length(64 * 1024L) - .build(), - 64 * KB, - getSha256Sum(cis, 64 * 1024)); - - cis = new ContentInputStream(0); - testGetObject( - "[zero sized object]", - 0, - null, - GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).build(), - 0, - getSha256Sum(cis, 0)); - - if (!isSecureEndpoint) { - return; - } - - testGetObject( - "[single upload, SSE-C]", - 1 * KB, - ssec, - GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).ssec(ssec).build(), - 1 * KB, - getSha256Sum(new ContentInputStream(1 * KB), 1 * KB)); - } - - public static void testDownloadObject( - String testTags, int objectSize, ServerSideEncryption sse, DownloadObjectArgs args) - throws Exception { - String methodName = "downloadObject()"; - long startTime = System.currentTimeMillis(); - try { - PutObjectArgs.Builder builder = - PutObjectArgs.builder().bucket(args.bucket()).object(args.object()).stream( - new ContentInputStream(objectSize), objectSize, -1); - if (sse != null) { - builder.sse(sse); - } - client.putObject(builder.build()); - client.downloadObject(args); - Files.delete(Paths.get(args.filename())); - mintSuccessLog(methodName, testTags, startTime); - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); - } - } - - public static void downloadObject() throws Exception { - String methodName = "downloadObject()"; - if (!mintEnv) { - System.out.println(methodName); - } - - String objectName = getRandomName(); - testDownloadObject( - "[single upload]", - 1 * KB, - null, - DownloadObjectArgs.builder() - .bucket(bucketName) - .object(objectName) - .filename(objectName + ".downloaded") - .build()); - - if (isQuickTest) { - return; - } - - String baseName = getRandomName(); - objectName = "path/to/" + baseName; - testDownloadObject( - "[single upload with multiple path segments]", - 1 * KB, - null, - DownloadObjectArgs.builder() - .bucket(bucketName) - .object(objectName) - .filename(baseName + ".downloaded") - .build()); - - if (!isSecureEndpoint) { - return; - } - - objectName = getRandomName(); - testDownloadObject( - "[single upload, SSE-C]", - 1 * KB, - ssec, - DownloadObjectArgs.builder() - .bucket(bucketName) - .object(objectName) - .ssec(ssec) - .filename(objectName + ".downloaded") - .build()); - } - - public static List createObjects(String bucketName, int count, int versions) - throws Exception { - List results = new LinkedList<>(); - for (int i = 0; i < count; i++) { - String objectName = getRandomName(); - results.add( - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1), 1, -1) - .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) - .build())); - } - } - } - - return results; - } - - public static void removeObjects(String bucketName, List results) - throws Exception { - List objects = - results.stream() - .map( - result -> { - return new DeleteObject(result.object(), result.versionId()); - }) - .collect(Collectors.toList()); - for (Result r : - client.removeObjects( - RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build())) { - ignore(r.get()); - } - } - - public static void testListObjects( - String testTags, ListObjectsArgs args, int objCount, int versions) throws Exception { - String methodName = "listObjects()"; - long startTime = System.currentTimeMillis(); - String bucketName = args.bucket(); - List results = null; - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - if (versions > 0) { - client.setBucketVersioning( - SetBucketVersioningArgs.builder() - .bucket(bucketName) - .config(new VersioningConfiguration(VersioningConfiguration.Status.ENABLED, null)) - .build()); - } - - results = createObjects(bucketName, objCount, versions); - - int i = 0; - for (Result r : client.listObjects(args)) { - r.get(); - i++; - } - - if (versions > 0) { - objCount *= versions; - } - - Assertions.assertEquals(i, objCount, "object count; expected=" + objCount + ", got=" + i); - mintSuccessLog(methodName, testTags, startTime); - } finally { - if (results != null) { - removeObjects(bucketName, results); - } - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void listObjects() throws Exception { - if (!mintEnv) { - System.out.println("listObjects()"); - } - - testListObjects("[bucket]", ListObjectsArgs.builder().bucket(getRandomName()).build(), 3, 0); - - testListObjects( - "[bucket, prefix]", - ListObjectsArgs.builder().bucket(getRandomName()).prefix("minio").build(), - 3, - 0); - - testListObjects( - "[bucket, prefix, recursive]", - ListObjectsArgs.builder().bucket(getRandomName()).prefix("minio").recursive(true).build(), - 3, - 0); - - testListObjects( - "[bucket, versions]", - ListObjectsArgs.builder().bucket(getRandomName()).includeVersions(true).build(), - 3, - 2); - - if (isQuickTest) { - return; - } - - testListObjects( - "[empty bucket]", ListObjectsArgs.builder().bucket(getRandomName()).build(), 0, 0); - - testListObjects( - "[bucket, prefix, recursive, 1050 objects]", - ListObjectsArgs.builder().bucket(getRandomName()).prefix("minio").recursive(true).build(), - 1050, - 0); - - testListObjects( - "[bucket, apiVersion1]", - ListObjectsArgs.builder().bucket(getRandomName()).useApiVersion1(true).build(), - 3, - 0); - } - - public static void testRemoveObject( - String testTags, ServerSideEncryption sse, RemoveObjectArgs args) throws Exception { - String methodName = "removeObject()"; - long startTime = System.currentTimeMillis(); - try { - PutObjectArgs.Builder builder = - PutObjectArgs.builder().bucket(args.bucket()).object(args.object()).stream( - new ContentInputStream(1), 1, -1); - if (sse != null) { - builder.sse(sse); - } - client.putObject(builder.build()); - client.removeObject(args); - mintSuccessLog(methodName, testTags, startTime); - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void removeObject() throws Exception { - String methodName = "removeObject()"; - if (!mintEnv) { - System.out.println(methodName); - } - - testRemoveObject( - "[base check]", - null, - RemoveObjectArgs.builder().bucket(bucketName).object(getRandomName()).build()); - testRemoveObject( - "[multiple path segments]", - null, - RemoveObjectArgs.builder().bucket(bucketName).object("path/to/" + getRandomName()).build()); - - if (isQuickTest) { - return; - } - - testRemoveObject( - "[SSE-S3]", - sseS3, - RemoveObjectArgs.builder().bucket(bucketName).object(getRandomName()).build()); - - if (!isSecureEndpoint) { - mintIgnoredLog(methodName, "[SSE-C]", System.currentTimeMillis()); - mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); - return; - } - - testRemoveObject( - "[SSE-C]", - ssec, - RemoveObjectArgs.builder().bucket(bucketName).object(getRandomName()).build()); - - if (sseKms == null) { - mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); - return; - } - - testRemoveObject( - "[SSE-KMS]", - sseKms, - RemoveObjectArgs.builder().bucket(bucketName).object(getRandomName()).build()); - } - - public static void testRemoveObjects(String testTags, List results) - throws Exception { - String methodName = "removeObjects()"; - long startTime = System.currentTimeMillis(); - try { - removeObjects(bucketName, results); - mintSuccessLog(methodName, testTags, startTime); - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } finally { - removeObjects(bucketName, results); - } - } - - public static void removeObjects() throws Exception { - String methodName = "removeObjects()"; - if (!mintEnv) { - System.out.println(methodName); - } - - testRemoveObjects("[basic]", createObjects(bucketName, 3, 0)); - - String testTags = "[3005 objects]"; - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - List results = new LinkedList<>(); - for (int i = 0; i < 3004; i++) { - results.add( - new ObjectWriteResponse(null, bucketName, null, objectName + "-" + i, null, null)); - } - List existingObject = createObjects(bucketName, 1, 0); - results.addAll(existingObject); - testRemoveObjects(testTags, results); - try { - client.statObject( - StatObjectArgs.builder() - .bucket(bucketName) - .object(existingObject.get(0).object()) - .build()); - handleException( - methodName, - testTags, - startTime, - new Exception("object " + existingObject.get(0).object() + " still exist")); - } catch (ErrorResponseException e) { - if (!e.errorResponse().code().equals("NoSuchKey")) { - throw e; - } - } - } - - public static void testGetPresignedUrl(GetPresignedObjectUrlArgs args, String expectedChecksum) - throws Exception { - String urlString = client.getPresignedObjectUrl(args); - byte[] data = readObject(urlString); - String checksum = getSha256Sum(new ByteArrayInputStream(data), data.length); - Assertions.assertEquals( - expectedChecksum, - checksum, - "content checksum differs; expected = " + expectedChecksum + ", got = " + checksum); - } - - public static void testGetPresignedObjectUrlForGet() throws Exception { - String methodName = "getPresignedObjectUrl()"; - String testTags = null; - long startTime = System.currentTimeMillis(); - try { - String expectedChecksum = getSha256Sum(new ContentInputStream(1 * KB), 1 * KB); - String objectName = getRandomName(); - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - - try { - testTags = "[GET]"; - testGetPresignedUrl( - GetPresignedObjectUrlArgs.builder() - .method(Method.GET) - .bucket(bucketName) - .object(objectName) - .build(), - expectedChecksum); - - testTags = "[GET, expiry]"; - testGetPresignedUrl( - GetPresignedObjectUrlArgs.builder() - .method(Method.GET) - .bucket(bucketName) - .object(objectName) - .expiry(1, TimeUnit.DAYS) - .build(), - expectedChecksum); - - testTags = "[GET, expiry, query params]"; - Map queryParams = new HashMap<>(); - queryParams.put("response-content-type", "application/json"); - testGetPresignedUrl( - GetPresignedObjectUrlArgs.builder() - .method(Method.GET) - .bucket(bucketName) - .object(objectName) - .expiry(1, TimeUnit.DAYS) - .extraQueryParams(queryParams) - .build(), - expectedChecksum); - - mintSuccessLog(methodName, testTags, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void testPutPresignedUrl( - String testTags, byte[] data, String expectedChecksum, GetPresignedObjectUrlArgs args) - throws Exception { - String methodName = "getPresignedObjectUrl()"; - long startTime = System.currentTimeMillis(); - try { - String urlString = client.getPresignedObjectUrl(args); - try { - writeObject(urlString, data); - InputStream is = - client.getObject( - GetObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); - data = readAllBytes(is); - String checksum = getSha256Sum(new ByteArrayInputStream(data), data.length); - Assertions.assertEquals( - expectedChecksum, - checksum, - "content checksum differs; expected = " + expectedChecksum + ", got = " + checksum); - mintSuccessLog(methodName, testTags, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void testGetPresignedObjectUrlForPut() throws Exception { - byte[] data = "hello, world".getBytes(StandardCharsets.UTF_8); - String expectedChecksum = getSha256Sum(new ByteArrayInputStream(data), data.length); - String objectName = getRandomName(); - - testPutPresignedUrl( - "[PUT]", - data, - expectedChecksum, - GetPresignedObjectUrlArgs.builder() - .method(Method.PUT) - .bucket(bucketName) - .object(objectName) - .build()); - - testPutPresignedUrl( - "[PUT, expiry]", - data, - expectedChecksum, - GetPresignedObjectUrlArgs.builder() - .method(Method.PUT) - .bucket(bucketName) - .object(objectName) - .expiry(1, TimeUnit.DAYS) - .build()); - } - - public static void getPresignedObjectUrl() throws Exception { - if (!mintEnv) { - System.out.println("getPresignedObjectUrl()"); - } - - testGetPresignedObjectUrlForGet(); - testGetPresignedObjectUrlForPut(); - } - - public static void getPresignedPostFormData() throws Exception { - String methodName = "getPresignedPostFormData()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - try { - String objectName = getRandomName(); - - PostPolicy policy = new PostPolicy(bucketName, ZonedDateTime.now().plusDays(7)); - policy.addEqualsCondition("key", objectName); - policy.addEqualsCondition("content-type", "image/png"); - policy.addContentLengthRangeCondition(1 * MB, 4 * MB); - Map formData = client.getPresignedPostFormData(policy); - - MultipartBody.Builder multipartBuilder = new MultipartBody.Builder(); - multipartBuilder.setType(MultipartBody.FORM); - for (Map.Entry entry : formData.entrySet()) { - multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue()); - } - multipartBuilder.addFormDataPart("key", objectName); - multipartBuilder.addFormDataPart("Content-Type", "image/png"); - multipartBuilder.addFormDataPart( - "file", - objectName, - RequestBody.create(readAllBytes(new ContentInputStream(1 * MB)), null)); - - Request.Builder requestBuilder = new Request.Builder(); - String urlString = - client.getPresignedObjectUrl( - GetPresignedObjectUrlArgs.builder() - .method(Method.GET) - .bucket(bucketName) - .object("x") - .build()); - urlString = urlString.split("\\?")[0]; // Remove query parameters. - // remove last two characters to get clean url string of bucket. - urlString = urlString.substring(0, urlString.length() - 2); - Request request = requestBuilder.url(urlString).post(multipartBuilder.build()).build(); - OkHttpClient transport = newHttpClient(); - Response response = transport.newCall(request).execute(); - Assertions.assertNotNull(response, "no response from server"); - - try { - if (!response.isSuccessful()) { - String errorXml = response.body().string(); - throw new Exception( - "failed to upload object. Response: " + response + ", Error: " + errorXml); - } - } finally { - response.close(); - } - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void testCopyObject( - String testTags, ServerSideEncryption sse, CopyObjectArgs args, boolean negativeCase) - throws Exception { - String methodName = "copyObject()"; - long startTime = System.currentTimeMillis(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(args.source().bucket()).build()); - try { - PutObjectArgs.Builder builder = - PutObjectArgs.builder().bucket(args.source().bucket()).object(args.source().object()) - .stream(new ContentInputStream(1 * KB), 1 * KB, -1); - if (sse != null) { - builder.sse(sse); - } - client.putObject(builder.build()); - - if (negativeCase) { - try { - client.copyObject(args); - } catch (ErrorResponseException e) { - if (!e.errorResponse().code().equals("PreconditionFailed")) { - throw e; - } - } - } else { - client.copyObject(args); - - ServerSideEncryptionCustomerKey ssec = null; - if (sse instanceof ServerSideEncryptionCustomerKey) { - ssec = (ServerSideEncryptionCustomerKey) sse; - } - client.statObject( - StatObjectArgs.builder() - .bucket(args.bucket()) - .object(args.object()) - .ssec(ssec) - .build()); - } - mintSuccessLog(methodName, testTags, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder() - .bucket(args.source().bucket()) - .object(args.source().object()) - .build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(args.source().bucket()).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void testCopyObjectMatchETag() throws Exception { - String methodName = "copyObject()"; - String testTags = "[match etag]"; - long startTime = System.currentTimeMillis(); - String srcBucketName = getRandomName(); - String srcObjectName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(srcBucketName).build()); - try { - ObjectWriteResponse result = - client.putObject( - PutObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - - client.copyObject( - CopyObjectArgs.builder() - .bucket(bucketName) - .object(srcObjectName + "-copy") - .source( - CopySource.builder() - .bucket(srcBucketName) - .object(srcObjectName) - .matchETag(result.etag()) - .build()) - .build()); - - client.statObject( - StatObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); - - mintSuccessLog(methodName, testTags, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(srcBucketName).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void testCopyObjectMetadataReplace() throws Exception { - String methodName = "copyObject()"; - String testTags = "[metadata replace]"; - long startTime = System.currentTimeMillis(); - String srcBucketName = getRandomName(); - String srcObjectName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(srcBucketName).build()); - try { - client.putObject( - PutObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - - Map headers = new HashMap<>(); - headers.put("Content-Type", customContentType); - headers.put("X-Amz-Meta-My-Project", "Project One"); - client.copyObject( - CopyObjectArgs.builder() - .bucket(bucketName) - .object(srcObjectName + "-copy") - .source(CopySource.builder().bucket(srcBucketName).object(srcObjectName).build()) - .headers(headers) - .metadataDirective(Directive.REPLACE) - .build()); - - StatObjectResponse stat = - client.statObject( - StatObjectArgs.builder() - .bucket(bucketName) - .object(srcObjectName + "-copy") - .build()); - Assertions.assertEquals( - customContentType, - stat.contentType(), - "content type differs. expected: " - + customContentType - + ", got: " - + stat.contentType()); - mintSuccessLog(methodName, testTags, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(srcBucketName).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void testCopyObjectEmptyMetadataReplace() throws Exception { - String methodName = "copyObject()"; - String testTags = "[empty metadata replace]"; - long startTime = System.currentTimeMillis(); - String srcBucketName = getRandomName(); - String srcObjectName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(srcBucketName).build()); - try { - Map headers = new HashMap<>(); - headers.put("Content-Type", customContentType); - headers.put("X-Amz-Meta-My-Project", "Project One"); - client.putObject( - PutObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .headers(headers) - .build()); - - client.copyObject( - CopyObjectArgs.builder() - .bucket(bucketName) - .object(srcObjectName + "-copy") - .source(CopySource.builder().bucket(srcBucketName).object(srcObjectName).build()) - .metadataDirective(Directive.REPLACE) - .build()); - - StatObjectResponse stat = - client.statObject( - StatObjectArgs.builder() - .bucket(bucketName) - .object(srcObjectName + "-copy") - .build()); - Assertions.assertFalse( - stat.userMetadata().containsKey("My-Project"), - "expected user metadata to be removed in new object"); - mintSuccessLog(methodName, testTags, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(srcBucketName).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void copyObject() throws Exception { - String methodName = "copyObject()"; - if (!mintEnv) { - System.out.println(methodName); - } - - String objectName = getRandomName(); - testCopyObject( - "[basic check]", - null, - CopyObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .source(CopySource.builder().bucket(getRandomName()).object(objectName).build()) - .build(), - false); - - if (isQuickTest) { - return; - } - - testCopyObject( - "[negative match etag]", - null, - CopyObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .source( - CopySource.builder() - .bucket(getRandomName()) - .object(getRandomName()) - .matchETag("invalid-etag") - .build()) - .build(), - true); - - testCopyObjectMatchETag(); - - testCopyObject( - "[not match etag]", - null, - CopyObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .source( - CopySource.builder() - .bucket(getRandomName()) - .object(getRandomName()) - .notMatchETag("not-etag-of-source-object") - .build()) - .build(), - false); - - testCopyObject( - "[modified since]", - null, - CopyObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .source( - CopySource.builder() - .bucket(getRandomName()) - .object(getRandomName()) - .modifiedSince(ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)) - .build()) - .build(), - false); - - testCopyObject( - "[negative unmodified since]", - null, - CopyObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .source( - CopySource.builder() - .bucket(getRandomName()) - .object(getRandomName()) - .unmodifiedSince(ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)) - .build()) - .build(), - true); - - testCopyObjectMetadataReplace(); - testCopyObjectEmptyMetadataReplace(); - - testCopyObject( - "[SSE-S3]", - sseS3, - CopyObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sse(sseS3) - .source(CopySource.builder().bucket(getRandomName()).object(getRandomName()).build()) - .build(), - false); - - if (!isSecureEndpoint) { - mintIgnoredLog(methodName, "[SSE-C]", System.currentTimeMillis()); - mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); - return; - } - - testCopyObject( - "[SSE-C]", - ssec, - CopyObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sse(ssec) - .source( - CopySource.builder() - .bucket(getRandomName()) - .object(getRandomName()) - .ssec(ssec) - .build()) - .build(), - false); - - if (sseKms == null) { - mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); - return; - } - - testCopyObject( - "[SSE-KMS]", - sseKms, - CopyObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sse(sseKms) - .source(CopySource.builder().bucket(getRandomName()).object(getRandomName()).build()) - .build(), - false); - } - - public static void testComposeObject(String testTags, ComposeObjectArgs args) throws Exception { - String methodName = "composeObject()"; - long startTime = System.currentTimeMillis(); - try { - client.composeObject(args); - client.removeObject( - RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); - mintSuccessLog(methodName, testTags, startTime); - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static List createComposeSourceList(ComposeSource... sources) { - return Arrays.asList(sources); - } - - public static void composeObjectTests(String object1Mb, String object6Mb, String object6MbSsec) - throws Exception { - testComposeObject( - "[single source]", - ComposeObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sources( - createComposeSourceList( - ComposeSource.builder().bucket(bucketName).object(object1Mb).build())) - .build()); - - testComposeObject( - "[single source with offset]", - ComposeObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sources( - createComposeSourceList( - ComposeSource.builder() - .bucket(bucketName) - .object(object1Mb) - .offset(2L * KB) - .build())) - .build()); - - testComposeObject( - "[single source with offset and length]", - ComposeObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sources( - createComposeSourceList( - ComposeSource.builder() - .bucket(bucketName) - .object(object1Mb) - .offset(2L * KB) - .length(5L * KB) - .build())) - .build()); - - testComposeObject( - "[single multipart source]", - ComposeObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sources( - createComposeSourceList( - ComposeSource.builder().bucket(bucketName).object(object6Mb).build())) - .build()); - - testComposeObject( - "[two multipart source]", - ComposeObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sources( - createComposeSourceList( - ComposeSource.builder().bucket(bucketName).object(object6Mb).build(), - ComposeSource.builder().bucket(bucketName).object(object6Mb).build())) - .build()); - - testComposeObject( - "[two multipart sources with offset and length]", - ComposeObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sources( - createComposeSourceList( - ComposeSource.builder() - .bucket(bucketName) - .object(object6Mb) - .offset(10L) - .length(6291436L) - .build(), - ComposeSource.builder().bucket(bucketName).object(object6Mb).build())) - .build()); - - if (isQuickTest) { - return; - } - - if (!isSecureEndpoint) { - return; - } - - testComposeObject( - "[two SSE-C multipart sources]", - ComposeObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sse(ssec) - .sources( - createComposeSourceList( - ComposeSource.builder() - .bucket(bucketName) - .object(object6MbSsec) - .ssec(ssec) - .build(), - ComposeSource.builder() - .bucket(bucketName) - .object(object6MbSsec) - .ssec(ssec) - .build())) - .build()); - - testComposeObject( - "[two multipart sources with one SSE-C]", - ComposeObjectArgs.builder() - .bucket(bucketName) - .object(getRandomName()) - .sources( - createComposeSourceList( - ComposeSource.builder() - .bucket(bucketName) - .object(object6MbSsec) - .ssec(ssec) - .build(), - ComposeSource.builder().bucket(bucketName).object(object6Mb).build())) - .build()); - } - - public static void composeObject() throws Exception { - String methodName = "composeObject()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - List createdObjects = new LinkedList<>(); - - try { - String object1Mb = null; - String object6Mb = null; - String object6MbSsec = null; - try { - ObjectWriteResponse response; - response = - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(1 * MB), 1 * MB, -1) - .build()); - createdObjects.add(response); - object1Mb = response.object(); - - response = - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(6 * MB), 6 * MB, -1) - .build()); - createdObjects.add(response); - object6Mb = response.object(); - - if (isSecureEndpoint) { - response = - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( - new ContentInputStream(6 * MB), 6 * MB, -1) - .sse(ssec) - .build()); - createdObjects.add(response); - object6MbSsec = response.object(); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - - composeObjectTests(object1Mb, object6Mb, object6MbSsec); - } finally { - removeObjects(bucketName, createdObjects); - } - } - - public static void checkObjectLegalHold(String bucketName, String objectName, boolean enableCheck) - throws Exception { - if (enableCheck) { - client.enableObjectLegalHold( - EnableObjectLegalHoldArgs.builder().bucket(bucketName).object(objectName).build()); - } else { - client.disableObjectLegalHold( - DisableObjectLegalHoldArgs.builder().bucket(bucketName).object(objectName).build()); - } - - boolean result = - client.isObjectLegalHoldEnabled( - IsObjectLegalHoldEnabledArgs.builder().bucket(bucketName).object(objectName).build()); - Assertions.assertEquals( - result, enableCheck, "object legal hold: expected: " + enableCheck + ", got: " + result); - } - - public static void enableObjectLegalHold() throws Exception { - if (bucketNameWithLock == null) return; - - String methodName = "enableObjectLegalHold()"; - if (!mintEnv) { - System.out.println(methodName); - } - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - ObjectWriteResponse objectInfo = null; - try { - try { - objectInfo = - client.putObject( - PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - - checkObjectLegalHold(bucketNameWithLock, objectName, true); - client.disableObjectLegalHold( - DisableObjectLegalHoldArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .build()); - mintSuccessLog(methodName, null, startTime); - } finally { - if (objectInfo != null) { - client.removeObject( - RemoveObjectArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .versionId(objectInfo.versionId()) - .build()); - } - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void disableObjectLegalHold() throws Exception { - if (bucketNameWithLock == null) return; - - String methodName = "disableObjectLegalHold()"; - if (!mintEnv) { - System.out.println(methodName); - } - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - ObjectWriteResponse objectInfo = null; - try { - try { - objectInfo = - client.putObject( - PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - - checkObjectLegalHold(bucketNameWithLock, objectName, false); - client.enableObjectLegalHold( - EnableObjectLegalHoldArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .build()); - checkObjectLegalHold(bucketNameWithLock, objectName, false); - mintSuccessLog(methodName, null, startTime); - } finally { - if (objectInfo != null) { - client.removeObject( - RemoveObjectArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .versionId(objectInfo.versionId()) - .build()); - } - } - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void isObjectLegalHoldEnabled() throws Exception { - if (bucketNameWithLock == null) return; - - String methodName = "isObjectLegalHoldEnabled()"; - if (!mintEnv) { - System.out.println(methodName); - } - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - ObjectWriteResponse objectInfo = null; - try { - try { - objectInfo = - client.putObject( - PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - - boolean result = - client.isObjectLegalHoldEnabled( - IsObjectLegalHoldEnabledArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .build()); - Assertions.assertFalse(result, "object legal hold: expected: false, got: " + result); - checkObjectLegalHold(bucketNameWithLock, objectName, true); - checkObjectLegalHold(bucketNameWithLock, objectName, false); - mintSuccessLog(methodName, null, startTime); - } finally { - if (objectInfo != null) { - client.removeObject( - RemoveObjectArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .versionId(objectInfo.versionId()) - .build()); - } - } - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void setObjectLockConfiguration() throws Exception { - String methodName = "setObjectLockConfiguration()"; - String testTags = "[COMPLIANCE, 10 days]"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); - try { - ObjectLockConfiguration config = - new ObjectLockConfiguration(RetentionMode.COMPLIANCE, new RetentionDurationDays(10)); - client.setObjectLockConfiguration( - SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(config).build()); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - mintSuccessLog(methodName, testTags, startTime); - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void testGetObjectLockConfiguration( - String bucketName, RetentionMode mode, RetentionDuration duration) throws Exception { - ObjectLockConfiguration expectedConfig = new ObjectLockConfiguration(mode, duration); - client.setObjectLockConfiguration( - SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(expectedConfig).build()); - ObjectLockConfiguration config = - client.getObjectLockConfiguration( - GetObjectLockConfigurationArgs.builder().bucket(bucketName).build()); - Assertions.assertEquals( - config.mode(), - expectedConfig.mode(), - "retention mode: expected: " + expectedConfig.mode() + ", got: " + config.mode()); - Assertions.assertFalse( - config.duration().unit() != expectedConfig.duration().unit() - || config.duration().duration() != expectedConfig.duration().duration(), - "retention duration: " + expectedConfig.duration() + ", got: " + config.duration()); - } - - public static void getObjectLockConfiguration() throws Exception { - String methodName = "getObjectLockConfiguration()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); - try { - testGetObjectLockConfiguration( - bucketName, RetentionMode.COMPLIANCE, new RetentionDurationDays(10)); - testGetObjectLockConfiguration( - bucketName, RetentionMode.GOVERNANCE, new RetentionDurationYears(1)); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void deleteObjectLockConfiguration() throws Exception { - String methodName = "deleteObjectLockConfiguration()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); - try { - client.deleteObjectLockConfiguration( - DeleteObjectLockConfigurationArgs.builder().bucket(bucketName).build()); - ObjectLockConfiguration config = - new ObjectLockConfiguration(RetentionMode.COMPLIANCE, new RetentionDurationDays(10)); - client.setObjectLockConfiguration( - SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(config).build()); - client.deleteObjectLockConfiguration( - DeleteObjectLockConfigurationArgs.builder().bucket(bucketName).build()); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void setObjectRetention() throws Exception { - if (bucketNameWithLock == null) return; - - String methodName = "setObjectRetention()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - ObjectWriteResponse objectInfo = null; - try { - try { - objectInfo = - client.putObject( - PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - - client.setObjectRetention( - SetObjectRetentionArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .config( - new Retention( - RetentionMode.GOVERNANCE, ZonedDateTime.now(Time.UTC).plusDays(1))) - .build()); - - client.setObjectRetention( - SetObjectRetentionArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .config(new Retention()) - .bypassGovernanceMode(true) - .build()); - } finally { - if (objectInfo != null) { - client.removeObject( - RemoveObjectArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .versionId(objectInfo.versionId()) - .build()); - } - } - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void testGetObjectRetention(SetObjectRetentionArgs args) throws Exception { - client.setObjectRetention(args); - Retention config = - client.getObjectRetention( - GetObjectRetentionArgs.builder().bucket(args.bucket()).object(args.object()).build()); - - if (args.config().mode() == null) { - Assertions.assertFalse( - config != null && config.mode() != null, - "retention mode: expected: , got: " + config.mode()); - } else { - Assertions.assertEquals( - args.config().mode(), - config.mode(), - "retention mode: expected: " + args.config().mode() + ", got: " + config.mode()); - } - - ZonedDateTime expectedDate = args.config().retainUntilDate(); - ZonedDateTime date = (config == null) ? null : config.retainUntilDate(); - - if (expectedDate == null) { - Assertions.assertNull(date, "retention retain-until-date: expected: , got: " + date); - } else { - Assertions.assertEquals( - date.withNano(0), - expectedDate.withNano(0), - "retention retain-until-date: expected: " - + expectedDate.withNano(0) - + ", got: " - + date.withNano(0)); - } - } - - public static void getObjectRetention() throws Exception { - if (bucketNameWithLock == null) return; - - String methodName = "getObjectRetention()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - ObjectWriteResponse objectInfo = null; - try { - try { - objectInfo = - client.putObject( - PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - - testGetObjectRetention( - SetObjectRetentionArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .config( - new Retention( - RetentionMode.GOVERNANCE, ZonedDateTime.now(Time.UTC).plusDays(3))) - .build()); - - // Check shortening retention until period - testGetObjectRetention( - SetObjectRetentionArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .config( - new Retention( - RetentionMode.GOVERNANCE, ZonedDateTime.now(Time.UTC).plusDays(1))) - .bypassGovernanceMode(true) - .build()); - - // Check empty retention. - // Enable below test when minio server release has a fix. - // testGetObjectRetention( - // SetObjectRetentionArgs.builder() - // .bucket(bucketNameWithLock) - // .object(objectName) - // .config(new Retention()) - // .bypassGovernanceMode(true) - // .build()); - } finally { - if (objectInfo != null) { - client.removeObject( - RemoveObjectArgs.builder() - .bucket(bucketNameWithLock) - .object(objectName) - .versionId(objectInfo.versionId()) - .bypassGovernanceMode(true) - .build()); - } - } - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getBucketPolicy() throws Exception { - String methodName = "getBucketPolicy()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - String config = - client.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build()); - Assertions.assertTrue(config.isEmpty(), "policy: expected: \"\", got: " + config); - String policy = - "{'Version':'2012-10-17','Statement':[{'Action':['s3:GetObject'],'Effect':'Allow'," - + "'Principal':{'AWS':['*']},'Resource':['arn:aws:s3:::" - + bucketName - + "/myobject*'],'Sid':''}]}"; - policy = policy.replaceAll("'", "\""); - client.setBucketPolicy( - SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build()); - client.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void setBucketPolicy() throws Exception { - String methodName = "setBucketPolicy()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - String policy = - "{'Version':'2012-10-17','Statement':[{'Action':['s3:GetObject'],'Effect':'Allow'," - + "'Principal':{'AWS':['*']},'Resource':['arn:aws:s3:::" - + bucketName - + "/myobject*'],'Sid':''}]}"; - policy = policy.replaceAll("'", "\""); - client.setBucketPolicy( - SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void deleteBucketPolicy() throws Exception { - String methodName = "deleteBucketPolicy()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - client.deleteBucketPolicy(DeleteBucketPolicyArgs.builder().bucket(bucketName).build()); - - String policy = - "{'Version':'2012-10-17','Statement':[{'Action':['s3:GetObject'],'Effect':'Allow'," - + "'Principal':{'AWS':['*']},'Resource':['arn:aws:s3:::" - + bucketName - + "/myobject*'],'Sid':''}]}"; - policy = policy.replaceAll("'", "\""); - client.setBucketPolicy( - SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build()); - client.deleteBucketPolicy(DeleteBucketPolicyArgs.builder().bucket(bucketName).build()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void testSetBucketLifecycle(String bucketName, LifecycleRule... rules) - throws Exception { - LifecycleConfiguration config = new LifecycleConfiguration(Arrays.asList(rules)); - client.setBucketLifecycle( - SetBucketLifecycleArgs.builder().bucket(bucketName).config(config).build()); - } - - public static void setBucketLifecycle() throws Exception { - String methodName = "setBucketLifecycle()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - testSetBucketLifecycle( - bucketName, - new LifecycleRule( - Status.ENABLED, - null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter("logs/"), - "rule2", - null, - null, - null)); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void deleteBucketLifecycle() throws Exception { - String methodName = "deleteBucketLifecycle()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - client.deleteBucketLifecycle( - DeleteBucketLifecycleArgs.builder().bucket(bucketName).build()); - testSetBucketLifecycle( - bucketName, - new LifecycleRule( - Status.ENABLED, - null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter("logs/"), - "rule2", - null, - null, - null)); - client.deleteBucketLifecycle( - DeleteBucketLifecycleArgs.builder().bucket(bucketName).build()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getBucketLifecycle() throws Exception { - String methodName = "getBucketLifecycle()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - LifecycleConfiguration config = - client.getBucketLifecycle(GetBucketLifecycleArgs.builder().bucket(bucketName).build()); - Assertions.assertNull(config, "config: expected: , got: "); - testSetBucketLifecycle( - bucketName, - new LifecycleRule( - Status.ENABLED, - null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter("logs/"), - "rule2", - null, - null, - null)); - config = - client.getBucketLifecycle(GetBucketLifecycleArgs.builder().bucket(bucketName).build()); - Assertions.assertNotNull(config, "config: expected: , got: "); - List rules = config.rules(); - Assertions.assertEquals( - 1, - config.rules().size(), - "config.rules().size(): expected: 1, got: " + config.rules().size()); - LifecycleRule rule = rules.get(0); - Assertions.assertEquals( - rule.status(), - Status.ENABLED, - "rule.status(): expected: " + Status.ENABLED + ", got: " + rule.status()); - Assertions.assertNotNull( - rule.expiration(), "rule.expiration(): expected: , got: "); - Assertions.assertEquals( - rule.expiration().days(), - Integer.valueOf(365), - "rule.expiration().days(): expected: 365, got: " + rule.expiration().days()); - Assertions.assertNotNull(rule.filter(), "rule.filter(): expected: , got: "); - Assertions.assertEquals( - "logs/", - rule.filter().prefix(), - "rule.filter().prefix(): expected: logs/, got: " + rule.filter().prefix()); - Assertions.assertEquals( - "rule2", rule.id(), "rule.id(): expected: rule2, got: " + rule.id()); - - testSetBucketLifecycle( - bucketName, - new LifecycleRule( - Status.ENABLED, - null, - new Expiration((ZonedDateTime) null, 365, null), - new RuleFilter(""), - null, - null, - null, - null)); - config = - client.getBucketLifecycle(GetBucketLifecycleArgs.builder().bucket(bucketName).build()); - Assertions.assertNotNull(config, "config: expected: , got: "); - Assertions.assertEquals( - config.rules().size(), - 1, - "config.rules().size(): expected: 1, got: " + config.rules().size()); - Assertions.assertNotNull( - config.rules().get(0).filter(), "rule.filter(): expected: , got: "); - Assertions.assertEquals( - "", - config.rules().get(0).filter().prefix(), - "rule.filter().prefix(): expected: , got: " - + config.rules().get(0).filter().prefix()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void setBucketNotification() throws Exception { - String methodName = "setBucketNotification()"; - long startTime = System.currentTimeMillis(); - if (sqsArn == null) { - mintIgnoredLog(methodName, null, startTime); - return; - } - - if (!mintEnv) { - System.out.println(methodName); - } - - try { - 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); - - client.setBucketNotification( - SetBucketNotificationArgs.builder().bucket(bucketName).config(config).build()); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getBucketNotification() throws Exception { - String methodName = "getBucketNotification()"; - long startTime = System.currentTimeMillis(); - if (sqsArn == null) { - mintIgnoredLog(methodName, null, startTime); - return; - } - - if (!mintEnv) { - System.out.println(methodName); - } - - try { - 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); - - client.setBucketNotification( - SetBucketNotificationArgs.builder().bucket(bucketName).config(expectedConfig).build()); - - NotificationConfiguration config = - 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) { - System.out.println( - "config: expected: " + Xml.marshal(expectedConfig) + ", got: " + Xml.marshal(config)); - } - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void deleteBucketNotification() throws Exception { - String methodName = "deleteBucketNotification()"; - long startTime = System.currentTimeMillis(); - if (sqsArn == null) { - mintIgnoredLog(methodName, null, startTime); - return; - } - - if (!mintEnv) { - System.out.println(methodName); - } - - try { - 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); - - client.setBucketNotification( - SetBucketNotificationArgs.builder().bucket(bucketName).config(config).build()); - - client.deleteBucketNotification( - DeleteBucketNotificationArgs.builder().bucket(bucketName).build()); - - config = - client.getBucketNotification( - GetBucketNotificationArgs.builder().bucket(bucketName).build()); - if (config.queueConfigurationList().size() != 0) { - System.out.println("config: expected: , got: " + Xml.marshal(config)); - } - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void listenBucketNotification() throws Exception { - String methodName = "listenBucketNotification()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String file = createFile1Kb(); - String bucketName = getRandomName(); - CloseableIterator> ci = null; - String mintArgs = - "prefix=prefix, suffix=suffix, events={\"s3:ObjectCreated:*\", \"s3:ObjectAccessed:*\"}"; - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build()); - - String[] events = {"s3:ObjectCreated:*", "s3:ObjectAccessed:*"}; - ci = - client.listenBucketNotification( - ListenBucketNotificationArgs.builder() - .bucket(bucketName) - .prefix("prefix") - .suffix("suffix") - .events(events) - .build()); - - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object("prefix-random-suffix").stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - - while (ci.hasNext()) { - NotificationRecords records = ci.next().get(); - if (records.events().size() == 0) { - continue; - } - - boolean found = false; - for (Event event : records.events()) { - if (event.objectName().equals("prefix-random-suffix")) { - found = true; - break; - } - } - - if (found) { - break; - } - } - - mintSuccessLog(methodName, mintArgs, startTime); - } catch (Exception e) { - handleException(methodName, mintArgs, startTime, e); - } finally { - if (ci != null) { - ci.close(); - } - - Files.delete(Paths.get(file)); - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object("prefix-random-suffix").build()); - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } - - public static void selectObjectContent() throws Exception { - String methodName = "selectObjectContent()"; - String sqlExpression = "select * from S3Object"; - String testArgs = "[sqlExpression: " + sqlExpression + ", requestProgress: true]"; - - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - SelectResponseStream responseStream = null; - try { - String expectedResult = - "1997,Ford,E350,\"ac, abs, moon\",3000.00\n" - + "1999,Chevy,\"Venture \"\"Extended Edition\"\"\",,4900.00\n" - + "1999,Chevy,\"Venture \"\"Extended Edition, Very Large\"\"\",,5000.00\n" - + "1996,Jeep,Grand Cherokee,\"MUST SELL!\n" - + "air, moon roof, loaded\",4799.00\n"; - byte[] data = - ("Year,Make,Model,Description,Price\n" + expectedResult).getBytes(StandardCharsets.UTF_8); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - bais, data.length, -1) - .build()); - - InputSerialization is = - new InputSerialization(null, false, null, null, FileHeaderInfo.USE, null, null, null); - OutputSerialization os = - new OutputSerialization(null, null, null, QuoteFields.ASNEEDED, null); - - responseStream = - client.selectObjectContent( - SelectObjectContentArgs.builder() - .bucket(bucketName) - .object(objectName) - .sqlExpression(sqlExpression) - .inputSerialization(is) - .outputSerialization(os) - .requestProgress(true) - .build()); - - String result = new String(readAllBytes(responseStream), StandardCharsets.UTF_8); - Assertions.assertEquals( - result, - expectedResult, - "result mismatch; expected: " + expectedResult + ", got: " + result); - - Stats stats = responseStream.stats(); - Assertions.assertNotNull(stats, "stats is null"); - Assertions.assertEquals( - stats.bytesScanned(), - 256, - "stats.bytesScanned mismatch; expected: 258, got: " + stats.bytesScanned()); - Assertions.assertEquals( - stats.bytesProcessed(), - 256, - "stats.bytesProcessed mismatch; expected: 258, got: " + stats.bytesProcessed()); - Assertions.assertEquals( - stats.bytesReturned(), - 222, - "stats.bytesReturned mismatch; expected: 222, got: " + stats.bytesReturned()); - mintSuccessLog(methodName, testArgs, startTime); - } catch (Exception e) { - handleException(methodName, testArgs, startTime, e); - } finally { - if (responseStream != null) { - responseStream.close(); - } - client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - } - } - - public static void setBucketEncryption() throws Exception { - String methodName = "setBucketEncryption()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - client.setBucketEncryption( - SetBucketEncryptionArgs.builder() - .bucket(bucketName) - .config(SseConfiguration.newConfigWithSseS3Rule()) - .build()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getBucketEncryption() throws Exception { - String methodName = "getBucketEncryption()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - SseConfiguration config = - client.getBucketEncryption( - GetBucketEncryptionArgs.builder().bucket(bucketName).build()); - Assertions.assertNull(config.rule(), "rule: expected: , got: "); - client.setBucketEncryption( - SetBucketEncryptionArgs.builder() - .bucket(bucketName) - .config(SseConfiguration.newConfigWithSseS3Rule()) - .build()); - config = - client.getBucketEncryption( - GetBucketEncryptionArgs.builder().bucket(bucketName).build()); - Assertions.assertNotNull(config.rule(), "rule: expected: , got: "); - Assertions.assertEquals( - config.rule().sseAlgorithm(), - SseAlgorithm.AES256, - "sse algorithm: expected: " - + SseAlgorithm.AES256 - + ", got: " - + config.rule().sseAlgorithm()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void deleteBucketEncryption() throws Exception { - String methodName = "deleteBucketEncryption()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - client.deleteBucketEncryption( - DeleteBucketEncryptionArgs.builder().bucket(bucketName).build()); - - client.setBucketEncryption( - SetBucketEncryptionArgs.builder() - .bucket(bucketName) - .config(SseConfiguration.newConfigWithSseS3Rule()) - .build()); - client.deleteBucketEncryption( - DeleteBucketEncryptionArgs.builder().bucket(bucketName).build()); - SseConfiguration config = - client.getBucketEncryption( - GetBucketEncryptionArgs.builder().bucket(bucketName).build()); - Assertions.assertNull(config.rule(), "rule: expected: , got: "); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void testBucketCors(String methodName, boolean getTest, boolean deleteTest) - throws Exception { - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - CORSConfiguration expectedConfig = - new CORSConfiguration( - Arrays.asList( - new CORSConfiguration.CORSRule[] { - // Rule 1 - new CORSConfiguration.CORSRule( - Arrays.asList(new String[] {"*"}), // Allowed headers - Arrays.asList(new String[] {"PUT", "POST", "DELETE"}), // Allowed methods - Arrays.asList(new String[] {"http://www.example.com"}), // Allowed origins - Arrays.asList( - new String[] {"x-amz-server-side-encryption"}), // Expose headers - null, // ID - 3000), // Maximum age seconds - // Rule 2 - new CORSConfiguration.CORSRule( - null, // Allowed headers - Arrays.asList(new String[] {"GET"}), // Allowed methods - Arrays.asList(new String[] {"*"}), // Allowed origins - null, // Expose headers - null, // ID - null // Maximum age seconds - ) - })); - client.setBucketCors( - SetBucketCorsArgs.builder().bucket(bucketName).config(expectedConfig).build()); - if (getTest) { - CORSConfiguration config = - client.getBucketCors(GetBucketCorsArgs.builder().bucket(bucketName).build()); - Assertions.assertEquals( - expectedConfig, config, "cors: expected: " + expectedConfig + ", got: " + config); - } - if (deleteTest) { - client.deleteBucketCors(DeleteBucketCorsArgs.builder().bucket(bucketName).build()); - } - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void setBucketCors() throws Exception { - testBucketCors("setBucketCors()", false, false); - } - - public static void getBucketCors() throws Exception { - testBucketCors("getBucketCors()", true, false); - } - - public static void deleteBucketCors() throws Exception { - testBucketCors("deleteBucketCors()", false, true); - } - - public static void setBucketTags() throws Exception { - String methodName = "setBucketTags()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - Map map = new HashMap<>(); - map.put("Project", "Project One"); - map.put("User", "jsmith"); - client.setBucketTags(SetBucketTagsArgs.builder().bucket(bucketName).tags(map).build()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getBucketTags() throws Exception { - String methodName = "getBucketTags()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - Map map = new HashMap<>(); - Tags tags = client.getBucketTags(GetBucketTagsArgs.builder().bucket(bucketName).build()); - Assertions.assertEquals(map, tags.get(), "tags: expected: " + map + ", got: " + tags.get()); - - map.put("Project", "Project One"); - map.put("User", "jsmith"); - client.setBucketTags(SetBucketTagsArgs.builder().bucket(bucketName).tags(map).build()); - tags = client.getBucketTags(GetBucketTagsArgs.builder().bucket(bucketName).build()); - Assertions.assertEquals(map, tags.get(), "tags: expected: " + map + ", got: " + tags.get()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void deleteBucketTags() throws Exception { - String methodName = "deleteBucketTags()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String bucketName = getRandomName(); - try { - client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); - try { - client.deleteBucketTags(DeleteBucketTagsArgs.builder().bucket(bucketName).build()); - - Map map = new HashMap<>(); - map.put("Project", "Project One"); - map.put("User", "jsmith"); - client.setBucketTags(SetBucketTagsArgs.builder().bucket(bucketName).tags(map).build()); - client.deleteBucketTags(DeleteBucketTagsArgs.builder().bucket(bucketName).build()); - Tags tags = client.getBucketTags(GetBucketTagsArgs.builder().bucket(bucketName).build()); - Assertions.assertTrue( - tags.get().isEmpty(), "tags: expected: " + ", got: " + tags.get()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void setObjectTags() throws Exception { - String methodName = "setObjectTags()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - try { - try { - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - Map map = new HashMap<>(); - map.put("Project", "Project One"); - map.put("User", "jsmith"); - client.setObjectTags( - SetObjectTagsArgs.builder().bucket(bucketName).object(objectName).tags(map).build()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getObjectTags() throws Exception { - String methodName = "getObjectTags()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - try { - try { - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - Map map = new HashMap<>(); - Tags tags = - client.getObjectTags( - GetObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); - Assertions.assertEquals(map, tags.get(), "tags: expected: " + map + ", got: " + tags.get()); - - map.put("Project", "Project One"); - map.put("User", "jsmith"); - client.setObjectTags( - SetObjectTagsArgs.builder().bucket(bucketName).object(objectName).tags(map).build()); - tags = - client.getObjectTags( - GetObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); - Assertions.assertEquals(map, tags.get(), "tags: expected: " + map + ", got: " + tags.get()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void deleteObjectTags() throws Exception { - String methodName = "deleteObjectTags()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - try { - try { - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - client.deleteObjectTags( - DeleteObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); - - Map map = new HashMap<>(); - map.put("Project", "Project One"); - map.put("User", "jsmith"); - client.setObjectTags( - SetObjectTagsArgs.builder().bucket(bucketName).object(objectName).tags(map).build()); - client.deleteObjectTags( - DeleteObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); - Tags tags = - client.getObjectTags( - GetObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); - Assertions.assertTrue(tags.get().isEmpty(), "tags: expected: , got: " + tags.get()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getObjectAcl() throws Exception { - String methodName = "getObjectAcl()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - try { - try { - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - AccessControlPolicy policy = - client.getObjectAcl( - GetObjectAclArgs.builder().bucket(bucketName).object(objectName).build()); - Assertions.assertEquals( - policy.accessControlList().grants().get(0).grantee().type(), - GranteeType.CANONICAL_USER, - "granteeType: expected: " - + GranteeType.CANONICAL_USER - + ", got: " - + policy.accessControlList().grants().get(0).grantee().type()); - Assertions.assertEquals( - policy.accessControlList().grants().get(0).permission(), - Permission.FULL_CONTROL, - "permission: expected: " - + Permission.FULL_CONTROL - + ", got: " - + policy.accessControlList().grants().get(0).permission()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getObjectAttributes() throws Exception { - String methodName = "getObjectAttributes()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String objectName = getRandomName(); - try { - try { - client.putObject( - PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( - new ContentInputStream(1 * KB), 1 * KB, -1) - .build()); - GetObjectAttributesResponse response = - client.getObjectAttributes( - GetObjectAttributesArgs.builder() - .bucket(bucketName) - .object(objectName) - .objectAttributes( - new String[] { - "ETag", "Checksum", "ObjectParts", "StorageClass", "ObjectSize" - }) - .build()); - Assertions.assertTrue( - response.result().objectSize() == (1 * KB), - "objectSize: expected: " + (1 * KB) + ", got: " + response.result().objectSize()); - Assertions.assertTrue( - response.result().objectParts().parts().get(0).partNumber() == 1, - "partNumber: expected: 1, got: " - + response.result().objectParts().parts().get(0).partNumber()); - Assertions.assertTrue( - response.result().objectParts().parts().get(0).partSize() == (1 * KB), - "partSize: expected: " - + (1 * KB) - + ", got: " - + response.result().objectParts().parts().get(0).partSize()); - mintSuccessLog(methodName, null, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void setBucketReplication() throws Exception { - String methodName = "setBucketReplication()"; - if (!mintEnv) { - System.out.println(methodName); - } - - if (replicationSrcBucket == null || replicationRole == null || replicationBucketArn == null) { - mintIgnoredLog(methodName, "", System.currentTimeMillis()); - return; - } - - long startTime = System.currentTimeMillis(); - try { - Map tags = new HashMap<>(); - 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), - null, - new RuleFilter(new AndOperator("TaxDocs", tags)), - "rule1", - null, - 1, - null, - Status.ENABLED); - - List rules = new LinkedList<>(); - rules.add(rule); - - ReplicationConfiguration config = new ReplicationConfiguration(replicationRole, rules); - - client.setBucketReplication( - SetBucketReplicationArgs.builder().bucket(replicationSrcBucket).config(config).build()); - client.deleteBucketReplication( - DeleteBucketReplicationArgs.builder().bucket(replicationSrcBucket).build()); - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void getBucketReplication() throws Exception { - String methodName = "getBucketReplication()"; - if (!mintEnv) { - System.out.println(methodName); - } - - if (replicationSrcBucket == null || replicationRole == null || replicationBucketArn == null) { - mintIgnoredLog(methodName, "", System.currentTimeMillis()); - return; - } - - long startTime = System.currentTimeMillis(); - try { - ReplicationConfiguration config = - client.getBucketReplication( - GetBucketReplicationArgs.builder().bucket(replicationSrcBucket).build()); - Assertions.assertNull(config, "config: expected: , got: "); - - Map tags = new HashMap<>(); - 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), - null, - new RuleFilter(new AndOperator("TaxDocs", tags)), - "rule1", - null, - 1, - null, - Status.ENABLED); - - List rules = new LinkedList<>(); - rules.add(rule); - - config = new ReplicationConfiguration(replicationRole, rules); - client.setBucketReplication( - SetBucketReplicationArgs.builder().bucket(replicationSrcBucket).config(config).build()); - config = - client.getBucketReplication( - GetBucketReplicationArgs.builder().bucket(replicationSrcBucket).build()); - Assertions.assertNotNull(config, "config: expected: , got: "); - client.deleteBucketReplication( - DeleteBucketReplicationArgs.builder().bucket(replicationSrcBucket).build()); - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void deleteBucketReplication() throws Exception { - String methodName = "deleteBucketReplication()"; - if (!mintEnv) { - System.out.println(methodName); - } - - if (replicationSrcBucket == null || replicationRole == null || replicationBucketArn == null) { - mintIgnoredLog(methodName, "", System.currentTimeMillis()); - return; - } - - long startTime = System.currentTimeMillis(); - try { - client.deleteBucketReplication( - DeleteBucketReplicationArgs.builder().bucket(replicationSrcBucket).build()); - - Map tags = new HashMap<>(); - 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), - null, - new RuleFilter(new AndOperator("TaxDocs", tags)), - "rule1", - null, - 1, - null, - Status.ENABLED); - - List rules = new LinkedList<>(); - rules.add(rule); - - ReplicationConfiguration config = new ReplicationConfiguration(replicationRole, rules); - client.setBucketReplication( - SetBucketReplicationArgs.builder().bucket(replicationSrcBucket).config(config).build()); - client.deleteBucketReplication( - DeleteBucketReplicationArgs.builder().bucket(replicationSrcBucket).build()); - config = - client.getBucketReplication( - GetBucketReplicationArgs.builder().bucket(replicationSrcBucket).build()); - Assertions.assertNull(config, "config: expected: , got: "); - mintSuccessLog(methodName, null, startTime); - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void testUploadSnowballObjects(String testTags, boolean compression) - throws Exception { - String methodName = "uploadSnowballObjects()"; - - long startTime = System.currentTimeMillis(); - String objectName1 = getRandomName(); - String objectName2 = getRandomName(); - try { - try { - List objects = new LinkedList(); - objects.add( - new SnowballObject( - objectName1, - new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)), - 5, - null)); - objects.add(new SnowballObject(objectName2, createFile1Kb())); - client.uploadSnowballObjects( - UploadSnowballObjectsArgs.builder() - .bucket(bucketName) - .objects(objects) - .compression(compression) - .build()); - - StatObjectResponse stat = - client.statObject( - StatObjectArgs.builder().bucket(bucketName).object(objectName1).build()); - Assertions.assertEquals(5, stat.size(), "object size: expected: 5, got: " + stat.size()); - stat = - client.statObject( - StatObjectArgs.builder().bucket(bucketName).object(objectName2).build()); - Assertions.assertEquals( - 1 * KB, stat.size(), "object size: expected: " + KB + ", got: " + stat.size()); - mintSuccessLog(methodName, testTags, startTime); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName1).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName2).build()); - } - } catch (Exception e) { - handleException(methodName, testTags, startTime, e); - } - } - - public static void uploadSnowballObjects() throws Exception { - String methodName = "uploadSnowballObjects()"; - if (!mintEnv) { - System.out.println(methodName); - } - - testUploadSnowballObjects("[no compression]", false); - testUploadSnowballObjects("[compression]", true); - } - - public static void putObjectFanOut() throws Exception { - String methodName = "putObjectFanOut()"; - if (!mintEnv) { - System.out.println(methodName); - } - - long startTime = System.currentTimeMillis(); - String objectName1 = getRandomName(); - String objectName2 = getRandomName(); - try { - try { - Map map = new HashMap<>(); - map.put("Project", "Project One"); - map.put("User", "jsmith"); - client.putObjectFanOut( - PutObjectFanOutArgs.builder().bucket(bucketName).stream( - new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)), 5) - .entries( - Arrays.asList( - new PutObjectFanOutEntry[] { - PutObjectFanOutEntry.builder().key(objectName1).userMetadata(map).build(), - PutObjectFanOutEntry.builder().key(objectName2).tags(map).build() - })) - .build()); - - StatObjectResponse stat = - client.statObject( - StatObjectArgs.builder().bucket(bucketName).object(objectName1).build()); - Assertions.assertTrue( - map.size() == stat.userMetadata().size() - && map.entrySet().stream() - .allMatch( - e -> - e.getValue() - .equals( - stat.userMetadata().get(e.getKey().toLowerCase(Locale.US)))), - "userMetadata: expected = " + map + ", got = " + stat.userMetadata()); - - Tags tags = - client.getObjectTags( - GetObjectTagsArgs.builder().bucket(bucketName).object(objectName2).build()); - Assertions.assertTrue( - map.equals(tags.get()), "tags: expected = " + map + ", got = " + tags.get()); - } finally { - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName1).build()); - client.removeObject( - RemoveObjectArgs.builder().bucket(bucketName).object(objectName2).build()); - } - } catch (Exception e) { - handleException(methodName, null, startTime, e); - } - } - - public static void runBucketTests() throws Exception { - makeBucket(); - bucketExists(); - removeBucket(); - listBuckets(); - - setBucketVersioning(); - getBucketVersioning(); - - setObjectLockConfiguration(); - getObjectLockConfiguration(); - - setBucketEncryption(); - getBucketEncryption(); - deleteBucketEncryption(); - - setBucketCors(); - getBucketCors(); - deleteBucketCors(); - - setBucketTags(); - getBucketTags(); - deleteBucketTags(); - - setBucketPolicy(); - getBucketPolicy(); - deleteBucketPolicy(); - - setBucketLifecycle(); - getBucketLifecycle(); - deleteBucketLifecycle(); - - setBucketNotification(); - getBucketNotification(); - deleteBucketNotification(); - - setBucketReplication(); - getBucketReplication(); - deleteBucketReplication(); - - listenBucketNotification(); - } - - public static void runObjectTests() throws Exception { - listObjects(); - - setup(); - - putObject(); - getObject(); - removeObject(); - removeObjects(); - statObject(); - - copyObject(); - composeObject(); - uploadObject(); - downloadObject(); - - setObjectRetention(); - getObjectRetention(); - - getPresignedObjectUrl(); - getPresignedPostFormData(); - - enableObjectLegalHold(); - disableObjectLegalHold(); - isObjectLegalHoldEnabled(); - - selectObjectContent(); - - setObjectTags(); - getObjectTags(); - deleteObjectTags(); - - getObjectAcl(); - getObjectAttributes(); - - uploadSnowballObjects(); - putObjectFanOut(); - - teardown(); - } - - public static void runTests() throws Exception { - runBucketTests(); - runObjectTests(); - adminClientTests.runAdminTests(); - } - - public static boolean downloadMinio() throws IOException { - String url = "https://dl.min.io/server/minio/release/"; - if (OS.contains("linux")) { - url += "linux-amd64/minio"; - } else if (OS.contains("windows")) { - url += "windows-amd64/minio.exe"; - } else if (OS.contains("mac")) { - url += "darwin-amd64/minio"; - } else { - System.out.println("unknown operating system " + OS); - return false; - } - - File file = new File(MINIO_BINARY); - if (file.exists()) { - return true; - } - - System.out.println("downloading " + MINIO_BINARY + " binary"); - - Request.Builder requestBuilder = new Request.Builder(); - Request request = requestBuilder.url(HttpUrl.parse(url)).method("GET", null).build(); - OkHttpClient transport = newHttpClient(); - Response response = transport.newCall(request).execute(); - - try { - if (!response.isSuccessful()) { - System.out.println("failed to download binary " + MINIO_BINARY); - return false; - } +import io.minio.MinioClient; +import io.minio.admin.MinioAdminClient; - BufferedSink bufferedSink = Okio.buffer(Okio.sink(new File(MINIO_BINARY))); - bufferedSink.writeAll(response.body().source()); - bufferedSink.flush(); - bufferedSink.close(); - } finally { - response.close(); +public class FunctionalTest { + public static void runS3Tests(TestArgs args) throws Exception { + if (!args.MINT_ENV) System.out.println(">>> Running S3 tests:"); + new TestMinioClient( + args, + args.IS_QUICK_TEST, + MinioClient.builder() + .endpoint(args.endpoint) + .credentials(args.accessKey, args.secretKey) + .build()) + .runTests(); + + if (args.automated) { + if (!args.MINT_ENV) { + System.out.println(); + System.out.println(">>> Running S3 tests on TLS endpoint:"); + } + MinioClient client = + MinioClient.builder() + .endpoint(args.endpointTLS) + .credentials(args.accessKey, args.secretKey) + .build(); + client.ignoreCertCheck(); + new TestMinioClient(args, args.IS_QUICK_TEST, client).runTests(); } - if (!OS.contains("windows")) { - file.setExecutable(true); + if (!args.MINT_ENV) { + System.out.println(); + System.out.println(">>> Running quick tests specific region:"); + new TestMinioClient( + args, + true, + MinioClient.builder() + .endpoint(args.endpoint) + .credentials(args.accessKey, args.secretKey) + .region(args.region) + .build()) + .runTests(); } - - return true; } - public static Process runMinio(boolean tls) throws Exception { - File binaryPath = new File(new File(System.getProperty("user.dir")), MINIO_BINARY); - ProcessBuilder pb; - if (tls) { - pb = - new ProcessBuilder( - binaryPath.getPath(), - "server", - "--address", - ":9001", - "--certs-dir", - ".cfg/certs", - ".d{1...4}"); - } else { - pb = new ProcessBuilder(binaryPath.getPath(), "server", ".d{1...4}"); - } - - Map env = pb.environment(); - env.put("MINIO_ROOT_USER", "minio"); - env.put("MINIO_ROOT_PASSWORD", "minio123"); - env.put("MINIO_CI_CD", "1"); - // echo -n abcdefghijklmnopqrstuvwxyzABCDEF | base64 - - env.put("MINIO_KMS_SECRET_KEY", "my-minio-key:YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUY="); - env.put("MINIO_NOTIFY_WEBHOOK_ENABLE_miniojavatest", "on"); - env.put("MINIO_NOTIFY_WEBHOOK_ENDPOINT_miniojavatest", "http://example.org/"); - sqsArn = "arn:minio:sqs::miniojavatest:webhook"; - - pb.redirectErrorStream(true); - pb.redirectOutput(ProcessBuilder.Redirect.to(new File(MINIO_BINARY + ".log"))); - - if (tls) { - System.out.println("starting minio server in TLS"); - } else { - System.out.println("starting minio server"); + public static void runMinioAdminTests(TestArgs args) throws Exception { + if (!args.MINT_ENV) { + System.out.println(); + System.out.println(">>> Running MinIO admin API tests:"); + new TestMinioAdminClient( + args, + MinioAdminClient.builder() + .endpoint(args.endpoint) + .credentials(args.accessKey, args.secretKey) + .build()) + .runAdminTests(); } - Process p = pb.start(); - Thread.sleep(10 * 1000); // wait for 10 seconds to do real start. - return p; } - public static void runEndpointTests(boolean automated) throws Exception { - client = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build(); - MinioAdminClient adminClient = - MinioAdminClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build(); - adminClientTests = new TestMinioAdminClient(adminClient, mintEnv); - // Enable trace for debugging. - // client.traceOn(System.out); - if (!mintEnv) System.out.println(">>> Running tests:"); - FunctionalTest.runTests(); - - if (automated) { - // Run tests on TLS endpoint - client = - MinioClient.builder().endpoint(endpointTLS).credentials(accessKey, secretKey).build(); - client.ignoreCertCheck(); - adminClient = - MinioAdminClient.builder() - .endpoint(endpointTLS) - .credentials(accessKey, secretKey) - .build(); - adminClient.ignoreCertCheck(); - adminClientTests = new TestMinioAdminClient(adminClient, mintEnv); - // Enable trace for debugging. - // client.traceOn(System.out); - if (!mintEnv) System.out.println(">>> Running tests on TLS endpoint:"); - isSecureEndpoint = true; - FunctionalTest.runTests(); - } - - if (!mintEnv) { - System.out.println(); - System.out.println(">>> Running tests for region:"); - isQuickTest = true; - isSecureEndpoint = endpoint.toLowerCase(Locale.US).contains("https://"); - // Get new bucket name to avoid minio azure gateway failure. - bucketName = getRandomName(); - bucketNameWithLock = getRandomName(); - client = - MinioClient.builder() - .endpoint(endpoint) - .credentials(accessKey, secretKey) - .region(region) - .build(); - adminClient = - MinioAdminClient.builder() - .endpoint(endpoint) - .credentials(accessKey, secretKey) - .region(region) - .build(); - adminClientTests = new TestMinioAdminClient(adminClient, mintEnv); - FunctionalTest.runTests(); - } + public static void runTests(TestArgs args) throws Exception { + runS3Tests(args); + runMinioAdminTests(args); } - /** main(). */ public static void main(String[] args) throws Exception { - String mintMode = System.getenv("MINT_MODE"); - mintEnv = (mintMode != null); - if (mintEnv) { - isQuickTest = !mintMode.equals("full"); - isRunOnFail = "1".equals(System.getenv("RUN_ON_FAIL")); - String dataDir = System.getenv("MINT_DATA_DIR"); - if (dataDir != null && !dataDir.equals("")) { - dataFile1Kb = Paths.get(dataDir, "datafile-1-kB"); - dataFile6Mb = Paths.get(dataDir, "datafile-6-MB"); - } + String endpoint = null; + String accessKey = null; + String secretKey = null; + String region = null; + if (args.length == 4) { + endpoint = args[0]; + accessKey = args[1]; + secretKey = args[2]; + region = args[3]; } - replicationSrcBucket = System.getenv("MINIO_JAVA_TEST_REPLICATION_SRC_BUCKET"); - replicationRole = System.getenv("MINIO_JAVA_TEST_REPLICATION_ROLE"); - replicationBucketArn = System.getenv("MINIO_JAVA_TEST_REPLICATION_BUCKET_ARN"); + TestArgs testArgs = new TestArgs(endpoint, accessKey, secretKey, region); Process minioProcess = null; Process minioProcessTLS = null; - - boolean automated = true; - String kmsKeyName = "my-minio-key"; if (args.length != 4) { - endpoint = "http://localhost:9000"; - endpointTLS = "https://localhost:9001"; - accessKey = "minio"; - secretKey = "minio123"; - region = "us-east-1"; - - if (!downloadMinio()) { + if (!TestArgs.downloadMinioServer()) { System.out.println("usage: FunctionalTest "); System.exit(-1); } - minioProcess = runMinio(false); + minioProcess = TestArgs.runMinioServer(false); try { int exitValue = minioProcess.exitValue(); System.out.println("minio server process exited with " + exitValue); System.out.println("usage: FunctionalTest "); System.exit(-1); } catch (IllegalThreadStateException e) { - ignore(); + TestArgs.ignore(); } - minioProcessTLS = runMinio(true); + minioProcessTLS = TestArgs.runMinioServer(true); try { int exitValue = minioProcessTLS.exitValue(); System.out.println("minio server process exited with " + exitValue); System.out.println("usage: FunctionalTest "); System.exit(-1); } catch (IllegalThreadStateException e) { - ignore(); + TestArgs.ignore(); } - } else { - kmsKeyName = System.getenv("MINIO_JAVA_TEST_KMS_KEY_NAME"); - if (kmsKeyName == null) { - kmsKeyName = System.getenv("MINT_KEY_ID"); - } - sqsArn = System.getenv("MINIO_JAVA_TEST_SQS_ARN"); - endpoint = args[0]; - accessKey = args[1]; - secretKey = args[2]; - region = args[3]; - automated = false; - } - - isSecureEndpoint = endpoint.toLowerCase(Locale.US).contains("https://"); - if (kmsKeyName != null) { - Map myContext = new HashMap<>(); - myContext.put("key1", "value1"); - sseKms = new ServerSideEncryptionKms(kmsKeyName, myContext); } int exitValue = 0; try { - runEndpointTests(automated); + runTests(testArgs); } catch (Exception e) { - if (!mintEnv) { - e.printStackTrace(); - } + if (!testArgs.MINT_ENV) e.printStackTrace(); exitValue = -1; } finally { - if (minioProcess != null) { - minioProcess.destroy(); - } - if (minioProcessTLS != null) { - minioProcessTLS.destroy(); - } + if (minioProcess != null) minioProcess.destroy(); + if (minioProcessTLS != null) minioProcessTLS.destroy(); } System.exit(exitValue); diff --git a/functional/MintLogger.java b/functional/MintLogger.java old mode 100755 new mode 100644 index f8f84e85c..ad5fcaa06 --- a/functional/MintLogger.java +++ b/functional/MintLogger.java @@ -17,7 +17,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -71,10 +71,10 @@ public MintLogger( /** Return JSON Log Entry. */ @JsonIgnore public String toString() { - try { return new ObjectMapper() - .setSerializationInclusion(Include.NON_NULL) + .setDefaultPropertyInclusion( + JsonInclude.Value.empty().withValueInclusion(JsonInclude.Include.NON_NULL)) .writeValueAsString(this); } catch (JsonProcessingException e) { e.printStackTrace(); diff --git a/functional/TestArgs.java b/functional/TestArgs.java new file mode 100644 index 000000000..14c4431db --- /dev/null +++ b/functional/TestArgs.java @@ -0,0 +1,421 @@ +/* + * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, + * (C) 2015-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. + */ + +import static java.nio.file.StandardOpenOption.APPEND; +import static java.nio.file.StandardOpenOption.CREATE; + +import io.minio.Checksum; +import io.minio.Http; +import io.minio.ServerSideEncryption; +import io.minio.errors.ErrorResponseException; +import io.minio.errors.MinioException; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import javax.crypto.KeyGenerator; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okio.BufferedSink; +import okio.Okio; +import org.junit.jupiter.api.Assertions; + +@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "REC", + justification = "Allow catching super class Exception since it's tests") +public class TestArgs { + public static final String OS = System.getProperty("os.name").toLowerCase(Locale.US); + public static final String MINIO_BINARY = OS.contains("windows") ? "minio.exe" : "minio"; + public static final String PASS = "PASS"; + public static final String FAILED = "FAIL"; + public static final String IGNORED = "NA"; + public static final int KB = 1024; + public static final int MB = 1024 * 1024; + public static final Random RANDOM = new Random(new SecureRandom().nextLong()); + public static final String CUSTOM_CONTENT_TYPE = "application/javascript"; + public static final ServerSideEncryption SSE_S3 = new ServerSideEncryption.S3(); + public static final ServerSideEncryption.CustomerKey SSE_C; + public static final boolean MINT_ENV; + public static final boolean IS_QUICK_TEST; + public static final boolean IS_RUN_ON_FAIL; + public static final Path DATA_FILE_1KB; + public static final Path DATA_FILE_6MB; + public static final String REPLICATION_SRC_BUCKET; + public static final String REPLICATION_ROLE; + public static final String REPLICATION_BUCKET_ARN; + + static { + try { + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + SSE_C = new ServerSideEncryption.CustomerKey(keyGen.generateKey()); + } catch (NoSuchAlgorithmException | MinioException e) { + throw new RuntimeException(e); + } + + String mintMode = System.getenv("MINT_MODE"); + String dataDir = System.getenv("MINT_DATA_DIR"); + MINT_ENV = mintMode != null; + IS_QUICK_TEST = MINT_ENV && !"full".equals(mintMode); + IS_RUN_ON_FAIL = MINT_ENV && "1".equals(System.getenv("RUN_ON_FAIL")); + DATA_FILE_1KB = + (MINT_ENV && dataDir != null && !dataDir.isEmpty()) + ? Paths.get(dataDir, "datafile-1-kB") + : null; + DATA_FILE_6MB = + (MINT_ENV && dataDir != null && !dataDir.isEmpty()) + ? Paths.get(dataDir, "datafile-6-MB") + : null; + REPLICATION_SRC_BUCKET = System.getenv("MINIO_JAVA_TEST_REPLICATION_SRC_BUCKET"); + REPLICATION_ROLE = System.getenv("MINIO_JAVA_TEST_REPLICATION_ROLE"); + REPLICATION_BUCKET_ARN = System.getenv("MINIO_JAVA_TEST_REPLICATION_BUCKET_ARN"); + } + + public boolean automated; + public String endpoint; + public String endpointTLS; + public String accessKey; + public String secretKey; + public String region; + public boolean isSecureEndpoint = false; + public String sqsArn = null; + public ServerSideEncryption sseKms = null; + + public TestArgs(TestArgs args) { + this.automated = args.automated; + this.endpoint = args.endpoint; + this.endpointTLS = args.endpointTLS; + this.accessKey = args.accessKey; + this.secretKey = args.secretKey; + this.region = args.region; + this.isSecureEndpoint = args.isSecureEndpoint; + this.sqsArn = args.sqsArn; + this.sseKms = args.sseKms; + } + + public TestArgs(String endpoint, String accessKey, String secretKey, String region) + throws MinioException { + this.automated = endpoint == null; + + String kmsKeyName = "my-minio-key"; + if (endpoint == null) { + this.endpoint = "http://localhost:9000"; + this.endpointTLS = "https://localhost:9001"; + this.accessKey = "minio"; + this.secretKey = "minio123"; + this.region = "us-east-1"; + this.sqsArn = "arn:minio:sqs::miniojavatest:webhook"; + } else { + if ((kmsKeyName = System.getenv("MINIO_JAVA_TEST_KMS_KEY_NAME")) == null) { + kmsKeyName = System.getenv("MINT_KEY_ID"); + } + this.sqsArn = System.getenv("MINIO_JAVA_TEST_SQS_ARN"); + this.endpoint = endpoint; + this.accessKey = accessKey; + this.secretKey = secretKey; + this.region = region; + } + this.isSecureEndpoint = this.endpoint.toLowerCase(Locale.US).contains("https://"); + + if (kmsKeyName != null) { + Map myContext = new HashMap<>(); + myContext.put("key1", "value1"); + this.sseKms = new ServerSideEncryption.KMS(kmsKeyName, myContext); + } + } + + public static OkHttpClient newHttpClient() { + try { + return Http.disableCertCheck(Http.newDefaultClient()); + } catch (MinioException e) { + throw new RuntimeException(e); + } + } + + /** Do no-op. */ + public static void ignore(Object... args) {} + + /** Create given sized file and returns its name. */ + public static String createFile(int size) throws IOException { + String filename = getRandomName(); + + try (OutputStream os = Files.newOutputStream(Paths.get(filename), CREATE, APPEND)) { + int totalBytesWritten = 0; + int bytesToWrite = 0; + byte[] buf = new byte[1 * MB]; + while (totalBytesWritten < size) { + RANDOM.nextBytes(buf); + bytesToWrite = size - totalBytesWritten; + if (bytesToWrite > buf.length) bytesToWrite = buf.length; + os.write(buf, 0, bytesToWrite); + totalBytesWritten += bytesToWrite; + } + } + + return filename; + } + + /** Create 1 KB temporary file. */ + public static String createFile1Kb() throws IOException { + if (MINT_ENV) { + String filename = getRandomName(); + Files.createSymbolicLink(Paths.get(filename).toAbsolutePath(), DATA_FILE_1KB); + return filename; + } + + return createFile(1 * KB); + } + + /** Create 6 MB temporary file. */ + public static String createFile6Mb() throws IOException { + if (MINT_ENV) { + String filename = getRandomName(); + Files.createSymbolicLink(Paths.get(filename).toAbsolutePath(), DATA_FILE_6MB); + return filename; + } + + return createFile(6 * MB); + } + + /** Generate random name. */ + public static String getRandomName() { + return "minio-java-test-" + new BigInteger(32, RANDOM).toString(32); + } + + /** Returns byte array contains all data in given InputStream. */ + public static byte[] readAllBytes(InputStream is) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int n; + byte[] data = new byte[16384]; + while ((n = is.read(data, 0, data.length)) != -1) buffer.write(data, 0, n); + return buffer.toByteArray(); + } + + /** Prints a success log entry in JSON format. */ + public static void mintSuccessLog(String function, String args, long startTime) { + if (MINT_ENV) { + System.out.println( + new MintLogger( + function, args, System.currentTimeMillis() - startTime, PASS, null, null, null)); + } + } + + /** Prints a failure log entry in JSON format. */ + public static void mintFailedLog( + String function, String args, long startTime, String message, String error) { + if (MINT_ENV) { + System.out.println( + new MintLogger( + function, + args, + System.currentTimeMillis() - startTime, + FAILED, + null, + message, + error)); + } + } + + /** Prints a ignore log entry in JSON format. */ + public static void mintIgnoredLog(String function, String args, long startTime) { + if (MINT_ENV) { + System.out.println( + new MintLogger( + function, args, System.currentTimeMillis() - startTime, IGNORED, null, null, null)); + } + } + + /** Read object content of the given url. */ + public static byte[] readObject(String urlString) throws Exception { + Request request = + new Request.Builder().url(HttpUrl.parse(urlString)).method("GET", null).build(); + try (Response response = newHttpClient().newCall(request).execute()) { + if (response.isSuccessful()) return response.body().bytes(); + String errorXml = response.body().string(); + throw new Exception( + "failed to create object. Response: " + response + ", Response body: " + errorXml); + } + } + + /** Write data to given object url. */ + public static void writeObject(String urlString, byte[] dataBytes) throws Exception { + // Set header 'x-amz-acl' to 'bucket-owner-full-control', so objects created + // anonymously, can be downloaded by bucket owner in AWS S3. + Request request = + new Request.Builder() + .url(HttpUrl.parse(urlString)) + .method("PUT", RequestBody.create(dataBytes, null)) + .addHeader("x-amz-acl", "bucket-owner-full-control") + .build(); + try (Response response = newHttpClient().newCall(request).execute()) { + if (!response.isSuccessful()) { + String errorXml = response.body().string(); + throw new Exception( + "failed to create object. Response: " + response + ", Response body: " + errorXml); + } + } + } + + public static String getSha256Sum(InputStream stream, int len) throws Exception { + Checksum.Hasher hasher = Checksum.Algorithm.SHA256.hasher(); + // 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; + bytesRead = stream.read(buf, 0, bytesToRead); + Assertions.assertFalse( + bytesRead < 0, "data length mismatch. expected: " + len + ", got: " + totalBytesRead); + if (bytesRead > 0) { + hasher.update(buf, 0, bytesRead); + totalBytesRead += bytesRead; + } + } + return Checksum.hexString(hasher.sum()).toLowerCase(Locale.US); + } + + public static void skipStream(InputStream stream, int len) throws Exception { + // 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; + bytesRead = stream.read(buf, 0, bytesToRead); + Assertions.assertFalse( + bytesRead < 0, "insufficient data. expected: " + len + ", got: " + totalBytesRead); + if (bytesRead > 0) totalBytesRead += bytesRead; + } + } + + public static void handleException(String methodName, String args, long startTime, Exception e) + throws Exception { + if (e instanceof ErrorResponseException) { + if (((ErrorResponseException) e).errorResponse().code().equals("NotImplemented")) { + mintIgnoredLog(methodName, args, startTime); + return; + } + } + + if (MINT_ENV) { + mintFailedLog( + methodName, + args, + startTime, + null, + e.toString() + " >>> " + Arrays.toString(e.getStackTrace())); + if (IS_RUN_ON_FAIL) return; + } else { + System.out.println(" " + methodName + " " + ((args == null) ? "" : args)); + } + + throw e; + } + + public static boolean downloadMinioServer() throws IOException { + String url = "https://dl.min.io/server/minio/release/"; + if (OS.contains("linux")) { + url += "linux-amd64/minio"; + } else if (OS.contains("windows")) { + url += "windows-amd64/minio.exe"; + } else if (OS.contains("mac")) { + url += "darwin-amd64/minio"; + } else { + System.out.println("unknown operating system " + OS); + return false; + } + + File file = new File(MINIO_BINARY); + if (file.exists()) return true; + + System.out.println("downloading " + MINIO_BINARY + " binary"); + + Request request = new Request.Builder().url(HttpUrl.parse(url)).method("GET", null).build(); + try (Response response = newHttpClient().newCall(request).execute()) { + if (!response.isSuccessful()) { + System.out.println("failed to download binary " + MINIO_BINARY); + return false; + } + + BufferedSink bufferedSink = Okio.buffer(Okio.sink(new File(MINIO_BINARY))); + bufferedSink.writeAll(response.body().source()); + bufferedSink.flush(); + bufferedSink.close(); + } + + if (!OS.contains("windows")) file.setExecutable(true); + + return true; + } + + public static Process runMinioServer(boolean tls) throws Exception { + File binaryPath = new File(new File(System.getProperty("user.dir")), MINIO_BINARY); + ProcessBuilder pb; + if (tls) { + pb = + new ProcessBuilder( + binaryPath.getPath(), + "server", + "--address", + ":9001", + "--certs-dir", + ".cfg/certs", + ".d{1...4}"); + } else { + pb = new ProcessBuilder(binaryPath.getPath(), "server", ".d{1...4}"); + } + + Map env = pb.environment(); + env.put("MINIO_ROOT_USER", "minio"); + env.put("MINIO_ROOT_PASSWORD", "minio123"); + env.put("MINIO_CI_CD", "1"); + // echo -n abcdefghijklmnopqrstuvwxyzABCDEF | base64 - + env.put("MINIO_KMS_SECRET_KEY", "my-minio-key:YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUY="); + env.put("MINIO_NOTIFY_WEBHOOK_ENABLE_miniojavatest", "on"); + env.put("MINIO_NOTIFY_WEBHOOK_ENDPOINT_miniojavatest", "http://example.org/"); + + pb.redirectErrorStream(true); + pb.redirectOutput(ProcessBuilder.Redirect.to(new File(MINIO_BINARY + ".log"))); + + if (tls) { + System.out.println("starting minio server in TLS"); + } else { + System.out.println("starting minio server"); + } + Process p = pb.start(); + Thread.sleep(10 * 1000); // wait for 10 seconds to do real start. + return p; + } +} diff --git a/functional/TestMinioAdminClient.java b/functional/TestMinioAdminClient.java index a37ad8d25..04208fcda 100644 --- a/functional/TestMinioAdminClient.java +++ b/functional/TestMinioAdminClient.java @@ -15,144 +15,129 @@ * limitations under the License. */ -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.minio.admin.*; +import io.minio.admin.MinioAdminClient; +import io.minio.admin.Status; +import io.minio.admin.UserInfo; import java.util.Map; import org.junit.jupiter.api.Assertions; -@SuppressFBWarnings( +@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value = "REC", justification = "Allow catching super class Exception since it's tests") -public class TestMinioAdminClient { - - private final MinioAdminClient adminClient; - private final boolean mintEnv; - - private static String userAccessKey = FunctionalTest.getRandomName(); - private static String userSecretKey = FunctionalTest.getRandomName(); - private static String policyName = FunctionalTest.getRandomName(); - - public TestMinioAdminClient(MinioAdminClient adminClient, boolean mintEnv) { - this.adminClient = adminClient; - this.mintEnv = mintEnv; +public class TestMinioAdminClient extends TestArgs { + private MinioAdminClient client; + private static String userAccessKey = getRandomName(); + private static String userSecretKey = getRandomName(); + private static String policyName = getRandomName(); + + public TestMinioAdminClient(TestArgs args, MinioAdminClient client) { + super(args); + this.client = client; } public void addUser() throws Exception { String methodName = "addUser()"; - if (!mintEnv) { - System.out.println(methodName); - } + if (!MINT_ENV) System.out.println(methodName); long startTime = System.currentTimeMillis(); + try { - adminClient.addUser(userAccessKey, UserInfo.Status.ENABLED, userSecretKey, null, null); + client.addUser(userAccessKey, Status.ENABLED, userSecretKey, null, null); } catch (Exception e) { - FunctionalTest.handleException(methodName, null, startTime, e); + handleException(methodName, null, startTime, e); } } public void addCannedPolicy() throws Exception { String methodName = "addCannedPolicy()"; - if (!mintEnv) { - System.out.println(methodName); - } + if (!MINT_ENV) System.out.println(methodName); long startTime = System.currentTimeMillis(); + try { String policyJson = "{'Version': '2012-10-17','Statement': [{'Action': ['s3:GetObject'],'Effect':" + " 'Allow','Resource': ['arn:aws:s3:::my-bucketname/*'],'Sid': ''}]}"; - adminClient.addCannedPolicy(policyName, policyJson.replaceAll("'", "\"")); + client.addCannedPolicy(policyName, policyJson.replaceAll("'", "\"")); } catch (Exception e) { - FunctionalTest.handleException(methodName, null, startTime, e); + handleException(methodName, null, startTime, e); } } public void listCannedPolicies() throws Exception { String methodName = "listCannedPolicies()"; - if (!mintEnv) { - System.out.println(methodName); - } - + if (!MINT_ENV) System.out.println(methodName); long startTime = System.currentTimeMillis(); + try { - Map policies = adminClient.listCannedPolicies(); + Map policies = client.listCannedPolicies(); String policy = policies.get(policyName); Assertions.assertTrue(policy != null); } catch (Exception e) { - FunctionalTest.handleException(methodName, null, startTime, e); + handleException(methodName, null, startTime, e); } } public void removeCannedPolicy() throws Exception { String methodName = "removeCannedPolicy()"; - if (!mintEnv) { - System.out.println(methodName); - } + if (!MINT_ENV) System.out.println(methodName); long startTime = System.currentTimeMillis(); + try { - adminClient.removeCannedPolicy(policyName); + client.removeCannedPolicy(policyName); } catch (Exception e) { - FunctionalTest.handleException(methodName, null, startTime, e); + handleException(methodName, null, startTime, e); } } public void setPolicy() throws Exception { String methodName = "setPolicy()"; - if (!mintEnv) { - System.out.println(methodName); - } - + if (!MINT_ENV) System.out.println(methodName); long startTime = System.currentTimeMillis(); + try { - adminClient.setPolicy(userAccessKey, false, policyName); + client.setPolicy(userAccessKey, false, policyName); } catch (Exception e) { - FunctionalTest.handleException(methodName, null, startTime, e); + handleException(methodName, null, startTime, e); } } public void getUserInfo() throws Exception { String methodName = "getUserInfo()"; - if (!mintEnv) { - System.out.println(methodName); - } - + if (!MINT_ENV) System.out.println(methodName); long startTime = System.currentTimeMillis(); + try { - UserInfo userInfo = adminClient.getUserInfo(userAccessKey); - Assertions.assertEquals(userInfo.status(), UserInfo.Status.ENABLED); + UserInfo userInfo = client.getUserInfo(userAccessKey); + Assertions.assertEquals(userInfo.status(), Status.ENABLED); Assertions.assertEquals(userInfo.policyName(), policyName); } catch (Exception e) { - FunctionalTest.handleException(methodName, null, startTime, e); + handleException(methodName, null, startTime, e); } } public void listUsers() throws Exception { String methodName = "listUsers()"; - if (!mintEnv) { - System.out.println(methodName); - } - + if (!MINT_ENV) System.out.println(methodName); long startTime = System.currentTimeMillis(); + try { - Map users = adminClient.listUsers(); + Map users = client.listUsers(); Assertions.assertTrue(users.containsKey(userAccessKey)); - Assertions.assertEquals(users.get(userAccessKey).status(), UserInfo.Status.ENABLED); + Assertions.assertEquals(users.get(userAccessKey).status(), Status.ENABLED); Assertions.assertEquals(users.get(userAccessKey).policyName(), policyName); } catch (Exception e) { - FunctionalTest.handleException(methodName, null, startTime, e); + handleException(methodName, null, startTime, e); } } public void deleteUser() throws Exception { String methodName = "deleteUser()"; - if (!mintEnv) { - System.out.println(methodName); - } - + if (!MINT_ENV) System.out.println(methodName); long startTime = System.currentTimeMillis(); + try { - adminClient.deleteUser(userAccessKey); + client.deleteUser(userAccessKey); } catch (Exception e) { - FunctionalTest.handleException(methodName, null, startTime, e); + handleException(methodName, null, startTime, e); } } diff --git a/functional/TestMinioClient.java b/functional/TestMinioClient.java new file mode 100644 index 000000000..7196f3052 --- /dev/null +++ b/functional/TestMinioClient.java @@ -0,0 +1,3620 @@ +/* + * MinIO Java SDK for Amazon S3 Compatible Cloud Storage, + * (C) 2015-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. + */ + +import io.minio.BucketExistsArgs; +import io.minio.CloseableIterator; +import io.minio.ComposeObjectArgs; +import io.minio.CopyObjectArgs; +import io.minio.DeleteBucketCorsArgs; +import io.minio.DeleteBucketEncryptionArgs; +import io.minio.DeleteBucketLifecycleArgs; +import io.minio.DeleteBucketNotificationArgs; +import io.minio.DeleteBucketPolicyArgs; +import io.minio.DeleteBucketReplicationArgs; +import io.minio.DeleteBucketTagsArgs; +import io.minio.DeleteObjectLockConfigurationArgs; +import io.minio.DeleteObjectTagsArgs; +import io.minio.Directive; +import io.minio.DisableObjectLegalHoldArgs; +import io.minio.DownloadObjectArgs; +import io.minio.EnableObjectLegalHoldArgs; +import io.minio.GetBucketCorsArgs; +import io.minio.GetBucketEncryptionArgs; +import io.minio.GetBucketLifecycleArgs; +import io.minio.GetBucketNotificationArgs; +import io.minio.GetBucketPolicyArgs; +import io.minio.GetBucketReplicationArgs; +import io.minio.GetBucketTagsArgs; +import io.minio.GetBucketVersioningArgs; +import io.minio.GetObjectAclArgs; +import io.minio.GetObjectArgs; +import io.minio.GetObjectAttributesArgs; +import io.minio.GetObjectAttributesResponse; +import io.minio.GetObjectLockConfigurationArgs; +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; +import io.minio.ListenBucketNotificationArgs; +import io.minio.MakeBucketArgs; +import io.minio.MinioClient; +import io.minio.ObjectWriteResponse; +import io.minio.PostPolicy; +import io.minio.PutObjectArgs; +import io.minio.PutObjectFanOutArgs; +import io.minio.PutObjectFanOutEntry; +import io.minio.RemoveBucketArgs; +import io.minio.RemoveObjectArgs; +import io.minio.RemoveObjectsArgs; +import io.minio.Result; +import io.minio.SelectObjectContentArgs; +import io.minio.SelectResponseStream; +import io.minio.ServerSideEncryption; +import io.minio.SetBucketCorsArgs; +import io.minio.SetBucketEncryptionArgs; +import io.minio.SetBucketLifecycleArgs; +import io.minio.SetBucketNotificationArgs; +import io.minio.SetBucketPolicyArgs; +import io.minio.SetBucketReplicationArgs; +import io.minio.SetBucketTagsArgs; +import io.minio.SetBucketVersioningArgs; +import io.minio.SetObjectLockConfigurationArgs; +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; +import io.minio.UploadObjectArgs; +import io.minio.UploadSnowballObjectsArgs; +import io.minio.Xml; +import io.minio.errors.ErrorResponseException; +import io.minio.messages.AccessControlList; +import io.minio.messages.AccessControlPolicy; +import io.minio.messages.CORSConfiguration; +import io.minio.messages.DeleteRequest; +import io.minio.messages.ErrorResponse; +import io.minio.messages.EventType; +import io.minio.messages.Filter; +import io.minio.messages.InputSerialization; +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; +import io.minio.messages.OutputSerialization; +import io.minio.messages.ReplicationConfiguration; +import io.minio.messages.Retention; +import io.minio.messages.RetentionMode; +import io.minio.messages.SseAlgorithm; +import io.minio.messages.SseConfiguration; +import io.minio.messages.Stats; +import io.minio.messages.Status; +import io.minio.messages.Tags; +import io.minio.messages.VersioningConfiguration; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import okhttp3.Headers; +import okhttp3.MultipartBody; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.junit.jupiter.api.Assertions; + +@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "REC", + justification = "Allow catching super class Exception since it's tests") +public class TestMinioClient extends TestArgs { + private String bucketName = getRandomName(); + private String bucketNameWithLock = getRandomName(); + public boolean isQuickTest; + private MinioClient client; + + public TestMinioClient(TestArgs args, boolean isQuickTest, MinioClient client) { + super(args); + this.isQuickTest = isQuickTest; + this.client = client; + } + + public void testBucketApi( + String methodName, + String testTags, + MakeBucketArgs args, + boolean existCheck, + boolean removeCheck) + throws Exception { + long startTime = System.currentTimeMillis(); + try { + client.makeBucket(args); + try { + Assertions.assertFalse( + existCheck + && !client.bucketExists( + BucketExistsArgs.builder().bucket(args.bucket()).region(args.region()).build()), + methodName + " failed after bucket creation"); + if (removeCheck) { + client.removeBucket( + RemoveBucketArgs.builder().bucket(args.bucket()).region(args.region()).build()); + } + mintSuccessLog(methodName, null, startTime); + } finally { + if (!removeCheck) { + client.removeBucket( + RemoveBucketArgs.builder().bucket(args.bucket()).region(args.region()).build()); + } + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void testBucketApiCases(String methodName, boolean existCheck, boolean removeCheck) + throws Exception { + testBucketApi( + methodName, + "[basic check]", + MakeBucketArgs.builder().bucket(getRandomName()).build(), + existCheck, + removeCheck); + + if (isQuickTest) return; + + testBucketApi( + methodName, + "[object lock]", + MakeBucketArgs.builder().bucket(getRandomName()).objectLock(true).build(), + existCheck, + removeCheck); + testBucketApi( + methodName, + "[name contains period]", + MakeBucketArgs.builder().bucket(getRandomName() + ".withperiod").build(), + existCheck, + removeCheck); + } + + public void makeBucket() throws Exception { + String methodName = "makeBucket()"; + if (!MINT_ENV) System.out.println(methodName); + + testBucketApiCases(methodName, false, false); + + if (isQuickTest) return; + + if (!endpoint.contains(".amazonaws.com")) { + mintIgnoredLog(methodName, "[region]", System.currentTimeMillis()); + mintIgnoredLog(methodName, "[region, object lock]", System.currentTimeMillis()); + return; + } + + testBucketApi( + methodName, + "[region]", + MakeBucketArgs.builder().bucket(getRandomName()).region("eu-west-1").build(), + false, + false); + testBucketApi( + methodName, + "[region, object lock]", + MakeBucketArgs.builder() + .bucket(getRandomName()) + .region("eu-central-1") + .objectLock(true) + .build(), + false, + false); + } + + public void listBuckets() throws Exception { + String methodName = "listBuckets()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + List expectedBucketNames = new ArrayList<>(); + try { + try { + String bucketName = getRandomName(); + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + expectedBucketNames.add(bucketName); + + bucketName = getRandomName(); + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); + expectedBucketNames.add(bucketName); + + bucketName = getRandomName() + ".withperiod"; + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + expectedBucketNames.add(bucketName); + + List bucketNames = new ArrayList<>(); + for (Result result : + client.listBuckets(ListBucketsArgs.builder().maxBuckets(1).build())) { + ListAllMyBucketsResult.Bucket bucket = result.get(); + if (expectedBucketNames.contains(bucket.name())) bucketNames.add(bucket.name()); + } + + Assertions.assertTrue( + expectedBucketNames.containsAll(bucketNames), + "bucket names differ; expected = " + expectedBucketNames + ", got = " + bucketNames); + + mintSuccessLog(methodName, null, startTime); + } finally { + for (String bucketName : expectedBucketNames) { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void bucketExists() throws Exception { + String methodName = "bucketExists()"; + if (!MINT_ENV) System.out.println(methodName); + + testBucketApiCases(methodName, true, false); + } + + public void removeBucket() throws Exception { + String methodName = "removeBucket()"; + if (!MINT_ENV) System.out.println(methodName); + + testBucketApiCases(methodName, false, true); + } + + public void setBucketVersioning() throws Exception { + String methodName = "setBucketVersioning()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String name = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(name).build()); + try { + client.setBucketVersioning( + SetBucketVersioningArgs.builder() + .bucket(name) + .config( + new VersioningConfiguration( + VersioningConfiguration.Status.ENABLED, null, null, null)) + .build()); + client.setBucketVersioning( + SetBucketVersioningArgs.builder() + .bucket(name) + .config( + new VersioningConfiguration( + VersioningConfiguration.Status.SUSPENDED, null, null, null)) + .build()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(name).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getBucketVersioning() throws Exception { + String methodName = "getBucketVersioning()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String name = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(name).build()); + try { + VersioningConfiguration config = + client.getBucketVersioning(GetBucketVersioningArgs.builder().bucket(name).build()); + Assertions.assertEquals( + config.status(), + VersioningConfiguration.Status.OFF, + "getBucketVersioning(); expected = \"\", got = " + config.status()); + client.setBucketVersioning( + SetBucketVersioningArgs.builder() + .bucket(name) + .config( + new VersioningConfiguration( + VersioningConfiguration.Status.ENABLED, null, null, null)) + .build()); + config = client.getBucketVersioning(GetBucketVersioningArgs.builder().bucket(name).build()); + Assertions.assertEquals( + config.status(), + VersioningConfiguration.Status.ENABLED, + "getBucketVersioning(); expected = " + + VersioningConfiguration.Status.ENABLED + + ", got = " + + config.status()); + + client.setBucketVersioning( + SetBucketVersioningArgs.builder() + .bucket(name) + .config( + new VersioningConfiguration( + VersioningConfiguration.Status.SUSPENDED, null, null, null)) + .build()); + config = client.getBucketVersioning(GetBucketVersioningArgs.builder().bucket(name).build()); + Assertions.assertEquals( + config.status(), + VersioningConfiguration.Status.SUSPENDED, + "getBucketVersioning(); expected = " + + VersioningConfiguration.Status.SUSPENDED + + ", got = " + + config.status()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(name).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void setup() throws Exception { + long startTime = System.currentTimeMillis(); + + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } catch (Exception e) { + handleException("makeBucket()", null, startTime, e); + } + + try { + client.makeBucket( + MakeBucketArgs.builder().bucket(bucketNameWithLock).objectLock(true).build()); + } catch (Exception e) { + if (e instanceof ErrorResponseException) { + if (((ErrorResponseException) e).errorResponse().code().equals("NotImplemented")) { + bucketNameWithLock = null; + return; + } + } + + handleException("makeBucket()", "[object lock]", startTime, e); + } + } + + public void teardown() throws Exception { + long startTime = System.currentTimeMillis(); + try { + if (bucketName != null) { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + + if (bucketNameWithLock != null) { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketNameWithLock).build()); + } + } catch (Exception e) { + handleException("removeBucket()", null, startTime, e); + } + } + + public void testUploadObject(String testTags, String filename, String contentType) + throws Exception { + String methodName = "uploadObject()"; + long startTime = System.currentTimeMillis(); + try { + try { + UploadObjectArgs.Builder builder = + UploadObjectArgs.builder().bucket(bucketName).object(filename).filename(filename); + if (contentType != null) builder.contentType(contentType); + client.uploadObject(builder.build()); + mintSuccessLog(methodName, testTags, startTime); + } finally { + Files.delete(Paths.get(filename)); + client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(filename).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void uploadObject() throws Exception { + String methodName = "uploadObject()"; + if (!MINT_ENV) System.out.println(methodName); + + testUploadObject("[single upload]", createFile1Kb(), null); + + if (isQuickTest) return; + + testUploadObject("[multi-part upload]", createFile6Mb(), null); + testUploadObject("[custom content-type]", createFile1Kb(), CUSTOM_CONTENT_TYPE); + } + + public void testPutObject(String testTags, PutObjectArgs args, String errorCode) + throws Exception { + String methodName = "putObject()"; + long startTime = System.currentTimeMillis(); + try { + ObjectWriteResponse objectInfo = null; + try { + objectInfo = client.putObject(args); + } catch (ErrorResponseException e) { + if (errorCode == null || !e.errorResponse().code().equals(errorCode)) throw e; + } + if (args.retention() != null) { + client.setObjectRetention( + SetObjectRetentionArgs.builder() + .bucket(args.bucket()) + .object(args.object()) + .config(new Retention()) + .bypassGovernanceMode(true) + .build()); + } + client.removeObject( + RemoveObjectArgs.builder() + .bucket(args.bucket()) + .object(args.object()) + .versionId(objectInfo != null ? objectInfo.versionId() : null) + .build()); + + mintSuccessLog(methodName, testTags, startTime); + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void testThreadedPutObject() throws Exception { + String methodName = "putObject()"; + String testTags = "[threaded]"; + long startTime = System.currentTimeMillis(); + try { + int count = 7; + Thread[] threads = new Thread[count]; + + for (int i = 0; i < count; i++) { + threads[i] = new Thread(new PutObjectRunnable(client, bucketName, createFile6Mb())); + } + + for (int i = 0; i < count; i++) threads[i].start(); + + // Waiting for threads to complete. + for (int i = 0; i < count; i++) threads[i].join(); + + // All threads are completed. + mintSuccessLog(methodName, testTags, startTime); + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void putObject() throws Exception { + String methodName = "putObject()"; + if (!MINT_ENV) System.out.println(methodName); + + testPutObject( + "[single upload]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .contentType(CUSTOM_CONTENT_TYPE) + .build(), + null); + + if (isQuickTest) return; + + testPutObject( + "[multi-part upload]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(11 * MB), 11L * MB, null) + .contentType(CUSTOM_CONTENT_TYPE) + .build(), + null); + + testPutObject( + "[object name with path segments]", + PutObjectArgs.builder().bucket(bucketName).object("path/to/" + getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .contentType(CUSTOM_CONTENT_TYPE) + .build(), + null); + + testPutObject( + "[zero sized object]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(0), 0L, null) + .build(), + null); + + testPutObject( + "[object name ends with '/']", + PutObjectArgs.builder().bucket(bucketName).object("path/to/" + getRandomName() + "/") + .stream(new ContentInputStream(0), 0L, null) + .contentType(CUSTOM_CONTENT_TYPE) + .build(), + null); + + testPutObject( + "[unknown stream size, single upload]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * KB), null, (long) PutObjectArgs.MIN_MULTIPART_SIZE) + .contentType(CUSTOM_CONTENT_TYPE) + .build(), + null); + + testPutObject( + "[unknown stream size, multi-part upload]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(11 * MB), null, (long) PutObjectArgs.MIN_MULTIPART_SIZE) + .contentType(CUSTOM_CONTENT_TYPE) + .build(), + null); + + Map userMetadata = new HashMap<>(); + userMetadata.put("My-Project", "Project One"); + userMetadata.put("My-header1", " a b c "); + userMetadata.put("My-Header2", "\"a b c\""); + userMetadata.put("My-Unicode-Tag", "商品"); + + testPutObject( + "[user metadata]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .userMetadata(userMetadata) + .build(), + null); + + Map headers = new HashMap<>(); + + headers.put("X-Amz-Storage-Class", "REDUCED_REDUNDANCY"); + testPutObject( + "[storage-class=REDUCED_REDUNDANCY]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .headers(headers) + .build(), + null); + + headers.put("X-Amz-Storage-Class", "STANDARD"); + testPutObject( + "[storage-class=STANDARD]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .headers(headers) + .build(), + null); + + headers.put("X-Amz-Storage-Class", "INVALID"); + testPutObject( + "[storage-class=INVALID negative case]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .headers(headers) + .build(), + "InvalidStorageClass"); + + testPutObject( + "[SSE-S3]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .contentType(CUSTOM_CONTENT_TYPE) + .sse(SSE_S3) + .build(), + null); + + if (bucketNameWithLock != null) { + testPutObject( + "[with retention]", + PutObjectArgs.builder().bucket(bucketNameWithLock).object(getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .retention( + new Retention(RetentionMode.GOVERNANCE, ZonedDateTime.now(Time.UTC).plusDays(1))) + .build(), + null); + } + + testThreadedPutObject(); + + if (!isSecureEndpoint) return; + + testPutObject( + "[SSE-C single upload]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .contentType(CUSTOM_CONTENT_TYPE) + .sse(SSE_C) + .build(), + null); + + testPutObject( + "[SSE-C multi-part upload]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(11 * MB), 11L * MB, null) + .contentType(CUSTOM_CONTENT_TYPE) + .sse(SSE_C) + .build(), + null); + + if (sseKms == null) { + mintIgnoredLog(methodName, null, System.currentTimeMillis()); + return; + } + + testPutObject( + "[SSE-KMS]", + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .contentType(CUSTOM_CONTENT_TYPE) + .sse(sseKms) + .build(), + null); + } + + public void testStatObject(String testTags, PutObjectArgs args, StatObjectResponse expectedStat) + throws Exception { + String methodName = "statObject()"; + long startTime = System.currentTimeMillis(); + try { + client.putObject(args); + try { + ServerSideEncryption.CustomerKey ssec = null; + if (args.sse() instanceof ServerSideEncryption.CustomerKey) { + ssec = (ServerSideEncryption.CustomerKey) args.sse(); + } + StatObjectResponse stat = + client.statObject( + StatObjectArgs.builder() + .bucket(args.bucket()) + .object(args.object()) + .ssec(ssec) + .build()); + + Assertions.assertEquals( + expectedStat.bucket(), + stat.bucket(), + "bucket name: expected = " + expectedStat.bucket() + ", got = " + stat.bucket()); + + Assertions.assertEquals( + expectedStat.object(), + stat.object(), + "object name: expected = " + expectedStat.object() + ", got = " + stat.object()); + + Assertions.assertEquals( + expectedStat.size(), + stat.size(), + "length: expected = " + expectedStat.size() + ", got = " + stat.size()); + + Assertions.assertEquals( + expectedStat.contentType(), + stat.contentType(), + "content-type: expected = " + + expectedStat.contentType() + + ", got = " + + stat.contentType()); + + for (String key : expectedStat.userMetadata().keySet()) { + Assertions.assertTrue( + stat.userMetadata().containsKey(key), "metadata " + key + " not found"); + Assertions.assertEquals( + expectedStat.userMetadata().get(key), + stat.userMetadata().get(key), + "metadata " + + key + + " value: expected: " + + expectedStat.userMetadata().get(key) + + ", got: " + + stat.userMetadata().get(key)); + } + + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void statObject() throws Exception { + String methodName = "statObject()"; + if (!MINT_ENV) System.out.println(methodName); + + String objectName = getRandomName(); + + PutObjectArgs.Builder builder = + PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( + new ContentInputStream(1024), 1024L, null); + Headers.Builder headersBuilder = + new Headers.Builder() + .add("Content-Type: application/octet-stream") + .add("Content-Length: 1024") + .add("Last-Modified", ZonedDateTime.now().format(Time.HTTP_HEADER_DATE_FORMAT)); + + testStatObject( + "[basic check]", + builder.build(), + new StatObjectResponse( + new HeadObjectResponse(headersBuilder.build(), bucketName, null, objectName))); + + Map headers = new HashMap<>(); + headers.put("Content-Type", CUSTOM_CONTENT_TYPE); + Map userMetadata = new HashMap<>(); + userMetadata.put("My-Project", "Project One"); + builder = builder.headers(headers).userMetadata(userMetadata); + builder = builder.stream(new ContentInputStream(1024), 1024L, null); + + StatObjectResponse stat = + new StatObjectResponse( + new HeadObjectResponse( + headersBuilder + .set("Content-Type", CUSTOM_CONTENT_TYPE) + .add("X-Amz-Meta-My-Project: Project One") + .build(), + bucketName, + null, + objectName)); + + testStatObject("[user metadata]", builder.build(), stat); + + if (isQuickTest) return; + + builder = builder.stream(new ContentInputStream(1024), 1024L, null); + testStatObject("[SSE-S3]", builder.sse(SSE_S3).build(), stat); + + if (!isSecureEndpoint) { + mintIgnoredLog(methodName, "[SSE-C]", System.currentTimeMillis()); + return; + } + + builder = builder.stream(new ContentInputStream(1024), 1024L, null); + testStatObject("[SSE-C]", builder.sse(SSE_C).build(), stat); + + if (sseKms == null) { + mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); + return; + } + + builder = builder.stream(new ContentInputStream(1024), 1024L, null); + testStatObject("[SSE-KMS]", builder.sse(sseKms).build(), stat); + } + + public void testGetObject( + String testTags, + long objectSize, + ServerSideEncryption sse, + GetObjectArgs args, + int length, + String sha256sum) + throws Exception { + String methodName = "getObject()"; + long startTime = System.currentTimeMillis(); + try { + PutObjectArgs.Builder builder = + PutObjectArgs.builder().bucket(args.bucket()).object(args.object()).stream( + new ContentInputStream(objectSize), objectSize, null); + if (sse != null) builder.sse(sse); + client.putObject(builder.build()); + + try (InputStream is = client.getObject(args)) { + String checksum = getSha256Sum(is, length); + Assertions.assertEquals( + checksum, + sha256sum, + "checksum mismatch. expected: " + sha256sum + ", got: " + checksum); + } + mintSuccessLog(methodName, testTags, startTime); + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); + } + } + + public void getObject() throws Exception { + String methodName = "getObject()"; + if (!MINT_ENV) System.out.println(methodName); + + testGetObject( + "[single upload]", + 1 * KB, + null, + GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).build(), + 1 * KB, + getSha256Sum(new ContentInputStream(1 * KB), 1 * KB)); + + if (isQuickTest) return; + + InputStream cis = new ContentInputStream(1 * KB); + skipStream(cis, 1000); + testGetObject( + "[single upload, offset]", + 1 * KB, + null, + GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).offset(1000L).build(), + 1 * KB - 1000, + getSha256Sum(cis, 1 * KB - 1000)); + + testGetObject( + "[single upload, length]", + 1 * KB, + null, + GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).length(256L).build(), + 256, + getSha256Sum(new ContentInputStream(1 * KB), 256)); + + cis = new ContentInputStream(1 * KB); + skipStream(cis, 1000); + testGetObject( + "[single upload, offset, length]", + 1 * KB, + null, + GetObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .offset(1000L) + .length(24L) + .build(), + 24, + getSha256Sum(cis, 24)); + + cis = new ContentInputStream(1 * KB); + skipStream(cis, 1000); + testGetObject( + "[single upload, offset, length beyond available]", + 1 * KB, + null, + GetObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .offset(1000L) + .length(30L) + .build(), + 24, + getSha256Sum(cis, 24)); + + testGetObject( + "[multi-part upload]", + 6 * MB, + null, + GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).build(), + 6 * MB, + getSha256Sum(new ContentInputStream(6 * MB), 6 * MB)); + + cis = new ContentInputStream(6 * MB); + skipStream(cis, 1000); + testGetObject( + "[multi-part upload, offset, length]", + 6 * MB, + null, + GetObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .offset(1000L) + .length(64 * 1024L) + .build(), + 64 * KB, + getSha256Sum(cis, 64 * 1024)); + + cis = new ContentInputStream(0); + testGetObject( + "[zero sized object]", + 0, + null, + GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).build(), + 0, + getSha256Sum(cis, 0)); + + if (!isSecureEndpoint) return; + + testGetObject( + "[single upload, SSE-C]", + 1 * KB, + SSE_C, + GetObjectArgs.builder().bucket(bucketName).object(getRandomName()).ssec(SSE_C).build(), + 1 * KB, + getSha256Sum(new ContentInputStream(1 * KB), 1 * KB)); + } + + public void testDownloadObject( + String testTags, int objectSize, ServerSideEncryption sse, DownloadObjectArgs args) + throws Exception { + String methodName = "downloadObject()"; + long startTime = System.currentTimeMillis(); + try { + PutObjectArgs.Builder builder = + PutObjectArgs.builder().bucket(args.bucket()).object(args.object()).stream( + new ContentInputStream(objectSize), (long) objectSize, null); + if (sse != null) builder.sse(sse); + client.putObject(builder.build()); + client.downloadObject(args); + Files.delete(Paths.get(args.filename())); + mintSuccessLog(methodName, testTags, startTime); + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); + } + } + + public void downloadObject() throws Exception { + String methodName = "downloadObject()"; + if (!MINT_ENV) System.out.println(methodName); + + String objectName = getRandomName(); + testDownloadObject( + "[single upload]", + 1 * KB, + null, + DownloadObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .filename(objectName + ".downloaded") + .build()); + + if (isQuickTest) return; + + String baseName = getRandomName(); + objectName = "path/to/" + baseName; + testDownloadObject( + "[single upload with multiple path segments]", + 1 * KB, + null, + DownloadObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .filename(baseName + ".downloaded") + .build()); + + if (!isSecureEndpoint) return; + + objectName = getRandomName(); + testDownloadObject( + "[single upload, SSE-C]", + 1 * KB, + SSE_C, + DownloadObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .ssec(SSE_C) + .filename(objectName + ".downloaded") + .build()); + } + + public List createObjects(String bucketName, int count, int versions) + throws Exception { + List results = new ArrayList<>(); + for (int i = 0; i < count; i++) { + String objectName = getRandomName(); + results.add( + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( + 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), 1L, null) + .build())); + } + } + } + + return results; + } + + public void removeObjects(String bucketName, List results) throws Exception { + List objects = + results.stream() + .map( + result -> { + return new DeleteRequest.Object(result.object(), result.versionId()); + }) + .collect(Collectors.toList()); + for (Result r : + client.removeObjects( + RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build())) { + ignore(r.get()); + } + } + + public void testListObjects(String testTags, ListObjectsArgs args, int objCount, int versions) + throws Exception { + String methodName = "listObjects()"; + long startTime = System.currentTimeMillis(); + String bucketName = args.bucket(); + List results = null; + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + if (versions > 0) { + client.setBucketVersioning( + SetBucketVersioningArgs.builder() + .bucket(bucketName) + .config( + new VersioningConfiguration( + VersioningConfiguration.Status.ENABLED, null, null, null)) + .build()); + } + + results = createObjects(bucketName, objCount, versions); + + int i = 0; + for (Result r : client.listObjects(args)) { + r.get(); + i++; + } + + if (versions > 0) objCount *= versions; + + Assertions.assertEquals(i, objCount, "object count; expected=" + objCount + ", got=" + i); + mintSuccessLog(methodName, testTags, startTime); + } finally { + if (results != null) removeObjects(bucketName, results); + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void listObjects() throws Exception { + if (!MINT_ENV) System.out.println("listObjects()"); + + testListObjects("[bucket]", ListObjectsArgs.builder().bucket(getRandomName()).build(), 3, 0); + + testListObjects( + "[bucket, prefix]", + ListObjectsArgs.builder().bucket(getRandomName()).prefix("minio").build(), + 3, + 0); + + testListObjects( + "[bucket, prefix, recursive]", + ListObjectsArgs.builder().bucket(getRandomName()).prefix("minio").recursive(true).build(), + 3, + 0); + + testListObjects( + "[bucket, versions]", + ListObjectsArgs.builder().bucket(getRandomName()).includeVersions(true).build(), + 3, + 2); + + if (isQuickTest) return; + + testListObjects( + "[empty bucket]", ListObjectsArgs.builder().bucket(getRandomName()).build(), 0, 0); + + testListObjects( + "[bucket, prefix, recursive, 1050 objects]", + ListObjectsArgs.builder().bucket(getRandomName()).prefix("minio").recursive(true).build(), + 1050, + 0); + + testListObjects( + "[bucket, apiVersion1]", + ListObjectsArgs.builder().bucket(getRandomName()).useApiVersion1(true).build(), + 3, + 0); + } + + public void testRemoveObject(String testTags, ServerSideEncryption sse, RemoveObjectArgs args) + throws Exception { + String methodName = "removeObject()"; + long startTime = System.currentTimeMillis(); + try { + PutObjectArgs.Builder builder = + PutObjectArgs.builder().bucket(args.bucket()).object(args.object()).stream( + new ContentInputStream(1), 1L, null); + if (sse != null) builder.sse(sse); + client.putObject(builder.build()); + client.removeObject(args); + mintSuccessLog(methodName, testTags, startTime); + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void removeObject() throws Exception { + String methodName = "removeObject()"; + if (!MINT_ENV) System.out.println(methodName); + + testRemoveObject( + "[base check]", + null, + RemoveObjectArgs.builder().bucket(bucketName).object(getRandomName()).build()); + testRemoveObject( + "[multiple path segments]", + null, + RemoveObjectArgs.builder().bucket(bucketName).object("path/to/" + getRandomName()).build()); + + if (isQuickTest) return; + + testRemoveObject( + "[SSE-S3]", + SSE_S3, + RemoveObjectArgs.builder().bucket(bucketName).object(getRandomName()).build()); + + if (!isSecureEndpoint) { + mintIgnoredLog(methodName, "[SSE-C]", System.currentTimeMillis()); + mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); + return; + } + + testRemoveObject( + "[SSE-C]", + SSE_C, + RemoveObjectArgs.builder().bucket(bucketName).object(getRandomName()).build()); + + if (sseKms == null) { + mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); + return; + } + + testRemoveObject( + "[SSE-KMS]", + sseKms, + RemoveObjectArgs.builder().bucket(bucketName).object(getRandomName()).build()); + } + + public void testRemoveObjects(String testTags, List results) + throws Exception { + String methodName = "removeObjects()"; + long startTime = System.currentTimeMillis(); + try { + removeObjects(bucketName, results); + mintSuccessLog(methodName, testTags, startTime); + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } finally { + removeObjects(bucketName, results); + } + } + + public void removeObjects() throws Exception { + String methodName = "removeObjects()"; + if (!MINT_ENV) System.out.println(methodName); + + testRemoveObjects("[basic]", createObjects(bucketName, 3, 0)); + + String testTags = "[3005 objects]"; + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + List results = new ArrayList<>(); + for (int i = 0; i < 3004; i++) { + results.add( + new ObjectWriteResponse(null, bucketName, null, objectName + "-" + i, null, null)); + } + List existingObject = createObjects(bucketName, 1, 0); + results.addAll(existingObject); + testRemoveObjects(testTags, results); + try { + client.statObject( + StatObjectArgs.builder() + .bucket(bucketName) + .object(existingObject.get(0).object()) + .build()); + handleException( + methodName, + testTags, + startTime, + new Exception("object " + existingObject.get(0).object() + " still exist")); + } catch (ErrorResponseException e) { + if (!e.errorResponse().code().equals("NoSuchKey")) throw e; + } + } + + public void testGetPresignedUrl(GetPresignedObjectUrlArgs args, String expectedChecksum) + throws Exception { + String urlString = client.getPresignedObjectUrl(args); + byte[] data = readObject(urlString); + String checksum = getSha256Sum(new ByteArrayInputStream(data), data.length); + Assertions.assertEquals( + expectedChecksum, + checksum, + "content checksum differs; expected = " + expectedChecksum + ", got = " + checksum); + } + + public void testGetPresignedObjectUrlForGet() throws Exception { + String methodName = "getPresignedObjectUrl()"; + String testTags = null; + long startTime = System.currentTimeMillis(); + try { + String expectedChecksum = getSha256Sum(new ContentInputStream(1 * KB), 1 * KB); + String objectName = getRandomName(); + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + + try { + testTags = "[GET]"; + testGetPresignedUrl( + GetPresignedObjectUrlArgs.builder() + .method(Http.Method.GET) + .bucket(bucketName) + .object(objectName) + .build(), + expectedChecksum); + + testTags = "[GET, expiry]"; + testGetPresignedUrl( + GetPresignedObjectUrlArgs.builder() + .method(Http.Method.GET) + .bucket(bucketName) + .object(objectName) + .expiry(1, TimeUnit.DAYS) + .build(), + expectedChecksum); + + testTags = "[GET, expiry, query params]"; + Map queryParams = new HashMap<>(); + queryParams.put("response-content-type", "application/json"); + testGetPresignedUrl( + GetPresignedObjectUrlArgs.builder() + .method(Http.Method.GET) + .bucket(bucketName) + .object(objectName) + .expiry(1, TimeUnit.DAYS) + .extraQueryParams(queryParams) + .build(), + expectedChecksum); + + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void testPutPresignedUrl( + String testTags, byte[] data, String expectedChecksum, GetPresignedObjectUrlArgs args) + throws Exception { + String methodName = "getPresignedObjectUrl()"; + long startTime = System.currentTimeMillis(); + try { + String urlString = client.getPresignedObjectUrl(args); + try { + writeObject(urlString, data); + InputStream is = + client.getObject( + GetObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); + data = readAllBytes(is); + String checksum = getSha256Sum(new ByteArrayInputStream(data), data.length); + Assertions.assertEquals( + expectedChecksum, + checksum, + "content checksum differs; expected = " + expectedChecksum + ", got = " + checksum); + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void testGetPresignedObjectUrlForPut() throws Exception { + byte[] data = "hello, world".getBytes(StandardCharsets.UTF_8); + String expectedChecksum = getSha256Sum(new ByteArrayInputStream(data), data.length); + String objectName = getRandomName(); + + testPutPresignedUrl( + "[PUT]", + data, + expectedChecksum, + GetPresignedObjectUrlArgs.builder() + .method(Http.Method.PUT) + .bucket(bucketName) + .object(objectName) + .build()); + + testPutPresignedUrl( + "[PUT, expiry]", + data, + expectedChecksum, + GetPresignedObjectUrlArgs.builder() + .method(Http.Method.PUT) + .bucket(bucketName) + .object(objectName) + .expiry(1, TimeUnit.DAYS) + .build()); + } + + public void getPresignedObjectUrl() throws Exception { + if (!MINT_ENV) System.out.println("getPresignedObjectUrl()"); + + testGetPresignedObjectUrlForGet(); + testGetPresignedObjectUrlForPut(); + } + + public void getPresignedPostFormData() throws Exception { + String methodName = "getPresignedPostFormData()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + try { + String objectName = getRandomName(); + + PostPolicy policy = new PostPolicy(bucketName, ZonedDateTime.now().plusDays(7)); + policy.addEqualsCondition("key", objectName); + policy.addEqualsCondition("content-type", "image/png"); + policy.addContentLengthRangeCondition(1 * MB, 4 * MB); + Map formData = client.getPresignedPostFormData(policy); + + MultipartBody.Builder multipartBuilder = new MultipartBody.Builder(); + multipartBuilder.setType(MultipartBody.FORM); + for (Map.Entry entry : formData.entrySet()) { + multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue()); + } + multipartBuilder.addFormDataPart("key", objectName); + multipartBuilder.addFormDataPart("Content-Type", "image/png"); + multipartBuilder.addFormDataPart( + "file", + objectName, + RequestBody.create(readAllBytes(new ContentInputStream(1 * MB)), null)); + + String urlString = + client.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Http.Method.GET) + .bucket(bucketName) + .object("x") + .build()); + urlString = urlString.split("\\?")[0]; // Remove query parameters. + // remove last two characters to get clean url string of bucket. + urlString = urlString.substring(0, urlString.length() - 2); + Request request = new Request.Builder().url(urlString).post(multipartBuilder.build()).build(); + try (Response response = newHttpClient().newCall(request).execute()) { + Assertions.assertNotNull(response, "no response from server"); + if (!response.isSuccessful()) { + String errorXml = response.body().string(); + throw new Exception( + "failed to upload object. Response: " + response + ", Error: " + errorXml); + } + } + client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void testCopyObject( + String testTags, ServerSideEncryption sse, CopyObjectArgs args, boolean negativeCase) + throws Exception { + String methodName = "copyObject()"; + long startTime = System.currentTimeMillis(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(args.source().bucket()).build()); + try { + PutObjectArgs.Builder builder = + PutObjectArgs.builder().bucket(args.source().bucket()).object(args.source().object()) + .stream(new ContentInputStream(1 * KB), 1L * KB, null); + if (sse != null) builder.sse(sse); + client.putObject(builder.build()); + + if (negativeCase) { + try { + client.copyObject(args); + } catch (ErrorResponseException e) { + if (!e.errorResponse().code().equals("PreconditionFailed")) { + throw e; + } + } + } else { + client.copyObject(args); + + ServerSideEncryption.CustomerKey ssec = null; + if (sse instanceof ServerSideEncryption.CustomerKey) { + ssec = (ServerSideEncryption.CustomerKey) sse; + } + client.statObject( + StatObjectArgs.builder() + .bucket(args.bucket()) + .object(args.object()) + .ssec(ssec) + .build()); + } + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder() + .bucket(args.source().bucket()) + .object(args.source().object()) + .build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); + client.removeBucket(RemoveBucketArgs.builder().bucket(args.source().bucket()).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void testCopyObjectMatchETag() throws Exception { + String methodName = "copyObject()"; + String testTags = "[match etag]"; + long startTime = System.currentTimeMillis(); + String srcBucketName = getRandomName(); + String srcObjectName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(srcBucketName).build()); + try { + ObjectWriteResponse result = + client.putObject( + PutObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + + client.copyObject( + CopyObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .source( + SourceObject.builder() + .bucket(srcBucketName) + .object(srcObjectName) + .matchETag(result.etag()) + .build()) + .build()); + + client.statObject( + StatObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); + + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); + client.removeBucket(RemoveBucketArgs.builder().bucket(srcBucketName).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void testCopyObjectMetadataReplace() throws Exception { + String methodName = "copyObject()"; + String testTags = "[metadata replace]"; + long startTime = System.currentTimeMillis(); + String srcBucketName = getRandomName(); + String srcObjectName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(srcBucketName).build()); + try { + client.putObject( + PutObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + + Map headers = new HashMap<>(); + headers.put("Content-Type", CUSTOM_CONTENT_TYPE); + headers.put("X-Amz-Meta-My-Project", "Project One"); + client.copyObject( + CopyObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .source(SourceObject.builder().bucket(srcBucketName).object(srcObjectName).build()) + .headers(headers) + .metadataDirective(Directive.REPLACE) + .build()); + + StatObjectResponse stat = + client.statObject( + StatObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .build()); + Assertions.assertEquals( + CUSTOM_CONTENT_TYPE, + stat.contentType(), + "content type differs. expected: " + + CUSTOM_CONTENT_TYPE + + ", got: " + + stat.contentType()); + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); + client.removeBucket(RemoveBucketArgs.builder().bucket(srcBucketName).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void testCopyObjectEmptyMetadataReplace() throws Exception { + String methodName = "copyObject()"; + String testTags = "[empty metadata replace]"; + long startTime = System.currentTimeMillis(); + String srcBucketName = getRandomName(); + String srcObjectName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(srcBucketName).build()); + try { + Map headers = new HashMap<>(); + headers.put("Content-Type", CUSTOM_CONTENT_TYPE); + headers.put("X-Amz-Meta-My-Project", "Project One"); + client.putObject( + PutObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .headers(headers) + .build()); + + client.copyObject( + CopyObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .source(SourceObject.builder().bucket(srcBucketName).object(srcObjectName).build()) + .metadataDirective(Directive.REPLACE) + .build()); + + StatObjectResponse stat = + client.statObject( + StatObjectArgs.builder() + .bucket(bucketName) + .object(srcObjectName + "-copy") + .build()); + Assertions.assertFalse( + stat.userMetadata().containsKey("My-Project"), + "expected user metadata to be removed in new object"); + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(srcObjectName + "-copy").build()); + client.removeBucket(RemoveBucketArgs.builder().bucket(srcBucketName).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void copyObject() throws Exception { + String methodName = "copyObject()"; + if (!MINT_ENV) System.out.println(methodName); + + String objectName = getRandomName(); + testCopyObject( + "[basic check]", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .source(SourceObject.builder().bucket(getRandomName()).object(objectName).build()) + .build(), + false); + + if (isQuickTest) return; + + testCopyObject( + "[negative match etag]", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .source( + SourceObject.builder() + .bucket(getRandomName()) + .object(getRandomName()) + .matchETag("invalid-etag") + .build()) + .build(), + true); + + testCopyObjectMatchETag(); + + testCopyObject( + "[not match etag]", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .source( + SourceObject.builder() + .bucket(getRandomName()) + .object(getRandomName()) + .notMatchETag("not-etag-of-source-object") + .build()) + .build(), + false); + + testCopyObject( + "[modified since]", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .source( + SourceObject.builder() + .bucket(getRandomName()) + .object(getRandomName()) + .modifiedSince(ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)) + .build()) + .build(), + false); + + testCopyObject( + "[negative unmodified since]", + null, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .source( + SourceObject.builder() + .bucket(getRandomName()) + .object(getRandomName()) + .unmodifiedSince(ZonedDateTime.of(2015, 05, 3, 3, 10, 10, 0, Time.UTC)) + .build()) + .build(), + true); + + testCopyObjectMetadataReplace(); + testCopyObjectEmptyMetadataReplace(); + + testCopyObject( + "[SSE-S3]", + SSE_S3, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sse(SSE_S3) + .source(SourceObject.builder().bucket(getRandomName()).object(getRandomName()).build()) + .build(), + false); + + if (!isSecureEndpoint) { + mintIgnoredLog(methodName, "[SSE-C]", System.currentTimeMillis()); + mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); + return; + } + + testCopyObject( + "[SSE-C]", + SSE_C, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sse(SSE_C) + .source( + SourceObject.builder() + .bucket(getRandomName()) + .object(getRandomName()) + .ssec(SSE_C) + .build()) + .build(), + false); + + if (sseKms == null) { + mintIgnoredLog(methodName, "[SSE-KMS]", System.currentTimeMillis()); + return; + } + + testCopyObject( + "[SSE-KMS]", + sseKms, + CopyObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sse(sseKms) + .source(SourceObject.builder().bucket(getRandomName()).object(getRandomName()).build()) + .build(), + false); + } + + public void testComposeObject(String testTags, ComposeObjectArgs args) throws Exception { + String methodName = "composeObject()"; + long startTime = System.currentTimeMillis(); + try { + client.composeObject(args); + client.removeObject( + RemoveObjectArgs.builder().bucket(args.bucket()).object(args.object()).build()); + mintSuccessLog(methodName, testTags, startTime); + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public List createSourceObjectList(SourceObject... sources) { + return Arrays.asList(sources); + } + + public void composeObjectTests(String object1Mb, String object6Mb, String object6MbSsec) + throws Exception { + testComposeObject( + "[single source]", + ComposeObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sources( + createSourceObjectList( + SourceObject.builder().bucket(bucketName).object(object1Mb).build())) + .build()); + + testComposeObject( + "[single source with offset]", + ComposeObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sources( + createSourceObjectList( + SourceObject.builder() + .bucket(bucketName) + .object(object1Mb) + .offset(2L * KB) + .build())) + .build()); + + testComposeObject( + "[single source with offset and length]", + ComposeObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sources( + createSourceObjectList( + SourceObject.builder() + .bucket(bucketName) + .object(object1Mb) + .offset(2L * KB) + .length(5L * KB) + .build())) + .build()); + + testComposeObject( + "[single multipart source]", + ComposeObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sources( + createSourceObjectList( + SourceObject.builder().bucket(bucketName).object(object6Mb).build())) + .build()); + + testComposeObject( + "[two multipart source]", + ComposeObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sources( + createSourceObjectList( + SourceObject.builder().bucket(bucketName).object(object6Mb).build(), + SourceObject.builder().bucket(bucketName).object(object6Mb).build())) + .build()); + + testComposeObject( + "[two multipart sources with offset and length]", + ComposeObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sources( + createSourceObjectList( + SourceObject.builder() + .bucket(bucketName) + .object(object6Mb) + .offset(10L) + .length(6291436L) + .build(), + SourceObject.builder().bucket(bucketName).object(object6Mb).build())) + .build()); + + if (isQuickTest) return; + + if (!isSecureEndpoint) return; + + testComposeObject( + "[two SSE-C multipart sources]", + ComposeObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sse(SSE_C) + .sources( + createSourceObjectList( + SourceObject.builder() + .bucket(bucketName) + .object(object6MbSsec) + .ssec(SSE_C) + .build(), + SourceObject.builder() + .bucket(bucketName) + .object(object6MbSsec) + .ssec(SSE_C) + .build())) + .build()); + + testComposeObject( + "[two multipart sources with one SSE-C]", + ComposeObjectArgs.builder() + .bucket(bucketName) + .object(getRandomName()) + .sources( + createSourceObjectList( + SourceObject.builder() + .bucket(bucketName) + .object(object6MbSsec) + .ssec(SSE_C) + .build(), + SourceObject.builder().bucket(bucketName).object(object6Mb).build())) + .build()); + } + + public void composeObject() throws Exception { + String methodName = "composeObject()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + List createdObjects = new ArrayList<>(); + + try { + String object1Mb = null; + String object6Mb = null; + String object6MbSsec = null; + try { + ObjectWriteResponse response; + response = + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(1 * MB), 1L * MB, null) + .build()); + createdObjects.add(response); + object1Mb = response.object(); + + response = + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(6 * MB), 6L * MB, null) + .build()); + createdObjects.add(response); + object6Mb = response.object(); + + if (isSecureEndpoint) { + response = + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(getRandomName()).stream( + new ContentInputStream(6 * MB), 6L * MB, null) + .sse(SSE_C) + .build()); + createdObjects.add(response); + object6MbSsec = response.object(); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + + composeObjectTests(object1Mb, object6Mb, object6MbSsec); + } finally { + removeObjects(bucketName, createdObjects); + } + } + + public void checkObjectLegalHold(String bucketName, String objectName, boolean enableCheck) + throws Exception { + if (enableCheck) { + client.enableObjectLegalHold( + EnableObjectLegalHoldArgs.builder().bucket(bucketName).object(objectName).build()); + } else { + client.disableObjectLegalHold( + DisableObjectLegalHoldArgs.builder().bucket(bucketName).object(objectName).build()); + } + + boolean result = + client.isObjectLegalHoldEnabled( + IsObjectLegalHoldEnabledArgs.builder().bucket(bucketName).object(objectName).build()); + Assertions.assertEquals( + result, enableCheck, "object legal hold: expected: " + enableCheck + ", got: " + result); + } + + public void enableObjectLegalHold() throws Exception { + if (bucketNameWithLock == null) return; + + String methodName = "enableObjectLegalHold()"; + if (!MINT_ENV) System.out.println(methodName); + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + ObjectWriteResponse objectInfo = null; + try { + try { + objectInfo = + client.putObject( + PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + + checkObjectLegalHold(bucketNameWithLock, objectName, true); + client.disableObjectLegalHold( + DisableObjectLegalHoldArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .build()); + mintSuccessLog(methodName, null, startTime); + } finally { + if (objectInfo != null) { + client.removeObject( + RemoveObjectArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .versionId(objectInfo.versionId()) + .build()); + } + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void disableObjectLegalHold() throws Exception { + if (bucketNameWithLock == null) return; + + String methodName = "disableObjectLegalHold()"; + if (!MINT_ENV) System.out.println(methodName); + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + ObjectWriteResponse objectInfo = null; + try { + try { + objectInfo = + client.putObject( + PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + + checkObjectLegalHold(bucketNameWithLock, objectName, false); + client.enableObjectLegalHold( + EnableObjectLegalHoldArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .build()); + checkObjectLegalHold(bucketNameWithLock, objectName, false); + mintSuccessLog(methodName, null, startTime); + } finally { + if (objectInfo != null) { + client.removeObject( + RemoveObjectArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .versionId(objectInfo.versionId()) + .build()); + } + } + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void isObjectLegalHoldEnabled() throws Exception { + if (bucketNameWithLock == null) return; + + String methodName = "isObjectLegalHoldEnabled()"; + if (!MINT_ENV) System.out.println(methodName); + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + ObjectWriteResponse objectInfo = null; + try { + try { + objectInfo = + client.putObject( + PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + + boolean result = + client.isObjectLegalHoldEnabled( + IsObjectLegalHoldEnabledArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .build()); + Assertions.assertFalse(result, "object legal hold: expected: false, got: " + result); + checkObjectLegalHold(bucketNameWithLock, objectName, true); + checkObjectLegalHold(bucketNameWithLock, objectName, false); + mintSuccessLog(methodName, null, startTime); + } finally { + if (objectInfo != null) { + client.removeObject( + RemoveObjectArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .versionId(objectInfo.versionId()) + .build()); + } + } + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void setObjectLockConfiguration() throws Exception { + String methodName = "setObjectLockConfiguration()"; + String testTags = "[COMPLIANCE, 10 days]"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); + try { + ObjectLockConfiguration config = + new ObjectLockConfiguration( + RetentionMode.COMPLIANCE, new ObjectLockConfiguration.RetentionDurationDays(10)); + client.setObjectLockConfiguration( + SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(config).build()); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + mintSuccessLog(methodName, testTags, startTime); + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void testGetObjectLockConfiguration( + String bucketName, RetentionMode mode, ObjectLockConfiguration.RetentionDuration duration) + throws Exception { + ObjectLockConfiguration expectedConfig = new ObjectLockConfiguration(mode, duration); + client.setObjectLockConfiguration( + SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(expectedConfig).build()); + ObjectLockConfiguration config = + client.getObjectLockConfiguration( + GetObjectLockConfigurationArgs.builder().bucket(bucketName).build()); + Assertions.assertEquals( + config.mode(), + expectedConfig.mode(), + "retention mode: expected: " + expectedConfig.mode() + ", got: " + config.mode()); + Assertions.assertFalse( + config.duration().unit() != expectedConfig.duration().unit() + || config.duration().duration() != expectedConfig.duration().duration(), + "retention duration: " + expectedConfig.duration() + ", got: " + config.duration()); + } + + public void getObjectLockConfiguration() throws Exception { + String methodName = "getObjectLockConfiguration()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); + try { + testGetObjectLockConfiguration( + bucketName, + RetentionMode.COMPLIANCE, + new ObjectLockConfiguration.RetentionDurationDays(10)); + testGetObjectLockConfiguration( + bucketName, + RetentionMode.GOVERNANCE, + new ObjectLockConfiguration.RetentionDurationYears(1)); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void deleteObjectLockConfiguration() throws Exception { + String methodName = "deleteObjectLockConfiguration()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).objectLock(true).build()); + try { + client.deleteObjectLockConfiguration( + DeleteObjectLockConfigurationArgs.builder().bucket(bucketName).build()); + ObjectLockConfiguration config = + new ObjectLockConfiguration( + RetentionMode.COMPLIANCE, new ObjectLockConfiguration.RetentionDurationDays(10)); + client.setObjectLockConfiguration( + SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(config).build()); + client.deleteObjectLockConfiguration( + DeleteObjectLockConfigurationArgs.builder().bucket(bucketName).build()); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void setObjectRetention() throws Exception { + if (bucketNameWithLock == null) return; + + String methodName = "setObjectRetention()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + ObjectWriteResponse objectInfo = null; + try { + try { + objectInfo = + client.putObject( + PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + + client.setObjectRetention( + SetObjectRetentionArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .config( + new Retention( + RetentionMode.GOVERNANCE, ZonedDateTime.now(Time.UTC).plusDays(1))) + .build()); + + client.setObjectRetention( + SetObjectRetentionArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .config(new Retention()) + .bypassGovernanceMode(true) + .build()); + } finally { + if (objectInfo != null) { + client.removeObject( + RemoveObjectArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .versionId(objectInfo.versionId()) + .build()); + } + } + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void testGetObjectRetention(SetObjectRetentionArgs args) throws Exception { + client.setObjectRetention(args); + Retention config = + client.getObjectRetention( + GetObjectRetentionArgs.builder().bucket(args.bucket()).object(args.object()).build()); + + if (args.config().mode() == null) { + Assertions.assertFalse( + config != null && config.mode() != null, + "retention mode: expected: , got: " + config.mode()); + } else { + Assertions.assertEquals( + args.config().mode(), + config.mode(), + "retention mode: expected: " + args.config().mode() + ", got: " + config.mode()); + } + + ZonedDateTime expectedDate = args.config().retainUntilDate(); + ZonedDateTime date = (config == null) ? null : config.retainUntilDate(); + + if (expectedDate == null) { + Assertions.assertNull(date, "retention retain-until-date: expected: , got: " + date); + } else { + Assertions.assertEquals( + date.withNano(0), + expectedDate.withNano(0), + "retention retain-until-date: expected: " + + expectedDate.withNano(0) + + ", got: " + + date.withNano(0)); + } + } + + public void getObjectRetention() throws Exception { + if (bucketNameWithLock == null) return; + + String methodName = "getObjectRetention()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + ObjectWriteResponse objectInfo = null; + try { + try { + objectInfo = + client.putObject( + PutObjectArgs.builder().bucket(bucketNameWithLock).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + + testGetObjectRetention( + SetObjectRetentionArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .config( + new Retention( + RetentionMode.GOVERNANCE, ZonedDateTime.now(Time.UTC).plusDays(3))) + .build()); + + // Check shortening retention until period + testGetObjectRetention( + SetObjectRetentionArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .config( + new Retention( + RetentionMode.GOVERNANCE, ZonedDateTime.now(Time.UTC).plusDays(1))) + .bypassGovernanceMode(true) + .build()); + + // Check empty retention. + // Enable below test when minio server release has a fix. + // testGetObjectRetention( + // SetObjectRetentionArgs.builder() + // .bucket(bucketNameWithLock) + // .object(objectName) + // .config(new Retention()) + // .bypassGovernanceMode(true) + // .build()); + } finally { + if (objectInfo != null) { + client.removeObject( + RemoveObjectArgs.builder() + .bucket(bucketNameWithLock) + .object(objectName) + .versionId(objectInfo.versionId()) + .bypassGovernanceMode(true) + .build()); + } + } + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getBucketPolicy() throws Exception { + String methodName = "getBucketPolicy()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + String config = + client.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build()); + Assertions.assertTrue(config.isEmpty(), "policy: expected: \"\", got: " + config); + String policy = + "{'Version':'2012-10-17','Statement':[{'Action':['s3:GetObject'],'Effect':'Allow'," + + "'Principal':{'AWS':['*']},'Resource':['arn:aws:s3:::" + + bucketName + + "/myobject*'],'Sid':''}]}"; + policy = policy.replaceAll("'", "\""); + client.setBucketPolicy( + SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build()); + client.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void setBucketPolicy() throws Exception { + String methodName = "setBucketPolicy()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + String policy = + "{'Version':'2012-10-17','Statement':[{'Action':['s3:GetObject'],'Effect':'Allow'," + + "'Principal':{'AWS':['*']},'Resource':['arn:aws:s3:::" + + bucketName + + "/myobject*'],'Sid':''}]}"; + policy = policy.replaceAll("'", "\""); + client.setBucketPolicy( + SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void deleteBucketPolicy() throws Exception { + String methodName = "deleteBucketPolicy()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + client.deleteBucketPolicy(DeleteBucketPolicyArgs.builder().bucket(bucketName).build()); + + String policy = + "{'Version':'2012-10-17','Statement':[{'Action':['s3:GetObject'],'Effect':'Allow'," + + "'Principal':{'AWS':['*']},'Resource':['arn:aws:s3:::" + + bucketName + + "/myobject*'],'Sid':''}]}"; + policy = policy.replaceAll("'", "\""); + client.setBucketPolicy( + SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build()); + client.deleteBucketPolicy(DeleteBucketPolicyArgs.builder().bucket(bucketName).build()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void testSetBucketLifecycle(String bucketName, LifecycleConfiguration.Rule... rules) + throws Exception { + LifecycleConfiguration config = new LifecycleConfiguration(Arrays.asList(rules)); + client.setBucketLifecycle( + SetBucketLifecycleArgs.builder().bucket(bucketName).config(config).build()); + } + + public void setBucketLifecycle() throws Exception { + String methodName = "setBucketLifecycle()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + testSetBucketLifecycle( + bucketName, + new LifecycleConfiguration.Rule( + Status.ENABLED, + null, + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null, null), + new Filter("logs/"), + "rule2", + null, + null, + null)); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void deleteBucketLifecycle() throws Exception { + String methodName = "deleteBucketLifecycle()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + client.deleteBucketLifecycle( + DeleteBucketLifecycleArgs.builder().bucket(bucketName).build()); + testSetBucketLifecycle( + bucketName, + new LifecycleConfiguration.Rule( + Status.ENABLED, + null, + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null, null), + new Filter("logs/"), + "rule2", + null, + null, + null)); + client.deleteBucketLifecycle( + DeleteBucketLifecycleArgs.builder().bucket(bucketName).build()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getBucketLifecycle() throws Exception { + String methodName = "getBucketLifecycle()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + LifecycleConfiguration config = + client.getBucketLifecycle(GetBucketLifecycleArgs.builder().bucket(bucketName).build()); + Assertions.assertNull(config, "config: expected: , got: "); + testSetBucketLifecycle( + bucketName, + new LifecycleConfiguration.Rule( + Status.ENABLED, + null, + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null, null), + new Filter("logs/"), + "rule2", + null, + null, + null)); + config = + client.getBucketLifecycle(GetBucketLifecycleArgs.builder().bucket(bucketName).build()); + Assertions.assertNotNull(config, "config: expected: , got: "); + List rules = config.rules(); + Assertions.assertEquals( + 1, + config.rules().size(), + "config.rules().size(): expected: 1, got: " + config.rules().size()); + LifecycleConfiguration.Rule rule = rules.get(0); + Assertions.assertEquals( + rule.status(), + Status.ENABLED, + "rule.status(): expected: " + Status.ENABLED + ", got: " + rule.status()); + Assertions.assertNotNull( + rule.expiration(), "rule.expiration(): expected: , got: "); + Assertions.assertEquals( + rule.expiration().days(), + Integer.valueOf(365), + "rule.expiration().days(): expected: 365, got: " + rule.expiration().days()); + Assertions.assertNotNull(rule.filter(), "rule.filter(): expected: , got: "); + Assertions.assertEquals( + "logs/", + rule.filter().prefix(), + "rule.filter().prefix(): expected: logs/, got: " + rule.filter().prefix()); + Assertions.assertEquals( + "rule2", rule.id(), "rule.id(): expected: rule2, got: " + rule.id()); + + testSetBucketLifecycle( + bucketName, + new LifecycleConfiguration.Rule( + Status.ENABLED, + null, + new LifecycleConfiguration.Expiration((ZonedDateTime) null, 365, null, null), + new Filter(""), + null, + null, + null, + null)); + config = + client.getBucketLifecycle(GetBucketLifecycleArgs.builder().bucket(bucketName).build()); + Assertions.assertNotNull(config, "config: expected: , got: "); + Assertions.assertEquals( + config.rules().size(), + 1, + "config.rules().size(): expected: 1, got: " + config.rules().size()); + Assertions.assertNotNull( + config.rules().get(0).filter(), "rule.filter(): expected: , got: "); + Assertions.assertEquals( + "", + config.rules().get(0).filter().prefix(), + "rule.filter().prefix(): expected: , got: " + + config.rules().get(0).filter().prefix()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void setBucketNotification() throws Exception { + String methodName = "setBucketNotification()"; + long startTime = System.currentTimeMillis(); + if (sqsArn == null) { + mintIgnoredLog(methodName, null, startTime); + return; + } + + if (!MINT_ENV) System.out.println(methodName); + + try { + String bucketName = getRandomName(); + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build()); + try { + 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 { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getBucketNotification() throws Exception { + String methodName = "getBucketNotification()"; + long startTime = System.currentTimeMillis(); + if (sqsArn == null) { + mintIgnoredLog(methodName, null, startTime); + return; + } + + if (!MINT_ENV) System.out.println(methodName); + + try { + String bucketName = getRandomName(); + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build()); + try { + 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()); + + NotificationConfiguration config = + client.getBucketNotification( + GetBucketNotificationArgs.builder().bucket(bucketName).build()); + + 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)); + } + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void deleteBucketNotification() throws Exception { + String methodName = "deleteBucketNotification()"; + long startTime = System.currentTimeMillis(); + if (sqsArn == null) { + mintIgnoredLog(methodName, null, startTime); + return; + } + + if (!MINT_ENV) System.out.println(methodName); + + try { + String bucketName = getRandomName(); + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build()); + try { + 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()); + + client.deleteBucketNotification( + DeleteBucketNotificationArgs.builder().bucket(bucketName).build()); + + config = + client.getBucketNotification( + GetBucketNotificationArgs.builder().bucket(bucketName).build()); + if (config.queueConfigurations().size() != 0) { + System.out.println("config: expected: , got: " + Xml.marshal(config)); + } + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void listenBucketNotification() throws Exception { + String methodName = "listenBucketNotification()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String file = createFile1Kb(); + String bucketName = getRandomName(); + CloseableIterator> ci = null; + String mintArgs = + "prefix=prefix, suffix=suffix, events={\"s3:ObjectCreated:*\", \"s3:ObjectAccessed:*\"}"; + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build()); + + String[] events = {"s3:ObjectCreated:*", "s3:ObjectAccessed:*"}; + ci = + client.listenBucketNotification( + ListenBucketNotificationArgs.builder() + .bucket(bucketName) + .prefix("prefix") + .suffix("suffix") + .events(events) + .build()); + + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object("prefix-random-suffix").stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + + while (ci.hasNext()) { + NotificationRecords records = ci.next().get(); + if (records.events().size() == 0) { + continue; + } + + boolean found = false; + for (NotificationRecords.Event event : records.events()) { + if ("prefix-random-suffix".equals(event.object().key())) { + found = true; + break; + } + } + + if (found) break; + } + + mintSuccessLog(methodName, mintArgs, startTime); + } catch (Exception e) { + handleException(methodName, mintArgs, startTime, e); + } finally { + if (ci != null) ci.close(); + + Files.delete(Paths.get(file)); + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object("prefix-random-suffix").build()); + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } + + public void selectObjectContent() throws Exception { + String methodName = "selectObjectContent()"; + String sqlExpression = "select * from S3Object"; + String testArgs = "[sqlExpression: " + sqlExpression + ", requestProgress: true]"; + + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + SelectResponseStream responseStream = null; + try { + String expectedResult = + "1997,Ford,E350,\"ac, abs, moon\",3000.00\n" + + "1999,Chevy,\"Venture \"\"Extended Edition\"\"\",,4900.00\n" + + "1999,Chevy,\"Venture \"\"Extended Edition, Very Large\"\"\",,5000.00\n" + + "1996,Jeep,Grand Cherokee,\"MUST SELL!\n" + + "air, moon roof, loaded\",4799.00\n"; + byte[] data = + ("Year,Make,Model,Description,Price\n" + expectedResult).getBytes(StandardCharsets.UTF_8); + ByteArrayInputStream bais = new ByteArrayInputStream(data); + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( + bais, (long) data.length, null) + .build()); + + InputSerialization is = + InputSerialization.newCSV( + null, false, null, null, InputSerialization.FileHeaderInfo.USE, null, null, null); + OutputSerialization os = + OutputSerialization.newCSV( + null, null, null, OutputSerialization.QuoteFields.ASNEEDED, null); + + responseStream = + client.selectObjectContent( + SelectObjectContentArgs.builder() + .bucket(bucketName) + .object(objectName) + .sqlExpression(sqlExpression) + .inputSerialization(is) + .outputSerialization(os) + .requestProgress(true) + .build()); + + String result = new String(readAllBytes(responseStream), StandardCharsets.UTF_8); + Assertions.assertEquals( + result, + expectedResult, + "result mismatch; expected: " + expectedResult + ", got: " + result); + + Stats stats = responseStream.stats(); + Assertions.assertNotNull(stats, "stats is null"); + Assertions.assertTrue( + stats.bytesScanned() == 256, + "stats.bytesScanned mismatch; expected: 258, got: " + stats.bytesScanned()); + Assertions.assertTrue( + stats.bytesProcessed() == 256, + "stats.bytesProcessed mismatch; expected: 258, got: " + stats.bytesProcessed()); + Assertions.assertTrue( + stats.bytesReturned() == 222, + "stats.bytesReturned mismatch; expected: 222, got: " + stats.bytesReturned()); + mintSuccessLog(methodName, testArgs, startTime); + } catch (Exception e) { + handleException(methodName, testArgs, startTime, e); + } finally { + if (responseStream != null) responseStream.close(); + client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } + } + + public void setBucketEncryption() throws Exception { + String methodName = "setBucketEncryption()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + client.setBucketEncryption( + SetBucketEncryptionArgs.builder() + .bucket(bucketName) + .config(SseConfiguration.newConfigWithSseS3Rule()) + .build()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getBucketEncryption() throws Exception { + String methodName = "getBucketEncryption()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + SseConfiguration config = + client.getBucketEncryption( + GetBucketEncryptionArgs.builder().bucket(bucketName).build()); + Assertions.assertNull(config.rule(), "rule: expected: , got: "); + client.setBucketEncryption( + SetBucketEncryptionArgs.builder() + .bucket(bucketName) + .config(SseConfiguration.newConfigWithSseS3Rule()) + .build()); + config = + client.getBucketEncryption( + GetBucketEncryptionArgs.builder().bucket(bucketName).build()); + Assertions.assertNotNull(config.rule(), "rule: expected: , got: "); + Assertions.assertEquals( + config.rule().sseAlgorithm(), + SseAlgorithm.AES256, + "sse algorithm: expected: " + + SseAlgorithm.AES256 + + ", got: " + + config.rule().sseAlgorithm()); + + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void deleteBucketEncryption() throws Exception { + String methodName = "deleteBucketEncryption()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + client.deleteBucketEncryption( + DeleteBucketEncryptionArgs.builder().bucket(bucketName).build()); + + client.setBucketEncryption( + SetBucketEncryptionArgs.builder() + .bucket(bucketName) + .config(SseConfiguration.newConfigWithSseS3Rule()) + .build()); + client.deleteBucketEncryption( + DeleteBucketEncryptionArgs.builder().bucket(bucketName).build()); + SseConfiguration config = + client.getBucketEncryption( + GetBucketEncryptionArgs.builder().bucket(bucketName).build()); + Assertions.assertNull(config.rule(), "rule: expected: , got: "); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void testBucketCors(String methodName, boolean getTest, boolean deleteTest) + throws Exception { + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + CORSConfiguration expectedConfig = + new CORSConfiguration( + Arrays.asList( + new CORSConfiguration.CORSRule[] { + // Rule 1 + new CORSConfiguration.CORSRule( + Arrays.asList(new String[] {"*"}), // Allowed headers + Arrays.asList(new String[] {"PUT", "POST", "DELETE"}), // Allowed methods + Arrays.asList(new String[] {"http://www.example.com"}), // Allowed origins + Arrays.asList( + new String[] {"x-amz-server-side-encryption"}), // Expose headers + null, // ID + 3000), // Maximum age seconds + // Rule 2 + new CORSConfiguration.CORSRule( + null, // Allowed headers + Arrays.asList(new String[] {"GET"}), // Allowed methods + Arrays.asList(new String[] {"*"}), // Allowed origins + null, // Expose headers + null, // ID + null // Maximum age seconds + ) + })); + client.setBucketCors( + SetBucketCorsArgs.builder().bucket(bucketName).config(expectedConfig).build()); + if (getTest) { + CORSConfiguration config = + client.getBucketCors(GetBucketCorsArgs.builder().bucket(bucketName).build()); + Assertions.assertEquals( + expectedConfig, config, "cors: expected: " + expectedConfig + ", got: " + config); + } + if (deleteTest) { + client.deleteBucketCors(DeleteBucketCorsArgs.builder().bucket(bucketName).build()); + } + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void setBucketCors() throws Exception { + testBucketCors("setBucketCors()", false, false); + } + + public void getBucketCors() throws Exception { + testBucketCors("getBucketCors()", true, false); + } + + public void deleteBucketCors() throws Exception { + testBucketCors("deleteBucketCors()", false, true); + } + + public void setBucketTags() throws Exception { + String methodName = "setBucketTags()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + Map map = new HashMap<>(); + map.put("Project", "Project One"); + map.put("User", "jsmith"); + client.setBucketTags(SetBucketTagsArgs.builder().bucket(bucketName).tags(map).build()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getBucketTags() throws Exception { + String methodName = "getBucketTags()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + Map map = new HashMap<>(); + Tags tags = client.getBucketTags(GetBucketTagsArgs.builder().bucket(bucketName).build()); + Assertions.assertEquals(map, tags.get(), "tags: expected: " + map + ", got: " + tags.get()); + + map.put("Project", "Project One"); + map.put("User", "jsmith"); + client.setBucketTags(SetBucketTagsArgs.builder().bucket(bucketName).tags(map).build()); + tags = client.getBucketTags(GetBucketTagsArgs.builder().bucket(bucketName).build()); + Assertions.assertEquals(map, tags.get(), "tags: expected: " + map + ", got: " + tags.get()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void deleteBucketTags() throws Exception { + String methodName = "deleteBucketTags()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String bucketName = getRandomName(); + try { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + try { + client.deleteBucketTags(DeleteBucketTagsArgs.builder().bucket(bucketName).build()); + + Map map = new HashMap<>(); + map.put("Project", "Project One"); + map.put("User", "jsmith"); + client.setBucketTags(SetBucketTagsArgs.builder().bucket(bucketName).tags(map).build()); + client.deleteBucketTags(DeleteBucketTagsArgs.builder().bucket(bucketName).build()); + Tags tags = client.getBucketTags(GetBucketTagsArgs.builder().bucket(bucketName).build()); + Assertions.assertTrue( + tags.get().isEmpty(), "tags: expected: " + ", got: " + tags.get()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void setObjectTags() throws Exception { + String methodName = "setObjectTags()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + try { + try { + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + Map map = new HashMap<>(); + map.put("Project", "Project One"); + map.put("User", "jsmith"); + client.setObjectTags( + SetObjectTagsArgs.builder().bucket(bucketName).object(objectName).tags(map).build()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getObjectTags() throws Exception { + String methodName = "getObjectTags()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + try { + try { + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + Map map = new HashMap<>(); + Tags tags = + client.getObjectTags( + GetObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); + Assertions.assertEquals(map, tags.get(), "tags: expected: " + map + ", got: " + tags.get()); + + map.put("Project", "Project One"); + map.put("User", "jsmith"); + client.setObjectTags( + SetObjectTagsArgs.builder().bucket(bucketName).object(objectName).tags(map).build()); + tags = + client.getObjectTags( + GetObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); + Assertions.assertEquals(map, tags.get(), "tags: expected: " + map + ", got: " + tags.get()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void deleteObjectTags() throws Exception { + String methodName = "deleteObjectTags()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + try { + try { + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + client.deleteObjectTags( + DeleteObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); + + Map map = new HashMap<>(); + map.put("Project", "Project One"); + map.put("User", "jsmith"); + client.setObjectTags( + SetObjectTagsArgs.builder().bucket(bucketName).object(objectName).tags(map).build()); + client.deleteObjectTags( + DeleteObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); + Tags tags = + client.getObjectTags( + GetObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); + Assertions.assertTrue(tags.get().isEmpty(), "tags: expected: , got: " + tags.get()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getObjectAcl() throws Exception { + String methodName = "getObjectAcl()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + try { + try { + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + AccessControlPolicy policy = + client.getObjectAcl( + GetObjectAclArgs.builder().bucket(bucketName).object(objectName).build()); + Assertions.assertEquals( + policy.accessControlList().grants().get(0).grantee().type(), + AccessControlList.Type.CANONICAL_USER, + "granteeType: expected: " + + AccessControlList.Type.CANONICAL_USER + + ", got: " + + policy.accessControlList().grants().get(0).grantee().type()); + Assertions.assertEquals( + policy.accessControlList().grants().get(0).permission(), + AccessControlList.Permission.FULL_CONTROL, + "permission: expected: " + + AccessControlList.Permission.FULL_CONTROL + + ", got: " + + policy.accessControlList().grants().get(0).permission()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getObjectAttributes() throws Exception { + String methodName = "getObjectAttributes()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String objectName = getRandomName(); + try { + try { + client.putObject( + PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( + new ContentInputStream(1 * KB), 1L * KB, null) + .build()); + GetObjectAttributesResponse response = + client.getObjectAttributes( + GetObjectAttributesArgs.builder() + .bucket(bucketName) + .object(objectName) + .objectAttributes( + new String[] { + "ETag", "Checksum", "ObjectParts", "StorageClass", "ObjectSize" + }) + .build()); + Assertions.assertTrue( + response.result().objectSize() == (1 * KB), + "objectSize: expected: " + (1 * KB) + ", got: " + response.result().objectSize()); + Assertions.assertTrue( + response.result().objectParts().parts().get(0).partNumber() == 1, + "partNumber: expected: 1, got: " + + response.result().objectParts().parts().get(0).partNumber()); + Assertions.assertTrue( + response.result().objectParts().parts().get(0).partSize() == (1 * KB), + "partSize: expected: " + + (1 * KB) + + ", got: " + + response.result().objectParts().parts().get(0).partSize()); + mintSuccessLog(methodName, null, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void setBucketReplication() throws Exception { + String methodName = "setBucketReplication()"; + if (!MINT_ENV) System.out.println(methodName); + + if (REPLICATION_SRC_BUCKET == null + || REPLICATION_ROLE == null + || REPLICATION_BUCKET_ARN == null) { + mintIgnoredLog(methodName, "", System.currentTimeMillis()); + return; + } + + long startTime = System.currentTimeMillis(); + try { + Map tags = new HashMap<>(); + tags.put("key1", "value1"); + tags.put("key2", "value2"); + + ReplicationConfiguration.Rule rule = + new ReplicationConfiguration.Rule( + Status.ENABLED, + new ReplicationConfiguration.Destination( + null, null, REPLICATION_BUCKET_ARN, null, null, null, null), + new ReplicationConfiguration.DeleteMarkerReplication(Status.DISABLED), + null, + new Filter(new Filter.And("TaxDocs", tags, null, null)), + "rule1", + null, + 1, + null, + null); + + List rules = new ArrayList<>(); + rules.add(rule); + + ReplicationConfiguration config = new ReplicationConfiguration(REPLICATION_ROLE, rules); + + client.setBucketReplication( + SetBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).config(config).build()); + client.deleteBucketReplication( + DeleteBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).build()); + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void getBucketReplication() throws Exception { + String methodName = "getBucketReplication()"; + if (!MINT_ENV) System.out.println(methodName); + + if (REPLICATION_SRC_BUCKET == null + || REPLICATION_ROLE == null + || REPLICATION_BUCKET_ARN == null) { + mintIgnoredLog(methodName, "", System.currentTimeMillis()); + return; + } + + long startTime = System.currentTimeMillis(); + try { + ReplicationConfiguration config = + client.getBucketReplication( + GetBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).build()); + Assertions.assertNull(config, "config: expected: , got: "); + + Map tags = new HashMap<>(); + tags.put("key1", "value1"); + tags.put("key2", "value2"); + + ReplicationConfiguration.Rule rule = + new ReplicationConfiguration.Rule( + Status.ENABLED, + new ReplicationConfiguration.Destination( + null, null, REPLICATION_BUCKET_ARN, null, null, null, null), + new ReplicationConfiguration.DeleteMarkerReplication(Status.DISABLED), + null, + new Filter(new Filter.And("TaxDocs", tags, null, null)), + "rule1", + null, + 1, + null, + null); + + List rules = new ArrayList<>(); + rules.add(rule); + + config = new ReplicationConfiguration(REPLICATION_ROLE, rules); + client.setBucketReplication( + SetBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).config(config).build()); + config = + client.getBucketReplication( + GetBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).build()); + Assertions.assertNotNull(config, "config: expected: , got: "); + client.deleteBucketReplication( + DeleteBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).build()); + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void deleteBucketReplication() throws Exception { + String methodName = "deleteBucketReplication()"; + if (!MINT_ENV) System.out.println(methodName); + + if (REPLICATION_SRC_BUCKET == null + || REPLICATION_ROLE == null + || REPLICATION_BUCKET_ARN == null) { + mintIgnoredLog(methodName, "", System.currentTimeMillis()); + return; + } + + long startTime = System.currentTimeMillis(); + try { + client.deleteBucketReplication( + DeleteBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).build()); + + Map tags = new HashMap<>(); + tags.put("key1", "value1"); + tags.put("key2", "value2"); + + ReplicationConfiguration.Rule rule = + new ReplicationConfiguration.Rule( + Status.ENABLED, + new ReplicationConfiguration.Destination( + null, null, REPLICATION_BUCKET_ARN, null, null, null, null), + new ReplicationConfiguration.DeleteMarkerReplication(Status.DISABLED), + null, + new Filter(new Filter.And("TaxDocs", tags, null, null)), + "rule1", + null, + 1, + null, + null); + + List rules = new ArrayList<>(); + rules.add(rule); + + ReplicationConfiguration config = new ReplicationConfiguration(REPLICATION_ROLE, rules); + client.setBucketReplication( + SetBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).config(config).build()); + client.deleteBucketReplication( + DeleteBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).build()); + config = + client.getBucketReplication( + GetBucketReplicationArgs.builder().bucket(REPLICATION_SRC_BUCKET).build()); + Assertions.assertNull(config, "config: expected: , got: "); + mintSuccessLog(methodName, null, startTime); + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void testUploadSnowballObjects(String testTags, boolean compression) throws Exception { + String methodName = "uploadSnowballObjects()"; + + long startTime = System.currentTimeMillis(); + String objectName1 = getRandomName(); + String objectName2 = getRandomName(); + try { + try { + List objects = new ArrayList(); + objects.add( + new SnowballObject( + objectName1, + new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)), + 5, + null)); + objects.add(new SnowballObject(objectName2, createFile1Kb())); + client.uploadSnowballObjects( + UploadSnowballObjectsArgs.builder() + .bucket(bucketName) + .objects(objects) + .compression(compression) + .build()); + + StatObjectResponse stat = + client.statObject( + StatObjectArgs.builder().bucket(bucketName).object(objectName1).build()); + Assertions.assertEquals(5, stat.size(), "object size: expected: 5, got: " + stat.size()); + stat = + client.statObject( + StatObjectArgs.builder().bucket(bucketName).object(objectName2).build()); + Assertions.assertEquals( + 1 * KB, stat.size(), "object size: expected: " + KB + ", got: " + stat.size()); + mintSuccessLog(methodName, testTags, startTime); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName1).build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName2).build()); + } + } catch (Exception e) { + handleException(methodName, testTags, startTime, e); + } + } + + public void uploadSnowballObjects() throws Exception { + String methodName = "uploadSnowballObjects()"; + if (!MINT_ENV) System.out.println(methodName); + + testUploadSnowballObjects("[no compression]", false); + testUploadSnowballObjects("[compression]", true); + } + + public void putObjectFanOut() throws Exception { + String methodName = "putObjectFanOut()"; + if (!MINT_ENV) System.out.println(methodName); + + long startTime = System.currentTimeMillis(); + String objectName1 = getRandomName(); + String objectName2 = getRandomName(); + try { + try { + Map map = new HashMap<>(); + map.put("Project", "Project One"); + map.put("User", "jsmith"); + client.putObjectFanOut( + PutObjectFanOutArgs.builder().bucket(bucketName).stream( + new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)), 5) + .entries( + Arrays.asList( + new PutObjectFanOutEntry[] { + PutObjectFanOutEntry.builder().key(objectName1).userMetadata(map).build(), + PutObjectFanOutEntry.builder().key(objectName2).tags(map).build() + })) + .build()); + + StatObjectResponse stat = + client.statObject( + StatObjectArgs.builder().bucket(bucketName).object(objectName1).build()); + Assertions.assertTrue( + map.size() == stat.userMetadata().size() + && map.entrySet().stream() + .allMatch(e -> e.getValue().equals(stat.userMetadata().getFirst(e.getKey()))), + "userMetadata: expected = " + map + ", got = " + stat.userMetadata()); + + Tags tags = + client.getObjectTags( + GetObjectTagsArgs.builder().bucket(bucketName).object(objectName2).build()); + Assertions.assertTrue( + map.equals(tags.get()), "tags: expected = " + map + ", got = " + tags.get()); + } finally { + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName1).build()); + client.removeObject( + RemoveObjectArgs.builder().bucket(bucketName).object(objectName2).build()); + } + } catch (Exception e) { + handleException(methodName, null, startTime, e); + } + } + + public void xmlSubstitutionTests() throws Exception { + String expectedXmlString = + "false${PATH}"; + String xmlString = + Xml.marshal( + new DeleteRequest( + Collections.singletonList(new DeleteRequest.Object("${PATH}")), false)); + if (!expectedXmlString.equals(xmlString)) { + throw new Exception( + "XML marshalling substituted value from environment variable; " + + "expected=" + + expectedXmlString + + ", got=" + + xmlString); + } + + expectedXmlString = + "false${java.version}"; + xmlString = + Xml.marshal( + new DeleteRequest( + Collections.singletonList(new DeleteRequest.Object("${java.version}")), false)); + if (!expectedXmlString.equals(xmlString)) { + throw new Exception( + "XML marshalling substituted value from system property; " + + "expected=" + + expectedXmlString + + ", got=" + + xmlString); + } + + xmlString = + "InvalidTagThe TagKey you have provided is" + + " invalid${PATH}mybucket/mybucket/${PATH}us-east-118669A9C255EC9B53e996b2f640d7e065d3a5c4e39a5538cefb82e3e77771990265e4698d8681eac"; + ErrorResponse response = Xml.unmarshal(ErrorResponse.class, xmlString); + if (!"${PATH}".equals(response.objectName())) { + throw new Exception("XML unmarshalling substituted value from environment variable"); + } + + xmlString = + "InvalidTagThe TagKey you have provided is" + + " invalid${java.version}mybucket/mybucket/${java.version}us-east-118669A9C386C05033e996b2f640d7e065d3a5c4e39a5538cefb82e3e77771990265e4698d8681eac"; + response = Xml.unmarshal(ErrorResponse.class, xmlString); + if (!"${java.version}".equals(response.objectName())) { + throw new Exception("XML unmarshalling substituted value from system property"); + } + } + + public void runBucketTests() throws Exception { + makeBucket(); + bucketExists(); + removeBucket(); + listBuckets(); + + setBucketVersioning(); + getBucketVersioning(); + + setObjectLockConfiguration(); + getObjectLockConfiguration(); + + setBucketEncryption(); + getBucketEncryption(); + deleteBucketEncryption(); + + setBucketCors(); + getBucketCors(); + deleteBucketCors(); + + setBucketTags(); + getBucketTags(); + deleteBucketTags(); + + setBucketPolicy(); + getBucketPolicy(); + deleteBucketPolicy(); + + setBucketLifecycle(); + getBucketLifecycle(); + deleteBucketLifecycle(); + + setBucketNotification(); + getBucketNotification(); + deleteBucketNotification(); + + setBucketReplication(); + getBucketReplication(); + deleteBucketReplication(); + + listenBucketNotification(); + } + + public void runObjectTests() throws Exception { + listObjects(); + + setup(); + + putObject(); + getObject(); + removeObject(); + removeObjects(); + statObject(); + + copyObject(); + composeObject(); + uploadObject(); + downloadObject(); + + setObjectRetention(); + getObjectRetention(); + + getPresignedObjectUrl(); + getPresignedPostFormData(); + + enableObjectLegalHold(); + disableObjectLegalHold(); + isObjectLegalHoldEnabled(); + + selectObjectContent(); + + setObjectTags(); + getObjectTags(); + deleteObjectTags(); + + getObjectAcl(); + getObjectAttributes(); + + uploadSnowballObjects(); + putObjectFanOut(); + + teardown(); + } + + public void runTests() throws Exception { + xmlSubstitutionTests(); + runBucketTests(); + runObjectTests(); + } +}