From be47f215e2c2fb9d0a3a2daf9dc62acd8f607b7b Mon Sep 17 00:00:00 2001 From: AWS Date: Tue, 21 Mar 2017 23:06:56 +0000 Subject: [PATCH] AWS SDK for Android 2.4.0 --- CHANGELOG.md | 24 +- README.md | 2 +- aws-android-sdk-apigateway-core/pom.xml | 4 +- .../apigateway/ApiClientHandler.java | 28 +- aws-android-sdk-autoscaling/pom.xml | 4 +- aws-android-sdk-cloudwatch/pom.xml | 4 +- aws-android-sdk-cognito/pom.xml | 6 +- ...azonaws.aws-android-sdk-cognito.properties | 2 +- .../pom.xml | 6 +- aws-android-sdk-core/pom.xml | 2 +- .../com/amazonaws/SDKGlobalConfiguration.java | 4 +- .../com/amazonaws/util/VersionInfoUtils.java | 2 +- .../amazonaws/util/json/DateDeserializer.java | 63 + ....amazonaws.aws-android-sdk-core.properties | 2 +- .../amazonaws/util/VersionInfoUtilsTest.java | 2 +- aws-android-sdk-ddb-mapper/pom.xml | 8 +- .../dynamodbmapper/DynamoDBMapper.java | 380 +-- .../dynamodbmapper/S3ClientCache.java | 25 +- .../dynamodbmapper/UnmarshallerTests.java | 29 +- .../dynamodbmapper/V1MarshallerTests.java | 23 +- .../V2CompatMarshallerTests.java | 17 +- .../dynamodbmapper/V2MarshallerTests.java | 75 +- aws-android-sdk-ddb/pom.xml | 4 +- aws-android-sdk-ec2/pom.xml | 4 +- aws-android-sdk-elb/pom.xml | 4 +- aws-android-sdk-iot/pom.xml | 4 +- .../iot/AWSIotMqttManager.java | 78 +- aws-android-sdk-kinesis/pom.xml | 4 +- aws-android-sdk-kms/pom.xml | 4 +- aws-android-sdk-lambda/pom.xml | 4 +- aws-android-sdk-lex/pom.xml | 4 +- .../lex/interactionkit/InteractionClient.java | 5 +- .../ui/InteractiveVoiceView.java | 290 ++- .../ui/InteractiveVoiceViewAdapter.java | 7 +- .../src/main/res/drawable-hdpi/lex_speak.png | Bin 0 -> 802 bytes .../src/main/res/drawable-mdpi/lex_speak.png | Bin 0 -> 565 bytes .../src/main/res/drawable-xhdpi/lex_speak.png | Bin 0 -> 968 bytes .../main/res/drawable-xxhdpi/lex_speak.png | Bin 0 -> 1336 bytes aws-android-sdk-machinelearning/pom.xml | 4 +- aws-android-sdk-mobileanalytics/pom.xml | 4 +- aws-android-sdk-pinpoint/pom.xml | 4 +- .../pinpoint/analytics/AnalyticsClient.java | 17 +- .../AmazonMonetizationEventBuilder.java | 44 +- .../CustomMonetizationEventBuilder.java | 27 +- .../GooglePlayMonetizationEventBuilder.java | 40 +- .../MonetizationEventBuilder.java | 8 +- .../internal/event/EventRecorder.java | 99 +- .../internal/event/PinpointDBBase.java | 62 +- .../pinpoint/targeting/TargetingClient.java | 13 +- .../endpointProfile/EndpointProfile.java | 13 +- .../notification/NotificationClient.java | 2 +- .../PinpointNotificationReceiver.java | 7 +- .../AmazonMonetizationEventBuilderTest.java | 45 +- .../CustomMonetizationEventBuilderTest.java | 16 +- ...ooglePlayMonetizationEventBuilderTest.java | 57 +- .../targeting/EndpointProfileTest.java | 2 - aws-android-sdk-polly/pom.xml | 4 +- aws-android-sdk-rekognition/pom.xml | 4 +- aws-android-sdk-s3/pom.xml | 25 +- .../auth/AwsChunkedEncodingInputStream.java | 75 +- .../auth/policy/actions/S3Actions.java | 157 +- .../policy/resources/S3BucketResource.java | 5 +- .../policy/resources/S3ObjectResource.java | 34 +- .../s3/transfermanager/TransferManager.java | 150 +- .../s3/transferutility/TransferDBBase.java | 85 +- .../s3/transferutility/TransferDBUtil.java | 80 +- .../s3/transferutility/TransferObserver.java | 53 +- .../s3/transferutility/TransferRecord.java | 1 + .../s3/transferutility/TransferService.java | 6 +- .../s3/transferutility/TransferUtility.java | 99 +- .../s3/transferutility/UploadTask.java | 54 +- .../com/amazonaws/services/s3/AmazonS3.java | 1013 ++++++-- .../amazonaws/services/s3/AmazonS3Client.java | 2249 +++++++++++------ .../services/s3/AmazonS3Encryption.java | 22 + .../services/s3/AmazonS3EncryptionClient.java | 258 +- .../amazonaws/services/s3/AmazonS3URI.java | 148 +- .../com/amazonaws/services/s3/Headers.java | 31 +- .../services/s3/KeyWrapException.java | 38 + .../amazonaws/services/s3/OnFileDelete.java | 33 + .../services/s3/S3ClientOptions.java | 159 +- .../services/s3/UploadObjectObserver.java | 294 +++ .../internal/AbstractS3ResponseHandler.java | 45 +- .../services/s3/internal/BucketNameUtils.java | 37 +- ...CompleteMultipartUploadRetryCondition.java | 62 + .../services/s3/internal/Constants.java | 52 +- .../DeleteObjectTaggingHeaderHandler.java | 32 + .../s3/internal/DeleteObjectsResponse.java | 18 +- .../s3/internal/FileDeletionEvent.java | 23 + ...GetObjectTaggingResponseHeaderHandler.java | 33 + .../MD5DigestCalculatingInputStream.java | 38 +- .../s3/internal/MultiFileOutputStream.java | 297 +++ .../internal/ObjectRestoreHeaderHandler.java | 5 +- .../s3/internal/PartCreationEvent.java | 58 + .../ProgressReportingInputStream.java | 30 +- .../services/s3/internal/RestUtils.java | 83 +- .../services/s3/internal/S3Direct.java | 17 +- .../services/s3/internal/S3DirectSpi.java | 56 + .../s3/internal/S3ErrorResponseHandler.java | 17 +- .../s3/internal/S3ObjectResponseHandler.java | 11 +- .../S3RequesterChargedHeaderHandler.java | 39 + .../s3/internal/S3RequesterChargedResult.java | 47 + .../services/s3/internal/S3Signer.java | 67 +- .../s3/internal/S3VersionHeaderHandler.java | 8 +- .../services/s3/internal/S3VersionResult.java | 50 + .../services/s3/internal/SSEResultBase.java | 12 - .../ServerSideEncryptionHeaderHandler.java | 7 +- .../internal/ServerSideEncryptionResult.java | 16 - .../services/s3/internal/ServiceUtils.java | 67 +- ...SetObjectTaggingResponseHeaderHandler.java | 33 + .../s3/internal/crypto/CipherLite.java | 17 + .../crypto/CipherLiteInputStream.java | 65 +- .../crypto/ContentCryptoMaterial.java | 706 +++++- .../internal/crypto/ContentCryptoScheme.java | 7 + .../crypto/CryptoModuleDispatcher.java | 127 +- .../s3/internal/crypto/CryptoRuntime.java | 7 +- .../crypto/JceEncryptionConstants.java | 8 +- .../s3/internal/crypto/KMSSecuredCEK.java | 36 + .../crypto/MultipartUploadCbcContext.java | 32 + .../crypto/MultipartUploadCryptoContext.java | 57 +- .../RenewableCipherLiteInputStream.java | 120 + .../s3/internal/crypto/S3CryptoModule.java | 40 +- .../s3/internal/crypto/S3CryptoModuleAE.java | 623 ++--- .../crypto/S3CryptoModuleAEStrict.java | 24 +- .../internal/crypto/S3CryptoModuleBase.java | 915 +++++-- .../s3/internal/crypto/S3CryptoModuleEO.java | 377 +-- .../s3/internal/crypto/S3KeyWrapScheme.java | 11 + .../s3/internal/crypto/S3ObjectWrapper.java | 37 +- .../s3/internal/crypto/SecuredCEK.java | 63 + .../services/s3/metrics/S3ServiceMetric.java | 4 +- .../model/AbortIncompleteMultipartUpload.java | 74 + .../s3/model/AbortMultipartUploadRequest.java | 75 +- .../s3/model/AbstractPutObjectRequest.java | 825 ++++++ .../services/s3/model/AccessControlList.java | 174 +- .../services/s3/model/AmazonS3Exception.java | 59 +- .../amazonaws/services/s3/model/Bucket.java | 3 +- .../model/BucketCrossOriginConfiguration.java | 5 +- .../model/BucketLifecycleConfiguration.java | 379 ++- .../s3/model/BucketLoggingConfiguration.java | 7 +- .../BucketNotificationConfiguration.java | 200 +- .../services/s3/model/BucketPolicy.java | 4 +- .../model/BucketReplicationConfiguration.java | 5 +- .../s3/model/BucketTaggingConfiguration.java | 5 +- .../model/BucketVersioningConfiguration.java | 14 +- .../s3/model/BucketWebsiteConfiguration.java | 3 +- .../s3/model/CannedAccessControlList.java | 9 +- .../services/s3/model/CanonicalGrantee.java | 6 +- .../s3/model/CloudFunctionConfiguration.java | 87 + .../model/CompleteMultipartUploadRequest.java | 91 +- .../model/CompleteMultipartUploadResult.java | 21 +- .../services/s3/model/CopyObjectRequest.java | 145 +- .../services/s3/model/CopyObjectResult.java | 24 +- .../services/s3/model/CopyPartRequest.java | 20 +- .../services/s3/model/CopyPartResult.java | 3 +- .../s3/model/CryptoConfiguration.java | 236 +- ...teBucketAnalyticsConfigurationRequest.java | 81 + ...eteBucketAnalyticsConfigurationResult.java | 25 + ...teBucketInventoryConfigurationRequest.java | 85 + ...eteBucketInventoryConfigurationResult.java | 25 + ...leteBucketMetricsConfigurationRequest.java | 81 + ...eleteBucketMetricsConfigurationResult.java | 25 + .../s3/model/DeleteBucketPolicyRequest.java | 4 +- .../s3/model/DeleteBucketRequest.java | 6 +- ...leteBucketTaggingConfigurationRequest.java | 4 +- ...leteBucketWebsiteConfigurationRequest.java | 4 +- .../s3/model/DeleteObjectRequest.java | 145 +- .../s3/model/DeleteObjectTaggingRequest.java | 130 + .../s3/model/DeleteObjectTaggingResult.java | 53 + .../s3/model/DeleteObjectsRequest.java | 78 +- .../s3/model/DeleteObjectsResult.java | 27 +- .../s3/model/DeleteVersionRequest.java | 4 +- .../s3/model/EncryptedGetObjectRequest.java | 207 ++ ...cryptedInitiateMultipartUploadRequest.java | 62 +- .../s3/model/EncryptedPutObjectRequest.java | 20 +- .../s3/model/EncryptionMaterials.java | 69 +- .../s3/model/EncryptionMaterialsFactory.java | 33 + .../s3/model/ExtraMaterialsDescription.java | 129 + .../amazonaws/services/s3/model/Filter.java | 57 + .../services/s3/model/FilterRule.java | 90 + .../s3/model/GeneratePresignedUrlRequest.java | 344 ++- .../s3/model/GenericBucketRequest.java | 4 +- ...etBucketAnalyticsConfigurationRequest.java | 81 + ...GetBucketAnalyticsConfigurationResult.java | 52 + ...BucketCrossOriginConfigurationRequest.java | 39 + ...etBucketInventoryConfigurationRequest.java | 85 + ...GetBucketInventoryConfigurationResult.java | 51 + ...etBucketLifecycleConfigurationRequest.java | 39 + .../s3/model/GetBucketLocationRequest.java | 4 +- .../GetBucketLoggingConfigurationRequest.java | 39 + .../GetBucketMetricsConfigurationRequest.java | 81 + .../GetBucketMetricsConfigurationResult.java | 52 + ...ucketNotificationConfigurationRequest.java | 42 + .../s3/model/GetBucketPolicyRequest.java | 4 +- ...BucketReplicationConfigurationRequest.java | 5 +- .../GetBucketTaggingConfigurationRequest.java | 39 + ...tBucketVersioningConfigurationRequest.java | 39 + .../s3/model/GetObjectAclRequest.java | 299 +++ .../s3/model/GetObjectMetadataRequest.java | 281 +- .../services/s3/model/GetObjectRequest.java | 673 +++-- .../s3/model/GetObjectTaggingRequest.java | 132 + .../s3/model/GetObjectTaggingResult.java | 89 + ...GetRequestPaymentConfigurationRequest.java | 4 +- .../s3/model/GetS3AccountOwnerRequest.java | 26 + .../model/InitiateMultipartUploadRequest.java | 146 +- .../model/InitiateMultipartUploadResult.java | 60 + .../services/s3/model/InstructionFileId.java | 61 + .../s3/model/KMSEncryptionMaterials.java | 88 + .../model/KMSEncryptionMaterialsProvider.java | 26 + .../s3/model/LambdaConfiguration.java | 65 + ...tBucketAnalyticsConfigurationsRequest.java | 107 + ...stBucketAnalyticsConfigurationsResult.java | 199 ++ ...tBucketInventoryConfigurationsRequest.java | 110 + ...stBucketInventoryConfigurationsResult.java | 201 ++ ...istBucketMetricsConfigurationsRequest.java | 86 + ...ListBucketMetricsConfigurationsResult.java | 199 ++ .../model/ListNextBatchOfObjectsRequest.java | 83 + .../model/ListNextBatchOfVersionsRequest.java | 84 + .../services/s3/model/ListPartsRequest.java | 73 + .../s3/model/MultiObjectDeleteException.java | 11 + .../s3/model/NotificationConfiguration.java | 198 ++ .../services/s3/model/ObjectListing.java | 10 +- .../services/s3/model/ObjectMetadata.java | 228 +- .../services/s3/model/ObjectTagging.java | 65 + .../services/s3/model/PartListing.java | 67 +- .../s3/model/PutInstructionFileRequest.java | 399 +++ .../services/s3/model/PutObjectRequest.java | 840 ++---- .../services/s3/model/PutObjectResult.java | 50 +- .../services/s3/model/QueueConfiguration.java | 88 + .../amazonaws/services/s3/model/Region.java | 11 +- .../s3/model/RestoreObjectRequest.java | 69 + .../services/s3/model/S3DataSource.java | 56 + .../amazonaws/services/s3/model/S3Event.java | 50 + .../services/s3/model/S3KeyFilter.java | 124 + .../amazonaws/services/s3/model/S3Object.java | 77 +- .../services/s3/model/S3ObjectId.java | 107 + .../services/s3/model/S3ObjectIdBuilder.java | 79 + .../s3/model/S3ObjectInputStream.java | 73 +- .../services/s3/model/S3ObjectSummary.java | 12 + .../services/s3/model/SSEAlgorithm.java | 63 + .../s3/model/SSEAwsKeyManagementParams.java | 61 + .../SSEAwsKeyManagementParamsProvider.java | 30 + .../services/s3/model/SSECustomerKey.java | 168 +- .../s3/model/SSECustomerKeyProvider.java | 29 + ...etBucketAnalyticsConfigurationRequest.java | 82 + ...SetBucketAnalyticsConfigurationResult.java | 26 + ...etBucketInventoryConfigurationRequest.java | 86 + ...SetBucketInventoryConfigurationResult.java | 25 + .../SetBucketMetricsConfigurationRequest.java | 83 + .../SetBucketMetricsConfigurationResult.java | 25 + .../s3/model/SetObjectAclRequest.java | 262 ++ .../s3/model/SetObjectTaggingRequest.java | 174 ++ .../s3/model/SetObjectTaggingResult.java | 52 + .../services/s3/model/StorageClass.java | 14 +- .../com/amazonaws/services/s3/model/Tag.java | 115 + .../services/s3/model/TopicConfiguration.java | 88 + .../s3/model/UploadObjectRequest.java | 265 ++ .../services/s3/model/UploadPartRequest.java | 139 +- .../services/s3/model/UploadPartResult.java | 19 +- .../s3/model/WebsiteConfiguration.java | 14 + .../model/analytics/AnalyticsAndOperator.java | 35 + .../analytics/AnalyticsConfiguration.java | 104 + .../analytics/AnalyticsExportDestination.java | 38 + .../s3/model/analytics/AnalyticsFilter.java | 67 + .../analytics/AnalyticsFilterPredicate.java | 28 + .../analytics/AnalyticsNAryOperator.java | 33 + .../analytics/AnalyticsPredicateVisitor.java | 43 + .../analytics/AnalyticsPrefixPredicate.java | 40 + .../AnalyticsS3BucketDestination.java | 137 + .../AnalyticsS3ExportFileFormat.java | 35 + .../analytics/AnalyticsTagPredicate.java | 39 + .../model/analytics/StorageClassAnalysis.java | 49 + .../StorageClassAnalysisDataExport.java | 88 + .../StorageClassAnalysisSchemaVersion.java | 36 + .../inventory/InventoryConfiguration.java | 257 ++ .../model/inventory/InventoryDestination.java | 54 + .../s3/model/inventory/InventoryFilter.java | 61 + .../inventory/InventoryFilterPredicate.java | 31 + .../s3/model/inventory/InventoryFormat.java | 39 + .../model/inventory/InventoryFrequency.java | 38 + .../InventoryIncludedObjectVersions.java | 38 + .../inventory/InventoryOptionalField.java | 49 + .../inventory/InventoryPredicateVisitor.java | 31 + .../inventory/InventoryPrefixPredicate.java | 40 + .../InventoryS3BucketDestination.java | 145 ++ .../s3/model/inventory/InventorySchedule.java | 68 + .../model/lifecycle/LifecycleAndOperator.java | 35 + .../s3/model/lifecycle/LifecycleFilter.java | 77 + .../lifecycle/LifecycleFilterPredicate.java | 34 + .../lifecycle/LifecycleNAryOperator.java | 33 + .../lifecycle/LifecyclePredicateVisitor.java | 44 + .../lifecycle/LifecyclePrefixPredicate.java | 42 + .../lifecycle/LifecycleTagPredicate.java | 40 + .../s3/model/metrics/MetricsAndOperator.java | 36 + .../model/metrics/MetricsConfiguration.java | 80 + .../s3/model/metrics/MetricsFilter.java | 67 + .../model/metrics/MetricsFilterPredicate.java | 33 + .../s3/model/metrics/MetricsNAryOperator.java | 33 + .../metrics/MetricsPredicateVisitor.java | 42 + .../model/metrics/MetricsPrefixPredicate.java | 40 + .../s3/model/metrics/MetricsTagPredicate.java | 39 + .../model/transform/AbstractSSEHandler.java | 29 +- .../BucketConfigurationXmlFactory.java | 727 +++++- ...ficationConfigurationStaxUnmarshaller.java | 97 + .../transform/FilterRuleStaxUnmarshaller.java | 67 + .../transform/FilterStaxUnmarshaller.java | 62 + .../LambdaConfigurationStaxUnmarshaller.java | 107 + ...ficationConfigurationStaxUnmarshaller.java | 102 + .../transform/ObjectTaggingXmlFactory.java | 38 + .../QueueConfigurationStaxUnmarshaller.java | 46 + .../S3KeyFilterStaxUnmarshaller.java | 63 + .../TopicConfigurationStaxUnmarshaller.java | 46 + .../s3/model/transform/Unmarshallers.java | 232 +- .../transform/XmlResponsesSaxParser.java | 1726 +++++++++++-- .../amazonaws/services/s3/package-info.java | 199 +- .../services/s3/Amazons3ClientTest.java | 80 +- .../services/s3/DefaultSigningMethodTest.java | 59 +- .../amazonaws/services/s3/RestUtilsTest.java | 34 +- .../s3/internal/AWSS3V4SignerTest.java | 66 +- .../services/s3/internal/ConstantsTest.java | 3 + .../internal/MultiFileOutputStreamTest.java | 80 + .../ProgressReportingInputStreamTest.java | 180 ++ .../s3/internal/S3QueryStringSignerTest.java | 6 +- .../services/s3/internal/S3SignerTest.java | 10 +- .../s3/internal/ServiceUtilsTest.java | 217 +- .../s3/model/BucketAccelerateStatusTest.java | 33 + .../GeneratePresignedUrlRequestTest.java | 28 + .../NonCurrentVersionTransitionTest.java | 57 + .../services/s3/model/ObjectMetadataTest.java | 140 + .../s3/model/PutObjectRequestTest.java | 120 + .../services/s3/model/RegionTest.java | 29 + .../services/s3/model/TransitionTest.java | 57 + .../BucketAnalyticsSaxUnmarshallerTest.java | 125 + .../BucketInventorySaxUnmarshallerTest.java | 123 + .../BucketLifecycleSaxUnmarshallerTest.java | 143 ++ .../BucketMetricsSaxUnmarshallerTest.java | 100 + ...tionConfigurationStaxUnmarshallerTest.java | 107 + .../transform/CloudFunctionConfiguration.xml | 21 + .../model/transform/GetObjectTagsResponse.xml | 16 + .../model/transform/LambdaConfiguration.xml | 20 + .../s3/model/transform/QueueConfiguration.xml | 20 + .../s3/model/transform/TopicConfiguration.xml | 20 + ...le-configuration-unknown-storage-class.xml | 92 + .../testResponses/errorResponse.xml | 12 + .../testResponses/fullResponse.xml | 18 + .../errorResponse/ErrorResponseBasicTags.xml | 6 + .../ErrorResponseChildTagError.xml | 7 + .../errorResponse/ErrorResponseChildTags.xml | 11 + .../errorResponse/ErrorResponseNoErrorTag.xml | 8 + .../ErrorResponseRepeatedXml.xml | 12 + .../ErrorResponseRootElementAsFoo.xml | 8 + .../ErrorResponseWithAdditionalDetails.xml | 10 + .../ErrorResponseXMLNotProperFormat.xml | 10 + .../marshalling/AnalyticsConfiguration.xml | 25 + .../marshalling/InventoryConfiguration.xml | 24 + .../marshalling/MetricsConfiguration.xml | 16 + .../marshalling/RestoreObjectWithTier.xml | 6 + .../unmarshalling/AnalyticsConfiguration.xml | 25 + .../unmarshalling/InventoryConfiguration.xml | 24 + ...tBucketAnalyticsConfigurationsResponse.xml | 51 + ...tBucketInventoryConfigurationsResponse.xml | 33 + ...istBucketMetricsConfigurationsResponse.xml | 26 + .../unmarshalling/MetricsConfiguration.xml | 16 + .../transform/CloudFunctionConfiguration.xml | 21 + .../model/transform/GetObjectTagsResponse.xml | 16 + .../model/transform/LambdaConfiguration.xml | 20 + .../s3/model/transform/QueueConfiguration.xml | 20 + .../s3/model/transform/TopicConfiguration.xml | 20 + ...le-configuration-unknown-storage-class.xml | 92 + .../errorResponse/ErrorResponseBasicTags.xml | 6 + .../ErrorResponseChildTagError.xml | 7 + .../errorResponse/ErrorResponseChildTags.xml | 11 + .../errorResponse/ErrorResponseNoErrorTag.xml | 8 + .../ErrorResponseRepeatedXml.xml | 12 + .../ErrorResponseRootElementAsFoo.xml | 8 + .../ErrorResponseWithAdditionalDetails.xml | 10 + .../ErrorResponseXMLNotProperFormat.xml | 10 + .../marshalling/AnalyticsConfiguration.xml | 25 + .../marshalling/InventoryConfiguration.xml | 24 + .../marshalling/MetricsConfiguration.xml | 16 + .../marshalling/RestoreObjectWithTier.xml | 6 + .../unmarshalling/AnalyticsConfiguration.xml | 25 + .../unmarshalling/InventoryConfiguration.xml | 24 + ...tBucketAnalyticsConfigurationsResponse.xml | 51 + ...tBucketInventoryConfigurationsResponse.xml | 33 + ...istBucketMetricsConfigurationsResponse.xml | 26 + .../unmarshalling/MetricsConfiguration.xml | 16 + aws-android-sdk-sdb/pom.xml | 4 +- aws-android-sdk-ses/pom.xml | 4 +- aws-android-sdk-sns/pom.xml | 6 +- aws-android-sdk-sqs/pom.xml | 4 +- pom.xml | 2 +- 390 files changed, 27428 insertions(+), 4917 deletions(-) create mode 100644 aws-android-sdk-core/src/main/java/com/amazonaws/util/json/DateDeserializer.java create mode 100644 aws-android-sdk-lex/src/main/res/drawable-hdpi/lex_speak.png create mode 100644 aws-android-sdk-lex/src/main/res/drawable-mdpi/lex_speak.png create mode 100644 aws-android-sdk-lex/src/main/res/drawable-xhdpi/lex_speak.png create mode 100644 aws-android-sdk-lex/src/main/res/drawable-xxhdpi/lex_speak.png create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3Encryption.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/KeyWrapException.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/OnFileDelete.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/UploadObjectObserver.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/CompleteMultipartUploadRetryCondition.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/DeleteObjectTaggingHeaderHandler.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/FileDeletionEvent.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/GetObjectTaggingResponseHeaderHandler.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/MultiFileOutputStream.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/PartCreationEvent.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3DirectSpi.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3RequesterChargedHeaderHandler.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3RequesterChargedResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3VersionResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/SetObjectTaggingResponseHeaderHandler.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/KMSSecuredCEK.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/MultipartUploadCbcContext.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/RenewableCipherLiteInputStream.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/SecuredCEK.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbortIncompleteMultipartUpload.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbstractPutObjectRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CloudFunctionConfiguration.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketAnalyticsConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketAnalyticsConfigurationResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketInventoryConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketInventoryConfigurationResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketMetricsConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketMetricsConfigurationResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectTaggingRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectTaggingResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedGetObjectRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptionMaterialsFactory.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ExtraMaterialsDescription.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Filter.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/FilterRule.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketAnalyticsConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketAnalyticsConfigurationResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketCrossOriginConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketInventoryConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketInventoryConfigurationResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLifecycleConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLoggingConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketMetricsConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketMetricsConfigurationResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketNotificationConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketTaggingConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketVersioningConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectAclRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectTaggingRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectTaggingResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetS3AccountOwnerRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InstructionFileId.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/KMSEncryptionMaterials.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/KMSEncryptionMaterialsProvider.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/LambdaConfiguration.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketAnalyticsConfigurationsRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketAnalyticsConfigurationsResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketInventoryConfigurationsRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketInventoryConfigurationsResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketMetricsConfigurationsRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketMetricsConfigurationsResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListNextBatchOfObjectsRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListNextBatchOfVersionsRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/NotificationConfiguration.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectTagging.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutInstructionFileRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/QueueConfiguration.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3DataSource.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3Event.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3KeyFilter.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectId.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectIdBuilder.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAlgorithm.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAwsKeyManagementParams.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAwsKeyManagementParamsProvider.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSECustomerKeyProvider.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketAnalyticsConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketAnalyticsConfigurationResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketInventoryConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketInventoryConfigurationResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketMetricsConfigurationRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketMetricsConfigurationResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectAclRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectTaggingRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectTaggingResult.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Tag.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/TopicConfiguration.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadObjectRequest.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsAndOperator.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsConfiguration.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsExportDestination.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsFilter.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsFilterPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsNAryOperator.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsPredicateVisitor.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsPrefixPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsS3BucketDestination.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsS3ExportFileFormat.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsTagPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysis.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysisDataExport.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysisSchemaVersion.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryConfiguration.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryDestination.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFilter.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFilterPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFormat.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFrequency.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryIncludedObjectVersions.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryOptionalField.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryPredicateVisitor.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryPrefixPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryS3BucketDestination.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventorySchedule.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleAndOperator.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleFilter.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleFilterPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleNAryOperator.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecyclePredicateVisitor.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecyclePrefixPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleTagPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsAndOperator.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsConfiguration.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsFilter.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsFilterPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsNAryOperator.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsPredicateVisitor.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsPrefixPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsTagPredicate.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/BucketNotificationConfigurationStaxUnmarshaller.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/FilterRuleStaxUnmarshaller.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/FilterStaxUnmarshaller.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/LambdaConfigurationStaxUnmarshaller.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/NotificationConfigurationStaxUnmarshaller.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/ObjectTaggingXmlFactory.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/QueueConfigurationStaxUnmarshaller.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/S3KeyFilterStaxUnmarshaller.java create mode 100644 aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/TopicConfigurationStaxUnmarshaller.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/internal/MultiFileOutputStreamTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/internal/ProgressReportingInputStreamTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/BucketAccelerateStatusTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/GeneratePresignedUrlRequestTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/NonCurrentVersionTransitionTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/ObjectMetadataTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/PutObjectRequestTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/RegionTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/TransitionTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/BucketAnalyticsSaxUnmarshallerTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/BucketInventorySaxUnmarshallerTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/BucketLifecycleSaxUnmarshallerTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/BucketMetricsSaxUnmarshallerTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/BucketNotificationConfigurationStaxUnmarshallerTest.java create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/CloudFunctionConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/GetObjectTagsResponse.xml create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/LambdaConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/QueueConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/TopicConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/model/transform/lifecycle-configuration-unknown-storage-class.xml create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/multiObjectDelete/testResponses/errorResponse.xml create mode 100644 aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/multiObjectDelete/testResponses/fullResponse.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseBasicTags.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseChildTagError.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseChildTags.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseNoErrorTag.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseRepeatedXml.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseRootElementAsFoo.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseWithAdditionalDetails.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseXMLNotProperFormat.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/marshalling/AnalyticsConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/marshalling/InventoryConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/marshalling/MetricsConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/marshalling/RestoreObjectWithTier.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/unmarshalling/AnalyticsConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/unmarshalling/InventoryConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketAnalyticsConfigurationsResponse.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketInventoryConfigurationsResponse.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketMetricsConfigurationsResponse.xml create mode 100644 aws-android-sdk-s3/src/test/java/resources/unmarshalling/MetricsConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/CloudFunctionConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/GetObjectTagsResponse.xml create mode 100644 aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/LambdaConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/QueueConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/TopicConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/lifecycle-configuration-unknown-storage-class.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseBasicTags.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseChildTagError.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseChildTags.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseNoErrorTag.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseRepeatedXml.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseRootElementAsFoo.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseWithAdditionalDetails.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseXMLNotProperFormat.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/marshalling/AnalyticsConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/marshalling/InventoryConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/marshalling/MetricsConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/marshalling/RestoreObjectWithTier.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/unmarshalling/AnalyticsConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/unmarshalling/InventoryConfiguration.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketAnalyticsConfigurationsResponse.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketInventoryConfigurationsResponse.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketMetricsConfigurationsResponse.xml create mode 100644 aws-android-sdk-s3/src/test/resources/resources/unmarshalling/MetricsConfiguration.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 42b465e24a..95c64497fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,31 @@ # Change Log - AWS SDK for Android +## [Release 2.4.0] (https://github.com/aws/aws-sdk-android/releases/tag/release_v2.4.0) (03/21/2017) + +### Improvements: +- **Amazon Lex**:`LexVoiceButton` will now show an image of a bot when audio response from lex is being played. + +### Bug Fixes: + +- **Amazon API Gateway**: Allow marshalling alternative date formats in API Gateway. +- **Amazon Cognito Identity Provider**: Bug fix for missing required attribute Exception +- **Amazon IoT**: fix typo's with variable names in `AWSIoTMQTTManager`. See [issue #220](https://github.com/aws/aws-sdk-android/pull/220). +- **Amazon Lex**: Fix a bug which caused `readyToFulfill` to not file in `InteractionListener`. +- **Amazon Pinpoint**:Fix cursor leakage in Pinpoint. +- **Amazon Pinpoint**:Bug fixes for campaign open rate. +- **Amazon Pinpoint**:`PinpointEndpointClient` to retain instance of endpoint. +- **Amazon Pinpoint**:corrected the implemented for `optout` for profile. +- **Amazon Pinpoint**:Deprecated formatted price in `MonetizationEventBuilder`. +- **Amazon S3**: Bug Fixes with encryption client. +- **Amazon S3**: SigV4 signing is now default for S3. See [issue 234](https://github.com/aws/aws-sdk-android/issues/234) & [issue #108](https://github.com/awslabs/aws-sdk-android-samples/issues/108). +- **Amazon S3**: Added feature to specify listener in `TransferUtility.upload()`. See [issue #210](https://github.com/aws/aws-sdk-android/issues/210). +- **Amazon S3**: Fixed a bug where when using `setAccelerateModeEnabled` caused uploads to fail. See [issue #264](https://github.com/aws/aws-sdk-android/issues/264). +- **General** : Fixed a bug which caused incompatibility between maven releases and releases on [marketing page](https://aws.amazon.com/mobile/sdk). + ## [Release 2.3.9] (https://github.com/aws/aws-sdk-android/releases/tag/release_v2.3.9) (02/02/2017) ### Improvements: -- **Amazon Kinesis Firehose & Amazon Kinesis Streams**: Allow setting a static partition key in the KenesisRecorderConfig. See [issue #228](https://github.com/aws/aws-sdk-android/pull/228). +- **Amazon Kinesis Firehose & Amazon Kinesis Streams**: Allow setting a static partition key in the KinesisRecorderConfig. See [issue #228](https://github.com/aws/aws-sdk-android/pull/228). - **AWS KMS**: Updated service to latest spec. ### Bug Fixes: diff --git a/README.md b/README.md index caeea636f5..b789c66544 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://travis-ci.org/aws/aws-sdk-android.png?branch=master)](https://travis-ci.org/aws/aws-sdk-android) [![GitHub release](https://img.shields.io/github/release/aws/aws-sdk-android.svg)]() [![Maven Central](https://img.shields.io/maven-central/v/com.amazonaws/aws-android-sdk-pom.svg)]() -[![Twitter Follow](https://img.shields.io/twitter/follow/awsformobile.svg?style=social&label=Follow)](https://twitter.com/AWSforMobile) +[![Twitter Follow](https://img.shields.io/twitter/follow/awsformobile.svg?style=social&label=Follow)]() The [AWS SDK for Android](http://aws.amazon.com/sdkforandroid) provides a library and documentation for developers to build connected mobile applications using AWS. diff --git a/aws-android-sdk-apigateway-core/pom.xml b/aws-android-sdk-apigateway-core/pom.xml index 5dbb44b430..be1c0015a7 100644 --- a/aws-android-sdk-apigateway-core/pom.xml +++ b/aws-android-sdk-apigateway-core/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,7 +20,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 diff --git a/aws-android-sdk-apigateway-core/src/main/java/com/amazonaws/mobileconnectors/apigateway/ApiClientHandler.java b/aws-android-sdk-apigateway-core/src/main/java/com/amazonaws/mobileconnectors/apigateway/ApiClientHandler.java index 1e70b1673d..5e8368bf43 100644 --- a/aws-android-sdk-apigateway-core/src/main/java/com/amazonaws/mobileconnectors/apigateway/ApiClientHandler.java +++ b/aws-android-sdk-apigateway-core/src/main/java/com/amazonaws/mobileconnectors/apigateway/ApiClientHandler.java @@ -29,9 +29,12 @@ import com.amazonaws.http.UrlHttpClient; import com.amazonaws.mobileconnectors.apigateway.annotation.Operation; import com.amazonaws.mobileconnectors.apigateway.annotation.Parameter; +import com.amazonaws.util.DateUtils; import com.amazonaws.util.IOUtils; import com.amazonaws.util.StringUtils; +import com.amazonaws.util.json.DateDeserializer; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -43,6 +46,7 @@ import java.lang.reflect.Type; import java.net.URI; import java.util.Collection; +import java.util.Date; import java.util.Map; /** @@ -50,7 +54,13 @@ * response. */ class ApiClientHandler implements InvocationHandler { - private static final Gson gson = new Gson(); + + + private static final Gson gson = new GsonBuilder() + .registerTypeAdapter(Date.class, new DateDeserializer(new String[] { + DateUtils.ISO8601_DATE_PATTERN, DateUtils.ALTERNATE_ISO8601_DATE_PATTERN, + DateUtils.COMPRESSED_DATE_PATTERN, DateUtils.RFC822_DATE_PATTERN + })).create(); private final String endpoint; private final String apiName; @@ -62,12 +72,13 @@ class ApiClientHandler implements InvocationHandler { // 'x-api-key' header. private final String apiKey; - private final HttpClient client; + private HttpClient client; private final HttpRequestFactory requestFactory; private final ClientConfiguration clientConfiguration; ApiClientHandler(String endpoint, String apiName, - Signer signer, AWSCredentialsProvider provider, String apiKey, ClientConfiguration clientConfiguration) { + Signer signer, AWSCredentialsProvider provider, String apiKey, + ClientConfiguration clientConfiguration) { this.endpoint = endpoint; this.apiName = apiName; this.signer = signer; @@ -197,9 +208,9 @@ Request buildRequest(Method method, Object[] args) { void processParameter(Request request, Parameter p, Object arg) { final String name = p.name(); final String location = p.location(); - + if (arg == null) { - return; + return; } if ("header".equals(location)) { @@ -211,8 +222,7 @@ void processParameter(Request request, Parameter p, Object arg) { } else if ("query".equals(location)) { if (Map.class.isAssignableFrom(arg.getClass())) { @SuppressWarnings("unchecked") - final - Map map = (Map) arg; + final Map map = (Map) arg; for (final Map.Entry entry : map.entrySet()) { request.addParameter(entry.getKey(), String.valueOf(entry.getValue())); } @@ -341,4 +351,8 @@ private String joinList(Collection objects) { } return sb.toString(); } + + void setClient(HttpClient client) { + this.client = client; + } } diff --git a/aws-android-sdk-autoscaling/pom.xml b/aws-android-sdk-autoscaling/pom.xml index fdeed03aa7..ce6a3e9f5a 100644 --- a/aws-android-sdk-autoscaling/pom.xml +++ b/aws-android-sdk-autoscaling/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,7 +20,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 diff --git a/aws-android-sdk-cloudwatch/pom.xml b/aws-android-sdk-cloudwatch/pom.xml index bfac570f06..6ba3c8662e 100644 --- a/aws-android-sdk-cloudwatch/pom.xml +++ b/aws-android-sdk-cloudwatch/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,7 +20,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 diff --git a/aws-android-sdk-cognito/pom.xml b/aws-android-sdk-cognito/pom.xml index 64831dfef9..ebec6d7990 100644 --- a/aws-android-sdk-cognito/pom.xml +++ b/aws-android-sdk-cognito/pom.xml @@ -6,14 +6,14 @@ aws-android-sdk-cognito jar AWS SDK for Android - Amazon Cognito Sync - 2.3.9 + 2.4.0 The AWS Android SDK for Amazon Cognito Sync module holds the client classes that are used for communicating with Amazon Cognito Sync Service http://aws.amazon.com/sdkforandroid com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -29,7 +29,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 com.google.android diff --git a/aws-android-sdk-cognito/src/main/resources/fabric/com.amazonaws.aws-android-sdk-cognito.properties b/aws-android-sdk-cognito/src/main/resources/fabric/com.amazonaws.aws-android-sdk-cognito.properties index 41227d1868..041c56298e 100644 --- a/aws-android-sdk-cognito/src/main/resources/fabric/com.amazonaws.aws-android-sdk-cognito.properties +++ b/aws-android-sdk-cognito/src/main/resources/fabric/com.amazonaws.aws-android-sdk-cognito.properties @@ -1,3 +1,3 @@ fabric-identifier=com.amazonaws.aws-android-sdk-cognito -fabric-version=2.3.9 +fabric-version=2.4.0 fabric-build-type=binary diff --git a/aws-android-sdk-cognitoidentityprovider/pom.xml b/aws-android-sdk-cognitoidentityprovider/pom.xml index c7694318cd..419bdfaa2c 100644 --- a/aws-android-sdk-cognitoidentityprovider/pom.xml +++ b/aws-android-sdk-cognitoidentityprovider/pom.xml @@ -6,14 +6,14 @@ aws-android-sdk-cognitoidentityprovider jar AWS SDK for Android - Amazon Cognito Identity Provider - 2.3.9 + 2.4.0 The AWS Android SDK for Amazon Cognito Identity Provider module holds the client classes that are used for communicating with Amazon Cognito Identity Provider Service http://aws.amazon.com/sdkforandroid com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -29,7 +29,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 com.google.android diff --git a/aws-android-sdk-core/pom.xml b/aws-android-sdk-core/pom.xml index 217c40a399..d4cebd4b51 100644 --- a/aws-android-sdk-core/pom.xml +++ b/aws-android-sdk-core/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 diff --git a/aws-android-sdk-core/src/main/java/com/amazonaws/SDKGlobalConfiguration.java b/aws-android-sdk-core/src/main/java/com/amazonaws/SDKGlobalConfiguration.java index 49e15adbb7..991e6675ab 100644 --- a/aws-android-sdk-core/src/main/java/com/amazonaws/SDKGlobalConfiguration.java +++ b/aws-android-sdk-core/src/main/java/com/amazonaws/SDKGlobalConfiguration.java @@ -60,7 +60,7 @@ public class SDKGlobalConfiguration { /** * Path to an override file for the region metadata loaded by the SDK that * maps service/region pairs to endpoints and vice versa. - * + * * @deprecated The SDK no longer supports a regions.xml override */ @Deprecated @@ -102,6 +102,7 @@ public class SDKGlobalConfiguration { * calculating a SHA-256 hash of the entire request body which can be * expensive for large upload requests. */ + @Deprecated public static final String ENABLE_S3_SIGV4_SYSTEM_PROPERTY = "com.amazonaws.services.s3.enableV4"; @@ -113,6 +114,7 @@ public class SDKGlobalConfiguration { * will cause authentication failures in code that accesses buckets in * regions other than US Standard without explicitly configuring a region. */ + @Deprecated public static final String ENFORCE_S3_SIGV4_SYSTEM_PROPERTY = "com.amazonaws.services.s3.enforceV4"; diff --git a/aws-android-sdk-core/src/main/java/com/amazonaws/util/VersionInfoUtils.java b/aws-android-sdk-core/src/main/java/com/amazonaws/util/VersionInfoUtils.java index a489b43c25..10f63387b2 100644 --- a/aws-android-sdk-core/src/main/java/com/amazonaws/util/VersionInfoUtils.java +++ b/aws-android-sdk-core/src/main/java/com/amazonaws/util/VersionInfoUtils.java @@ -23,7 +23,7 @@ */ public class VersionInfoUtils { /** SDK version info */ - private static volatile String version = "2.3.9"; + private static volatile String version = "2.4.0"; // changed build // logic diff --git a/aws-android-sdk-core/src/main/java/com/amazonaws/util/json/DateDeserializer.java b/aws-android-sdk-core/src/main/java/com/amazonaws/util/json/DateDeserializer.java new file mode 100644 index 0000000000..bdefbe2365 --- /dev/null +++ b/aws-android-sdk-core/src/main/java/com/amazonaws/util/json/DateDeserializer.java @@ -0,0 +1,63 @@ +package com.amazonaws.util.json; + +import com.amazonaws.util.DateUtils; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +public class DateDeserializer implements JsonDeserializer, JsonSerializer { + + private SimpleDateFormat mSimpleDateFormat; + private final List dateFormats; + private final SimpleDateFormat mIso8601DateFormat; + + public DateDeserializer(String[] dateFormats) { + this.dateFormats = Arrays.asList(dateFormats); + this.mIso8601DateFormat = new SimpleDateFormat(DateUtils.ISO8601_DATE_PATTERN); + } + + @Override + public Date deserialize(JsonElement element, Type arg1, JsonDeserializationContext context) + throws JsonParseException { + final String dateString = element.getAsString(); + Date date = null; + for (final String df : dateFormats) { + try { + date = new Date(); + mSimpleDateFormat = new SimpleDateFormat(df); + date.setTime(mSimpleDateFormat.parse(dateString).getTime()); + return date; + } catch (final ParseException e) { + // swallow , will try next type of date format + } + } + // default to default implementation + try { + return DateFormat.getDateInstance(DateFormat.DEFAULT).parse(dateString); + } catch (final ParseException e) { + throw new JsonParseException(e.getMessage(), e); + } + + } + + @Override + public JsonElement serialize(Date src, Type typeOfSrc, + JsonSerializationContext context) { + synchronized (mIso8601DateFormat) { + final String dateFormatAsString = mIso8601DateFormat.format(src); + return new JsonPrimitive(dateFormatAsString); + } + } +} diff --git a/aws-android-sdk-core/src/main/resources/fabric/com.amazonaws.aws-android-sdk-core.properties b/aws-android-sdk-core/src/main/resources/fabric/com.amazonaws.aws-android-sdk-core.properties index 4e8def53ec..0ad9298964 100644 --- a/aws-android-sdk-core/src/main/resources/fabric/com.amazonaws.aws-android-sdk-core.properties +++ b/aws-android-sdk-core/src/main/resources/fabric/com.amazonaws.aws-android-sdk-core.properties @@ -1,3 +1,3 @@ fabric-identifier=com.amazonaws.aws-android-sdk-core -fabric-version=2.3.9 +fabric-version=2.4.0 fabric-build-type=binary diff --git a/aws-android-sdk-core/src/test/java/com/amazonaws/util/VersionInfoUtilsTest.java b/aws-android-sdk-core/src/test/java/com/amazonaws/util/VersionInfoUtilsTest.java index 13a9cee551..d5f6ae9f2a 100644 --- a/aws-android-sdk-core/src/test/java/com/amazonaws/util/VersionInfoUtilsTest.java +++ b/aws-android-sdk-core/src/test/java/com/amazonaws/util/VersionInfoUtilsTest.java @@ -24,7 +24,7 @@ public class VersionInfoUtilsTest { @Test public void getVersion() { - assertEquals("2.3.9", VersionInfoUtils.getVersion()); + assertEquals("2.4.0", VersionInfoUtils.getVersion()); } @Test diff --git a/aws-android-sdk-ddb-mapper/pom.xml b/aws-android-sdk-ddb-mapper/pom.xml index 16267b3cae..69a4cee0be 100644 --- a/aws-android-sdk-ddb-mapper/pom.xml +++ b/aws-android-sdk-ddb-mapper/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,19 +20,19 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 com.amazonaws aws-android-sdk-ddb false - 2.3.9 + 2.4.0 com.amazonaws aws-android-sdk-s3 false - 2.3.9 + 2.4.0 junit diff --git a/aws-android-sdk-ddb-mapper/src/main/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/DynamoDBMapper.java b/aws-android-sdk-ddb-mapper/src/main/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/DynamoDBMapper.java index 28095fa45f..354631eec4 100644 --- a/aws-android-sdk-ddb-mapper/src/main/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/DynamoDBMapper.java +++ b/aws-android-sdk-ddb-mapper/src/main/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/DynamoDBMapper.java @@ -91,45 +91,45 @@ *
  * @DynamoDBTable(tableName = "TestTable")
  * public class TestClass {
- * 
+ *
  *     private Long key;
  *     private double rangeKey;
  *     private Long version;
- * 
+ *
  *     private Set<Integer> integerSetAttribute;
- * 
+ *
  *     @DynamoDBHashKey
  *     public Long getKey() {
  *         return key;
  *     }
- * 
+ *
  *     public void setKey(Long key) {
  *         this.key = key;
  *     }
- * 
+ *
  *     @DynamoDBRangeKey
  *     public double getRangeKey() {
  *         return rangeKey;
  *     }
- * 
+ *
  *     public void setRangeKey(double rangeKey) {
  *         this.rangeKey = rangeKey;
  *     }
- * 
+ *
  *     @DynamoDBAttribute(attributeName = "integerSetAttribute")
  *     public Set<Integer> getIntegerAttribute() {
  *         return integerSetAttribute;
  *     }
- * 
+ *
  *     public void setIntegerAttribute(Set<Integer> integerAttribute) {
  *         this.integerSetAttribute = integerAttribute;
  *     }
- * 
+ *
  *     @DynamoDBVersionAttribute
  *     public Long getVersion() {
  *         return version;
  *     }
- * 
+ *
  *     public void setVersion(Long version) {
  *         this.version = version;
  *     }
@@ -317,7 +317,7 @@ public DynamoDBMapper(
         if (s3CredentialsProvider == null) {
             this.s3cc = null;
         } else {
-            this.s3cc = new S3ClientCache(s3CredentialsProvider.getCredentials());
+            this.s3cc = new S3ClientCache(s3CredentialsProvider);
         }
     }
 
@@ -374,29 +374,30 @@ public  T load(T keyObject) {
      */
     public  T load(T keyObject, DynamoDBMapperConfig config) {
         @SuppressWarnings("unchecked")
+        final
         Class clazz = (Class) keyObject.getClass();
 
         config = mergeConfig(config);
-        ItemConverter converter = getConverter(config);
+        final ItemConverter converter = getConverter(config);
 
-        String tableName = getTableName(clazz, keyObject, config);
+        final String tableName = getTableName(clazz, keyObject, config);
 
-        GetItemRequest rq = new GetItemRequest()
+        final GetItemRequest rq = new GetItemRequest()
                 .withRequestMetricCollector(config.getRequestMetricCollector());
 
-        Map key = getKey(converter, keyObject, clazz);
+        final Map key = getKey(converter, keyObject, clazz);
 
         rq.setKey(key);
         rq.setTableName(tableName);
         rq.setConsistentRead(config.getConsistentReads() == ConsistentReads.CONSISTENT);
 
-        GetItemResult item = db.getItem(applyUserAgent(rq));
-        Map itemAttributes = item.getItem();
+        final GetItemResult item = db.getItem(applyUserAgent(rq));
+        final Map itemAttributes = item.getItem();
         if (itemAttributes == null) {
             return null;
         }
 
-        T object = privateMarshallIntoObject(
+        final T object = privateMarshallIntoObject(
                 converter,
                 toParameters(itemAttributes, clazz, tableName, config));
 
@@ -422,12 +423,12 @@ private  Map getKey(
             T keyObject,
             Class clazz) {
 
-        Map key = new HashMap();
-        for (Method keyGetter : reflector.getPrimaryKeyGetters(clazz)) {
-            Object getterResult =
+        final Map key = new HashMap();
+        for (final Method keyGetter : reflector.getPrimaryKeyGetters(clazz)) {
+            final Object getterResult =
                     ReflectionUtils.safeInvoke(keyGetter, keyObject);
 
-            AttributeValue keyAttributeValue =
+            final AttributeValue keyAttributeValue =
                     converter.convert(keyGetter, getterResult);
 
             if (keyAttributeValue == null) {
@@ -462,7 +463,7 @@ private  Map getKey(
     public  T load(Class clazz, Object hashKey, Object rangeKey,
             DynamoDBMapperConfig config) {
         config = mergeConfig(config);
-        T keyObject = createKeyObject(clazz, hashKey, rangeKey);
+        final T keyObject = createKeyObject(clazz, hashKey, rangeKey);
         return load(keyObject, config);
     }
 
@@ -474,12 +475,12 @@  T createKeyObject(Class clazz, Object hashKey, Object rangeKey) {
         T keyObject = null;
         try {
             keyObject = clazz.newInstance();
-        } catch (Exception e) {
+        } catch (final Exception e) {
             throw new DynamoDBMappingException("Failed to instantiate class", e);
         }
         boolean seenHashKey = false;
         boolean seenRangeKey = false;
-        for (Method getter : reflector.getPrimaryKeyGetters(clazz)) {
+        for (final Method getter : reflector.getPrimaryKeyGetters(clazz)) {
             if (ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBHashKey.class)) {
                 if (seenHashKey) {
                     throw new DynamoDBMappingException(
@@ -529,18 +530,18 @@ private Map getHashKeyEqualsConditions(
             ItemConverter converter,
             Object obj) {
 
-        Map conditions = new HashMap();
+        final Map conditions = new HashMap();
         if (obj == null) {
             return conditions;
         }
 
-        for (Method getter : reflector.getRelevantGetters(obj.getClass())) {
+        for (final Method getter : reflector.getRelevantGetters(obj.getClass())) {
             if (ReflectionUtils.getterOrFieldHasAnnotation(
                     getter, DynamoDBHashKey.class)
                     || ReflectionUtils.getterOrFieldHasAnnotation(
                             getter, DynamoDBIndexHashKey.class)) {
 
-                Object getterReturnResult =
+                final Object getterReturnResult =
                         ReflectionUtils.safeInvoke(getter, obj);
 
                 if (getterReturnResult != null) {
@@ -581,7 +582,7 @@ static String internalGetTableName(final Class clazz,
             final DynamoDBMapperConfig config) {
 
         // Resolve by object, if possible and desired
-        DynamoDBMapperConfig.ObjectTableNameResolver objectResolver = config
+        final DynamoDBMapperConfig.ObjectTableNameResolver objectResolver = config
                 .getObjectTableNameResolver();
         if (object != null && objectResolver != null) {
             return objectResolver.getTableName(object, config);
@@ -614,9 +615,9 @@ public  T marshallIntoObject(
             Class clazz,
             Map itemAttributes) {
 
-        ItemConverter converter = getConverter(config);
+        final ItemConverter converter = getConverter(config);
 
-        String tableName = getTableName(clazz, config);
+        final String tableName = getTableName(clazz, config);
 
         return privateMarshallIntoObject(
                 converter,
@@ -630,8 +631,8 @@ private  T privateMarshallIntoObject(
             ItemConverter converter,
             AttributeTransformer.Parameters parameters) {
 
-        Class clazz = parameters.getModelClass();
-        Map values = untransformAttributes(parameters);
+        final Class clazz = parameters.getModelClass();
+        final Map values = untransformAttributes(parameters);
 
         return converter.unconvert(clazz, values);
     }
@@ -646,8 +647,8 @@ private  T privateMarshallIntoObject(
      */
     public  List marshallIntoObjects(Class clazz,
             List> itemAttributes) {
-        List result = new ArrayList(itemAttributes.size());
-        for (Map item : itemAttributes) {
+        final List result = new ArrayList(itemAttributes.size());
+        for (final Map item : itemAttributes) {
             result.add(marshallIntoObject(clazz, item));
         }
         return result;
@@ -664,14 +665,14 @@ public  List marshallIntoObjects(Class clazz,
     final  List marshallIntoObjects(
             final List> parameters
             ) {
-        List result = new ArrayList(parameters.size());
+        final List result = new ArrayList(parameters.size());
 
         ItemConverter converter = null;
         if (!parameters.isEmpty()) {
             converter = getConverter(parameters.get(0).getMapperConfig());
         }
 
-        for (AttributeTransformer.Parameters entry : parameters) {
+        for (final AttributeTransformer.Parameters entry : parameters) {
             result.add(privateMarshallIntoObject(converter, entry));
         }
 
@@ -700,15 +701,15 @@ public  void save(T object, DynamoDBSaveExpression saveExpress
     }
 
     boolean needAutoGenerateAssignableKey(Class clazz, Object object) {
-        Collection keyGetters = reflector.getPrimaryKeyGetters(clazz);
+        final Collection keyGetters = reflector.getPrimaryKeyGetters(clazz);
         boolean forcePut = false;
         /*
          * Determine if there are any auto-assigned keys to assign. If so, force
          * a put and assign the keys.
          */
         boolean hashKeyGetterFound = false;
-        for (Method method : keyGetters) {
-            Object getterResult = ReflectionUtils.safeInvoke(method, object);
+        for (final Method method : keyGetters) {
+            final Object getterResult = ReflectionUtils.safeInvoke(method, object);
             if (getterResult == null && reflector.isAssignableKey(method)) {
                 forcePut = true;
             }
@@ -768,15 +769,15 @@ public  void save(T object, DynamoDBSaveExpression saveExpress
         final ItemConverter converter = getConverter(finalConfig);
 
         @SuppressWarnings("unchecked")
-        Class clazz = (Class) object.getClass();
-        String tableName = getTableName(clazz, object, finalConfig);
+        final Class clazz = (Class) object.getClass();
+        final String tableName = getTableName(clazz, object, finalConfig);
 
         /*
          * We force a putItem request instead of updateItem request either when
          * CLOBBER is configured, or part of the primary key of the object needs
          * to be auto-generated.
          */
-        boolean forcePut = (finalConfig.getSaveBehavior() == SaveBehavior.CLOBBER)
+        final boolean forcePut = (finalConfig.getSaveBehavior() == SaveBehavior.CLOBBER)
                 || needAutoGenerateAssignableKey(clazz, object);
 
         SaveObjectHandler saveObjectHandler;
@@ -870,7 +871,7 @@ protected void onNullNonKeyAttribute(String attributeName) {
 
                 @Override
                 protected void executeLowLevelRequest() {
-                    UpdateItemResult updateItemResult = doUpdateItem();
+                    final UpdateItemResult updateItemResult = doUpdateItem();
 
                     // The UpdateItem request is specified to return ALL_NEW
                     // attributes of the affected item. So if the returned
@@ -886,7 +887,7 @@ protected void executeLowLevelRequest() {
                         // the key attributes (prepared for the
                         // UpdateItemRequest) into the AttributeValueUpdates
                         // collection.
-                        for (String keyAttributeName : getKeyAttributeValues().keySet()) {
+                        for (final String keyAttributeName : getKeyAttributeValues().keySet()) {
                             getAttributeValueUpdates().put(
                                     keyAttributeName,
                                     new AttributeValueUpdate()
@@ -984,21 +985,21 @@ public SaveObjectHandler(
          * The general workflow of a save operation.
          */
         public void execute() {
-            Collection keyGetters = reflector.getPrimaryKeyGetters(clazz);
+            final Collection keyGetters = reflector.getPrimaryKeyGetters(clazz);
 
             /*
              * First handle keys
              */
-            for (Method method : keyGetters) {
-                Object getterResult = ReflectionUtils.safeInvoke(method, object);
-                String attributeName = reflector.getAttributeName(method);
+            for (final Method method : keyGetters) {
+                final Object getterResult = ReflectionUtils.safeInvoke(method, object);
+                final String attributeName = reflector.getAttributeName(method);
 
                 if (getterResult == null && reflector.isAssignableKey(method)) {
                     onAutoGenerateAssignableKey(method, attributeName);
                 }
 
                 else {
-                    AttributeValue newAttributeValue = converter.convert(method, getterResult);
+                    final AttributeValue newAttributeValue = converter.convert(method, getterResult);
                     if (newAttributeValue == null) {
                         throw new DynamoDBMappingException(
                                 "Null or empty value for key: " + method);
@@ -1021,14 +1022,15 @@ public void execute() {
             /*
              * Next construct an update for every non-key property
              */
-            for (Method method : reflector.getRelevantGetters(clazz)) {
+            for (final Method method : reflector.getRelevantGetters(clazz)) {
 
                 // Skip any key methods, since they are handled separately
-                if (keyGetters.contains(method))
+                if (keyGetters.contains(method)) {
                     continue;
+                }
 
-                Object getterResult = ReflectionUtils.safeInvoke(method, object);
-                String attributeName = reflector.getAttributeName(method);
+                final Object getterResult = ReflectionUtils.safeInvoke(method, object);
+                final String attributeName = reflector.getAttributeName(method);
 
                 /*
                  * If this is a versioned field, update it
@@ -1041,7 +1043,7 @@ public void execute() {
                  * Otherwise apply the update value for this attribute.
                  */
                 else {
-                    AttributeValue currentValue = converter.convert(method, getterResult);
+                    final AttributeValue currentValue = converter.convert(method, getterResult);
                     if (currentValue != null) {
                         onNonKeyAttribute(attributeName, currentValue);
                     } else {
@@ -1061,7 +1063,7 @@ public void execute() {
              * currently takes into account of auto-generated keys and versioned
              * attributes.
              */
-            for (ValueUpdate update : inMemoryUpdates) {
+            for (final ValueUpdate update : inMemoryUpdates) {
                 update.apply();
             }
         }
@@ -1155,7 +1157,7 @@ protected List getInMemoryUpdates() {
          * the returned attributes to detect silent failure on the server-side.
          */
         protected UpdateItemResult doUpdateItem() {
-            UpdateItemRequest req = new UpdateItemRequest()
+            final UpdateItemRequest req = new UpdateItemRequest()
                     .withTableName(getTableName())
                     .withKey(getKeyAttributeValues())
                     .withAttributeUpdates(
@@ -1192,7 +1194,7 @@ protected PutItemResult doPutItem() {
                             this.clazz,
                             getTableName(),
                             saveConfig));
-            PutItemRequest req = new PutItemRequest()
+            final PutItemRequest req = new PutItemRequest()
                     .withTableName(getTableName())
                     .withItem(attributeValues)
                     .withExpected(mergeExpectedAttributeValueConditions())
@@ -1203,7 +1205,7 @@ protected PutItemResult doPutItem() {
         }
 
         private void onAutoGenerateAssignableKey(Method method, String attributeName) {
-            AttributeValue newVersionValue = getAutoGeneratedKeyAttributeValue(converter, method);
+            final AttributeValue newVersionValue = getAutoGeneratedKeyAttributeValue(converter, method);
 
             updateValues.put(attributeName,
                     new AttributeValueUpdate().withAction("PUT").withValue(newVersionValue));
@@ -1213,7 +1215,7 @@ private void onAutoGenerateAssignableKey(Method method, String attributeName) {
                     && !internalExpectedValueAssertions.containsKey(attributeName)) {
                 // Add an expect clause to make sure that the item
                 // doesn't already exist, since it's supposed to be new
-                ExpectedAttributeValue expected = new ExpectedAttributeValue();
+                final ExpectedAttributeValue expected = new ExpectedAttributeValue();
                 expected.setExists(false);
                 internalExpectedValueAssertions.put(attributeName, expected);
             }
@@ -1225,11 +1227,11 @@ private void onVersionAttribute(Method method, Object getterResult,
                     && !internalExpectedValueAssertions.containsKey(attributeName)) {
                 // First establish the expected (current) value for the
                 // update call
-                ExpectedAttributeValue expected = new ExpectedAttributeValue();
+                final ExpectedAttributeValue expected = new ExpectedAttributeValue();
 
                 // For new objects, insist that the value doesn't exist.
                 // For existing ones, insist it has the old value.
-                AttributeValue currentValue = converter.convert(method, getterResult);
+                final AttributeValue currentValue = converter.convert(method, getterResult);
                 expected.setExists(currentValue != null);
                 if (currentValue != null) {
                     expected.setValue(currentValue);
@@ -1237,8 +1239,8 @@ private void onVersionAttribute(Method method, Object getterResult,
                 internalExpectedValueAssertions.put(attributeName, expected);
             }
 
-            Object newVersion = incrementor.increment(method, getterResult);
-            AttributeValue newVersionValue = converter.convert(method, newVersion);
+            final Object newVersion = incrementor.increment(method, getterResult);
+            final AttributeValue newVersionValue = converter.convert(method, newVersion);
             updateValues.put(attributeName, new AttributeValueUpdate()
                     .withAction("PUT")
                     .withValue(newVersionValue));
@@ -1285,33 +1287,34 @@ public void delete(Object object, DynamoDBMapperConfig config) {
     public  void delete(T object, DynamoDBDeleteExpression deleteExpression,
             DynamoDBMapperConfig config) {
         config = mergeConfig(config);
-        ItemConverter converter = getConverter(config);
+        final ItemConverter converter = getConverter(config);
 
         @SuppressWarnings("unchecked")
-        Class clazz = (Class) object.getClass();
+        final Class clazz = (Class) object.getClass();
 
-        String tableName = getTableName(clazz, object, config);
+        final String tableName = getTableName(clazz, object, config);
 
-        Map key = getKey(converter, object, clazz);
+        final Map key = getKey(converter, object, clazz);
 
         /*
          * If there is a version field, make sure we assert its value. If the
          * version field is null (only should happen in unusual circumstances),
          * pretend it doesn't have a version field after all.
          */
-        Map internalAssertions = new HashMap();
+        final Map internalAssertions = new HashMap();
         if (config.getSaveBehavior() != SaveBehavior.CLOBBER) {
-            for (Method method : reflector.getRelevantGetters(clazz)) {
+            for (final Method method : reflector.getRelevantGetters(clazz)) {
 
                 if (reflector.isVersionAttributeGetter(method)) {
-                    Object getterResult = ReflectionUtils.safeInvoke(method, object);
-                    String attributeName = reflector.getAttributeName(method);
+                    final Object getterResult = ReflectionUtils.safeInvoke(method, object);
+                    final String attributeName = reflector.getAttributeName(method);
 
-                    ExpectedAttributeValue expected = new ExpectedAttributeValue();
-                    AttributeValue currentValue = converter.convert(method, getterResult);
+                    final ExpectedAttributeValue expected = new ExpectedAttributeValue();
+                    final AttributeValue currentValue = converter.convert(method, getterResult);
                     expected.setExists(currentValue != null);
-                    if (currentValue != null)
+                    if (currentValue != null) {
                         expected.setValue(currentValue);
+                    }
                     internalAssertions.put(attributeName, expected);
                     break;
                 }
@@ -1323,7 +1326,7 @@ public  void delete(T object, DynamoDBDeleteExpression deleteExpression,
                 .withRequestMetricCollector(config.getRequestMetricCollector());
 
         if (deleteExpression != null) {
-            String conditionalExpression = deleteExpression.getConditionExpression();
+            final String conditionalExpression = deleteExpression.getConditionExpression();
 
             if (conditionalExpression != null) {
                 if (internalAssertions != null && !internalAssertions.isEmpty()) {
@@ -1470,25 +1473,25 @@ public List batchWrite(List objectsToWrite,
             List objectsToDelete, DynamoDBMapperConfig config) {
         config = mergeConfig(config);
 
-        List totalFailedBatches = new LinkedList();
+        final List totalFailedBatches = new LinkedList();
 
-        HashMap> requestItems = new HashMap>();
+        final HashMap> requestItems = new HashMap>();
 
-        ItemConverter converter = getConverter(config);
+        final ItemConverter converter = getConverter(config);
 
-        List inMemoryUpdates = new LinkedList();
-        for (Object toWrite : objectsToWrite) {
-            Class clazz = toWrite.getClass();
-            String tableName = getTableName(clazz, toWrite, config);
+        final List inMemoryUpdates = new LinkedList();
+        for (final Object toWrite : objectsToWrite) {
+            final Class clazz = toWrite.getClass();
+            final String tableName = getTableName(clazz, toWrite, config);
 
-            Map attributeValues = new HashMap();
+            final Map attributeValues = new HashMap();
 
             // Look at every getter and construct a value object for it
-            for (Method method : reflector.getRelevantGetters(clazz)) {
-                Object getterResult =
+            for (final Method method : reflector.getRelevantGetters(clazz)) {
+                final Object getterResult =
                         ReflectionUtils.safeInvoke(method, toWrite);
 
-                String attributeName = reflector.getAttributeName(method);
+                final String attributeName = reflector.getAttributeName(method);
 
                 AttributeValue currentValue = null;
                 if (getterResult == null && reflector.isAssignableKey(method)) {
@@ -1507,7 +1510,7 @@ public List batchWrite(List objectsToWrite,
                 requestItems.put(tableName, new LinkedList());
             }
 
-            AttributeTransformer.Parameters parameters =
+            final AttributeTransformer.Parameters parameters =
                     toParameters(attributeValues, clazz, tableName, config);
 
             requestItems.get(tableName).add(
@@ -1516,12 +1519,12 @@ public List batchWrite(List objectsToWrite,
                                     transformAttributes(parameters))));
         }
 
-        for (Object toDelete : objectsToDelete) {
-            Class clazz = toDelete.getClass();
+        for (final Object toDelete : objectsToDelete) {
+            final Class clazz = toDelete.getClass();
 
-            String tableName = getTableName(clazz, toDelete, config);
+            final String tableName = getTableName(clazz, toDelete, config);
 
-            Map key = getKey(converter, toDelete);
+            final Map key = getKey(converter, toDelete);
 
             if (!requestItems.containsKey(tableName)) {
                 requestItems.put(tableName, new LinkedList());
@@ -1534,22 +1537,22 @@ public List batchWrite(List objectsToWrite,
         // Break into chunks of 25 items and make service requests to DynamoDB
         while (!requestItems.isEmpty()) {
 
-            HashMap> batch =
+            final HashMap> batch =
                     new HashMap>();
 
             int i = 0;
 
-            Iterator>> tableIter = requestItems.entrySet()
+            final Iterator>> tableIter = requestItems.entrySet()
                     .iterator();
             while (tableIter.hasNext() && i < MAX_ITEMS_PER_BATCH) {
 
-                Entry> tableRequest = tableIter.next();
+                final Entry> tableRequest = tableIter.next();
 
                 batch.put(tableRequest.getKey(), new LinkedList());
-                Iterator writeRequestIter = tableRequest.getValue().iterator();
+                final Iterator writeRequestIter = tableRequest.getValue().iterator();
 
                 while (writeRequestIter.hasNext() && i++ < MAX_ITEMS_PER_BATCH) {
-                    WriteRequest writeRequest = writeRequestIter.next();
+                    final WriteRequest writeRequest = writeRequestIter.next();
                     batch.get(tableRequest.getKey()).add(writeRequest);
                     writeRequestIter.remove();
                 }
@@ -1561,7 +1564,7 @@ public List batchWrite(List objectsToWrite,
                 }
             }
 
-            List failedBatches = writeOneBatch(batch);
+            final List failedBatches = writeOneBatch(batch);
             if (failedBatches != null) {
                 totalFailedBatches.addAll(failedBatches);
 
@@ -1569,7 +1572,7 @@ public List batchWrite(List objectsToWrite,
                 if (containsThrottlingException(failedBatches)) {
                     try {
                         Thread.sleep(1000 * 2);
-                    } catch (InterruptedException e) {
+                    } catch (final InterruptedException e) {
                         Thread.currentThread().interrupt();
                         throw new AmazonClientException(e.getMessage(), e);
                     }
@@ -1578,7 +1581,7 @@ public List batchWrite(List objectsToWrite,
         }
 
         // Once the entire batch is processed, update assigned keys in memory
-        for (ValueUpdate update : inMemoryUpdates) {
+        for (final ValueUpdate update : inMemoryUpdates) {
             update.apply();
         }
 
@@ -1592,10 +1595,10 @@ public List batchWrite(List objectsToWrite,
      */
     List writeOneBatch(Map> batch) {
 
-        List failedBatches = new LinkedList();
-        Map> firstHalfBatch = new HashMap>();
-        Map> secondHalfBatch = new HashMap>();
-        FailedBatch failedBatch = callUntilCompletion(batch);
+        final List failedBatches = new LinkedList();
+        final Map> firstHalfBatch = new HashMap>();
+        final Map> secondHalfBatch = new HashMap>();
+        final FailedBatch failedBatch = callUntilCompletion(batch);
 
         if (failedBatch != null) {
             // If the exception is request entity too large, we divide the batch
@@ -1629,8 +1632,8 @@ List writeOneBatch(Map> batch) {
      * Check whether there are throttling exception in the failed batches.
      */
     boolean containsThrottlingException(List failedBatches) {
-        for (FailedBatch failedBatch : failedBatches) {
-            Exception e = failedBatch.getException();
+        for (final FailedBatch failedBatch : failedBatches) {
+            final Exception e = failedBatch.getException();
             if (e instanceof AmazonServiceException
                     && RetryUtils.isThrottlingException((AmazonServiceException) e)) {
                 return true;
@@ -1646,13 +1649,13 @@ boolean containsThrottlingException(List failedBatches) {
     private void divideBatch(Map> batch,
             Map> firstHalfBatch,
             Map> secondHalfBatch) {
-        for (String key : batch.keySet()) {
+        for (final String key : batch.keySet()) {
 
-            List requests = batch.get(key);
+            final List requests = batch.get(key);
 
-            List firstHalfRequests = requests.subList(0, requests.size() / 2);
+            final List firstHalfRequests = requests.subList(0, requests.size() / 2);
 
-            List secondHalfRequests = requests.subList(requests.size() / 2,
+            final List secondHalfRequests = requests.subList(requests.size() / 2,
                     requests.size());
 
             firstHalfBatch.put(key, firstHalfRequests);
@@ -1671,7 +1674,7 @@ private int computeFailedBatchSize(FailedBatch failedBatch) {
 
         int count = 0;
 
-        for (String tableName : failedBatch.getUnprocessedItems().keySet()) {
+        for (final String tableName : failedBatch.getUnprocessedItems().keySet()) {
             count += failedBatch.getUnprocessedItems().get(tableName).size();
         }
         return count;
@@ -1690,7 +1693,7 @@ private FailedBatch callUntilCompletion(Map> batch) {
             try {
                 result = db.batchWriteItem(applyBatchOperationUserAgent(
                         new BatchWriteItemRequest().withRequestItems(batch)));
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 failedBatch = new FailedBatch();
                 failedBatch.setUnprocessedItems(batch);
                 failedBatch.setException(e);
@@ -1737,23 +1740,23 @@ public Map> batchLoad(List itemsToGet) {
      */
     public Map> batchLoad(List itemsToGet, DynamoDBMapperConfig config) {
         config = mergeConfig(config);
-        boolean consistentReads = (config.getConsistentReads() == ConsistentReads.CONSISTENT);
+        final boolean consistentReads = (config.getConsistentReads() == ConsistentReads.CONSISTENT);
 
         if (itemsToGet == null || itemsToGet.isEmpty()) {
             return new HashMap>();
         }
 
-        Map requestItems = new HashMap();
-        Map> classesByTableName = new HashMap>();
-        Map> resultSet = new HashMap>();
+        final Map requestItems = new HashMap();
+        final Map> classesByTableName = new HashMap>();
+        final Map> resultSet = new HashMap>();
         int count = 0;
 
-        ItemConverter converter = getConverter(config);
+        final ItemConverter converter = getConverter(config);
 
-        for (Object keyObject : itemsToGet) {
-            Class clazz = keyObject.getClass();
+        for (final Object keyObject : itemsToGet) {
+            final Class clazz = keyObject.getClass();
 
-            String tableName = getTableName(clazz, keyObject, config);
+            final String tableName = getTableName(clazz, keyObject, config);
             classesByTableName.put(tableName, clazz);
 
             if (!requestItems.containsKey(tableName)) {
@@ -1821,11 +1824,11 @@ public Map> batchLoad(Map, List> itemsToG
     public Map> batchLoad(Map, List> itemsToGet,
             DynamoDBMapperConfig config) {
 
-        List keys = new ArrayList();
+        final List keys = new ArrayList();
         if (itemsToGet != null) {
-            for (Class clazz : itemsToGet.keySet()) {
+            for (final Class clazz : itemsToGet.keySet()) {
                 if (itemsToGet.get(clazz) != null) {
-                    for (KeyPair keyPair : itemsToGet.get(clazz)) {
+                    for (final KeyPair keyPair : itemsToGet.get(clazz)) {
                         keys.add(createKeyObject(clazz, keyPair.getHashKey(), keyPair.getRangeKey()));
                     }
                 }
@@ -1846,7 +1849,7 @@ private void processBatchGetRequest(
             final ItemConverter converter) {
 
         BatchGetItemResult batchGetItemResult = null;
-        BatchGetItemRequest batchGetItemRequest = new BatchGetItemRequest()
+        final BatchGetItemRequest batchGetItemRequest = new BatchGetItemRequest()
                 .withRequestMetricCollector(config.getRequestMetricCollector());
         batchGetItemRequest.setRequestItems(requestItems);
 
@@ -1870,9 +1873,9 @@ private void processBatchGetRequest(
             batchGetItemResult = db.batchGetItem(
                     applyBatchOperationUserAgent(batchGetItemRequest));
 
-            Map>> responses = batchGetItemResult
+            final Map>> responses = batchGetItemResult
                     .getResponses();
-            for (String tableName : responses.keySet()) {
+            for (final String tableName : responses.keySet()) {
                 List objects = null;
                 if (resultSet.get(tableName) != null) {
                     objects = resultSet.get(tableName);
@@ -1880,10 +1883,10 @@ private void processBatchGetRequest(
                     objects = new LinkedList();
                 }
 
-                Class clazz = classesByTableName.get(tableName);
+                final Class clazz = classesByTableName.get(tableName);
 
-                for (Map item : responses.get(tableName)) {
-                    AttributeTransformer.Parameters parameters =
+                for (final Map item : responses.get(tableName)) {
+                    final AttributeTransformer.Parameters parameters =
                             toParameters(item, clazz, tableName, config);
                     objects.add(privateMarshallIntoObject(converter, parameters));
                 }
@@ -1916,8 +1919,8 @@ public ValueUpdate(
         }
 
         public void apply() {
-            Method setter = reflector.getSetter(method);
-            Object pojo = converter.unconvert(method, setter, newValue);
+            final Method setter = reflector.getSetter(method);
+            final Object pojo = converter.unconvert(method, setter, newValue);
             ReflectionUtils.safeInvoke(setter, target, pojo);
         }
     }
@@ -1927,11 +1930,11 @@ public void apply() {
      * {@link AttributeValue} map.
      */
     private Map convertToItem(Map putValues) {
-        Map map = new HashMap();
-        for (Entry entry : putValues.entrySet()) {
-            String attributeName = entry.getKey();
-            AttributeValue attributeValue = entry.getValue().getValue();
-            String attributeAction = entry.getValue().getAction();
+        final Map map = new HashMap();
+        for (final Entry entry : putValues.entrySet()) {
+            final String attributeName = entry.getKey();
+            final AttributeValue attributeValue = entry.getValue().getValue();
+            final String attributeAction = entry.getValue().getAction();
 
             /*
              * AttributeValueUpdate allows nulls for its values, since they are
@@ -1949,7 +1952,7 @@ private AttributeValue getAutoGeneratedKeyAttributeValue(
             ItemConverter converter,
             Method getter) {
 
-        Class returnType = getter.getReturnType();
+        final Class returnType = getter.getReturnType();
         if (String.class.isAssignableFrom(returnType)) {
             return converter.convert(getter, UUID.randomUUID().toString());
         }
@@ -2002,9 +2005,9 @@ public  PaginatedScanList scan(Class clazz, DynamoDBScanExpression scan
             DynamoDBMapperConfig config) {
         config = mergeConfig(config);
 
-        ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config);
+        final ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config);
 
-        ScanResult scanResult = db.scan(applyUserAgent(scanRequest));
+        final ScanResult scanResult = db.scan(applyUserAgent(scanRequest));
         return new PaginatedScanList(this, clazz, db, scanRequest, scanResult,
                 config.getPaginationLoadingStrategy(), config);
     }
@@ -2063,9 +2066,9 @@ public  PaginatedParallelScanList parallelScan(Class clazz,
 
         // Create hard copies of the original scan request with difference
         // segment number.
-        List parallelScanRequests = createParallelScanRequestsFromExpression(clazz,
+        final List parallelScanRequests = createParallelScanRequestsFromExpression(clazz,
                 scanExpression, totalSegments, config);
-        ParallelScanTask parallelScanTask = new ParallelScanTask(this, db, parallelScanRequests);
+        final ParallelScanTask parallelScanTask = new ParallelScanTask(this, db, parallelScanRequests);
 
         return new PaginatedParallelScanList(this, clazz, db, parallelScanTask,
                 config.getPaginationLoadingStrategy(), config);
@@ -2090,11 +2093,11 @@ public  ScanResultPage scanPage(Class clazz, DynamoDBScanExpression sca
             DynamoDBMapperConfig config) {
         config = mergeConfig(config);
 
-        ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config);
+        final ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config);
 
-        ScanResult scanResult = db.scan(applyUserAgent(scanRequest));
-        ScanResultPage result = new ScanResultPage();
-        List> parameters =
+        final ScanResult scanResult = db.scan(applyUserAgent(scanRequest));
+        final ScanResultPage result = new ScanResultPage();
+        final List> parameters =
                 toParameters(scanResult.getItems(), clazz, scanRequest.getTableName(), config);
 
         result.setResults(marshallIntoObjects(parameters));
@@ -2163,9 +2166,9 @@ public  PaginatedQueryList query(Class clazz,
             DynamoDBQueryExpression queryExpression, DynamoDBMapperConfig config) {
         config = mergeConfig(config);
 
-        QueryRequest queryRequest = createQueryRequestFromExpression(clazz, queryExpression, config);
+        final QueryRequest queryRequest = createQueryRequestFromExpression(clazz, queryExpression, config);
 
-        QueryResult queryResult = db.query(applyUserAgent(queryRequest));
+        final QueryResult queryResult = db.query(applyUserAgent(queryRequest));
         return new PaginatedQueryList(this, clazz, db, queryRequest, queryResult,
                 config.getPaginationLoadingStrategy(), config);
     }
@@ -2204,11 +2207,11 @@ public  QueryResultPage queryPage(Class clazz,
             DynamoDBQueryExpression queryExpression, DynamoDBMapperConfig config) {
         config = mergeConfig(config);
 
-        QueryRequest queryRequest = createQueryRequestFromExpression(clazz, queryExpression, config);
+        final QueryRequest queryRequest = createQueryRequestFromExpression(clazz, queryExpression, config);
 
-        QueryResult scanResult = db.query(applyUserAgent(queryRequest));
-        QueryResultPage result = new QueryResultPage();
-        List> parameters =
+        final QueryResult scanResult = db.query(applyUserAgent(queryRequest));
+        final QueryResultPage result = new QueryResultPage();
+        final List> parameters =
                 toParameters(scanResult.getItems(), clazz, queryRequest.getTableName(), config);
 
         result.setResults(marshallIntoObjects(parameters));
@@ -2247,7 +2250,7 @@ public int count(Class clazz, DynamoDBScanExpression scanExpression,
             DynamoDBMapperConfig config) {
         config = mergeConfig(config);
 
-        ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config);
+        final ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config);
         scanRequest.setSelect(Select.COUNT);
 
         // Count scans can also be truncated for large datasets
@@ -2289,7 +2292,7 @@ public  int count(Class clazz, DynamoDBQueryExpression queryExpression,
             DynamoDBMapperConfig config) {
         config = mergeConfig(config);
 
-        QueryRequest queryRequest = createQueryRequestFromExpression(clazz, queryExpression, config);
+        final QueryRequest queryRequest = createQueryRequestFromExpression(clazz, queryExpression, config);
         queryRequest.setSelect(Select.COUNT);
 
         // Count queries can also be truncated for large datasets
@@ -2309,8 +2312,9 @@ public  int count(Class clazz, DynamoDBQueryExpression queryExpression,
      * returns the result.
      */
     private DynamoDBMapperConfig mergeConfig(DynamoDBMapperConfig config) {
-        if (config != this.config)
+        if (config != this.config) {
             config = new DynamoDBMapperConfig(this.config, config);
+        }
         return config;
     }
 
@@ -2319,7 +2323,7 @@ private DynamoDBMapperConfig mergeConfig(DynamoDBMapperConfig config) {
      */
     ScanRequest createScanRequestFromExpression(Class clazz,
             DynamoDBScanExpression scanExpression, DynamoDBMapperConfig config) {
-        ScanRequest scanRequest = new ScanRequest();
+        final ScanRequest scanRequest = new ScanRequest();
         scanRequest.setTableName(getTableName(clazz, config));
         scanRequest.setScanFilter(scanExpression.getScanFilter());
         scanRequest.setLimit(scanExpression.getLimit());
@@ -2354,9 +2358,9 @@ List createParallelScanRequestsFromExpression(Class clazz,
             log.info("The Segment and TotalSegments parameters specified in the DynamoDBScanExpression are ignored.");
         }
 
-        List parallelScanRequests = new LinkedList();
+        final List parallelScanRequests = new LinkedList();
         for (int segment = 0; segment < totalSegments; segment++) {
-            ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config);
+            final ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config);
             parallelScanRequests.add(scanRequest
                     .withSegment(segment).withTotalSegments(totalSegments)
                     .withExclusiveStartKey(null));
@@ -2366,19 +2370,19 @@ List createParallelScanRequestsFromExpression(Class clazz,
 
     private  QueryRequest createQueryRequestFromExpression(Class clazz,
             DynamoDBQueryExpression queryExpression, DynamoDBMapperConfig config) {
-        QueryRequest queryRequest = new QueryRequest();
+        final QueryRequest queryRequest = new QueryRequest();
         queryRequest.setConsistentRead(queryExpression.isConsistentRead());
         queryRequest.setTableName(getTableName(clazz, queryExpression.getHashKeyValues(), config));
         queryRequest.setIndexName(queryExpression.getIndexName());
 
-        ItemConverter converter = getConverter(config);
+        final ItemConverter converter = getConverter(config);
 
         // Hash key (primary or index) conditions
-        Map hashKeyConditions = getHashKeyEqualsConditions(
+        final Map hashKeyConditions = getHashKeyEqualsConditions(
                 converter, queryExpression.getHashKeyValues());
 
         // Range key (primary or index) conditions
-        Map rangeKeyConditions = queryExpression.getRangeKeyConditions();
+        final Map rangeKeyConditions = queryExpression.getRangeKeyConditions();
         processKeyConditions(clazz, queryRequest, hashKeyConditions, rangeKeyConditions);
 
         queryRequest.setScanIndexForward(queryExpression.isScanIndexForward());
@@ -2450,7 +2454,7 @@ private void processKeyConditions(Class clazz,
         // Range key condition
         String rangeKeyNameForThisQuery = null;
         if (hasRangeKeyCondition) {
-            for (String rangeKeyName : rangeKeyConditions.keySet()) {
+            for (final String rangeKeyName : rangeKeyConditions.keySet()) {
                 rangeKeyNameForThisQuery = rangeKeyName;
 
                 if (reflector.hasPrimaryRangeKey(clazz)
@@ -2458,12 +2462,12 @@ private void processKeyConditions(Class clazz,
                     hasPrimaryRangeKeyCondition = true;
                 }
 
-                Collection annotatedLSI = parsedIndexesInfo
+                final Collection annotatedLSI = parsedIndexesInfo
                         .getLsiNamesByIndexRangeKey(rangeKeyName);
                 if (annotatedLSI != null) {
                     annotatedLSIsOnRangeKey.addAll(annotatedLSI);
                 }
-                Collection annotatedGSI = parsedIndexesInfo
+                final Collection annotatedGSI = parsedIndexesInfo
                         .getGsiNamesByIndexRangeKey(rangeKeyName);
                 if (annotatedGSI != null) {
                     annotatedGSIsOnRangeKey.addAll(annotatedGSI);
@@ -2505,12 +2509,12 @@ private void processKeyConditions(Class clazz,
         }
 
         // Hash key conditions
-        for (String hashKeyName : hashKeyConditions.keySet()) {
+        for (final String hashKeyName : hashKeyConditions.keySet()) {
             if (hashKeyName.equals(primaryHashKeyName)) {
                 hasPrimaryHashKeyCondition = true;
             }
 
-            Collection annotatedGSINames = parsedIndexesInfo
+            final Collection annotatedGSINames = parsedIndexesInfo
                     .getGsiNamesByIndexHashKey(hashKeyName);
             annotatedGSIsOnHashKeys.put(hashKeyName,
                     annotatedGSINames == null ? new HashSet() : new HashSet(
@@ -2547,7 +2551,7 @@ private void processKeyConditions(Class clazz,
         }
 
         // Collate all the key conditions
-        Map keyConditions = new HashMap();
+        final Map keyConditions = new HashMap();
 
         // With user-provided index name
         if (userProvidedIndexName != null) {
@@ -2584,7 +2588,7 @@ private void processKeyConditions(Class clazz,
                     // range key
                     // with the set of indexes applicable to each hash key
                     // condition.
-                    for (String hashKeyName : annotatedGSIsOnHashKeys.keySet()) {
+                    for (final String hashKeyName : annotatedGSIsOnHashKeys.keySet()) {
                         boolean foundValidQueryExpressionWithInferredIndex = false;
                         String indexNameInferredByThisHashKey = null;
                         if (hashKeyName.equals(primaryHashKeyName)) {
@@ -2597,7 +2601,7 @@ private void processKeyConditions(Class clazz,
                             }
                         }
 
-                        Set annotatedGSIsOnHashKey = annotatedGSIsOnHashKeys
+                        final Set annotatedGSIsOnHashKey = annotatedGSIsOnHashKeys
                                 .get(hashKeyName);
                         // We don't need the data in annotatedGSIsOnHashKeys
                         // afterwards,
@@ -2664,7 +2668,7 @@ private void processKeyConditions(Class clazz,
 
                 } else {
                     // Only one hash key condition
-                    String hashKeyName = annotatedGSIsOnHashKeys.keySet().iterator().next();
+                    final String hashKeyName = annotatedGSIsOnHashKeys.keySet().iterator().next();
                     if (!hasPrimaryHashKeyCondition) {
                         if (annotatedGSIsOnHashKeys.get(hashKeyName).size() == 1) {
                             // Set the index if the index hash key is only
@@ -2724,11 +2728,11 @@ final  List> toParameters(
             final String tableName,
             final DynamoDBMapperConfig mapperConfig
             ) {
-        List> rval =
+        final List> rval =
                 new ArrayList>(
                         attributeValues.size());
 
-        for (Map item : attributeValues) {
+        for (final Map item : attributeValues) {
             rval.add(toParameters(item, modelClass, tableName, mapperConfig));
         }
 
@@ -2796,7 +2800,7 @@ public String getTableName() {
         @Override
         public String getHashKeyName() {
             if (hashKeyName == null) {
-                Method hashKeyGetter = reflector.getPrimaryHashKeyGetter(modelClass);
+                final Method hashKeyGetter = reflector.getPrimaryHashKeyGetter(modelClass);
                 hashKeyName = reflector.getAttributeName(hashKeyGetter);
             }
             return hashKeyName;
@@ -2805,7 +2809,7 @@ public String getHashKeyName() {
         @Override
         public String getRangeKeyName() {
             if (rangeKeyName == null) {
-                Method rangeKeyGetter =
+                final Method rangeKeyGetter =
                         reflector.getPrimaryRangeKeyGetter(modelClass);
                 if (rangeKeyGetter == null) {
                     rangeKeyName = NO_RANGE_KEY;
@@ -2849,18 +2853,18 @@ Map transformAttributeUpdates(
             ) {
         Map item = convertToItem(updateValues);
 
-        HashSet keysAdded = new HashSet();
-        for (Map.Entry e : keys.entrySet()) {
+        final HashSet keysAdded = new HashSet();
+        for (final Map.Entry e : keys.entrySet()) {
             if (!item.containsKey(e.getKey())) {
                 keysAdded.add(e.getKey());
                 item.put(e.getKey(), e.getValue());
             }
         }
 
-        AttributeTransformer.Parameters parameters =
+        final AttributeTransformer.Parameters parameters =
                 toParameters(item, true, clazz, tableName, config);
 
-        String hashKey = parameters.getHashKeyName();
+        final String hashKey = parameters.getHashKeyName();
 
         if (!item.containsKey(hashKey)) {
             item.put(hashKey, keys.get(hashKey));
@@ -2868,14 +2872,14 @@ Map transformAttributeUpdates(
 
         item = transformAttributes(parameters);
 
-        for (Map.Entry entry : item.entrySet()) {
+        for (final Map.Entry entry : item.entrySet()) {
             if (keysAdded.contains(entry.getKey())) {
                 // This was added in for context before calling
                 // transformAttributes, but isn't actually being changed.
                 continue;
             }
 
-            AttributeValueUpdate update = updateValues.get(entry.getKey());
+            final AttributeValueUpdate update = updateValues.get(entry.getKey());
             if (update != null) {
                 update.getValue()
                         .withB(entry.getValue().getB())
@@ -2895,9 +2899,9 @@ Map transformAttributeUpdates(
     }
 
     ItemConverter getConverter(DynamoDBMapperConfig config) {
-        ConversionSchema schema = config.getConversionSchema();
+        final ConversionSchema schema = config.getConversionSchema();
 
-        ConversionSchema.Dependencies params = new ConversionSchema.Dependencies()
+        final ConversionSchema.Dependencies params = new ConversionSchema.Dependencies()
                 .with(DynamoDBReflector.class, reflector)
                 .with(S3ClientCache.class, s3cc);
 
@@ -2909,15 +2913,15 @@ private void pauseExponentially(int retries) {
             return;
         }
 
-        Random random = new Random();
+        final Random random = new Random();
         long delay = 0;
-        long scaleFactor = 500 + random.nextInt(100);
+        final long scaleFactor = 500 + random.nextInt(100);
         delay = (long) (Math.pow(2, retries) * scaleFactor);
         delay = Math.min(delay, MAX_BACKOFF_IN_MILLISECONDS);
 
         try {
             Thread.sleep(delay);
-        } catch (InterruptedException e) {
+        } catch (final InterruptedException e) {
             Thread.currentThread().interrupt();
             throw new AmazonClientException(e.getMessage(), e);
         }
@@ -2948,12 +2952,12 @@ static Map mergeExpectedAttributeValueConditions
         }
 
         // Start from a copy of the internal conditions
-        Map mergedExpectedValues =
+        final Map mergedExpectedValues =
                 new HashMap(internalAssertions);
 
         // Remove internal conditions that are going to be overlaid by
         // user-provided ones.
-        for (String attrName : userProvidedConditions.keySet()) {
+        for (final String attrName : userProvidedConditions.keySet()) {
             mergedExpectedValues.remove(attrName);
         }
 
@@ -3058,7 +3062,7 @@ public S3Link createS3Link(Region s3region, String bucketName, String key) {
      * the default projection type - KEY_ONLY.
      */
     public CreateTableRequest generateCreateTableRequest(Class clazz) {
-        ItemConverter converter = getConverter(config);
+        final ItemConverter converter = getConverter(config);
         return schemaParser.parseTablePojoToCreateTableRequest(
                 clazz, config, reflector, converter);
     }
diff --git a/aws-android-sdk-ddb-mapper/src/main/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/S3ClientCache.java b/aws-android-sdk-ddb-mapper/src/main/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/S3ClientCache.java
index b7796a6757..249e44b1b7 100644
--- a/aws-android-sdk-ddb-mapper/src/main/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/S3ClientCache.java
+++ b/aws-android-sdk-ddb-mapper/src/main/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/S3ClientCache.java
@@ -16,6 +16,7 @@
 package com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper;
 
 import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.AWSCredentialsProvider;
 import com.amazonaws.mobileconnectors.s3.transfermanager.TransferManager;
 import com.amazonaws.regions.Regions;
 import com.amazonaws.services.s3.AmazonS3Client;
@@ -37,7 +38,10 @@ public class S3ClientCache {
     private final Map transferManagersByRegion = new EnumMap(
             Region.class);
 
-    private final AWSCredentials credentials;
+    @Deprecated
+    private AWSCredentials credentials;
+
+    private AWSCredentialsProvider credentialProvider;
 
     /**
      * Create a client cache with a set of credentials. If
@@ -48,10 +52,16 @@ public class S3ClientCache {
      * @param credentials The credentials to use when creating new
      *            {@link AmazonS3Client}.
      */
+    @Deprecated
     S3ClientCache(AWSCredentials credentials) {
         this.credentials = credentials;
     }
 
+
+    S3ClientCache(AWSCredentialsProvider credentialsProvider) {
+        this.credentialProvider = credentialsProvider;
+    }
+
     /**
      * Force the client cache to provide a certain client for the region which
      * that client is configured. This can be useful to provide clients with
@@ -65,10 +75,10 @@ public class S3ClientCache {
      *            will be detected automatically.
      */
     public void useClient(AmazonS3Client client) {
-        Region s3region = client.getRegion();
+        final Region s3region = client.getRegion();
 
         synchronized (transferManagersByRegion) {
-            TransferManager tm = transferManagersByRegion.remove(s3region);
+            final TransferManager tm = transferManagersByRegion.remove(s3region);
             if (tm != null) {
                 tm.shutdownNow();
             }
@@ -99,12 +109,13 @@ public AmazonS3Client getClient(Region s3region) {
         if (client != null) {
             return client;
         }
-        if (credentials == null) {
-            throw new IllegalArgumentException("No client provided for S3 region: " + s3region);
+        if (credentialProvider == null) {
+            client = new AmazonS3Client(credentialProvider);
+        } else {
+            client = new AmazonS3Client(credentials);
         }
-        client = new AmazonS3Client(credentials);
         client.setRegion(s3region.toAWSRegion());
-        AmazonS3Client prev = clientsByRegion.putIfAbsent(s3region, client);
+        final AmazonS3Client prev = clientsByRegion.putIfAbsent(s3region, client);
         return prev == null ? client : prev;
     }
 
diff --git a/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/UnmarshallerTests.java b/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/UnmarshallerTests.java
index f74e4ee568..d4738cb51c 100644
--- a/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/UnmarshallerTests.java
+++ b/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/UnmarshallerTests.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.amazonaws.auth.AWSCredentials;
 import com.amazonaws.services.dynamodbv2.model.AttributeValue;
 import com.amazonaws.services.s3.model.Region;
 import com.amazonaws.util.StringUtils;
@@ -39,7 +40,7 @@
 
 public class UnmarshallerTests {
 
-    private static final S3ClientCache S3CC = new S3ClientCache(null);
+    private static final S3ClientCache S3CC = new S3ClientCache((AWSCredentials) null);
 
     private static final ItemConverter CONVERTER = ConversionSchemas.V1
             .getConverter(new ConversionSchema.Dependencies()
@@ -86,7 +87,7 @@ public void testDate() {
         assertEquals(new Date(0), unconvert("getDate", "setDate",
                 new AttributeValue("1970-01-01T00:00:00.000Z")));
 
-        Calendar c = Calendar.getInstance();
+        final Calendar c = Calendar.getInstance();
         c.setTimeInMillis(0);
 
         assertEquals(c, unconvert("getCalendar", "setCalendar",
@@ -134,7 +135,7 @@ public void testNumbers() {
 
     @Test
     public void testBinary() {
-        ByteBuffer test = ByteBuffer.wrap("test".getBytes(StringUtils.UTF8));
+        final ByteBuffer test = ByteBuffer.wrap("test".getBytes(StringUtils.UTF8));
         Assert.assertTrue(Arrays.equals("test".getBytes(StringUtils.UTF8), (byte[]) unconvert(
                 "getByteArray", "setByteArray",
                 new AttributeValue().withB(test.slice()))));
@@ -192,7 +193,7 @@ public void testDateSet() {
                 unconvert("getDateSet", "setDateSet", new AttributeValue()
                         .withSS("1970-01-01T00:00:00.000Z")));
 
-        Calendar c = Calendar.getInstance();
+        final Calendar c = Calendar.getInstance();
         c.setTimeInMillis(0);
 
         assertEquals(Collections.singleton(c),
@@ -292,9 +293,9 @@ public void testBinarySet() {
         Assert.assertNull(unconvert("getByteBufferSet", "setByteBufferSet",
                 new AttributeValue().withNULL(true)));
 
-        ByteBuffer test = ByteBuffer.wrap("test".getBytes(StringUtils.UTF8));
+        final ByteBuffer test = ByteBuffer.wrap("test".getBytes(StringUtils.UTF8));
 
-        Set result = (Set) unconvert(
+        final Set result = (Set) unconvert(
                 "getByteArraySet", "setByteArraySet",
                 new AttributeValue().withBS(test.slice()));
 
@@ -418,20 +419,20 @@ public void testObject() {
 
     @Test
     public void testUnannotatedObject() throws Exception {
-        Method getter = UnannotatedSubClass.class.getMethod("getChild");
-        Method setter = UnannotatedSubClass.class
+        final Method getter = UnannotatedSubClass.class.getMethod("getChild");
+        final Method setter = UnannotatedSubClass.class
                 .getMethod("setChild", UnannotatedSubClass.class);
 
         try {
             CONVERTER.unconvert(getter, setter, new AttributeValue().withS(""));
             Assert.fail("Expected DynamoDBMappingException");
-        } catch (DynamoDBMappingException e) {
+        } catch (final DynamoDBMappingException e) {
         }
     }
 
     @Test
     public void testS3Link() {
-        S3Link link = (S3Link) unconvert("getS3Link", "setS3Link",
+        final S3Link link = (S3Link) unconvert("getS3Link", "setS3Link",
                 new AttributeValue("{\"s3\":{"
                         + "\"bucket\":\"bucket\","
                         + "\"key\":\"key\","
@@ -445,13 +446,13 @@ public void testS3Link() {
     public Object unconvert(String getter, String setter, AttributeValue value) {
         try {
 
-            Method gm = TestClass.class.getMethod(getter);
-            Method sm = TestClass.class.getMethod(setter, gm.getReturnType());
+            final Method gm = TestClass.class.getMethod(getter);
+            final Method sm = TestClass.class.getMethod(setter, gm.getReturnType());
             return CONVERTER.unconvert(gm, sm, value);
 
-        } catch (RuntimeException e) {
+        } catch (final RuntimeException e) {
             throw e;
-        } catch (Exception e) {
+        } catch (final Exception e) {
             throw new RuntimeException("BOOM", e);
         }
     }
diff --git a/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V1MarshallerTests.java b/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V1MarshallerTests.java
index 373d292419..dcc0554b0d 100644
--- a/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V1MarshallerTests.java
+++ b/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V1MarshallerTests.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.amazonaws.auth.AWSCredentials;
 import com.amazonaws.services.dynamodbv2.model.AttributeValue;
 import com.amazonaws.util.StringUtils;
 
@@ -61,7 +62,7 @@ public void testDate() {
         assertEquals("1970-01-01T00:00:00.000Z",
                 convert("getDate", new Date(0)).getS());
 
-        Calendar c = Calendar.getInstance();
+        final Calendar c = Calendar.getInstance();
         c.setTimeInMillis(0);
 
         assertEquals("1970-01-01T00:00:00.000Z",
@@ -109,7 +110,7 @@ public void testNumbers() {
 
     @Test
     public void testBinary() {
-        ByteBuffer value = ByteBuffer.wrap("value".getBytes(StringUtils.UTF8));
+        final ByteBuffer value = ByteBuffer.wrap("value".getBytes(StringUtils.UTF8));
         assertEquals(value.slice(), convert("getByteArray", "value".getBytes(StringUtils.UTF8))
                 .getB());
         assertEquals(value.slice(), convert("getByteBuffer", value.slice()).getB());
@@ -155,7 +156,7 @@ public void testDateSet() {
                 convert("getDateSet", Collections.singleton(new Date(0)))
                         .getSS());
 
-        Calendar c = Calendar.getInstance();
+        final Calendar c = Calendar.getInstance();
         c.setTimeInMillis(0);
 
         assertEquals(Collections.singletonList("1970-01-01T00:00:00.000Z"),
@@ -219,7 +220,7 @@ public void testBinarySet() {
 
     @Test
     public void testObjectSet() {
-        Object o = new Object() {
+        final Object o = new Object() {
             @Override
             public String toString() {
                 return "hello";
@@ -235,7 +236,7 @@ public void testList() {
         try {
             convert("getList", Arrays.asList("a", "b", "c"));
             Assert.fail("Expected DynamoDBMappingException");
-        } catch (DynamoDBMappingException e) {
+        } catch (final DynamoDBMappingException e) {
         }
     }
 
@@ -244,7 +245,7 @@ public void testMap() {
         try {
             convert("getMap", Collections.singletonMap("a", "b"));
             Assert.fail("Expected DynamoDBMappingException");
-        } catch (DynamoDBMappingException e) {
+        } catch (final DynamoDBMappingException e) {
         }
     }
 
@@ -253,7 +254,7 @@ public void testObject() {
         try {
             convert("getObject", new SubClass());
             Assert.fail("Expected DynamoDBMappingException");
-        } catch (DynamoDBMappingException e) {
+        } catch (final DynamoDBMappingException e) {
         }
     }
 
@@ -264,7 +265,7 @@ public void testUnannotatedObject() throws Exception {
                     new UnannotatedSubClass());
 
             Assert.fail("Expected DynamoDBMappingException");
-        } catch (DynamoDBMappingException e) {
+        } catch (final DynamoDBMappingException e) {
         }
     }
 
@@ -275,7 +276,7 @@ public void testS3Link() {
                 + "\"key\":\"key\","
                 + "\"region\":null}}",
                 convert("getS3Link",
-                        new S3Link(new S3ClientCache(null), "bucket", "key"))
+                        new S3Link(new S3ClientCache((AWSCredentials) null), "bucket", "key"))
                         .getS());
     }
 
@@ -284,9 +285,9 @@ private static AttributeValue convert(String method, Object value) {
 
             return CONVERTER.convert(TestClass.class.getMethod(method), value);
 
-        } catch (RuntimeException e) {
+        } catch (final RuntimeException e) {
             throw e;
-        } catch (Exception e) {
+        } catch (final Exception e) {
             throw new RuntimeException(e);
         }
     }
diff --git a/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V2CompatMarshallerTests.java b/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V2CompatMarshallerTests.java
index cd0f103cc3..a857e5ae75 100644
--- a/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V2CompatMarshallerTests.java
+++ b/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V2CompatMarshallerTests.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.amazonaws.auth.AWSCredentials;
 import com.amazonaws.services.dynamodbv2.model.AttributeValue;
 import com.amazonaws.util.StringUtils;
 
@@ -64,7 +65,7 @@ public void testDate() {
         assertEquals("1970-01-01T00:00:00.000Z",
                 convert("getDate", new Date(0)).getS());
 
-        Calendar c = Calendar.getInstance();
+        final Calendar c = Calendar.getInstance();
         c.setTimeInMillis(0);
 
         assertEquals("1970-01-01T00:00:00.000Z",
@@ -112,7 +113,7 @@ public void testNumbers() {
 
     @Test
     public void testBinary() {
-        ByteBuffer value = ByteBuffer.wrap("value".getBytes(StringUtils.UTF8));
+        final ByteBuffer value = ByteBuffer.wrap("value".getBytes(StringUtils.UTF8));
         assertEquals(value.slice(), convert("getByteArray", "value".getBytes(StringUtils.UTF8))
                 .getB());
         assertEquals(value.slice(), convert("getByteBuffer", value.slice()).getB());
@@ -158,7 +159,7 @@ public void testDateSet() {
                 convert("getDateSet", Collections.singleton(new Date(0)))
                         .getSS());
 
-        Calendar c = Calendar.getInstance();
+        final Calendar c = Calendar.getInstance();
         c.setTimeInMillis(0);
 
         assertEquals(Collections.singletonList("1970-01-01T00:00:00.000Z"),
@@ -222,7 +223,7 @@ public void testBinarySet() {
 
     @Test
     public void testObjectSet() {
-        Object o = new Object() {
+        final Object o = new Object() {
             @Override
             public String toString() {
                 return "hello";
@@ -323,7 +324,7 @@ public void testUnannotatedObject() throws Exception {
                     new UnannotatedSubClass());
 
             Assert.fail("Expected DynamoDBMappingException");
-        } catch (DynamoDBMappingException e) {
+        } catch (final DynamoDBMappingException e) {
         }
     }
 
@@ -334,7 +335,7 @@ public void testS3Link() {
                 + "\"key\":\"key\","
                 + "\"region\":null}}",
                 convert("getS3Link",
-                        new S3Link(new S3ClientCache(null), "bucket", "key"))
+                        new S3Link(new S3ClientCache((AWSCredentials) null), "bucket", "key"))
                         .getS());
     }
 
@@ -343,9 +344,9 @@ private static AttributeValue convert(String method, Object value) {
 
             return CONVERTER.convert(TestClass.class.getMethod(method), value);
 
-        } catch (RuntimeException e) {
+        } catch (final RuntimeException e) {
             throw e;
-        } catch (Exception e) {
+        } catch (final Exception e) {
             throw new RuntimeException(e);
         }
     }
diff --git a/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V2MarshallerTests.java b/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V2MarshallerTests.java
index 61c543e103..9f9d4821ce 100644
--- a/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V2MarshallerTests.java
+++ b/aws-android-sdk-ddb-mapper/src/test/java/com/amazonaws/mobileconnectors/dynamodbv2/dynamodbmapper/V2MarshallerTests.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.amazonaws.auth.AWSCredentials;
 import com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBMapperFieldModel.DynamoDBAttributeType;
 import com.amazonaws.services.dynamodbv2.model.AttributeValue;
 import com.amazonaws.util.StringUtils;
@@ -67,7 +68,7 @@ public void testDate() {
         assertEquals("1970-01-01T00:00:00.000Z",
                 convert("getDate", new Date(0)).getS());
 
-        Calendar c = Calendar.getInstance();
+        final Calendar c = Calendar.getInstance();
         c.setTimeInMillis(0);
 
         assertEquals("1970-01-01T00:00:00.000Z",
@@ -115,7 +116,7 @@ public void testNumbers() {
 
     @Test
     public void testBinary() {
-        ByteBuffer value = ByteBuffer.wrap("value".getBytes(StringUtils.UTF8));
+        final ByteBuffer value = ByteBuffer.wrap("value".getBytes(StringUtils.UTF8));
         assertEquals(value.slice(), convert("getByteArray", "value".getBytes(StringUtils.UTF8))
                 .getB());
         assertEquals(value.slice(), convert("getByteBuffer", value.slice()).getB());
@@ -128,7 +129,7 @@ public void testBooleanSet() {
         try {
             convert("getBooleanSet", Collections.singleton(true));
             Assert.fail("Expected DynamoDBMappingException");
-        } catch (DynamoDBMappingException e) {
+        } catch (final DynamoDBMappingException e) {
         }
     }
 
@@ -155,7 +156,7 @@ public void testDateSet() {
                 convert("getDateSet", Collections.singleton(new Date(0)))
                         .getSS());
 
-        Calendar c = Calendar.getInstance();
+        final Calendar c = Calendar.getInstance();
         c.setTimeInMillis(0);
 
         assertEquals(Collections.singletonList("1970-01-01T00:00:00.000Z"),
@@ -224,7 +225,7 @@ public void testObjectSet() {
         try {
             convert("getObjectSet", Collections.singleton(new Object()));
             Assert.fail("Expected DynamoDBMappingException");
-        } catch (DynamoDBMappingException e) {
+        } catch (final DynamoDBMappingException e) {
         }
     }
 
@@ -247,7 +248,7 @@ public void testSetList() {
                 convert("getSetList", Arrays.asList(
                         Collections. singleton("a"))).getL());
 
-        List> list = new ArrayList>();
+        final List> list = new ArrayList>();
         list.add(null);
 
         assertEquals(
@@ -321,7 +322,7 @@ public void testUnannotatedObject() throws Exception {
                     new UnannotatedSubClass());
 
             Assert.fail("Expected DynamoDBMappingException");
-        } catch (DynamoDBMappingException e) {
+        } catch (final DynamoDBMappingException e) {
         }
     }
 
@@ -332,97 +333,97 @@ public void testS3Link() {
                 + "\"key\":\"key\","
                 + "\"region\":null}}",
                 convert("getS3Link",
-                        new S3Link(new S3ClientCache(null), "bucket", "key"))
+                        new S3Link(new S3ClientCache((AWSCredentials) null), "bucket", "key"))
                         .getS());
     }
 
     @Test
     public void getFieldModelString() throws NoSuchMethodException, SecurityException {
-        MockClass mock = new MockClass();
+        final MockClass mock = new MockClass();
 
-        DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
+        final DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
                 "getString"));
-        DynamoDBAttributeType type = model.getDynamoDBAttributeType();
+        final DynamoDBAttributeType type = model.getDynamoDBAttributeType();
         assertEquals(type, DynamoDBAttributeType.S);
     }
 
     @Test
     public void getFieldModelStringSet() throws NoSuchMethodException, SecurityException {
-        MockClass mock = new MockClass();
+        final MockClass mock = new MockClass();
 
-        DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
+        final DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
                 "getStringSet"));
-        DynamoDBAttributeType type = model.getDynamoDBAttributeType();
+        final DynamoDBAttributeType type = model.getDynamoDBAttributeType();
         assertEquals(type, DynamoDBAttributeType.SS);
     }
 
     @Test
     public void getFieldModelNumber() throws NoSuchMethodException, SecurityException {
-        MockClass mock = new MockClass();
+        final MockClass mock = new MockClass();
 
-        DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
+        final DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
                 "getNumber"));
-        DynamoDBAttributeType type = model.getDynamoDBAttributeType();
+        final DynamoDBAttributeType type = model.getDynamoDBAttributeType();
         assertEquals(type, DynamoDBAttributeType.N);
     }
 
     @Test
     public void getFieldModelNumberSet() throws NoSuchMethodException, SecurityException {
-        MockClass mock = new MockClass();
+        final MockClass mock = new MockClass();
 
-        DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
+        final DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
                 "getNumberSet"));
-        DynamoDBAttributeType type = model.getDynamoDBAttributeType();
+        final DynamoDBAttributeType type = model.getDynamoDBAttributeType();
         assertEquals(type, DynamoDBAttributeType.NS);
     }
 
     @Test
     public void getFieldModelBinary() throws NoSuchMethodException, SecurityException {
-        MockClass mock = new MockClass();
+        final MockClass mock = new MockClass();
 
-        DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
+        final DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
                 "getBinary"));
-        DynamoDBAttributeType type = model.getDynamoDBAttributeType();
+        final DynamoDBAttributeType type = model.getDynamoDBAttributeType();
         assertEquals(type, DynamoDBAttributeType.B);
     }
 
     @Test
     public void getFieldModelBinarySet() throws NoSuchMethodException, SecurityException {
-        MockClass mock = new MockClass();
+        final MockClass mock = new MockClass();
 
-        DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
+        final DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
                 "getBinarySet"));
-        DynamoDBAttributeType type = model.getDynamoDBAttributeType();
+        final DynamoDBAttributeType type = model.getDynamoDBAttributeType();
         assertEquals(type, DynamoDBAttributeType.BS);
     }
 
     @Test
     public void getFieldModelBoolean() throws NoSuchMethodException, SecurityException {
-        MockClass mock = new MockClass();
+        final MockClass mock = new MockClass();
 
-        DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
+        final DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
                 "getBool"));
-        DynamoDBAttributeType type = model.getDynamoDBAttributeType();
+        final DynamoDBAttributeType type = model.getDynamoDBAttributeType();
         assertEquals(type, DynamoDBAttributeType.BOOL);
     }
 
     @Test
     public void getFieldModelList() throws NoSuchMethodException, SecurityException {
-        MockClass mock = new MockClass();
+        final MockClass mock = new MockClass();
 
-        DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
+        final DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
                 "getList"));
-        DynamoDBAttributeType type = model.getDynamoDBAttributeType();
+        final DynamoDBAttributeType type = model.getDynamoDBAttributeType();
         assertEquals(type, DynamoDBAttributeType.L);
     }
 
     @Test
     public void getFieldModelMap() throws NoSuchMethodException, SecurityException {
-        MockClass mock = new MockClass();
+        final MockClass mock = new MockClass();
 
-        DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
+        final DynamoDBMapperFieldModel model = CONVERTER.getFieldModel(mock.getClass().getMethod(
                 "getMap"));
-        DynamoDBAttributeType type = model.getDynamoDBAttributeType();
+        final DynamoDBAttributeType type = model.getDynamoDBAttributeType();
         assertEquals(type, DynamoDBAttributeType.M);
     }
 
@@ -516,9 +517,9 @@ private static AttributeValue convert(String method, Object value) {
 
             return CONVERTER.convert(TestClass.class.getMethod(method), value);
 
-        } catch (RuntimeException e) {
+        } catch (final RuntimeException e) {
             throw e;
-        } catch (Exception e) {
+        } catch (final Exception e) {
             throw new RuntimeException(e);
         }
     }
diff --git a/aws-android-sdk-ddb/pom.xml b/aws-android-sdk-ddb/pom.xml
index 97c391e6d2..ee0c151a3a 100644
--- a/aws-android-sdk-ddb/pom.xml
+++ b/aws-android-sdk-ddb/pom.xml
@@ -12,7 +12,7 @@
   
     com.amazonaws
     aws-android-sdk-pom
-    2.3.9
+    2.4.0
   
 
   
@@ -20,7 +20,7 @@
       com.amazonaws
       aws-android-sdk-core
       false
-      2.3.9
+      2.4.0
     
         
         junit
diff --git a/aws-android-sdk-ec2/pom.xml b/aws-android-sdk-ec2/pom.xml
index 8cacc14357..39036f91e3 100644
--- a/aws-android-sdk-ec2/pom.xml
+++ b/aws-android-sdk-ec2/pom.xml
@@ -12,7 +12,7 @@
   
     com.amazonaws
     aws-android-sdk-pom
-    2.3.9
+    2.4.0
   
 
   
@@ -20,7 +20,7 @@
       com.amazonaws
       aws-android-sdk-core
       false
-      2.3.9
+      2.4.0
     
   
 
diff --git a/aws-android-sdk-elb/pom.xml b/aws-android-sdk-elb/pom.xml
index c1d11152c6..3b89df527c 100644
--- a/aws-android-sdk-elb/pom.xml
+++ b/aws-android-sdk-elb/pom.xml
@@ -12,7 +12,7 @@
   
     com.amazonaws
     aws-android-sdk-pom
-    2.3.9
+    2.4.0
   
 
   
@@ -20,7 +20,7 @@
       com.amazonaws
       aws-android-sdk-core
       false
-      2.3.9
+      2.4.0
     
   
 
diff --git a/aws-android-sdk-iot/pom.xml b/aws-android-sdk-iot/pom.xml
index 166478fdec..7f5aa2dd17 100644
--- a/aws-android-sdk-iot/pom.xml
+++ b/aws-android-sdk-iot/pom.xml
@@ -12,7 +12,7 @@
   
     com.amazonaws
     aws-android-sdk-pom
-    2.3.9
+    2.4.0
   
 
   
@@ -20,7 +20,7 @@
       com.amazonaws
       aws-android-sdk-core
       false
-      2.3.9
+      2.4.0
     
     
       org.eclipse.paho
diff --git a/aws-android-sdk-iot/src/main/java/com/amazonaws/mobileconnectors/iot/AWSIotMqttManager.java b/aws-android-sdk-iot/src/main/java/com/amazonaws/mobileconnectors/iot/AWSIotMqttManager.java
index 8186082239..c5efb5a1a2 100644
--- a/aws-android-sdk-iot/src/main/java/com/amazonaws/mobileconnectors/iot/AWSIotMqttManager.java
+++ b/aws-android-sdk-iot/src/main/java/com/amazonaws/mobileconnectors/iot/AWSIotMqttManager.java
@@ -99,12 +99,12 @@ public class AWSIotMqttManager {
     private AWSIotWebSocketUrlSigner signer;
 
     /** Customer specific prefix for data endpoint. */
-    private String accountEndpointPrefix;
+    private final String accountEndpointPrefix;
 
     /** MQTT client ID, used for both initial connection and reconnections. */
-    private String mqttClientId;
+    private final String mqttClientId;
     /** AWS IoT region hosting the MQTT service. */
-    private Region region;
+    private final Region region;
 
     /** Is this client a WebSocket Client?  Setup on initial connect and then used for reconnect logic. */
     private Boolean isWebSocketClient;
@@ -277,7 +277,7 @@ public int getMaxAutoReconnectAttempts() {
      * @param attempts number of reconnects attempted automatically. Retry
      *            forever = -1.
      */
-    public void setMaxAutoReconnectAttempts(int attempts) {
+    public void setMaxAutoReconnectAttepts(int attempts) {
         if (attempts <= 0 && attempts != -1) {
             throw new IllegalArgumentException("Max reconnection attempts must be postive or -1");
         }
@@ -604,8 +604,8 @@ public void connect(KeyStore keyStore, final AWSIotMqttClientStatusCallback stat
                 mqttClient = new MqttAsyncClient(mqttBrokerURL, mqttClientId, new MemoryPersistence());
             }
 
-            SocketFactory socketFactory = AWSIotSslUtility.getSocketFactoryWithKeyStore(keyStore);
-            MqttConnectOptions options = new MqttConnectOptions();
+            final SocketFactory socketFactory = AWSIotSslUtility.getSocketFactoryWithKeyStore(keyStore);
+            final MqttConnectOptions options = new MqttConnectOptions();
 
             if (mqttLWT != null) {
                 options.setWill(mqttLWT.getTopic(), mqttLWT.getMessage().getBytes(),
@@ -616,15 +616,15 @@ public void connect(KeyStore keyStore, final AWSIotMqttClientStatusCallback stat
             options.setSocketFactory(clientSocketFactory);
 
             mqttConnect(options, statusCallback);
-        } catch (NoSuchAlgorithmException e) {
+        } catch (final NoSuchAlgorithmException e) {
             throw new AWSIotCertificateException("A certificate error occurred.", e);
-        } catch (KeyManagementException e) {
+        } catch (final KeyManagementException e) {
             throw new AWSIotCertificateException("A certificate error occurred.", e);
-        } catch (KeyStoreException e) {
+        } catch (final KeyStoreException e) {
             throw new AWSIotCertificateException("A certificate error occurred.", e);
-        } catch (UnrecoverableKeyException e) {
+        } catch (final UnrecoverableKeyException e) {
             throw new AWSIotCertificateException("A certificate error occurred.", e);
-        } catch (MqttException e) {
+        } catch (final MqttException e) {
             throw new AmazonClientException("An error occured in the MQTT client.", e);
         }
     }
@@ -661,15 +661,15 @@ public void run() {
 
                 signer = new AWSIotWebSocketUrlSigner("iotdata");
 
-                String endpoint = String.format("%s.iot.%s.%s:443", accountEndpointPrefix, region.getName(),
+                final String endpoint = String.format("%s.iot.%s.%s:443", accountEndpointPrefix, region.getName(),
                         region.getDomain());
                 isWebSocketClient = true;
 
                 try {
-                    String mqttWebSocketURL = signer.getSignedUrl(endpoint, clientCredentialsProvider.getCredentials(),
+                    final String mqttWebSocketURL = signer.getSignedUrl(endpoint, clientCredentialsProvider.getCredentials(),
                             System.currentTimeMillis());
 
-                    MqttConnectOptions options = new MqttConnectOptions();
+                    final MqttConnectOptions options = new MqttConnectOptions();
 
                     // Specify the URL through the server URI array.  This is checked
                     // at connect time and allows us to specify a new URL (with new
@@ -687,7 +687,7 @@ public void run() {
                     }
                     mqttConnect(options, statusCallback);
 
-                } catch (MqttException e) {
+                } catch (final MqttException e) {
                     throw new AmazonClientException("An error occurred in the MQTT client.", e);
                 }
             }
@@ -757,7 +757,7 @@ public void onFailure(IMqttToken asyncActionToken, Throwable e) {
                     }
                 }
             });
-        } catch (MqttException e) {
+        } catch (final MqttException e) {
             switch (e.getReasonCode()) {
                 case MqttException.REASON_CODE_CLIENT_CONNECTED:
                     connectionState = MqttManagerConnectionState.Connected;
@@ -797,7 +797,7 @@ void reset() {
             if (mqttClient.isConnected()) {
                 try {
                     mqttClient.disconnect(0);
-                } catch (MqttException e) {
+                } catch (final MqttException e) {
                     throw new AmazonClientException("Client error when disconnecting.", e);
                 }
             }
@@ -812,7 +812,7 @@ void reconnectToSession() {
         // status will be ConnectionLost if user calls disconnect() during reconnect logic
         if (null != mqttClient && connectionState != MqttManagerConnectionState.Disconnected) {
 
-            MqttConnectOptions options = new MqttConnectOptions();
+            final MqttConnectOptions options = new MqttConnectOptions();
 
             options.setCleanSession(true);
             // when cleanSession is added, this should mirror cleanSession
@@ -827,11 +827,11 @@ void reconnectToSession() {
             if (isWebSocketClient) {
                 signer = new AWSIotWebSocketUrlSigner("iotdata");
 
-                String endpoint = String
+                final String endpoint = String
                         .format("%s.iot.%s.%s:443", accountEndpointPrefix, region.getName(),
                                 region.getDomain());
 
-                String mqttWebSocketURL = signer
+                final String mqttWebSocketURL = signer
                         .getSignedUrl(endpoint, clientCredentialsProvider.getCredentials(),
                                 System.currentTimeMillis());
 
@@ -876,7 +876,7 @@ public void onFailure(IMqttToken asyncActionToken, Throwable e) {
                         }
                     }
                 });
-            } catch (MqttException e) {
+            } catch (final MqttException e) {
                 if (scheduleReconnect()) {
                     connectionState = MqttManagerConnectionState.Reconnecting;
                     userConnectionCallback();
@@ -943,10 +943,10 @@ public void subscribeToTopic(String topic, AWSIotMqttQos qos,
         if (null != mqttClient) {
             try {
                 mqttClient.subscribe(topic, qos.asInt());
-            } catch (MqttException e) {
+            } catch (final MqttException e) {
                 throw new AmazonClientException("Client error when subscribing.", e);
             }
-            AWSIotMqttTopic topicModel = new AWSIotMqttTopic(topic, qos, callback);
+            final AWSIotMqttTopic topicModel = new AWSIotMqttTopic(topic, qos, callback);
             topicListeners.put(topic, topicModel);
         }
     }
@@ -965,7 +965,7 @@ public void unsubscribeTopic(String topic) {
         if (mqttClient != null) {
             try {
                 mqttClient.unsubscribe(topic);
-            } catch (MqttException e) {
+            } catch (final MqttException e) {
                 throw new AmazonClientException("Client error while unsubscribing.", e);
             }
             topicListeners.remove(topic);
@@ -978,11 +978,11 @@ public void unsubscribeTopic(String topic) {
     void resubscribeToTopics() {
         needResubscribe = false;
 
-        for (AWSIotMqttTopic topic : topicListeners.values()) {
+        for (final AWSIotMqttTopic topic : topicListeners.values()) {
             if (mqttClient != null) {
                 try {
                     mqttClient.subscribe(topic.getTopic(), topic.getQos().asInt());
-                } catch (MqttException e) {
+                } catch (final MqttException e) {
                     Log.e(LOG_TAG, "Error while resubscribing to previously subscribed toipcs.", e);
                 }
             }
@@ -1078,13 +1078,13 @@ public void publishData(byte[] data, String topic, AWSIotMqttQos qos,
             throw new IllegalArgumentException("QoS cannot be null");
         }
 
-        PublishMessageUserData publishMessageUserData = new PublishMessageUserData(callback, userData);
+        final PublishMessageUserData publishMessageUserData = new PublishMessageUserData(callback, userData);
 
         if (connectionState == MqttManagerConnectionState.Connected) {
             if (mqttMessageQueue.isEmpty()) {
                 try {
                     mqttClient.publish(topic, data, qos.asInt(), false, publishMessageUserData, null);
-                } catch (MqttException e) {
+                } catch (final MqttException e) {
                     if (callback != null) {
                         userPublishCallback(callback,
                                 AWSIotMqttMessageDeliveryCallback.MessageDeliveryStatus.Fail,
@@ -1134,7 +1134,7 @@ public void publishData(byte[] data, String topic, AWSIotMqttQos qos,
      */
     boolean putMessageInQueue(byte[] data, String topic, AWSIotMqttQos qos,
             PublishMessageUserData publishMessageUserData) {
-        AWSIotMqttQueueMessage message = new AWSIotMqttQueueMessage(topic, data, qos, publishMessageUserData);
+        final AWSIotMqttQueueMessage message = new AWSIotMqttQueueMessage(topic, data, qos, publishMessageUserData);
 
         if (mqttMessageQueue.size() >= offlinePublishQueueBound) {
             if (fullQueueKeepsOldest) {
@@ -1169,7 +1169,7 @@ void publishMessagesFromQueue() {
                                 .publish(message.getTopic(), message.getMessage(), message.getQos()
                                         .asInt(), false);
                     }
-                } catch (MqttException e) {
+                } catch (final MqttException e) {
                     // Call this message a failure.  It is possible that this is due to a
                     // connection issue (we are in this path because the connection dropped),
                     // however there are also exceptions inherent to the message (valid topic),
@@ -1223,11 +1223,11 @@ public void connectionLost(Throwable cause) {
 
             @Override
             public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
-                byte[] data = mqttMessage.getPayload();
+                final byte[] data = mqttMessage.getPayload();
 
-                for (String topicKey : topicListeners.keySet()) {
+                for (final String topicKey : topicListeners.keySet()) {
                     if (isTopicMatch(topicKey, topic)) {
-                        AWSIotMqttTopic topicModel = topicListeners.get(topicKey);
+                        final AWSIotMqttTopic topicModel = topicListeners.get(topicKey);
                         if (topicModel != null) {
                             if (topicModel.getCallback() != null) {
                                 topicModel.getCallback().onMessageArrived(topic, data);
@@ -1240,9 +1240,9 @@ public void messageArrived(String topic, MqttMessage mqttMessage) throws Excepti
             @Override
             public void deliveryComplete(IMqttDeliveryToken token) {
                 if (token != null) {
-                    Object o = token.getUserContext();
+                    final Object o = token.getUserContext();
                     if (o instanceof PublishMessageUserData) {
-                        PublishMessageUserData pmud = (PublishMessageUserData) o;
+                        final PublishMessageUserData pmud = (PublishMessageUserData) o;
                         userPublishCallback(pmud.getUserCallback(),
                                 AWSIotMqttMessageDeliveryCallback.MessageDeliveryStatus.Success,
                                 pmud.getUserData());
@@ -1323,16 +1323,16 @@ void userPublishCallback(AWSIotMqttMessageDeliveryCallback cb,
      * @return true if the topic matches the filter, false otherwise.
      */
     static boolean isTopicMatch(String topicFilter, String topic) {
-        String[] topicFilterTokens = topicFilter.split("/");
-        String[] topicTokens = topic.split("/");
+        final String[] topicFilterTokens = topicFilter.split("/");
+        final String[] topicTokens = topic.split("/");
 
         if (topicFilterTokens.length > topicTokens.length) {
             return false;
         }
 
         for (int i = 0; i < topicFilterTokens.length; i++) {
-            String topicFilterToken = topicFilterTokens[i];
-            String topicToken = topicTokens[i];
+            final String topicFilterToken = topicFilterTokens[i];
+            final String topicToken = topicTokens[i];
 
             // we're equal up to this point, the # matches all that is left
             if (topicFilterToken.equals("#")) {
diff --git a/aws-android-sdk-kinesis/pom.xml b/aws-android-sdk-kinesis/pom.xml
index 939eb9a753..d44c9ef0de 100644
--- a/aws-android-sdk-kinesis/pom.xml
+++ b/aws-android-sdk-kinesis/pom.xml
@@ -12,7 +12,7 @@
   
     com.amazonaws
     aws-android-sdk-pom
-    2.3.9
+    2.4.0
   
 
   
@@ -20,7 +20,7 @@
       com.amazonaws
       aws-android-sdk-core
       false
-      2.3.9
+      2.4.0
     
     
       junit
diff --git a/aws-android-sdk-kms/pom.xml b/aws-android-sdk-kms/pom.xml
index c27c52854e..a071f18632 100644
--- a/aws-android-sdk-kms/pom.xml
+++ b/aws-android-sdk-kms/pom.xml
@@ -12,7 +12,7 @@
   
     com.amazonaws
     aws-android-sdk-pom
-    2.3.9
+    2.4.0
   
 
   
@@ -20,7 +20,7 @@
       com.amazonaws
       aws-android-sdk-core
       false
-      2.3.9
+      2.4.0
     
   
 
diff --git a/aws-android-sdk-lambda/pom.xml b/aws-android-sdk-lambda/pom.xml
index e75007bee4..6c8a8b4433 100644
--- a/aws-android-sdk-lambda/pom.xml
+++ b/aws-android-sdk-lambda/pom.xml
@@ -12,7 +12,7 @@
   
     com.amazonaws
     aws-android-sdk-pom
-    2.3.9
+    2.4.0
   
 
   
@@ -20,7 +20,7 @@
       com.amazonaws
       aws-android-sdk-core
       false
-      2.3.9
+      2.4.0
     
     
       junit
diff --git a/aws-android-sdk-lex/pom.xml b/aws-android-sdk-lex/pom.xml
index 1a30edd997..845fa8251f 100644
--- a/aws-android-sdk-lex/pom.xml
+++ b/aws-android-sdk-lex/pom.xml
@@ -12,7 +12,7 @@
     
         com.amazonaws
         aws-android-sdk-pom
-        2.3.9
+        2.4.0
     
 
    
@@ -33,7 +33,7 @@
             com.amazonaws
             aws-android-sdk-core
             false
-            2.3.9
+            2.4.0
         
         
             com.google.android
diff --git a/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/InteractionClient.java b/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/InteractionClient.java
index 651f46ae0f..66c3102f08 100644
--- a/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/InteractionClient.java
+++ b/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/InteractionClient.java
@@ -596,11 +596,14 @@ public void run() {
                     }
                 };
             } else if (DialogState.ReadyForFulfillment.toString().equals(result.getDialogState())) {
-                // The current dlalog is ready for fulfilled by the client.
+                // The current dialog is ready for fulfillment by the client, no
+                // further action is required.
                 response = new Runnable() {
                     @Override
                     public void run() {
                         interactionListener.onReadyForFulfillment(new Response(result));
+
+                        interactionListener.promptUserToRespond(serviceResponse, null);
                     }
                 };
             } else if (DialogState.Fulfilled.toString().equals(result.getDialogState())) {
diff --git a/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/ui/InteractiveVoiceView.java b/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/ui/InteractiveVoiceView.java
index b1d5fb6345..129df2974c 100644
--- a/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/ui/InteractiveVoiceView.java
+++ b/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/ui/InteractiveVoiceView.java
@@ -16,11 +16,12 @@
 package com.amazonaws.mobileconnectors.lex.interactionkit.ui;
 
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -52,6 +53,18 @@ public class InteractiveVoiceView extends View {
     private static final int NORMAL = 0;
     private static final int LISTENING = 1;
     private static final int AWAITING_RESPONSE = 2;
+    /**
+     * Transitioning from TALKING TO LISTENING.
+     */
+    private static final int TRANSITION_TO_LISTENING = 3;
+    /**
+     * Transitioning from LISTENING to TALKING.
+     */
+    private static final int TRANSITION_TO_TALKING = 4;
+    /**
+     * Lex is talking.
+     */
+    private static final int TALKING = 5;
 
     // Default boundary sizes of the.
     private static final int BIB_INT_DP = 32;
@@ -65,17 +78,19 @@ public class InteractiveVoiceView extends View {
     private static final float GOLDEN_RATIO = 1.809f;
 
     // Default colors.
-    private static final String DEFAULT_COLOR_BUTTON_BACKGROUND = "#FFFFFF";
-    private static final String DEFAULT_COLOR_BUTTON_BOUNDARY = "#8D9496";
-    private static final String DEFAULT_COLOR_TINT_NORMAL = "#329AD6";
-    private static final String DEFAULT_COLOR_TINT_LISTENING = "#2A5C91";
-    private static final String DEFAULT_COLOR_TINT_WAITING = "#8D9496";
-    private static final String DEFAULT_COLOR_ANIMATED_CIRCLE = "#4EA9DC";
-    private static final String DEFAULT_COLOR_ANIMATED_RING = "#2A5C91";
+    private static final int DEFAULT_COLOR_BUTTON_BACKGROUND = Color.parseColor("#FFFFFF");
+    private static final int DEFAULT_COLOR_BUTTON_BOUNDARY = Color.parseColor("#8D9496");
+    private static final int DEFAULT_COLOR_TINT_NORMAL = Color.parseColor("#329AD6");
+    private static final int DEFAULT_COLOR_TINT_LISTENING = Color.parseColor("#2A5C91");
+    private static final int DEFAULT_COLOR_TINT_TALKING = Color.parseColor("#4383c4");
+    private static final int DEFAULT_COLOR_TINT_WAITING = Color.parseColor("#8D9496");
+    private static final int DEFAULT_COLOR_ANIMATED_CIRCLE = Color.parseColor("#4EA9DC");
+    private static final int DEFAULT_COLOR_ANIMATED_RING = Color.parseColor("#2A5C91");
 
     // Animation colors.
     private int mIconColorNormal;
     private int mIconColorListening;
+    private int mIconColorTalking;
     private int mIconColorWaiting;
     private int mButtonBoundaryColor;
 
@@ -83,8 +98,8 @@ public class InteractiveVoiceView extends View {
     private final Context context;
 
     // Bitmaps to store button icons.
-    private Bitmap mIconBmp;
-    BitmapDrawable bitmapDrawableIcon;
+    BitmapDrawable bitmapDrawableMicrophoneIcon;
+    BitmapDrawable bitmapDrawableLexIcon;
 
     // Boundary for animated wait arc.
     private RectF oval;
@@ -105,6 +120,10 @@ public class InteractiveVoiceView extends View {
     // Animators for wait.
     private ValueAnimator mValueAnimator;
 
+    // Animator for switching image between Lex and Microphone.
+    private PushTransitionAnimator pushTransitionAnimator;
+    private boolean animateOnImageSwitching;
+
     // Current widget state.
     private int state = NORMAL;
 
@@ -134,30 +153,39 @@ public InteractiveVoiceView(Context context, AttributeSet attrs) {
     private void init() {
         mAnimatorSet = new AnimatorSet();
 
-        mIconColorNormal = Color.parseColor(DEFAULT_COLOR_TINT_NORMAL);
-        mIconColorWaiting = Color.parseColor(DEFAULT_COLOR_TINT_WAITING);
-        mIconColorListening = Color.parseColor(DEFAULT_COLOR_TINT_LISTENING);
-        mButtonBoundaryColor = Color.parseColor(DEFAULT_COLOR_BUTTON_BOUNDARY);
+        mIconColorNormal = DEFAULT_COLOR_TINT_NORMAL;
+        mIconColorWaiting = DEFAULT_COLOR_TINT_WAITING;
+        mIconColorListening = DEFAULT_COLOR_TINT_LISTENING;
+        mIconColorTalking = DEFAULT_COLOR_TINT_TALKING;
+        mButtonBoundaryColor = DEFAULT_COLOR_BUTTON_BOUNDARY;
 
         // Set image bitmaps for normal and listening states.
-        mIconBmp = BitmapFactory.decodeResource(getResources(), R.drawable.mic);
-        bitmapDrawableIcon = new BitmapDrawable(getResources(), mIconBmp);
+
+        // Create microphone
+        bitmapDrawableMicrophoneIcon = new BitmapDrawable(
+                getResources(),
+                BitmapFactory.decodeResource(getResources(), R.drawable.mic)
+        );
+        bitmapDrawableLexIcon = new BitmapDrawable(
+                getResources(),
+                BitmapFactory.decodeResource(getResources(), R.drawable.lex_speak)
+        );
 
         // Color and style for animated components.
         mPaintAnimationListening = new Paint();
         mPaintAnimationListening.setAntiAlias(true);
-        mPaintAnimationListening.setColor(Color.parseColor(DEFAULT_COLOR_ANIMATED_CIRCLE));
+        mPaintAnimationListening.setColor(DEFAULT_COLOR_ANIMATED_CIRCLE);
 
         mPaintAnimationWaiting = new Paint();
         mPaintAnimationWaiting.setAntiAlias(true);
-        mPaintAnimationWaiting.setColor(Color.parseColor(DEFAULT_COLOR_ANIMATED_RING));
+        mPaintAnimationWaiting.setColor(DEFAULT_COLOR_ANIMATED_RING);
         mCurrentStrokePix = 20.0f;
         mPaintAnimationWaiting.setStrokeWidth(mCurrentStrokePix);
 
         mPaintButtonBackground = new Paint();
         mPaintButtonBackground.setAntiAlias(true);
         mPaintButtonBackground.setStrokeWidth(5);
-        mPaintButtonBackground.setColor(Color.parseColor(DEFAULT_COLOR_BUTTON_BACKGROUND));
+        mPaintButtonBackground.setColor(DEFAULT_COLOR_BUTTON_BACKGROUND);
 
         mPaintButtonBoundary = new Paint();
         mPaintButtonBoundary.setAntiAlias(true);
@@ -196,49 +224,76 @@ protected void onDraw(Canvas canvas) {
         canvasWidth = canvas.getWidth();
         canvasHeight = canvas.getHeight();
         final Rect bounds = canvas.getClipBounds();
+        Rect calculatedBounds = calculateButtonBounds(bounds);
 
         int buttonColor = mIconColorNormal;
+        BitmapDrawable bitmapDrawableIcon = bitmapDrawableMicrophoneIcon;
 
         switch (state) {
+            case TRANSITION_TO_LISTENING:
             case LISTENING:
-                if (mCurrentRadiusPix > mButtonRadiusPix) {
-                    // Draw circle to animate voice.
-                    canvas.drawCircle(canvasWidth / 2, canvasHeight / 2, mCurrentRadiusPix, mPaintAnimationListening);
+                if (state != TRANSITION_TO_LISTENING) {
+                    if (mCurrentRadiusPix > mButtonRadiusPix) {
+                        // Draw circle to animate voice.
+                        canvas.drawCircle(canvasWidth / 2, canvasHeight / 2, mCurrentRadiusPix, mPaintAnimationListening);
+                    }
                 }
 
-                // Draw the button boundary and background.
-                oval.set(calculateButtonBounds(bounds));
-                canvas.drawArc(oval, 0, 360, false, mPaintButtonBackground);
-                canvas.drawArc(oval, 0, 360, false, mPaintButtonBoundary);
+                drawButtonShape(calculatedBounds, canvas);
                 buttonColor = mIconColorListening;
                 break;
+            case TALKING:
+                buttonColor = mIconColorTalking;
+                bitmapDrawableIcon = bitmapDrawableLexIcon;
+                drawButtonShape(calculatedBounds, canvas);
+                break;
+            case TRANSITION_TO_TALKING:
+                buttonColor = mIconColorWaiting;
             case NORMAL:
-                // Draw the button boundary and background.
-                oval.set(calculateButtonBounds(bounds));
-                canvas.drawArc(oval, 0, 360, false, mPaintButtonBackground);
-                canvas.drawArc(oval, 0, 360, false, mPaintButtonBoundary);
-
+                drawButtonShape(calculatedBounds, canvas);
                 break;
             case AWAITING_RESPONSE:
                 // Animate circular wait indicator.
-                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
-                oval.set(calculateButtonBounds(bounds));
-                canvas.drawArc(oval, 0, 360, false, mPaintButtonBackground);
+                drawButtonShape(calculatedBounds, canvas);
                 canvas.drawArc(oval, mCurrentArcPosition, ARC_LEN_ANGLE, false, mPaintAnimationWaiting);
-
-                // Draw the button boundary.
-                canvas.drawArc(oval, 0, 360, false, mPaintButtonBoundary);
                 buttonColor = mIconColorWaiting;
                 break;
         }
 
+        // Clip so that the image does not get rendered beyond the boundary.
+        canvas.clipRect(calculatedBounds);
+
+        if (pushTransitionAnimator != null) {
+            float offsetY = pushTransitionAnimator.animatedFraction * calculatedBounds.height();
+            calculatedBounds.top += offsetY;
+            calculatedBounds.bottom += offsetY;
+        }
+
         // Draw the button icon.
-        bitmapDrawableIcon.setBounds(calculateButtonBounds(bounds));
+        bitmapDrawableIcon.setBounds(calculatedBounds);
         mDrawable = bitmapDrawableIcon.mutate();
         mDrawable.setColorFilter(buttonColor, PorterDuff.Mode.SRC_ATOP);
         mDrawable.draw(canvas);
     }
 
+    void animateListening() {
+        animateImageSwitch(new ActionHandler() {
+            @Override
+            public void handle() {
+                state = TRANSITION_TO_LISTENING;
+            }
+        });
+    }
+
+    void animateAudioPlayback() {
+        state = TRANSITION_TO_TALKING;
+        animateImageSwitch(new ActionHandler() {
+            @Override
+            public void handle() {
+                state = TALKING;
+            }
+        });
+    }
 
     /**
      * Animates sound by modulating radius of a displayed circle. This animation just performs one
@@ -246,14 +301,17 @@ protected void onDraw(Canvas canvas) {
      * @param soundLevel
      */
     public void animateSoundLevel(float soundLevel) {
-        reset();
-        state = LISTENING;
-        final float radius = soundlevel2radius(soundLevel);
-        mAnimatorSet.playSequentially(
-                ObjectAnimator.ofFloat(this, "CurrentRadius", getCurrentRadius(), radius).setDuration(20),
-                ObjectAnimator.ofFloat(this, "CurrentRadius", radius, mButtonRadiusPix).setDuration(400)
-        );
-        mAnimatorSet.start();
+        // Ignore if push transition animation is running.
+        if (pushTransitionAnimator == null || !pushTransitionAnimator.isRunning()) {
+            reset();
+            state = LISTENING;
+            final float radius = soundlevel2radius(soundLevel);
+            mAnimatorSet.playSequentially(
+                    ObjectAnimator.ofFloat(this, "CurrentRadius", getCurrentRadius(), radius).setDuration(20),
+                    ObjectAnimator.ofFloat(this, "CurrentRadius", radius, mButtonRadiusPix).setDuration(400)
+            );
+            mAnimatorSet.start();
+        }
     }
 
     /**
@@ -279,6 +337,22 @@ public void onAnimationUpdate(ValueAnimator animation) {
         invalidate();
     }
 
+    /**
+     * Start slide-out-up and in-up animation for image switching between Lex and microphone.
+     */
+    private void animateImageSwitch(final ActionHandler handler) {
+        reset();
+        if (animateOnImageSwitching) {
+            pushTransitionAnimator = new PushTransitionAnimator(handler);
+            pushTransitionAnimator.start();
+        } else {
+            handler.handle();
+            // invalidate() gets called during the animation inside the animator class.
+            // Since the animation is disabled, we would need to call to repaint here.
+            invalidate();
+        }
+    }
+
     /**
      * Stop current, if any, animation and change the button state to normal state.
      */
@@ -319,6 +393,10 @@ private void setCurrentAngle(float currentAngle) {
      * Stops current animation and prepares the widget for a new drawing.
      */
     private void reset() {
+        if (pushTransitionAnimator != null) {
+            pushTransitionAnimator.reset();
+        }
+
         if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
             mAnimatorSet.cancel();
         }
@@ -354,6 +432,17 @@ private Rect calculateButtonBounds(Rect bounds){
                 bounds.bottom - mAnimationGapPix);
     }
 
+    /**
+     * Draw the button boundary and background.
+     * @param bounds the complete drawable area.
+     * @param canvas bounds for the button boundary.
+     */
+    private void drawButtonShape(Rect bounds, Canvas canvas) {
+        oval.set(bounds);
+        canvas.drawArc(oval, 0, 360, false, mPaintButtonBackground);
+        canvas.drawArc(oval, 0, 360, false, mPaintButtonBoundary);
+    }
+
     /**
      * Converts the current rms value of sound to radius. This implementation assumes RMS value in
      * the {@code soundLevel} and performs linear translation.
@@ -430,6 +519,14 @@ public void setColorIconListening(String color) {
         mIconColorListening = getColor(color);
     }
 
+    /**
+     * Sets the color of the button icon when Lex is talking.
+     * @param color The hex-decimal color code as a string, e.g. "#FFFFFF".
+     */
+    public void setColorIconTalking(String color) {
+        mIconColorTalking = getColor(color);
+    }
+
     /**
      * Sets the color of the button icon when the client is waiting for response
      * from Amazon Lex bot.
@@ -440,6 +537,14 @@ public void setColorIconAwaitingResponse(String color) {
         mIconColorWaiting = getColor(color);
     }
 
+    /**
+     * Set the animation flag for switching image between microphone and bot.
+     * By default, the flag is false.
+     * @param animate true to activate animation otherwise false.
+     */
+    public void setAnimateOnImageSwitching(boolean animate) {
+        animateOnImageSwitching = animate;
+    }
     /**
      * Returns the int value of the hex-decimal color.
      * @param color The hex-decimal color code as a string, e.g. "#FFFFFF".
@@ -483,4 +588,97 @@ public interface InteractiveVoiceListener {
          */
         void onError(String responseText, Exception e);
     }
+
+    /**
+     * Animator for image switching between microphone and bot.
+     */
+    private class PushTransitionAnimator {
+
+        /**
+         * Constant for animatedFraction property.
+         */
+        static final String ANIMATED_FRACTION_PROPERTY_NAME = "animatedFraction";
+
+        /**
+         * Constant for initial value that is used by animatedFraction for both slideIn and slideOut push transition.
+         */
+        static final float ANIMATED_FRACTION_INITIAL_PROPERTY_VALUE = 0.0f;
+
+        /**
+         * The duration for whole animation.
+         * We want to use approximately 0.25 second to provide a better visual indication
+         * so that user does not speak too early.
+         */
+        static final long ANIMATION_DURATION = 250 / 2;
+        ObjectAnimator slideOutAnimator;
+        ObjectAnimator slideInAnimator;
+        ActionHandler handler;
+        /**
+         * The elapsed fraction of the push animation,
+         * range from 0.0 to 1.0 for slideIn and -1.0 to 0.0 for slideOut animation.
+         */
+        float animatedFraction;
+        boolean isRunning;
+
+        PushTransitionAnimator(ActionHandler handler) {
+            this.handler = handler;
+        }
+
+        void start() {
+            isRunning = true;
+            // Move image out toward top.
+            slideOutAnimator = ObjectAnimator.
+                    ofFloat(this, ANIMATED_FRACTION_PROPERTY_NAME, -1.0f).
+                    setDuration(ANIMATION_DURATION);
+            slideOutAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    handler.handle();
+                    // Move image from bottom.
+                    slideInAnimator = ObjectAnimator
+                            .ofFloat(PushTransitionAnimator.this, ANIMATED_FRACTION_PROPERTY_NAME, 1.0f, 0.0f)
+                            .setDuration(ANIMATION_DURATION);
+                    slideInAnimator.start();
+                    slideInAnimator.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            super.onAnimationEnd(animation);
+                            isRunning = false;
+                        }
+                    });
+                }
+            });
+            slideOutAnimator.start();
+        }
+
+        void setAnimatedFraction(float y) {
+            animatedFraction = y;
+            InteractiveVoiceView.this.invalidate();
+        }
+
+        void reset() {
+            if (slideOutAnimator != null && slideOutAnimator.isRunning()) {
+                slideOutAnimator.cancel();
+            }
+            if (slideInAnimator != null && slideInAnimator.isRunning()) {
+                slideInAnimator.cancel();
+            }
+            animatedFraction = ANIMATED_FRACTION_INITIAL_PROPERTY_VALUE;
+            isRunning = false;
+        }
+
+        boolean isRunning() {
+            return isRunning;
+        }
+
+    }
+
+    /**
+     * Naive action handler.
+     */
+    private interface ActionHandler {
+        void handle();
+    }
+
 }
diff --git a/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/ui/InteractiveVoiceViewAdapter.java b/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/ui/InteractiveVoiceViewAdapter.java
index 0069d1e8cf..2c1b8223b0 100644
--- a/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/ui/InteractiveVoiceViewAdapter.java
+++ b/aws-android-sdk-lex/src/main/java/com/amazonaws/mobileconnectors/lex/interactionkit/ui/InteractiveVoiceViewAdapter.java
@@ -91,6 +91,7 @@ public static InteractiveVoiceViewAdapter getInstance(Context context,
     @Override
     public void onAudioPlaybackStarted() {
         state = STATE_AUDIO_PLAYBACK;
+        micButton.animateAudioPlayback();
     }
 
     @Override
@@ -99,6 +100,7 @@ public void onAudioPlayBackCompleted() {
             if (this.continuation != null) {
                 state = STATE_LISTENING;
                 continuation.continueWithCurrentMode();
+                micButton.animateListening();
             } else {
                 // Cannot continue, must start new dialog.
                 state = STATE_READY;
@@ -112,6 +114,7 @@ public void onAudioPlaybackError(Exception e) {
         // Audio playback failed.
         Log.e(TAG, "InteractiveVoiceViewAdapter: Audio playback failed", e);
         state = STATE_READY;
+        micButton.animateNone();
     }
 
     @Override
@@ -128,13 +131,13 @@ public void onReadyForFulfillment(Response response) {
     @Override
     public void promptUserToRespond(Response response,
                                     LexServiceContinuation continuation) {
-        micButton.animateNone();
         if (response == null) {
             Log.e(TAG, "InteractiveVoiceViewAdapter: Received null response from Amazon Lex bot");
         }
 
         if (DialogState.Fulfilled.toString().equals(response.getDialogState())) {
             // The request has been fulfilled, the bot is ready for a new dialog.
+            micButton.animateNone();
             state = STATE_READY;
             this.continuation = null;
         } else {
@@ -149,8 +152,8 @@ public void promptUserToRespond(Response response,
     @Override
     public void onInteractionError(Response response, Exception e) {
         Log.e(TAG, "InteractiveVoiceViewAdapter: Interaction error", e);
-        micButton.animateNone();
         if (state != STATE_AUDIO_PLAYBACK) {
+            micButton.animateNone();
             state = STATE_READY;
         }
         continuation = null;
diff --git a/aws-android-sdk-lex/src/main/res/drawable-hdpi/lex_speak.png b/aws-android-sdk-lex/src/main/res/drawable-hdpi/lex_speak.png
new file mode 100644
index 0000000000000000000000000000000000000000..24ab598405e82fd2f0dc910ed45cf62c08a7265d
GIT binary patch
literal 802
zcmV+-1Ks?IP){!c9!$1(lDEFiToC@I1As}%l9mG@+7l4!;C}2|oTS5vziaBskI&eBjDhM;^
zS=w0kdVe-Sc%>C49?#61|88_Tov!}!y1x43s|U~n=mGpU08ejUqqdZB;eBl>K+>PA
z4G`d&L5A@fSPGcZ9@2iGy{@6-cL`{2@bjLPqb)yP@3R^aOn*x|W?4jcFwrZr>BPiX
z(4RRmk7@uis7V8xPc}rAg2aQBS<$;RxCgLwR!Lyx%zzKm3I`e90Lm7Np^WVs(YH%H
zb6DlBIt)CNXT{$Gsc7iAjo(j|4$h6jX3sVQQ4hGwU0gfphaH`cP80AM^AYDlp`NWivt2ZZ9o_VpO9Ez=)zTaZcc@Fe`2)F2(~z!m_gToWcurqxa89L9_xO
zbDs(nDhIgRZg8t0G03fy=61yM3hyLnHvvj;kPu2yb)MOx03^zmReDAW4;@1!c90~7
zaFN-02Yrqlg!S&-T(jq07*qoM6N<$f-}irSpWb4

literal 0
HcmV?d00001

diff --git a/aws-android-sdk-lex/src/main/res/drawable-mdpi/lex_speak.png b/aws-android-sdk-lex/src/main/res/drawable-mdpi/lex_speak.png
new file mode 100644
index 0000000000000000000000000000000000000000..7980a28f09ca99b5861c27821add6c7b21dfca60
GIT binary patch
literal 565
zcmV-50?Pe~P){mTbgg_KtHuTO7mK!XEoyAgEn#c`gPar0i78D*}kHFqw?M=iOI}465-T+U4
z@0pKeG8vcwXS15{k{6PB!}q>24}GzsndydqN9F
zLKjDe
z1!B1icIcQ+4&gOIEiHSQq$#5=!EL8=xK
zPFJDYOTA^0_r!ovjE(>SGT7RX-^k#kOH*Vh)HSe1r$89OTcZv#gDr!tcENHgigzq-HdEQL3@R-
zIl!H8!@%UgWe}|p9dDB`SHNu|(W*|PIGB1Tq(~H6O%(s~LNYo6Xg^Lx))gcQk1PJ5
zPe^YB;3QTq7(DA=VcwwMxg8rKQMnQbN08-HzmPtWUXh+FBl(W>gY+Js?ZA5tV8m|W
z^bOA%s;+I1T>}E4P7Sl7bopd^X~X%)WG3KCz6BTnp8cySe+3{>x?+b|HM4d3M-#6PI-Jy?g{tGuA{3!>Z$R0evF{Q|o$upZEZbjV$Kf!!C>
zdV#w{#|X1Qb>CZ9JcGTRs*z#_QC%~Kr17N0^EHRD5r3;VFoQ89PWS4yM_taL@9_c+*
z;Ng89!E+B8&N;!O1_&7{H#)$rZO+LA02#vGK$j6wUDN4N(&Cd2^Qm+}?qvcfKZO`r
zmrBI;b&YjgeAu-lHtu2HB^=LErz!)!qG2Ja#uRdlJ?m2x?Uf7x^__6$tMY|7??b_U
z6P;Z&OHs@>-JiW(kk=3oT?d=7IEBDnZc
z-xux^>b?=$U3>)Y6SxKhHqF7N2FPm9cQ$RyvJv_01y#cSOW%MbPnyvY%km=LstxiJpdj6
q4}b^21K^TK6=A_gl*UQs-|7)9h>z9QZMeUX}V=T!GFT1t;$ovp7gKa
z*UV;92$B@_+^cwBx~_Wt`t@>&{O>cEgdCVS@Zy4tFRsTPSgyaH?UnzMOH;k9r#8)>
zZ+}cr%K2(($F6AMx
zhMR{5aGEOF)dr@{(_5vl&e^aqX+84`>r0Q8zv}<-+tJz8D{`7T@(wAb4GXt0VM##?nuDi9)URFFKVu8B}M7YP%bf3@}KKVAv
z{3o-U_jLF0%YEcsTk7cDT>i|mWhT#*Lo&_hq<*peW?ArbMo-CK=Cv17T7Dl}JL3s+
z__bYf30C_jJ%7KQ?Premm49nh4v0Lmx_0aHxeKLdKIC<6eg2@2=ebzQf{?bT?ft&r
zo^@0?UYI&>#*SU$EB%-rO>%J&ohW(ppI9`DO9A6lUrXnxU+EHcaSzMi?2&rj`>S-9
zP^!Vl!}qn%giX2n!$&h(G_5qxe~l?e^4YIu!aS!R%n5uM+^YM3+q=@ow?YN%9PS+3
z8(uV<^HbFIJHNzNtiFF_QLVBTN9+zQPKgETJw;J(wye38S&?|+Y^`JZ1jz(N+quhc
zR{Y;KQziHJyla9B1PcXcZ;z4W{4J|_S(3?Y=5uL}YiDiRXv%+zkT7cK_GLIbH6)WGZRM7G5T3DN#rCjvz!rh|+4TrD3tU0?VBf<4Rok8t<
z!-W4QUNJX)_;STiDaTdtlUwc!Mm=GJ_Sv4z>m>;lQ>EKjGd
zmVa5ov*LlwMwX|Yt7mVTqvRlI{pH_#^|_gAf6sZH^=ZkAd!?od#xf=2~Nmh
zt1CX+Q
     com.amazonaws
     aws-android-sdk-pom
-    2.3.9
+    2.4.0
   
 
   
@@ -20,7 +20,7 @@
       com.amazonaws
       aws-android-sdk-core
       false
-      2.3.9
+      2.4.0
     
   
 
diff --git a/aws-android-sdk-mobileanalytics/pom.xml b/aws-android-sdk-mobileanalytics/pom.xml
index 41828006e6..310c42d0c0 100644
--- a/aws-android-sdk-mobileanalytics/pom.xml
+++ b/aws-android-sdk-mobileanalytics/pom.xml
@@ -12,7 +12,7 @@
   
     com.amazonaws
     aws-android-sdk-pom
-    2.3.9
+    2.4.0
   
 
   
@@ -20,7 +20,7 @@
       com.amazonaws
       aws-android-sdk-core
       false
-      2.3.9
+      2.4.0
     
     
       junit
diff --git a/aws-android-sdk-pinpoint/pom.xml b/aws-android-sdk-pinpoint/pom.xml
index dea6cdcd32..ba5b1122a8 100644
--- a/aws-android-sdk-pinpoint/pom.xml
+++ b/aws-android-sdk-pinpoint/pom.xml
@@ -12,7 +12,7 @@
     
         com.amazonaws
         aws-android-sdk-pom
-        2.3.9
+        2.4.0
     
 
     
@@ -20,7 +20,7 @@
             com.amazonaws
             aws-android-sdk-core
             false
-            2.3.9
+            2.4.0
         
         
             junit
diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/AnalyticsClient.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/AnalyticsClient.java
index c9b83547ef..587ca26333 100644
--- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/AnalyticsClient.java
+++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/AnalyticsClient.java
@@ -111,10 +111,14 @@ public AnalyticsEvent createEvent(String eventType) throws IllegalArgumentExcept
             log.error("The event type is too long, the max event type length is " + MAX_EVENT_TYPE_LENGTH
                     + " characters");
             throw new IllegalArgumentException("The eventType passed into create event was too long");
-
         }
 
-        AnalyticsEvent event = createEvent(eventType, sessionStartTime, null, null);
+        return createEvent(eventType, sessionStartTime, null, null);
+    }
+
+    protected AnalyticsEvent createEvent(String eventType, long sessionStart, Long sessionEnd, Long sessionDuration) {
+        AnalyticsEvent event = AnalyticsEvent.newInstance(context, sessionId, sessionStart, sessionEnd,
+                                                       sessionDuration, System.currentTimeMillis(), eventType);
 
         for (final Entry attr : globalAttributes.entrySet()) {
             event.addAttribute(attr.getKey(), attr.getValue());
@@ -122,7 +126,7 @@ public AnalyticsEvent createEvent(String eventType) throws IllegalArgumentExcept
 
         if (eventTypeAttributes.containsKey(event.getEventType())) {
             for (final Entry attr : eventTypeAttributes.get(
-                    event.getEventType()).entrySet()) {
+                event.getEventType()).entrySet()) {
                 event.addAttribute(attr.getKey(), attr.getValue());
             }
         }
@@ -133,7 +137,7 @@ public AnalyticsEvent createEvent(String eventType) throws IllegalArgumentExcept
 
         if (eventTypeMetrics.containsKey(event.getEventType())) {
             for (final Entry metric : eventTypeMetrics.get(
-                    event.getEventType()).entrySet()) {
+                event.getEventType()).entrySet()) {
                 event.addMetric(metric.getKey(), metric.getValue());
             }
         }
@@ -141,11 +145,6 @@ public AnalyticsEvent createEvent(String eventType) throws IllegalArgumentExcept
         return event;
     }
 
-    protected AnalyticsEvent createEvent(String eventType, long sessionStart, Long sessionEnd, Long sessionDuration) {
-        return AnalyticsEvent.newInstance(context, sessionId, sessionStart, sessionEnd,
-                sessionDuration, System.currentTimeMillis(), eventType);
-    }
-
     /**
      * Record the specified event to the local filestore Please note if the
      * amount of data stored events takes up EXCEEDS 5MiB further recordings
diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/AmazonMonetizationEventBuilder.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/AmazonMonetizationEventBuilder.java
index 06610f6717..65897e5a17 100644
--- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/AmazonMonetizationEventBuilder.java
+++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/AmazonMonetizationEventBuilder.java
@@ -21,7 +21,7 @@
 /**
  * Builds monetization events representing an Amazon purchase. This Builder
  * automatically sets the store attribute to "Amazon". This Builder will return
- * null if the ProductId, Formatted Localized Price, or Quantity are not set.
+ * null if the ProductId, Currency, Item Price, or Quantity are not set.
  * 

Example: * *
@@ -29,9 +29,9 @@
  * // create a builder that can record purchase events for Amazon IAP
  * AmazonMonetizationEventBuilder builder = AmazonMonetizationEventBuilder.create(pinpointManager.getAnalyticsClient());
  *
- * // build the monetization event with the product id, formatted item price, and
+ * // build the monetization event with the product id, currency, item price, and
  * // quantity
- * // product id and formatted item price are obtained from the Item object
+ * // product id and item price are obtained from the Item object
  * // Amazon IAP currently only supports a quantity of 1
  * Event purchaseEvent = builder.withProductId(purchasedItem.getSku())
  *         .withFormattedItemPrice(purchasedItem.getPrice()).withQuantity(1).build();
@@ -73,7 +73,11 @@ public AmazonMonetizationEventBuilder withProductId(String productId) {
      *
      * @param formattedItemPrice The localized formatted price of the item
      * @return this for chaining
+     *
+     * @deprecated  Will be removed. Please set Currency and Item Price. Replaced by
+     *    {@link #withCurrency(String)} and {@link #withItemPrice(Double)}
      */
+    @Deprecated
     public AmazonMonetizationEventBuilder withFormattedItemPrice(String formattedItemPrice) {
         setFormattedItemPrice(formattedItemPrice);
         return this;
@@ -91,6 +95,29 @@ public AmazonMonetizationEventBuilder withQuantity(Double quantity) {
         return this;
     }
 
+    /**
+     * Sets the item price of the item purchased.
+     *
+     * @param itemPrice The numerical price of the item
+     * @return this for chaining
+     */
+    public AmazonMonetizationEventBuilder withItemPrice(Double itemPrice) {
+        setItemPrice(itemPrice);
+        return this;
+    }
+
+    /**
+     * Sets the currency of the item purchased.
+     *
+     * @param currency The currency code of the currency used to purchase this
+     *            item (i.e. "USD" or "$")
+     * @return this for chaining
+     */
+    public AmazonMonetizationEventBuilder withCurrency(String currency) {
+        setCurrency(currency);
+        return this;
+    }
+
     /**
      * Construct a AmazonMonetizationEventBuilder with the specified EventClient
      *
@@ -114,9 +141,14 @@ protected boolean isValid() {
             return false;
         }
 
-        if (getFormattedItemPrice() == null) {
-            log.warn("Amazon Monetization event is not valid: it is missing the formatted localized price");
-            return false;
+        if (getFormattedItemPrice() == null){
+            if (getCurrency() == null) {
+                log.warn("Amazon Monetization event is not valid: it is missing the localized currency");
+                return false;
+            } else if (getItemPrice() == null) {
+                log.warn("Amazon Monetization event is not valid: it is missing the localized item price");
+                return false;
+            }
         }
 
         return true;
diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/CustomMonetizationEventBuilder.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/CustomMonetizationEventBuilder.java
index 239a0d9acc..00957a0865 100644
--- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/CustomMonetizationEventBuilder.java
+++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/CustomMonetizationEventBuilder.java
@@ -21,8 +21,7 @@
 /**
  * Builds monetization events representing a purchase from an IAP framework not
  * defined by a specific builder. The store attribute must be set by the user.
- * This Builder will return null if the Store, ProductId, Quantity, and either
- * the Formatted Localized Price or Price and Currency are not set.
+ * This Builder will return null if the Store, ProductId, Quantity, Price and Currency are not set.
  * 

* The example below demonstrates how to create a monetization event after you * receive a purchase confirmation from the IAP framework you are using. You are @@ -111,20 +110,22 @@ public CustomMonetizationEventBuilder withQuantity(Double quantity) { } /** - * Sets the formatted localized price of the item being purchased (Required - * if numerical price and currency are not specified) + * Sets the formatted localized price of the item being purchased * * @param formattedItemPrice The formatted localized price of the item * @return this for chaining + * + * @deprecated Will be removed. Please set Currency and Item Price. Replaced by + * {@link #withCurrency(String)} and {@link #withItemPrice(double) */ + @Deprecated public CustomMonetizationEventBuilder withFormattedItemPrice(String formattedItemPrice) { setFormattedItemPrice(formattedItemPrice); return this; } /** - * Sets the numerical price of the item being purchased (Required if - * formatted localized price is not specified) + * Sets the numerical price of the item being purchased (Required) * * @param itemPrice The numerical price of the item * @return this for chaining @@ -135,8 +136,7 @@ public CustomMonetizationEventBuilder withItemPrice(double itemPrice) { } /** - * Sets the currency of the item being purchased (Required if formatted - * localized price is not specified) + * Sets the currency of the item being purchased (Required) * * @param currency The currency code of the currency used to purchase this * item (i.e. "USD") @@ -169,8 +169,7 @@ protected CustomMonetizationEventBuilder(AnalyticsClient analyticsClient) { @Override protected boolean isValid() { - // we must always have the store, product id, quantity, either formatted - // localized price or item price and currency + // we must always have the store, product id, quantity, item price and currency if (getStore() == null) { log.warn("Custom Monetization event is not valid: it is missing the store"); return false; @@ -187,11 +186,9 @@ protected boolean isValid() { } if (getCurrency() == null || getItemPrice() == null) { - if (getFormattedItemPrice() == null) { - log.warn("Custom Monetization event is not valid: it requires the formatted " + - "localized price or the currency and price"); - return false; - } + log.warn("Custom Monetization event is not valid: it requires the formatted " + + "localized price or the currency and price"); + return false; } return true; diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/GooglePlayMonetizationEventBuilder.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/GooglePlayMonetizationEventBuilder.java index 2cbfa288cf..683e9b1c3a 100644 --- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/GooglePlayMonetizationEventBuilder.java +++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/GooglePlayMonetizationEventBuilder.java @@ -21,7 +21,7 @@ /** * Builds monetization events representing a Google Play purchase. This Builder * automatically sets the store attribute to "Google Play". This Builder will - * return null if the ProductId, Formatted Localized Price, Quantity, or + * return null if the ProductId, Currency, Item Price, Quantity, or * TransactionId are not set. *

* The example below demonstrates how to create a monetization event after you @@ -87,7 +87,11 @@ public GooglePlayMonetizationEventBuilder withProductId(String productId) { * * @param formattedItemPrice The localized formatted price of the item * @return this for chaining + * + * @deprecated Will be removed. Please set Currency and Item Price. Replaced by + * {@link #withCurrency(String)} and {@link #withItemPrice(Double) */ + @Deprecated public GooglePlayMonetizationEventBuilder withFormattedItemPrice(String formattedItemPrice) { setFormattedItemPrice(formattedItemPrice); return this; @@ -105,6 +109,29 @@ public GooglePlayMonetizationEventBuilder withQuantity(Double quantity) { return this; } + /** + * Sets the item price of the item purchased. + * + * @param itemPrice The numerical price of the item + * @return this for chaining + */ + public GooglePlayMonetizationEventBuilder withItemPrice(Double itemPrice) { + setItemPrice(itemPrice); + return this; + } + + /** + * Sets the currency of the item purchased. + * + * @param currency The currency code of the currency used to purchase this + * item (i.e. "USD" or "$") + * @return this for chaining + */ + public GooglePlayMonetizationEventBuilder withCurrency(String currency) { + setCurrency(currency); + return this; + } + /** * The transaction identifier of the purchase. * @@ -129,7 +156,7 @@ protected GooglePlayMonetizationEventBuilder(AnalyticsClient analyticsClient) { } /** - * Returns true only if the ProductId, Quantity, Formatted Item Price, and + * Returns true only if the ProductId, Quantity, Currency, Item Price, and * Transaction Id are set */ @Override @@ -146,8 +173,13 @@ protected boolean isValid() { } if (getFormattedItemPrice() == null) { - log.warn("Google Monetization event is not valid: it is missing the formatted localized price"); - return false; + if (getCurrency() == null) { + log.warn("Google Monetization event is not valid: it is missing the localized currency"); + return false; + } else if (getItemPrice() == null) { + log.warn("Google Monetization event is not valid: it is missing the localized item price"); + return false; + } } if (getTransactionId() == null) { diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/MonetizationEventBuilder.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/MonetizationEventBuilder.java index 1bf4948165..68c0dfa824 100644 --- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/MonetizationEventBuilder.java +++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/MonetizationEventBuilder.java @@ -184,7 +184,10 @@ protected void setItemPrice(Double itemPrice) { * @return The formatted item price that will be used to build the * monetization event. Price should be formatted for the locale and * currency representing the transaction. + * + * @deprecated Will be removed. Please set Currency and Item Price */ + @Deprecated protected String getFormattedItemPrice() { return formattedItemPrice; } @@ -194,7 +197,10 @@ protected String getFormattedItemPrice() { * building the monetization event * * @param formattedItemPrice the formatted item price in its local currency + * + * @deprecated Will be removed. Please set Currency and Item Price */ + @Deprecated protected void setFormattedItemPrice(String formattedItemPrice) { this.formattedItemPrice = formattedItemPrice; } @@ -284,7 +290,7 @@ private boolean doBaseValidation() { // we must always have the (currency and price) or formatted price if (StringUtil.isNullOrEmpty(currency) || itemPrice == null) { if (StringUtil.isNullOrEmpty(formattedItemPrice)) { - log.warn("Base Monetization event is not valid: it requires the formatted price or the currency and price"); + log.warn("Base Monetization event is not valid: it requires the currency and price"); return false; } } diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/internal/event/EventRecorder.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/internal/event/EventRecorder.java index d194e232de..fb6c2a2a25 100644 --- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/internal/event/EventRecorder.java +++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/internal/event/EventRecorder.java @@ -112,12 +112,19 @@ public Uri recordEvent(AnalyticsEvent event) { final Uri uri = this.dbUtil.saveEvent(event); if (uri != null) { - while(this.dbUtil.getTotalSize() > maxPendingSize) { - final Cursor cursor = this.dbUtil.queryOldestEvents(5); - while(this.dbUtil.getTotalSize() > maxPendingSize && cursor.moveToNext()) { - this.dbUtil.deleteEvent(cursor.getInt(EventTable.COLUMN_INDEX.ID.getValue()), - cursor.getInt(EventTable.COLUMN_INDEX.SIZE.getValue())); + Cursor cursor = null; + try { + cursor = this.dbUtil.queryOldestEvents(5); + while (this.dbUtil.getTotalSize() > maxPendingSize && cursor.moveToNext()) { + this.dbUtil.deleteEvent( + cursor.getInt(EventTable.COLUMN_INDEX.ID.getValue()), + cursor.getInt(EventTable.COLUMN_INDEX.SIZE.getValue())); + } + } finally { + if (cursor != null) { + cursor.close(); + } } } @@ -184,53 +191,67 @@ JSONArray getBatchOfEvents(Cursor cursor, List idsToDeletes, List getAllEvents() { final List events = new ArrayList(); - final Cursor cursor = dbUtil.queryAllEvents(); - while (cursor.moveToNext()) { - events.add(translateFromCursor(cursor)); + Cursor cursor = null; + try { + cursor = dbUtil.queryAllEvents(); + while (cursor.moveToNext()) { + events.add(translateFromCursor(cursor)); + } + } finally { + if (cursor != null) { + cursor.close(); + } } - cursor.close(); return events; } void processEvents() { final long start = System.currentTimeMillis(); - final Cursor cursor = dbUtil.queryAllEvents(); - - final List idsToDeletes = new ArrayList(); - final List sizeToDeletes = new ArrayList(); - boolean successful; - int submissions = 0; - final long maxSubmissionsAllowed = pinpointContext.getConfiguration().optInt( - KEY_MAX_SUBMISSIONS_ALLOWED, DEFAULT_MAX_SUBMISSIONS_ALLOWED); + Cursor cursor = null; - while (cursor.moveToNext()) { - final List batchIdsToDeletes = new ArrayList(); - final List batchSizeToDeletes = new ArrayList(); - successful = submitEvents(this.getBatchOfEvents(cursor, batchIdsToDeletes, batchSizeToDeletes)); - if (successful) { - idsToDeletes.addAll(batchIdsToDeletes); - sizeToDeletes.addAll(batchSizeToDeletes); - submissions++; - } - if (submissions >= maxSubmissionsAllowed) { - break; + try { + cursor = dbUtil.queryAllEvents(); + + final List idsToDeletes = new ArrayList(); + final List sizeToDeletes = new ArrayList(); + boolean successful; + int submissions = 0; + final long maxSubmissionsAllowed = pinpointContext.getConfiguration().optInt( + KEY_MAX_SUBMISSIONS_ALLOWED, DEFAULT_MAX_SUBMISSIONS_ALLOWED); + + while (cursor.moveToNext()) { + final List batchIdsToDeletes = new ArrayList(); + final List batchSizeToDeletes = new ArrayList(); + successful = submitEvents( + this.getBatchOfEvents(cursor, batchIdsToDeletes, batchSizeToDeletes)); + if (successful) { + idsToDeletes.addAll(batchIdsToDeletes); + sizeToDeletes.addAll(batchSizeToDeletes); + submissions++; + } + if (submissions >= maxSubmissionsAllowed) { + break; + } } - } - cursor.close(); - if (sizeToDeletes.size() > 0) { - for(int i = 0; i < sizeToDeletes.size(); i++) { - try { - dbUtil.deleteEvent(idsToDeletes.get(i), sizeToDeletes.get(i)); - } catch (final Exception exc) { - log.error("Failed to delete event: " + idsToDeletes.get(i), exc); + if (sizeToDeletes.size() > 0) { + for (int i = 0; i < sizeToDeletes.size(); i++) { + try { + dbUtil.deleteEvent(idsToDeletes.get(i), sizeToDeletes.get(i)); + } catch (final Exception exc) { + log.error("Failed to delete event: " + idsToDeletes.get(i), exc); + } } } - } - log.info(String.format("Time of attemptDelivery: %d", - System.currentTimeMillis() - start)); + log.info(String.format("Time of attemptDelivery: %d", + System.currentTimeMillis() - start)); + } finally { + if (cursor != null) { + cursor.close(); + } + } } boolean submitEvents(final JSONArray eventArray) { diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/internal/event/PinpointDBBase.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/internal/event/PinpointDBBase.java index a554790d1b..df9dc10b99 100644 --- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/internal/event/PinpointDBBase.java +++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/internal/event/PinpointDBBase.java @@ -15,6 +15,9 @@ package com.amazonaws.mobileconnectors.pinpoint.internal.event; +import static com.amazonaws.mobileconnectors.pinpoint.internal.event.EventTable.COLUMN_SIZE; +import static com.amazonaws.mobileconnectors.pinpoint.internal.event.EventTable.TABLE_EVENT; + import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; @@ -24,9 +27,6 @@ import android.net.Uri; import android.text.TextUtils; -import static com.amazonaws.mobileconnectors.pinpoint.internal.event.EventTable.COLUMN_SIZE; -import static com.amazonaws.mobileconnectors.pinpoint.internal.event.EventTable.TABLE_EVENT; - /** * Provides methods to access database through which applications can interact with tasks. */ @@ -48,7 +48,7 @@ public class PinpointDBBase { */ public PinpointDBBase(Context context) { this.context = context; - String mAuthority = context.getApplicationContext().getPackageName(); + final String mAuthority = context.getApplicationContext().getPackageName(); databaseHelper = new PinpointDatabaseHelper(this.context); contentUri = Uri.parse("content://" + mAuthority + "/" + BASE_PATH); uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); @@ -88,12 +88,12 @@ public Uri getContentUri() { * @return The Uri of the inserted record. */ public Uri insert(Uri uri, ContentValues values) { - int uriType = uriMatcher.match(uri); - SQLiteDatabase db = databaseHelper.getWritableDatabase(); + final int uriType = uriMatcher.match(uri); + final SQLiteDatabase db = databaseHelper.getWritableDatabase(); long id = 0; switch (uriType) { case EVENTS: - long size = getTotalSize(); + final long size = getTotalSize(); id = db.insertOrThrow(TABLE_EVENT, null, values); totalSize = size + values.getAsLong(COLUMN_SIZE); break; @@ -108,15 +108,23 @@ public Uri insert(Uri uri, ContentValues values) { * @return Total size. */ public long getTotalSize() { - if (totalSize < 0) { - Cursor cursor = databaseHelper.getReadableDatabase().rawQuery("SELECT SUM(" + COLUMN_SIZE + ") FROM " - + TABLE_EVENT, null); - if (!cursor.moveToNext()) { - totalSize = 0; - } else if (cursor.isNull(0)) { - totalSize = 0; - } else { - totalSize = cursor.getLong(0); + Cursor cursor = null; + try { + if (totalSize < 0) { + cursor = databaseHelper.getReadableDatabase() + .rawQuery("SELECT SUM(" + COLUMN_SIZE + ") FROM " + + TABLE_EVENT, null); + if (!cursor.moveToNext()) { + totalSize = 0; + } else if (cursor.isNull(0)) { + totalSize = 0; + } else { + totalSize = cursor.getLong(0); + } + } + } finally { + if (cursor != null) { + cursor.close(); } } return totalSize; @@ -135,12 +143,12 @@ public long getTotalSize() { */ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, String limit) { - SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); + final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); // TODO: currently all methods calling this pass null to projection. // In the future we want to update projection to be more specific for // performance and must handle that here. queryBuilder.setTables(TABLE_EVENT); - int uriType = uriMatcher.match(uri); + final int uriType = uriMatcher.match(uri); switch (uriType) { case EVENTS: break; @@ -150,8 +158,8 @@ public Cursor query(Uri uri, String[] projection, String selection, String[] sel default: throw new IllegalArgumentException("Unknown URI: " + uri); } - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, + final SQLiteDatabase db = databaseHelper.getWritableDatabase(); + final Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder, limit); return cursor; } @@ -167,8 +175,8 @@ public Cursor query(Uri uri, String[] projection, String selection, String[] sel */ public synchronized int update(Uri uri, ContentValues values, String whereClause, String[] whereArgs) { - int uriType = uriMatcher.match(uri); - SQLiteDatabase db = databaseHelper.getWritableDatabase(); + final int uriType = uriMatcher.match(uri); + final SQLiteDatabase db = databaseHelper.getWritableDatabase(); int rowsUpdated = 0; switch (uriType) { case EVENTS: @@ -176,7 +184,7 @@ public synchronized int update(Uri uri, ContentValues values, String whereClause whereArgs); break; case EVENT_ID: - String id = uri.getLastPathSegment(); + final String id = uri.getLastPathSegment(); if (TextUtils.isEmpty(whereClause)) { rowsUpdated = db.update(TABLE_EVENT, values, EventTable.COLUMN_ID + "=" + id, null); @@ -202,8 +210,8 @@ public synchronized int update(Uri uri, ContentValues values, String whereClause * @return Number of rows deleted. */ public int delete(Uri uri, String selection, String[] selectionArgs, Integer knownSize) { - int uriType = uriMatcher.match(uri); - SQLiteDatabase db = databaseHelper.getWritableDatabase(); + final int uriType = uriMatcher.match(uri); + final SQLiteDatabase db = databaseHelper.getWritableDatabase(); int rowsDeleted = 0; switch (uriType) { case EVENTS: @@ -211,8 +219,8 @@ public int delete(Uri uri, String selection, String[] selectionArgs, Integer kno totalSize = -1; break; case EVENT_ID: - String id = uri.getLastPathSegment(); - long size = getTotalSize(); + final String id = uri.getLastPathSegment(); + final long size = getTotalSize(); if (TextUtils.isEmpty(selection)) { rowsDeleted = db.delete(TABLE_EVENT, EventTable.COLUMN_ID + "=" + id, null); diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/TargetingClient.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/TargetingClient.java index 47be6b98be..40c1ff03b5 100644 --- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/TargetingClient.java +++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/TargetingClient.java @@ -61,12 +61,14 @@ public class TargetingClient { private final Map> globalAttributes; private final Map globalMetrics; private final ExecutorService endpointRunnableQueue; + private final EndpointProfile endpointProfile; //For testing public TargetingClient(final PinpointContext context, ThreadPoolExecutor executor) { checkNotNull(context, "A valid pinpointContext must be provided"); this.endpointRunnableQueue = executor; this.context = context; + this.endpointProfile = new EndpointProfile(this.context); globalAttributes = loadAttributes(); globalMetrics = loadMetrics(); } @@ -83,20 +85,19 @@ public TargetingClient(final PinpointContext context) { * @returns the current device endpoint profile */ public EndpointProfile currentEndpoint() { - EndpointProfile endpointProfile = new EndpointProfile(this.context); // Add global attributes. if (!this.globalAttributes.isEmpty()) { for (Map.Entry> pair : globalAttributes.entrySet()) { - endpointProfile.addAttribute(pair.getKey(), pair.getValue()); + this.endpointProfile.addAttribute(pair.getKey(), pair.getValue()); } } if (!this.globalMetrics.isEmpty()) { for (Map.Entry pair : globalMetrics.entrySet()) { - endpointProfile.addMetric(pair.getKey(), pair.getValue()); + this.endpointProfile.addMetric(pair.getKey(), pair.getValue()); } } - return endpointProfile; + return this.endpointProfile; } /** @@ -270,7 +271,7 @@ public void removeAttribute(final String attributeName) { log.warn("Null attribute name provided to removeGlobalAttribute"); return; } - + this.endpointProfile.addAttribute(attributeName, null); globalAttributes.remove(attributeName); saveAttributes(); } @@ -308,7 +309,7 @@ public void removeMetric(String metricName) { log.warn("Null metric name provided to removeGlobalMetric"); return; } - + this.endpointProfile.addMetric(metricName, null); globalMetrics.remove(metricName); saveMetrics(); } diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/endpointProfile/EndpointProfile.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/endpointProfile/EndpointProfile.java index c91714b218..46ed996f12 100644 --- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/endpointProfile/EndpointProfile.java +++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/endpointProfile/EndpointProfile.java @@ -73,7 +73,6 @@ public EndpointProfile(final PinpointContext pinpointContext) { this.pinpointContext = pinpointContext; this.effectiveDate = DateUtil.getCorrectedDate().getTime(); - this.optOut = (this.pinpointContext.getNotificationClient().areAppNotificationsEnabled())?"NONE":"ALL"; this.demographic = new EndpointProfileDemographic(this.pinpointContext); this.location = new EndpointProfileLocation(); this.user = new EndpointProfileUser(); @@ -175,16 +174,8 @@ public void setEffectiveDate(long effectiveDate) { * @return String (ALL | NONE) */ public String getOptOut() { - return this.optOut; - } - - /** - * Set weather the endpoint is opted out of notification. - * - * @param optOut The optOut option. (ALL | NONE) - */ - public void setOptOut(String optOut) { - this.optOut = optOut; + return (this.pinpointContext.getNotificationClient().areAppNotificationsEnabled() && + !StringUtil.isBlank(this.pinpointContext.getNotificationClient().getGCMDeviceToken())) ? "NONE" : "ALL"; } private static String processAttributeMetricKey(String key) { diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/notification/NotificationClient.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/notification/NotificationClient.java index f92a82e401..9793d938bd 100644 --- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/notification/NotificationClient.java +++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/notification/NotificationClient.java @@ -100,7 +100,7 @@ public enum CampaignPushResult { private static final String GCM_TOKEN_PREF_KEY = "AWSPINPOINT.GCMTOKEN"; //Pinpoint - private static final String PINPOINT_PUSH_KEY_PREFIX = "pinpoint."; + protected static final String PINPOINT_PUSH_KEY_PREFIX = "pinpoint."; //Notification private static final String GCM_NOTIFICATION_PUSH_KEY_PREFIX = PINPOINT_PUSH_KEY_PREFIX + "notification."; diff --git a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/notification/PinpointNotificationReceiver.java b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/notification/PinpointNotificationReceiver.java index bddca81eb6..ceb08ee22a 100644 --- a/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/notification/PinpointNotificationReceiver.java +++ b/aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/notification/PinpointNotificationReceiver.java @@ -35,10 +35,11 @@ public static void setWeakNotificationClient(NotificationClient notificationClie @Override public void onReceive(Context context, Intent intent) { if (weakNotificationClient != null) { + String prefix = NotificationClient.CAMPAIGN_PUSH_KEY_PREFIX; Map campaignAttributes = new HashMap(); - campaignAttributes.put(NotificationClient.CAMPAIGN_ID_ATTRIBUTE_KEY, intent.getStringExtra(NotificationClient.CAMPAIGN_ID_ATTRIBUTE_KEY)); - campaignAttributes.put(NotificationClient.CAMPAIGN_TREATMENT_ID_ATTRIBUTE_KEY, intent.getStringExtra(NotificationClient.CAMPAIGN_TREATMENT_ID_ATTRIBUTE_KEY)); - campaignAttributes.put(NotificationClient.CAMPAIGN_ACTIVITY_ID_ATTRIBUTE_KEY, intent.getStringExtra(NotificationClient.CAMPAIGN_ACTIVITY_ID_ATTRIBUTE_KEY)); + campaignAttributes.put(NotificationClient.CAMPAIGN_ID_ATTRIBUTE_KEY, intent.getStringExtra(prefix.concat(NotificationClient.CAMPAIGN_ID_ATTRIBUTE_KEY))); + campaignAttributes.put(NotificationClient.CAMPAIGN_TREATMENT_ID_ATTRIBUTE_KEY, intent.getStringExtra(prefix.concat(NotificationClient.CAMPAIGN_TREATMENT_ID_ATTRIBUTE_KEY))); + campaignAttributes.put(NotificationClient.CAMPAIGN_ACTIVITY_ID_ATTRIBUTE_KEY, intent.getStringExtra(prefix.concat(NotificationClient.CAMPAIGN_ACTIVITY_ID_ATTRIBUTE_KEY))); weakNotificationClient.get().handleNotificationOpen(campaignAttributes, intent.getExtras()); } else { PackageManager pm = context.getPackageManager(); diff --git a/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/AmazonMonetizationEventBuilderTest.java b/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/AmazonMonetizationEventBuilderTest.java index a6339dd30b..705a962e51 100644 --- a/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/AmazonMonetizationEventBuilderTest.java +++ b/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/AmazonMonetizationEventBuilderTest.java @@ -72,48 +72,54 @@ public void setup() { @Test public void build_productIdNotSet_returnsNull() { AmazonMonetizationEventBuilder builder = new AmazonMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, false, null, "$.99", 1.0); + verifyMonetizationEvent(builder, false, null, "$.99", 1.0, "USD", .99); } @Test public void build_productIdEmpty_returnsNull() { AmazonMonetizationEventBuilder builder = new AmazonMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, false, "", "$.99", 1.0); + verifyMonetizationEvent(builder, false, "", "$.99", 1.0, "USD", .99); } @Test public void build_quantityNotSet_returnsNull() { AmazonMonetizationEventBuilder builder = new AmazonMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, false, "com.amazon.item", "$.99", null); + verifyMonetizationEvent(builder, false, "com.amazon.item", "$.99", null, "USD", .99); } @Test public void build_eventClientNotSet_returnsNull() { AmazonMonetizationEventBuilder builder = new AmazonMonetizationEventBuilder(null); - verifyMonetizationEvent(builder, false, "com.amazon.item", "$.99", 1.0); + verifyMonetizationEvent(builder, false, "com.amazon.item", "$.99", 1.0, "USD", .99); } @Test - public void build_noFormattedPriceSet_returnsNull() { + public void build_NullCurrency_returnsEvent() { AmazonMonetizationEventBuilder builder = new AmazonMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, false, "com.amazon.item", null, 1.0); + verifyMonetizationEvent(builder, true, "com.amazon.item", "$.99", 1.0, null, .99); } @Test - public void build_emptyFormattedPriceSet_returnsNull() { + public void build_emptyCurrency_returnsEvent() { AmazonMonetizationEventBuilder builder = new AmazonMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, false, "com.amazon.item", "", 1.0); + verifyMonetizationEvent(builder, true, "com.amazon.item", "$.99", 1.0, "", .99); + } + + @Test + public void build_NullItemPrice_returnsEvent() { + AmazonMonetizationEventBuilder builder = new AmazonMonetizationEventBuilder(mockEventClient); + verifyMonetizationEvent(builder, true, "com.amazon.item", "$.99", 1.0, "USD", null); } @Test public void build_allValuesSet_returnsEvent() { AmazonMonetizationEventBuilder builder = new AmazonMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, true, "com.amazon.item", "$.99", 1.0); + verifyMonetizationEvent(builder, true, "com.amazon.item", "$.99", 1.0, "USD", .99); } private static void verifyMonetizationEvent(AmazonMonetizationEventBuilder builder, boolean successfulBuild, String productId, String formattedPrice, - Double quantity) { + Double quantity, String currency, Double itemPrice) { if (productId != null) { builder.setProductId(productId); @@ -124,6 +130,12 @@ private static void verifyMonetizationEvent(AmazonMonetizationEventBuilder build if (quantity != null) { builder.setQuantity(quantity); } + if (currency != null) { + builder.setCurrency(currency); + } + if (itemPrice != null) { + builder.setItemPrice(itemPrice); + } AnalyticsEvent purchaseEvent = builder.build(); @@ -135,21 +147,20 @@ private static void verifyMonetizationEvent(AmazonMonetizationEventBuilder build Map attributes = purchaseEvent.getAllAttributes(); assertThat(attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_PRODUCT_ID_ATTR), is(productId)); - assertThat( - attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_PRICE_FORMATTED_ATTR), - is(formattedPrice)); assertThat(attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_TRANSACTION_ID_ATTR), is(nullValue())); assertThat(attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_STORE_ATTR), is(MonetizationEventBuilder.AMAZON_STORE)); - assertThat(attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_CURRENCY_ATTR), - is(nullValue())); Map metrics = purchaseEvent.getAllMetrics(); assertThat(metrics.get(MonetizationEventBuilder.PURCHASE_EVENT_QUANTITY_METRIC) .doubleValue(), is(quantity)); - assertThat(metrics.get(MonetizationEventBuilder.PURCHASE_EVENT_ITEM_PRICE_METRIC), - is(nullValue())); + + if (currency == null || itemPrice == null){ + assertThat( + attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_PRICE_FORMATTED_ATTR), + is(formattedPrice)); + } } else { assertThat(purchaseEvent, is(nullValue())); diff --git a/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/CustomMonetizationEventBuilderTest.java b/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/CustomMonetizationEventBuilderTest.java index 0a1796271e..2e935031f5 100644 --- a/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/CustomMonetizationEventBuilderTest.java +++ b/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/CustomMonetizationEventBuilderTest.java @@ -149,30 +149,30 @@ public void build_formattedPriceNotSet_itemPriceSet_currencyEmpty_returnsNull() } @Test - public void build_formattedPriceSet_itemPriceNotSet_currencyNotSet_returnsEvent() { + public void build_formattedPriceSet_itemPriceNotSet_currencyNotSet_returnsNull() { CustomMonetizationEventBuilder builder = new CustomMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, true, "Custom Store", "com.amazon.item", 1.0, "$1.99", + verifyMonetizationEvent(builder, false, "Custom Store", "com.amazon.item", 1.0, "$1.99", null, null, "123456ABCDEFG"); } @Test - public void build_formattedPriceSet_itemPriceNotSet_currencySet_returnsEvent() { + public void build_formattedPriceSet_itemPriceNotSet_currencySet_returnsNull() { CustomMonetizationEventBuilder builder = new CustomMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, true, "Custom Store", "com.amazon.item", 1.0, "$1.99", + verifyMonetizationEvent(builder, false, "Custom Store", "com.amazon.item", 1.0, "$1.99", null, "USD", "123456ABCDEFG"); } @Test - public void build_formattedPriceSet_itemPriceNotSet_currencyEmpty_returnsEvent() { + public void build_formattedPriceSet_itemPriceNotSet_currencyEmpty_returnsNull() { CustomMonetizationEventBuilder builder = new CustomMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, true, "Custom Store", "com.amazon.item", 1.0, "$1.99", + verifyMonetizationEvent(builder, false, "Custom Store", "com.amazon.item", 1.0, "$1.99", null, "", "123456ABCDEFG"); } @Test - public void build_formattedPriceSet_itemPriceSet_currencyNotSet_returnsEvent() { + public void build_formattedPriceSet_itemPriceSet_currencyNotSet_returnsNull() { CustomMonetizationEventBuilder builder = new CustomMonetizationEventBuilder(mockEventClient); - verifyMonetizationEvent(builder, true, "Custom Store", "com.amazon.item", 1.0, "$1.99", + verifyMonetizationEvent(builder, false, "Custom Store", "com.amazon.item", 1.0, "$1.99", 1.99, null, "123456ABCDEFG"); } diff --git a/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/GooglePlayMonetizationEventBuilderTest.java b/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/GooglePlayMonetizationEventBuilderTest.java index 7193c87142..ce7dfaad85 100644 --- a/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/GooglePlayMonetizationEventBuilderTest.java +++ b/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/analytics/monetization/GooglePlayMonetizationEventBuilderTest.java @@ -73,77 +73,101 @@ public void setup() { public void build_productIdNotSet_returnsNull() { GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( mockEventClient); - verifyMonetizationEvent(builder, false, null, "$.99", 1.0, "abc123"); + verifyMonetizationEvent(builder, false, null, "USD", 1.0, "abc123", 1.0); } @Test public void build_productIdEmpty_returnsNull() { GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( mockEventClient); - verifyMonetizationEvent(builder, false, "", "$.99", 1.0, "abc123"); + verifyMonetizationEvent(builder, false, "", "USD", 1.0, "abc123", 1.0); } @Test public void build_quantityNotSet_returnsNull() { GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( mockEventClient); - verifyMonetizationEvent(builder, false, "com.amazon.item", "$.99", null, "abc123"); + verifyMonetizationEvent(builder, false, "com.amazon.item", "USD", null, "abc123", 1.0); } @Test public void build_eventClientNotSet_returnsNull() { GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder(null); - verifyMonetizationEvent(builder, false, "com.amazon.item", "$.99", 1.0, "abc123"); + verifyMonetizationEvent(builder, false, "com.amazon.item", "USD", 1.0, "abc123", 1.0); } @Test public void build_noFormattedPriceSet_returnsNull() { GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( mockEventClient); - verifyMonetizationEvent(builder, false, "com.amazon.item", null, 1.0, "abc123"); + verifyMonetizationEvent(builder, false, "com.amazon.item", null, 1.0, "abc123", 1.0); } @Test public void build_emptyFormattedPriceSet_returnsNull() { GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( mockEventClient); - verifyMonetizationEvent(builder, false, "com.amazon.item", "", 1.0, "abc123"); + verifyMonetizationEvent(builder, false, "com.amazon.item", "", 1.0, "abc123", 1.0); } @Test public void build_noTransactionIdSet_returnsNull() { GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( mockEventClient); - verifyMonetizationEvent(builder, false, "com.amazon.item", null, 1.0, null); + verifyMonetizationEvent(builder, false, "com.amazon.item", null, 1.0, null, 1.0); } @Test public void build_emptyTransactionIdSet_returnsNull() { GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( mockEventClient); - verifyMonetizationEvent(builder, false, "com.amazon.item", "", 1.0, ""); + verifyMonetizationEvent(builder, false, "com.amazon.item", "", 1.0, "", 1.0); } @Test public void build_allValuesSet_returnsEvent() { GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( mockEventClient); - verifyMonetizationEvent(builder, true, "com.amazon.item", "$.99", 1.0, "abc123"); + verifyMonetizationEvent(builder, true, "com.amazon.item", "USD", 1.0, "abc123", 1.0); + } + + @Test + public void build_noCurrencySet_returnsNull() { + GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( + mockEventClient); + verifyMonetizationEvent(builder, false, "com.amazon.item", null, 1.0, null, 1.0); + } + + @Test + public void build_emptyCurrencySet_returnsNull() { + GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( + mockEventClient); + verifyMonetizationEvent(builder, false, "com.amazon.item", "", 1.0, "", 1.0); + } + + @Test + public void build_NoPrice_returnsNull() { + GooglePlayMonetizationEventBuilder builder = new GooglePlayMonetizationEventBuilder( + mockEventClient); + verifyMonetizationEvent(builder, false, "com.amazon.item", "USD", 1.0, "abc123", null); } private static void verifyMonetizationEvent(GooglePlayMonetizationEventBuilder builder, - boolean successfulBuild, String productId, String formattedPrice, - Double quantity, String transactionId) { + boolean successfulBuild, String productId, String currency, + Double quantity, String transactionId, Double itemPrice) { if (productId != null) { builder.setProductId(productId); } - if (formattedPrice != null) { - builder.setFormattedItemPrice(formattedPrice); + if (currency != null) { + builder.setCurrency(currency); } if (quantity != null) { builder.setQuantity(quantity); } + if (itemPrice != null) { + builder.setItemPrice(itemPrice); + } if (transactionId != null) { builder.setTransactionId(transactionId); } @@ -158,21 +182,18 @@ private static void verifyMonetizationEvent(GooglePlayMonetizationEventBuilder b Map attributes = purchaseEvent.getAllAttributes(); assertThat(attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_PRODUCT_ID_ATTR), is(productId)); - assertThat( - attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_PRICE_FORMATTED_ATTR), - is(formattedPrice)); assertThat(attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_TRANSACTION_ID_ATTR), is(transactionId)); assertThat(attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_STORE_ATTR), is(MonetizationEventBuilder.GOOGLE_PLAY_STORE)); assertThat(attributes.get(MonetizationEventBuilder.PURCHASE_EVENT_CURRENCY_ATTR), - is(nullValue())); + is(not(nullValue()))); Map metrics = purchaseEvent.getAllMetrics(); assertThat(metrics.get(MonetizationEventBuilder.PURCHASE_EVENT_QUANTITY_METRIC) .doubleValue(), is(quantity)); assertThat(metrics.get(MonetizationEventBuilder.PURCHASE_EVENT_ITEM_PRICE_METRIC), - is(nullValue())); + is(not(nullValue()))); } else { assertThat(purchaseEvent, is(nullValue())); diff --git a/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/targeting/EndpointProfileTest.java b/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/targeting/EndpointProfileTest.java index d09e1f496f..ab56457864 100644 --- a/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/targeting/EndpointProfileTest.java +++ b/aws-android-sdk-pinpoint/src/test/java/com/amazonaws/mobileconnectors/pinpoint/targeting/EndpointProfileTest.java @@ -94,8 +94,6 @@ public void testProfile() { assertNotNull(target.getEffectiveDate()); assertNotNull(target.getLocation()); assertEquals(target.getOptOut(), "ALL"); - target.setOptOut("TEST"); - assertEquals(target.getOptOut(), "TEST"); assertNull(target.getAddress()); assertEquals(target.getDemographic().getPlatform(), "ANDROID"); assertEquals(target.getChannelType(), "GCM"); diff --git a/aws-android-sdk-polly/pom.xml b/aws-android-sdk-polly/pom.xml index 5dc9e8f5fb..8d1b1ded23 100644 --- a/aws-android-sdk-polly/pom.xml +++ b/aws-android-sdk-polly/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,7 +20,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 junit diff --git a/aws-android-sdk-rekognition/pom.xml b/aws-android-sdk-rekognition/pom.xml index 0c0cfd25b4..6859a99d1d 100644 --- a/aws-android-sdk-rekognition/pom.xml +++ b/aws-android-sdk-rekognition/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,7 +20,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 junit diff --git a/aws-android-sdk-s3/pom.xml b/aws-android-sdk-s3/pom.xml index afc1defe4e..47e25d57ff 100644 --- a/aws-android-sdk-s3/pom.xml +++ b/aws-android-sdk-s3/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,7 +20,13 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 + + + com.amazonaws + aws-android-sdk-kms + false + 2.4.0 junit @@ -34,23 +40,32 @@ 1.10.5 test + + org.hamcrest + hamcrest-all + 1.3 + test + + + joda-time + joda-time + 2.0 + test + org.apache.commons commons-io 1.3.2 - test commons-io commons-io 2.4 - test org.bouncycastle bcprov-jdk16 1.44 - test diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/AwsChunkedEncodingInputStream.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/AwsChunkedEncodingInputStream.java index e30bcc0bdd..dc048762ff 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/AwsChunkedEncodingInputStream.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/AwsChunkedEncodingInputStream.java @@ -98,7 +98,7 @@ public AwsChunkedEncodingInputStream(InputStream in, int maxBufferSize, if (in instanceof AwsChunkedEncodingInputStream) { // This could happen when the request is retried, and we need to // re-calculate the signatures. - AwsChunkedEncodingInputStream originalChunkedStream = (AwsChunkedEncodingInputStream) in; + final AwsChunkedEncodingInputStream originalChunkedStream = (AwsChunkedEncodingInputStream) in; maxBufferSize = Math.max(originalChunkedStream.maxBufferSize, maxBufferSize); is = originalChunkedStream.is; decodedStreamBuffer = originalChunkedStream.decodedStreamBuffer; @@ -108,8 +108,9 @@ public AwsChunkedEncodingInputStream(InputStream in, int maxBufferSize, decodedStreamBuffer = null; } - if (maxBufferSize < DEFAULT_CHUNK_SIZE) + if (maxBufferSize < DEFAULT_CHUNK_SIZE) { throw new IllegalArgumentException("Max buffer size should not be less than chunk size"); + } this.maxBufferSize = maxBufferSize; this.kSigning = kSigning; this.dateTime = datetime; @@ -121,12 +122,13 @@ public AwsChunkedEncodingInputStream(InputStream in, int maxBufferSize, @Override public int read() throws IOException { - byte[] tmp = new byte[1]; - int count = read(tmp, 0, 1); + final byte[] tmp = new byte[1]; + final int count = read(tmp, 0, 1); if (count != -1) { - if (log.isDebugEnabled()) + if (log.isDebugEnabled()) { log.debug("One byte read from the stream."); - int unsignedByte = tmp[0] & 0xFF; + } + final int unsignedByte = tmp[0] & 0xFF; return unsignedByte; } else { return count; @@ -146,18 +148,19 @@ public int read(byte[] b, int off, int len) throws IOException { if (null == currentChunkIterator || !currentChunkIterator.hasNext()) { - if (isTerminating) + if (isTerminating) { return -1; - else { + } else { isTerminating = setUpNextChunk(); } } - int count = currentChunkIterator.read(b, off, len); + final int count = currentChunkIterator.read(b, off, len); if (count > 0) { isAtStart = false; - if (log.isDebugEnabled()) + if (log.isDebugEnabled()) { log.debug(count + " byte read from the stream."); + } } return count; } @@ -168,8 +171,8 @@ public long skip(long n) throws IOException { return 0; } long remaining = n; - int toskip = (int) Math.min(DEFAULT_BUFFER_SIZE, n); - byte[] temp = new byte[toskip]; + final int toskip = (int) Math.min(DEFAULT_BUFFER_SIZE, n); + final byte[] temp = new byte[toskip]; while (remaining > 0) { int count; if ((count = read(temp, 0, toskip)) < 0) { @@ -196,9 +199,10 @@ public boolean markSupported() { @Override public synchronized void mark(int readlimit) { abortIfNeeded(); - if (!isAtStart) + if (!isAtStart) { throw new UnsupportedOperationException( "Chunk-encoded stream only supports mark() at the start of the stream."); + } if (is.markSupported()) { if (log.isDebugEnabled()) { log.debug("AwsChunkedEncodingInputStream marked at the start of the stream " @@ -228,15 +232,17 @@ public synchronized void reset() throws IOException { // Reset the wrapped stream if it is mark-supported, // otherwise use our buffered data. if (is.markSupported()) { - if (log.isDebugEnabled()) + if (log.isDebugEnabled()) { log.debug("AwsChunkedEncodingInputStream reset " + "(will reset the wrapped stream because it is mark-supported)."); + } is.reset(); } else { - if (log.isDebugEnabled()) + if (log.isDebugEnabled()) { log.debug("AwsChunkedEncodingInputStream reset " + "(will use the buffer of the decoded stream)."); + } if (null == decodedStreamBuffer) { throw new IOException("Cannot reset the stream because the mark is not set."); } @@ -253,8 +259,8 @@ public static long calculateStreamContentLength(long originalLength) { throw new IllegalArgumentException("Nonnegative content length expected."); } - long maxSizeChunks = originalLength / DEFAULT_CHUNK_SIZE; - long remainingBytes = originalLength % DEFAULT_CHUNK_SIZE; + final long maxSizeChunks = originalLength / DEFAULT_CHUNK_SIZE; + final long remainingBytes = originalLength % DEFAULT_CHUNK_SIZE; return maxSizeChunks * calculateSignedChunkLength(DEFAULT_CHUNK_SIZE) + (remainingBytes > 0 ? calculateSignedChunkLength(remainingBytes) : 0) + calculateSignedChunkLength(0); @@ -286,65 +292,66 @@ private boolean setUpNextChunk() throws IOException { } /** Read from the wrapped stream */ else { - int bytesToRead = DEFAULT_CHUNK_SIZE - chunkSizeInBytes; - int count = is.read(chunkData, chunkSizeInBytes, bytesToRead); + final int bytesToRead = DEFAULT_CHUNK_SIZE - chunkSizeInBytes; + final int count = is.read(chunkData, chunkSizeInBytes, bytesToRead); if (count != -1) { - if (null != decodedStreamBuffer) + if (null != decodedStreamBuffer) { decodedStreamBuffer.buffer(chunkData, chunkSizeInBytes, count); + } chunkSizeInBytes += count; - } - else + } else { break; + } } } if (chunkSizeInBytes == 0) { - byte[] signedFinalChunk = createSignedChunk(FINAL_CHUNK); + final byte[] signedFinalChunk = createSignedChunk(FINAL_CHUNK); currentChunkIterator = new ChunkContentIterator(signedFinalChunk); return true; } else { if (chunkSizeInBytes < chunkData.length) { - byte[] temp = new byte[chunkSizeInBytes]; + final byte[] temp = new byte[chunkSizeInBytes]; System.arraycopy(chunkData, 0, temp, 0, chunkSizeInBytes); chunkData = temp; } - byte[] signedChunkContent = createSignedChunk(chunkData); + final byte[] signedChunkContent = createSignedChunk(chunkData); currentChunkIterator = new ChunkContentIterator(signedChunkContent); return false; } } private byte[] createSignedChunk(byte[] chunkData) { - StringBuilder chunkHeader = new StringBuilder(); + final StringBuilder chunkHeader = new StringBuilder(); // chunk-size chunkHeader.append(Integer.toHexString(chunkData.length)); // nonsig-extension - String nonsigExtension = ""; + final String nonsigExtension = ""; // sig-extension - String chunkStringToSign = + final String chunkStringToSign = CHUNK_STRING_TO_SIGN_PREFIX + "\n" + dateTime + "\n" + keyPath + "\n" + priorChunkSignature + "\n" + BinaryUtils.toHex(aws4Signer.hash(nonsigExtension)) + "\n" + BinaryUtils.toHex(aws4Signer.hash(chunkData)); - String chunkSignature = BinaryUtils.toHex(aws4Signer.sign(chunkStringToSign, kSigning, + final String chunkSignature = BinaryUtils.toHex(aws4Signer.sign(chunkStringToSign, kSigning, SigningAlgorithm.HmacSHA256)); priorChunkSignature = chunkSignature; chunkHeader.append(nonsigExtension + CHUNK_SIGNATURE_HEADER + chunkSignature); chunkHeader.append(CLRF); try { - byte[] header = chunkHeader.toString().getBytes(UTF8); - byte[] trailer = CLRF.getBytes(UTF8); - byte[] signedChunk = new byte[header.length + chunkData.length + trailer.length]; + final byte[] header = chunkHeader.toString().getBytes(UTF8); + final byte[] trailer = CLRF.getBytes(UTF8); + final byte[] signedChunk = new byte[header.length + chunkData.length + trailer.length]; System.arraycopy(header, 0, signedChunk, 0, header.length); System.arraycopy(chunkData, 0, signedChunk, header.length, chunkData.length); System.arraycopy(trailer, 0, signedChunk, header.length + chunkData.length, trailer.length); return signedChunk; - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException("Unable to sign the chunked data. " + e.getMessage(), e); } } @@ -353,4 +360,4 @@ private byte[] createSignedChunk(byte[] chunkData) { protected InputStream getWrappedInputStream() { return is; } -} +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/actions/S3Actions.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/actions/S3Actions.java index af8110fe2f..0b7c417d45 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/actions/S3Actions.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/actions/S3Actions.java @@ -122,6 +122,27 @@ public enum S3Actions implements Action { */ DeleteObjectVersion("s3:DeleteObjectVersion"), + /** + * Action for listing parts that have been uploaded for a multipart upload. + * + * @see AmazonS3#listParts(com.amazonaws.services.s3.model.ListPartsRequest) + */ + ListMultipartUploadParts("s3:ListMultipartUploadParts"), + + /** + * Action for aborting a multipart upload. + * + * @see AmazonS3#abortMultipartUpload(com.amazonaws.services.s3.model.AbortMultipartUploadRequest) + */ + AbortMultipartUpload("s3:AbortMultipartUpload"), + + /** + * Action for restoring a temporary copy of an archived object. + * + * @see AmazonS3#restoreObject(com.amazonaws.services.s3.model.RestoreObjectRequest) + */ + RestoreObject("s3:RestoreObject"), + /** * Action for creating a new Amazon S3 bucket. *

@@ -167,6 +188,16 @@ public enum S3Actions implements Action { */ ListBuckets("s3:ListAllMyBuckets"), + /** + * Actions for listing the in-progress multipart uploads for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#listMultipartUploads(com.amazonaws.services.s3.model.ListMultipartUploadsRequest) + */ + ListBucketMultipartUploads("s3:ListBucketMultipartUploads"), + /** * Action for retrieving the ACL of an Amazon S3 bucket. *

@@ -188,6 +219,26 @@ public enum S3Actions implements Action { */ SetBucketAcl("s3:PutBucketAcl"), + /** + * Action for getting the Cross origin configuration configuration + * information set for the bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#getBucketCrossOriginConfiguration(String) + */ + GetBucketCrossOriginConfiguration("s3:GetBucketCORS"), + + /** + * Action for setting the Cross origin configuration configuration + * information set for the bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#setBucketCrossOriginConfiguration(com.amazonaws.services.s3.model.SetBucketCrossOriginConfigurationRequest) + */ + SetBucketCrossOriginConfiguration("s3:PutBucketCORS"), + /** * Action for retrieving the versioning configuration of an Amazon S3 * bucket. @@ -212,14 +263,14 @@ public enum S3Actions implements Action { *

* Valid for use with {@link S3BucketResource} resources. */ - GetBucketRequesterPays("s3:GetBucketRequesterPays"), + GetBucketRequesterPays("s3:GetBucketRequestPayment"), /** * Action for setting the requester pays status of an Amazon S3 bucket. *

* Valid for use with {@link S3BucketResource} resources. */ - SetBucketRequesterPays("s3:PutBucketRequesterPays"), + SetBucketRequesterPays("s3:PutBucketRequestPayment"), /** * Action for retrieving the bucket location of an Amazon S3 bucket. @@ -248,6 +299,15 @@ public enum S3Actions implements Action { */ SetBucketPolicy("s3:PutBucketPolicy"), + /** + * Action for deleting the access control policy for an Amazon S3 Bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#deleteBucketPolicy(com.amazonaws.services.s3.model.DeleteBucketPolicyRequest) + */ + DeleteBucketPolicy("s3:DeleteBucketPolicy"), + /** * Action for retrieving the bucket notification configuration for an Amazon * S3 bucket. @@ -267,7 +327,97 @@ public enum S3Actions implements Action { * @see AmazonS3#setBucketNotificationConfiguration(String, * com.amazonaws.services.s3.model.BucketNotificationConfiguration) */ - SetBucketNotificationConfiguration("s3:PutBucketNotification"); + SetBucketNotificationConfiguration("s3:PutBucketNotification"), + + /** + * Action for getting the bucket logging configuration for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#getBucketLoggingConfiguration(String) + */ + GetBucketLogging("s3:GetBucketLogging"), + + /** + * Action for setting the bucket logging configuration for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#setBucketLoggingConfiguration(com.amazonaws.services.s3.model.SetBucketLoggingConfigurationRequest) + */ + SetBucketLogging("s3:PutBucketLogging"), + + /** + * Action for getting the bucket tagging configuration for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#getBucketTaggingConfiguration(String) + */ + GetBucketTagging("s3:GetBucketTagging"), + + /** + * Action for setting the bucket tagging configuration for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#setBucketTaggingConfiguration(com.amazonaws.services.s3.model.SetBucketTaggingConfigurationRequest) + */ + SetBucketTagging("s3:PutBucketTagging"), + + /** + * Action for getting the bucket website configuration for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#getBucketWebsiteConfiguration(com.amazonaws.services.s3.model.GetBucketWebsiteConfigurationRequest) + */ + GetBucketWebsiteConfiguration("s3:GetBucketWebsite"), + + /** + * Action for setting the bucket website configuration for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#setBucketWebsiteConfiguration(com.amazonaws.services.s3.model.SetBucketWebsiteConfigurationRequest) + */ + SetBucketWebsiteConfiguration("s3:PutBucketWebsite"), + + /** + * Action for deleting the bucket website configuration for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#deleteBucketWebsiteConfiguration(com.amazonaws.services.s3.model.DeleteBucketWebsiteConfigurationRequest) + */ + DeleteBucketWebsiteConfiguration("s3:DeleteBucketWebsite"), + + /** + * Action for getting the bucket lifecycle configuration for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#getBucketLifecycleConfiguration(String) + */ + GetBucketLifecycleConfiguration("s3:GetLifecycleConfiguration"), + + /** + * Action for setting the bucket lifecycle configuration for an Amazon S3 + * bucket. + *

+ * Valid for use with {@link S3BucketResource} resources. + * + * @see AmazonS3#setBucketLifecycleConfiguration(com.amazonaws.services.s3.model.SetBucketLifecycleConfigurationRequest) + */ + SetBucketLifecycleConfiguration("s3:PutLifecycleConfiguration"); private final String action; @@ -277,6 +427,7 @@ private S3Actions(String action) { /* * (non-Javadoc) + * * @see com.amazonaws.auth.policy.Action#getId() */ @Override diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/resources/S3BucketResource.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/resources/S3BucketResource.java index 3aa8d79e1a..c0de0c8e94 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/resources/S3BucketResource.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/resources/S3BucketResource.java @@ -55,8 +55,9 @@ public class S3BucketResource extends Resource { * Constructs a new bucket resource that represents the the specified bucket * but not any of the contained objects. * - * @param bucketName The name of the bucket represented by this AWS access - * control policy resource. + * @param bucketName + * The name of the bucket represented by this AWS access control + * policy resource. */ public S3BucketResource(String bucketName) { super("arn:aws:s3:::" + bucketName); diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/resources/S3ObjectResource.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/resources/S3ObjectResource.java index 6f2b477fb2..86b5f7ef79 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/resources/S3ObjectResource.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/auth/policy/resources/S3ObjectResource.java @@ -54,17 +54,43 @@ public class S3ObjectResource extends Resource { /** * Constructs a new object resource that represents the specified objects. + * This constructor defaults to aws partition. Use {@link #S3ObjectResource(String, String, String)} + * to specify other partitions if needed. * The keyPattern argument may contain the '*' wildcard to match multiple * objects. For example, an object resource created for bucket 'mybucket' * and key pattern 'foo*' will match any object stored in 'mybucket' with a * key that starts with 'foo'. * - * @param bucketName The name of the bucket containing the object or objects + * @param bucketName + * The name of the bucket containing the object or objects * represented by this resource. - * @param keyPattern The key or key pattern, which can optionally contain - * the '*' wildcard to include multiple objects in the resource. + * @param keyPattern + * The key or key pattern, which can optionally contain the '*' + * wildcard to include multiple objects in the resource. + * + * @see #S3ObjectResource(String, String, String) */ public S3ObjectResource(String bucketName, String keyPattern) { - super("arn:aws:s3:::" + bucketName + "/" + keyPattern); + this("aws", bucketName, keyPattern); + } + + /** + * Constructs a new object resource that represents the specified objects. + * The keyPattern argument may contain the '*' wildcard to match multiple + * objects. For example, an object resource created for bucket 'mybucket' + * and key pattern 'foo*' will match any object stored in 'mybucket' with a + * key that starts with 'foo'. + * + * @param partitionName + * The name of the partition in which the specified bucket is located. + * @param bucketName + * The name of the bucket containing the object or objects + * represented by this resource. + * @param keyPattern + * The key or key pattern, which can optionally contain the '*' + * wildcard to include multiple objects in the resource. + */ + public S3ObjectResource(String partitionName, String bucketName, String keyPattern) { + super(String.format("arn:%s:s3:::%s/%s", partitionName, bucketName, keyPattern)); } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transfermanager/TransferManager.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transfermanager/TransferManager.java index fc8cc6a8e2..30d2566de3 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transfermanager/TransferManager.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transfermanager/TransferManager.java @@ -111,7 +111,7 @@ * TransferManager tx = new TransferManager( * credentialProviderChain.getCredentials()); * Upload myUpload = tx.upload(myBucket, myFile.getName(), myFile); - * + * * // You can poll your transfer's status to check its progress * if (myUpload.isDone() == false) { * System.out.println("Transfer: " + myUpload.getDescription()); @@ -119,16 +119,16 @@ * System.out.println(" - Progress: " * + myUpload.getProgress().getBytesTransferred()); * } - * + * * // Transfers also allow you to set a <code>ProgressListener</code> to receive * // asynchronous notifications about your transfer's progress. * myUpload.addProgressListener(myProgressListener); - * + * * // Or you can block the current thread and wait for your transfer to * // to complete. If the transfer fails, this method will throw an * // AmazonClientException or AmazonServiceException detailing the reason. * myUpload.waitForCompletion(); - * + * * // After the upload is complete, call shutdownNow to release the resources. * tx.shutdownNow(); *

@@ -465,14 +465,15 @@ private Upload doUpload(final PutObjectRequest putObjectRequest, appendSingleObjectUserAgent(putObjectRequest); - String multipartUploadId = persistableUpload != null ? persistableUpload + final String multipartUploadId = persistableUpload != null ? persistableUpload .getMultipartUploadId() : null; - if (putObjectRequest.getMetadata() == null) + if (putObjectRequest.getMetadata() == null) { putObjectRequest.setMetadata(new ObjectMetadata()); - ObjectMetadata metadata = putObjectRequest.getMetadata(); + } + final ObjectMetadata metadata = putObjectRequest.getMetadata(); - File file = TransferManagerUtils.getRequestFile(putObjectRequest); + final File file = TransferManagerUtils.getRequestFile(putObjectRequest); if (file != null) { // Always set the content length, even if it's already set @@ -489,24 +490,24 @@ private Upload doUpload(final PutObjectRequest putObjectRequest, } } - String description = "Uploading to " + putObjectRequest.getBucketName() + final String description = "Uploading to " + putObjectRequest.getBucketName() + "/" + putObjectRequest.getKey(); - TransferProgress transferProgress = new TransferProgress(); + final TransferProgress transferProgress = new TransferProgress(); transferProgress.setTotalBytesToTransfer(TransferManagerUtils .getContentLength(putObjectRequest)); - S3ProgressListenerChain listenerChain = new S3ProgressListenerChain( + final S3ProgressListenerChain listenerChain = new S3ProgressListenerChain( new TransferProgressUpdatingListener(transferProgress), putObjectRequest.getGeneralProgressListener(), progressListener); putObjectRequest.setGeneralProgressListener(listenerChain); - UploadImpl upload = new UploadImpl(description, transferProgress, + final UploadImpl upload = new UploadImpl(description, transferProgress, listenerChain, stateListener); - UploadCallable uploadCallable = new UploadCallable(this, threadPool, + final UploadCallable uploadCallable = new UploadCallable(this, threadPool, upload, putObjectRequest, listenerChain, multipartUploadId, transferProgress); - UploadMonitor watcher = new UploadMonitor(this, upload, threadPool, + final UploadMonitor watcher = new UploadMonitor(this, upload, threadPool, uploadCallable, putObjectRequest, listenerChain); watcher.setTimedThreadPool(timedThreadPool); upload.setMonitor(watcher); @@ -600,13 +601,13 @@ private Download doDownload(final GetObjectRequest getObjectRequest, appendSingleObjectUserAgent(getObjectRequest); - String description = "Downloading from " + getObjectRequest.getBucketName() + "/" + final String description = "Downloading from " + getObjectRequest.getBucketName() + "/" + getObjectRequest.getKey(); - TransferProgress transferProgress = new TransferProgress(); + final TransferProgress transferProgress = new TransferProgress(); // S3 progress listener to capture the persistable transfer when // available - S3ProgressListenerChain listenerChain = new S3ProgressListenerChain( + final S3ProgressListenerChain listenerChain = new S3ProgressListenerChain( new TransferProgressUpdatingListener(transferProgress), // The // listener // for @@ -620,7 +621,7 @@ private Download doDownload(final GetObjectRequest getObjectRequest, // The listener chain used by the low-level GetObject request. // This listener chain ignores any COMPLETE event, so that we could // delay firing the signal until the high-level download fully finishes. - ProgressListenerChain listeners = new ProgressListenerChain( + final ProgressListenerChain listeners = new ProgressListenerChain( new ProgressEventFilter() { @Override public ProgressEvent filter(ProgressEvent progressEvent) { @@ -635,7 +636,7 @@ public ProgressEvent filter(ProgressEvent progressEvent) { }, listenerChain); getObjectRequest.setGeneralProgressListener(listeners); - GetObjectMetadataRequest getObjectMetadataRequest = new GetObjectMetadataRequest( + final GetObjectMetadataRequest getObjectMetadataRequest = new GetObjectMetadataRequest( getObjectRequest.getBucketName(), getObjectRequest.getKey()); if (getObjectRequest.getSSECustomerKey() != null) { @@ -662,7 +663,7 @@ public ProgressEvent filter(ProgressEvent progressEvent) { if (resumeExistingDownload) { if (file.exists()) { - long numberOfBytesRead = file.length(); + final long numberOfBytesRead = file.length(); startingByte = startingByte + numberOfBytesRead; getObjectRequest.setRange(startingByte, lastByte); transferProgress.updateProgress(Math.min(numberOfBytesRead, @@ -677,7 +678,7 @@ public ProgressEvent filter(ProgressEvent progressEvent) { } final CountDownLatch latch = new CountDownLatch(1); - Future future = submitDownloadTask(getObjectRequest, file, + final Future future = submitDownloadTask(getObjectRequest, file, resumeExistingDownload, latch, download); download.setMonitor(new DownloadMonitor(download, future)); latch.countDown(); @@ -689,18 +690,18 @@ private Future submitDownloadTask( final boolean resumeExistingDownload, final CountDownLatch latch, final DownloadImpl download) { - Future future = threadPool.submit(new Callable() { + final Future future = threadPool.submit(new Callable() { @Override public Object call() throws Exception { try { latch.await(); download.setState(TransferState.InProgress); - S3Object s3Object = ServiceUtils.retryableDownloadS3ObjectToFile(file, + final S3Object s3Object = ServiceUtils.retryableDownloadS3ObjectToFile(file, new ServiceUtils.RetryableS3DownloadTask() { @Override public S3Object getS3ObjectStream() { - S3Object s3Object = s3.getObject(getObjectRequest); + final S3Object s3Object = s3.getObject(getObjectRequest); download.setS3Object(s3Object); return s3Object; } @@ -715,8 +716,9 @@ public boolean needIntegrityCheck() { // won't match up. boolean performIntegrityCheck = true; if (ServiceUtils.skipMd5CheckPerRequest(getObjectRequest) - || s3 instanceof AmazonS3EncryptionClient) + || s3 instanceof AmazonS3EncryptionClient) { performIntegrityCheck = false; + } return performIntegrityCheck; } }, resumeExistingDownload); @@ -729,15 +731,16 @@ public boolean needIntegrityCheck() { download.setState(TransferState.Completed); return true; - } catch (Throwable t) { + } catch (final Throwable t) { // Downloads aren't allowed to move from canceled to failed if (download.getState() != TransferState.Canceled) { download.setState(TransferState.Failed); } - if (t instanceof Exception) + if (t instanceof Exception) { throw (Exception) t; - else + } else { throw (Error) t; + } } } }); @@ -759,23 +762,24 @@ public boolean needIntegrityCheck() { public MultipleFileDownload downloadDirectory(String bucketName, String keyPrefix, File destinationDirectory) { - if (keyPrefix == null) + if (keyPrefix == null) { keyPrefix = ""; + } - List objectSummaries = new LinkedList(); - Stack commonPrefixes = new Stack(); + final List objectSummaries = new LinkedList(); + final Stack commonPrefixes = new Stack(); commonPrefixes.add(keyPrefix); long totalSize = 0; // Recurse all virtual subdirectories to get a list of object summaries. // This is a depth-first search. do { - String prefix = commonPrefixes.pop(); + final String prefix = commonPrefixes.pop(); ObjectListing listObjectsResponse = null; do { if (listObjectsResponse == null) { - ListObjectsRequest listObjectsRequest = new ListObjectsRequest() + final ListObjectsRequest listObjectsRequest = new ListObjectsRequest() .withBucketName(bucketName) .withDelimiter(DEFAULT_DELIMITER).withPrefix(prefix); listObjectsResponse = s3.listObjects(listObjectsRequest); @@ -783,7 +787,7 @@ public MultipleFileDownload downloadDirectory(String bucketName, String keyPrefi listObjectsResponse = s3.listNextBatchOfObjects(listObjectsResponse); } - for (S3ObjectSummary s : listObjectsResponse.getObjectSummaries()) { + for (final S3ObjectSummary s : listObjectsResponse.getObjectSummaries()) { // Skip any files that are also virtual directories, since // we can't save both a directory and a file of the same // name. @@ -803,21 +807,21 @@ public MultipleFileDownload downloadDirectory(String bucketName, String keyPrefi } while (!commonPrefixes.isEmpty()); /* This is the hook for adding additional progress listeners */ - ProgressListenerChain additionalListeners = new ProgressListenerChain(); + final ProgressListenerChain additionalListeners = new ProgressListenerChain(); - TransferProgress transferProgress = new TransferProgress(); + final TransferProgress transferProgress = new TransferProgress(); transferProgress.setTotalBytesToTransfer(totalSize); /* * Bind additional progress listeners to this * MultipleFileTransferProgressUpdatingListener to receive * ByteTransferred events from each single-file download implementation. */ - ProgressListener listener = new MultipleFileTransferProgressUpdatingListener( + final ProgressListener listener = new MultipleFileTransferProgressUpdatingListener( transferProgress, additionalListeners); - List downloads = new ArrayList(); + final List downloads = new ArrayList(); - String description = "Downloading from " + bucketName + "/" + keyPrefix; + final String description = "Downloading from " + bucketName + "/" + keyPrefix; final MultipleFileDownloadImpl multipleFileDownload = new MultipleFileDownloadImpl( description, transferProgress, additionalListeners, keyPrefix, bucketName, downloads); @@ -825,13 +829,13 @@ public MultipleFileDownload downloadDirectory(String bucketName, String keyPrefi downloads)); final CountDownLatch latch = new CountDownLatch(1); - MultipleFileTransferStateChangeListener transferListener = new MultipleFileTransferStateChangeListener( + final MultipleFileTransferStateChangeListener transferListener = new MultipleFileTransferStateChangeListener( latch, multipleFileDownload); - for (S3ObjectSummary summary : objectSummaries) { + for (final S3ObjectSummary summary : objectSummaries) { // TODO: non-standard delimiters - File f = new File(destinationDirectory, summary.getKey()); - File parentFile = f.getParentFile(); + final File f = new File(destinationDirectory, summary.getKey()); + final File parentFile = f.getParentFile(); if (!parentFile.exists() && !parentFile.mkdirs()) { throw new RuntimeException("Couldn't create parent directories for " + f.getAbsolutePath()); @@ -908,7 +912,7 @@ public MultipleFileUpload uploadDirectory(String bucketName, String virtualDirec throw new IllegalArgumentException("Must provide a directory to upload"); } - List files = new LinkedList(); + final List files = new LinkedList(); listFiles(directory, files, includeSubdirectories); return uploadFileList(bucketName, virtualDirectoryKeyPrefix, directory, files, @@ -974,24 +978,24 @@ public MultipleFileUpload uploadFileList(String bucketName, String virtualDirect } /* This is the hook for adding additional progress listeners */ - ProgressListenerChain additionalListeners = new ProgressListenerChain(); + final ProgressListenerChain additionalListeners = new ProgressListenerChain(); - TransferProgress progress = new TransferProgress(); + final TransferProgress progress = new TransferProgress(); /* * Bind additional progress listeners to this * MultipleFileTransferProgressUpdatingListener to receive * ByteTransferred events from each single-file upload implementation. */ - ProgressListener listener = new MultipleFileTransferProgressUpdatingListener( + final ProgressListener listener = new MultipleFileTransferProgressUpdatingListener( progress, additionalListeners); - List uploads = new LinkedList(); - MultipleFileUploadImpl multipleFileUpload = new MultipleFileUploadImpl("Uploading etc", + final List uploads = new LinkedList(); + final MultipleFileUploadImpl multipleFileUpload = new MultipleFileUploadImpl("Uploading etc", progress, additionalListeners, virtualDirectoryKeyPrefix, bucketName, uploads); multipleFileUpload.setMonitor(new MultipleFileTransferMonitor(multipleFileUpload, uploads)); final CountDownLatch latch = new CountDownLatch(1); - MultipleFileTransferStateChangeListener transferListener = new MultipleFileTransferStateChangeListener( + final MultipleFileTransferStateChangeListener transferListener = new MultipleFileTransferStateChangeListener( latch, multipleFileUpload); if (files == null || files.isEmpty()) { @@ -1006,19 +1010,20 @@ public MultipleFileUpload uploadFileList(String bucketName, String virtualDirect * the starting position by one. */ int startingPosition = directory.getAbsolutePath().length(); - if (!(directory.getAbsolutePath().endsWith(File.separator))) + if (!(directory.getAbsolutePath().endsWith(File.separator))) { startingPosition++; + } long totalSize = 0; - for (File f : files) { + for (final File f : files) { // Check, if file, since only files can be uploaded. if (f.isFile()) { totalSize += f.length(); - String key = f.getAbsolutePath().substring(startingPosition) + final String key = f.getAbsolutePath().substring(startingPosition) .replaceAll("\\\\", "/"); - ObjectMetadata metadata = new ObjectMetadata(); + final ObjectMetadata metadata = new ObjectMetadata(); // Invoke the callback if it's present. // The callback allows the user to customize the metadata @@ -1031,7 +1036,7 @@ public MultipleFileUpload uploadFileList(String bucketName, String virtualDirect // MultipleFileTransferProgressUpdatingListener and // MultipleFileTransferStateChangeListener uploads.add((UploadImpl) doUpload( - new PutObjectRequest(bucketName, + (PutObjectRequest) new PutObjectRequest(bucketName, virtualDirectoryKeyPrefix + key, f) .withMetadata(metadata) .withGeneralProgressListener( @@ -1054,9 +1059,9 @@ public MultipleFileUpload uploadFileList(String bucketName, String virtualDirect * passed in, optionally adding subdirectories recursively. */ private void listFiles(File dir, List results, boolean includeSubDirectories) { - File[] found = dir.listFiles(); + final File[] found = dir.listFiles(); if (found != null) { - for (File f : found) { + for (final File f : found) { if (f.isDirectory()) { if (includeSubDirectories) { listFiles(f, results, includeSubDirectories); @@ -1090,14 +1095,14 @@ public void abortMultipartUploads(String bucketName, Date date) MultipartUploadListing uploadListing = s3.listMultipartUploads(appendSingleObjectUserAgent( new ListMultipartUploadsRequest(bucketName))); do { - for (MultipartUpload upload : uploadListing.getMultipartUploads()) { + for (final MultipartUpload upload : uploadListing.getMultipartUploads()) { if (upload.getInitiated().compareTo(date) < 0) { s3.abortMultipartUpload(appendSingleObjectUserAgent(new AbortMultipartUploadRequest( bucketName, upload.getKey(), upload.getUploadId()))); } } - ListMultipartUploadsRequest request = new ListMultipartUploadsRequest(bucketName) + final ListMultipartUploadsRequest request = new ListMultipartUploadsRequest(bucketName) .withUploadIdMarker(uploadListing.getNextUploadIdMarker()) .withKeyMarker(uploadListing.getNextKeyMarker()); uploadListing = s3.listMultipartUploads(appendSingleObjectUserAgent(request)); @@ -1182,8 +1187,8 @@ public static X appendMultipartUserAgent(X r @Override public Thread newThread(Runnable r) { - int threadNumber = threadCount.incrementAndGet(); - Thread thread = new Thread(r); + final int threadNumber = threadCount.incrementAndGet(); + final Thread thread = new Thread(r); thread.setDaemon(true); thread.setName("S3TransferManagerTimedThread-" + threadNumber); return thread; @@ -1313,30 +1318,30 @@ public Copy copy(final CopyObjectRequest copyObjectRequest, copyObjectRequest.getDestinationKey(), "The destination object key must be specified when a copy request is initiated."); - String description = "Copying object from " + final String description = "Copying object from " + copyObjectRequest.getSourceBucketName() + "/" + copyObjectRequest.getSourceKey() + " to " + copyObjectRequest.getDestinationBucketName() + "/" + copyObjectRequest.getDestinationKey(); - GetObjectMetadataRequest getObjectMetadataRequest = + final GetObjectMetadataRequest getObjectMetadataRequest = new GetObjectMetadataRequest( copyObjectRequest.getSourceBucketName(), copyObjectRequest.getSourceKey()) .withSSECustomerKey(copyObjectRequest.getSourceSSECustomerKey()); - ObjectMetadata metadata = s3.getObjectMetadata(getObjectMetadataRequest); + final ObjectMetadata metadata = s3.getObjectMetadata(getObjectMetadataRequest); - TransferProgress transferProgress = new TransferProgress(); + final TransferProgress transferProgress = new TransferProgress(); transferProgress.setTotalBytesToTransfer(metadata.getContentLength()); - ProgressListenerChain listenerChain = new ProgressListenerChain( + final ProgressListenerChain listenerChain = new ProgressListenerChain( new TransferProgressUpdatingListener(transferProgress)); - CopyImpl copy = new CopyImpl(description, transferProgress, + final CopyImpl copy = new CopyImpl(description, transferProgress, listenerChain, stateChangeListener); - CopyCallable copyCallable = new CopyCallable(this, threadPool, copy, + final CopyCallable copyCallable = new CopyCallable(this, threadPool, copy, copyObjectRequest, metadata, listenerChain); - CopyMonitor watcher = new CopyMonitor(this, copy, threadPool, + final CopyMonitor watcher = new CopyMonitor(this, copy, threadPool, copyCallable, copyObjectRequest, listenerChain); watcher.setTimedThreadPool(timedThreadPool); copy.setMonitor(watcher); @@ -1386,12 +1391,12 @@ public Upload resumeUpload(PersistableUpload persistableUpload) { public Download resumeDownload(PersistableDownload persistableDownload) { assertParameterNotNull(persistableDownload, "PausedDownload is mandatory to resume a download."); - GetObjectRequest request = new GetObjectRequest( + final GetObjectRequest request = new GetObjectRequest( persistableDownload.getBucketName(), persistableDownload.getKey(), persistableDownload.getVersionId()); if (persistableDownload.getRange() != null && persistableDownload.getRange().length == 2) { - long[] range = persistableDownload.getRange(); + final long[] range = persistableDownload.getRange(); request.setRange(range[0], range[1]); } request.setRequesterPays(persistableDownload.isRequesterPays()); @@ -1413,8 +1418,9 @@ public Download resumeDownload(PersistableDownload persistableDownload) { * IllegalArgumentException if the specified parameter is null. */ private void assertParameterNotNull(Object parameterValue, String errorMessage) { - if (parameterValue == null) + if (parameterValue == null) { throw new IllegalArgumentException(errorMessage); + } } /** diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferDBBase.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferDBBase.java index 870f0d2f16..47af38b4b0 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferDBBase.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferDBBase.java @@ -40,16 +40,18 @@ class TransferDBBase { private final Uri contentUri; private final UriMatcher uriMatcher; private final TransferDatabaseHelper databaseHelper; + private SQLiteDatabase database; /** - * Constructs TransferDBBase with the given Context. + * Constructs TransferdatabaseBase with the given Context. * * @param context A Context instance. */ public TransferDBBase(Context context) { this.context = context; - String mAuthority = context.getApplicationContext().getPackageName(); + final String mAuthority = context.getApplicationContext().getPackageName(); databaseHelper = new TransferDatabaseHelper(this.context); + database = databaseHelper.getWritableDatabase(); contentUri = Uri.parse("content://" + mAuthority + "/" + BASE_PATH); uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); @@ -74,6 +76,11 @@ public TransferDBBase(Context context) { uriMatcher.addURI(mAuthority, BASE_PATH + "/state/*", TRANSFER_STATE); } + + /* package private */ TransferDatabaseHelper getDatabaseHelper() { + return databaseHelper; + } + /** * Closes the database helper. */ @@ -98,12 +105,13 @@ public Uri getContentUri() { * @return The Uri of the inserted record. */ public Uri insert(Uri uri, ContentValues values) { - int uriType = uriMatcher.match(uri); - SQLiteDatabase db = databaseHelper.getWritableDatabase(); + final int uriType = uriMatcher.match(uri); long id = 0; + ensureDatabaseOpen(); + switch (uriType) { case TRANSFERS: - id = db.insert(TransferTable.TABLE_TRANSFER, null, values); + id = database.insertOrThrow(TransferTable.TABLE_TRANSFER, null, values); break; default: throw new IllegalArgumentException("Unknown URI: " + uri); @@ -122,14 +130,15 @@ public Uri insert(Uri uri, ContentValues values) { * @param type Type of transfers to query. * @return A Cursor pointing to records. */ - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { - SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); + final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); // TODO: currently all methods calling this pass null to projection. // In the future we want to update projection to be more specific for // performance and must handle that here. queryBuilder.setTables(TransferTable.TABLE_TRANSFER); - int uriType = uriMatcher.match(uri); + final int uriType = uriMatcher.match(uri); switch (uriType) { case TRANSFERS: queryBuilder.appendWhere(TransferTable.COLUMN_PART_NUM + "=" + 0); @@ -148,8 +157,9 @@ public Cursor query(Uri uri, String[] projection, String selection, String[] sel default: throw new IllegalArgumentException("Unknown URI: " + uri); } - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, + ensureDatabaseOpen(); + final Cursor cursor = queryBuilder.query(database, projection, selection, selectionArgs, + null, null, sortOrder); return cursor; } @@ -165,21 +175,21 @@ public Cursor query(Uri uri, String[] projection, String selection, String[] sel */ public synchronized int update(Uri uri, ContentValues values, String whereClause, String[] whereArgs) { - int uriType = uriMatcher.match(uri); - SQLiteDatabase db = databaseHelper.getWritableDatabase(); + final int uriType = uriMatcher.match(uri); int rowsUpdated = 0; + ensureDatabaseOpen(); switch (uriType) { case TRANSFERS: - rowsUpdated = db.update(TransferTable.TABLE_TRANSFER, values, whereClause, + rowsUpdated = database.update(TransferTable.TABLE_TRANSFER, values, whereClause, whereArgs); break; case TRANSFER_ID: - String id = uri.getLastPathSegment(); + final String id = uri.getLastPathSegment(); if (TextUtils.isEmpty(whereClause)) { - rowsUpdated = db.update(TransferTable.TABLE_TRANSFER, values, + rowsUpdated = database.update(TransferTable.TABLE_TRANSFER, values, TransferTable.COLUMN_ID + "=" + id, null); } else { - rowsUpdated = db + rowsUpdated = database .update(TransferTable.TABLE_TRANSFER, values, TransferTable.COLUMN_ID + "=" + id + " and " + whereClause, whereArgs); } @@ -199,20 +209,21 @@ public synchronized int update(Uri uri, ContentValues values, String whereClause * @return Number of rows deleted. */ public int delete(Uri uri, String selection, String[] selectionArgs) { - int uriType = uriMatcher.match(uri); - SQLiteDatabase db = databaseHelper.getWritableDatabase(); + final int uriType = uriMatcher.match(uri); int rowsDeleted = 0; + ensureDatabaseOpen(); switch (uriType) { case TRANSFERS: - rowsDeleted = db.delete(TransferTable.TABLE_TRANSFER, selection, selectionArgs); + rowsDeleted = database.delete(TransferTable.TABLE_TRANSFER, selection, + selectionArgs); break; case TRANSFER_ID: - String id = uri.getLastPathSegment(); + final String id = uri.getLastPathSegment(); if (TextUtils.isEmpty(selection)) { - rowsDeleted = db.delete(TransferTable.TABLE_TRANSFER, + rowsDeleted = database.delete(TransferTable.TABLE_TRANSFER, TransferTable.COLUMN_ID + "=" + id, null); } else { - rowsDeleted = db + rowsDeleted = database .delete(TransferTable.TABLE_TRANSFER, TransferTable.COLUMN_ID + "=" + id + " and " + selection, selectionArgs); } @@ -229,25 +240,25 @@ public int delete(Uri uri, String selection, String[] selectionArgs) { * @return Number of rows inserted. */ public int bulkInsert(Uri uri, ContentValues[] valuesArray) { - int uriType = uriMatcher.match(uri); - SQLiteDatabase db = databaseHelper.getWritableDatabase(); + final int uriType = uriMatcher.match(uri); int mainUploadId = 0; + ensureDatabaseOpen(); switch (uriType) { case TRANSFERS: try { - db.beginTransaction(); - mainUploadId = (int) db.insertOrThrow(TransferTable.TABLE_TRANSFER, null, + database.beginTransaction(); + mainUploadId = (int) database.insertOrThrow(TransferTable.TABLE_TRANSFER, null, valuesArray[0]); for (int i = 1; i < valuesArray.length; i++) { valuesArray[i].put(TransferTable.COLUMN_MAIN_UPLOAD_ID, mainUploadId); - db.insertOrThrow(TransferTable.TABLE_TRANSFER, null, valuesArray[i]); + database.insertOrThrow(TransferTable.TABLE_TRANSFER, null, valuesArray[i]); } - db.setTransactionSuccessful(); - } catch (Exception e) { + database.setTransactionSuccessful(); + } catch (final Exception e) { Log.e(TransferDBBase.class.getSimpleName(), "bulkInsert error : " + e.getMessage()); } finally { - db.endTransaction(); + database.endTransaction(); } break; default: @@ -255,4 +266,18 @@ public int bulkInsert(Uri uri, ContentValues[] valuesArray) { } return mainUploadId; } + + private void ensureDatabaseOpen() { + // close and reopen database. + if (!database.isOpen()) { + database = databaseHelper.getWritableDatabase(); + } + } + + SQLiteDatabase getDatabase() { + return database; + } + } + + diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferDBUtil.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferDBUtil.java index b0b6b45aa7..a0fbcdcea7 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferDBUtil.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferDBUtil.java @@ -76,7 +76,7 @@ public void closeDB() { */ public Uri insertMultipartUploadRecord(String bucket, String key, File file, long fileOffset, int partNumber, String uploadId, long bytesTotal, int isLastPart) { - ContentValues values = generateContentValuesForMultiPartUpload(bucket, key, file, + final ContentValues values = generateContentValuesForMultiPartUpload(bucket, key, file, fileOffset, partNumber, uploadId, bytesTotal, isLastPart, new ObjectMetadata(), null); return transferDBBase.insert(transferDBBase.getContentUri(), values); @@ -112,7 +112,7 @@ public Uri insertSingleTransferRecord(TransferType type, String bucket, String k */ public Uri insertSingleTransferRecord(TransferType type, String bucket, String key, File file, ObjectMetadata metadata, CannedAccessControlList cannedAcl) { - ContentValues values = generateContentValuesForSinglePartTransfer(type, bucket, key, file, + final ContentValues values = generateContentValuesForSinglePartTransfer(type, bucket, key, file, metadata, cannedAcl); return transferDBBase.insert(transferDBBase.getContentUri(), values); } @@ -150,7 +150,7 @@ public int bulkInsertTransferRecords(ContentValues[] valuesArray) { * @return Number of rows updated. */ public int updateTransferRecord(TransferRecord transfer) { - ContentValues cv = new ContentValues(); + final ContentValues cv = new ContentValues(); cv.put(TransferTable.COLUMN_ID, transfer.id); cv.put(TransferTable.COLUMN_STATE, transfer.state.toString()); cv.put(TransferTable.COLUMN_BYTES_TOTAL, transfer.bytesTotal); @@ -166,7 +166,7 @@ public int updateTransferRecord(TransferRecord transfer) { * @return Number of rows updated. */ public int updateBytesTransferred(int id, long bytes) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_BYTES_CURRENT, bytes); return transferDBBase.update(getRecordUri(id), values, null, null); } @@ -179,7 +179,7 @@ public int updateBytesTransferred(int id, long bytes) { * @return Number of rows updated. */ public int updateBytesTotalForDownload(int id, long bytes) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_BYTES_TOTAL, bytes); return transferDBBase.update(getRecordUri(id), values, null, null); } @@ -197,7 +197,7 @@ public int updateBytesTotalForDownload(int id, long bytes) { * @return Number of rows updated. */ public int updateState(int id, TransferState state) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_STATE, state.toString()); if (TransferState.FAILED.equals(state)) { return transferDBBase.update(getRecordUri(id), values, TransferTable.COLUMN_STATE @@ -224,7 +224,7 @@ public int updateState(int id, TransferState state) { * @return Number of rows updated. */ public int updateStateAndNotifyUpdate(int id, TransferState state) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_STATE, state.toString()); return transferDBBase.update(transferDBBase.getContentUri(), values, TransferTable.COLUMN_ID + "=" + id, null); @@ -238,7 +238,7 @@ public int updateStateAndNotifyUpdate(int id, TransferState state) { * @return Number of rows updated. */ public int updateMultipartId(int id, String multipartId) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_MULTIPART_ID, multipartId); return transferDBBase.update(getRecordUri(id), values, null, null); } @@ -251,7 +251,7 @@ public int updateMultipartId(int id, String multipartId) { * @return Number of rows updated. */ public int updateETag(int id, String etag) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_ETAG, etag); return transferDBBase.update(getRecordUri(id), values, null, null); } @@ -263,7 +263,7 @@ public int updateETag(int id, String etag) { * @return Number of rows updated. */ public int updateNetworkDisconnected() { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_STATE, TransferState.PENDING_NETWORK_DISCONNECT.toString()); return transferDBBase.update(transferDBBase.getContentUri(), values, @@ -282,7 +282,7 @@ public int updateNetworkDisconnected() { * @return Number of rows updated. */ public int updateNetworkConnected() { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_STATE, TransferState.RESUMED_WAITING.toString()); return transferDBBase.update(transferDBBase.getContentUri(), values, TransferTable.COLUMN_STATE @@ -299,7 +299,7 @@ public int updateNetworkConnected() { * @return Number of rows updated. */ public int setAllRunningRecordsToPausedBeforeShutdownService() { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_STATE, TransferState.PAUSED.toString()); return transferDBBase.update( transferDBBase.getContentUri(), @@ -322,7 +322,7 @@ public int setAllRunningRecordsToPausedBeforeShutdownService() { * @return Number of rows updated. */ public int pauseAllWithType(TransferType type) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_STATE, TransferState.PENDING_PAUSE.toString()); String selection = null; String[] selectionArgs = null; @@ -355,7 +355,7 @@ public int pauseAllWithType(TransferType type) { * @return Number of rows updated. */ public int cancelAllWithType(TransferType type) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_STATE, TransferState.PENDING_CANCEL.toString()); String selection = null; String[] selectionArgs = null; @@ -438,18 +438,21 @@ public Cursor queryTransferById(int id) { * @return The bytes already uploaded for this multipart upload task */ public long queryBytesTransferredByMainUploadId(int mainUploadId) { - Cursor c = transferDBBase.query(getPartUri(mainUploadId), null, null, null, null); + Cursor c = null; long bytesTotal = 0; try { + c = transferDBBase.query(getPartUri(mainUploadId), null, null, null, null); while (c.moveToNext()) { - String state = c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_STATE)); + final String state = c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_STATE)); if (TransferState.PART_COMPLETED.equals(TransferState.getState(state))) { bytesTotal += c.getLong(c .getColumnIndexOrThrow(TransferTable.COLUMN_BYTES_TOTAL)); } } } finally { - c.close(); + if (c != null) { + c.close(); + } } return bytesTotal; } @@ -474,18 +477,21 @@ public int deleteTransferRecords(int id) { * @return A list of PartEtag of completed parts */ public List queryPartETagsOfUpload(int mainUploadId) { - List partETags = new ArrayList(); - Cursor c = transferDBBase.query(getPartUri(mainUploadId), null, null, null, null); + final List partETags = new ArrayList(); + Cursor c = null; int partNum = 0; String eTag = null; try { + c = transferDBBase.query(getPartUri(mainUploadId), null, null, null, null); while (c.moveToNext()) { partNum = c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_PART_NUM)); eTag = c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_ETAG)); partETags.add(new PartETag(partNum, eTag)); } } finally { - c.close(); + if (c != null) { + c.close(); + } } return partETags; } @@ -501,15 +507,16 @@ public List queryPartETagsOfUpload(int mainUploadId) { */ public List getNonCompletedPartRequestsFromDB(int mainUploadId, String multipartId) { - ArrayList list = new ArrayList(); - Cursor c = transferDBBase.query(getPartUri(mainUploadId), null, null, null, null); + final ArrayList list = new ArrayList(); + Cursor c = null; try { + c = transferDBBase.query(getPartUri(mainUploadId), null, null, null, null); while (c.moveToNext()) { if (TransferState.PART_COMPLETED.equals(TransferState.getState(c.getString(c .getColumnIndexOrThrow(TransferTable.COLUMN_STATE))))) { continue; } - UploadPartRequest putPartRequest = new UploadPartRequest() + final UploadPartRequest putPartRequest = new UploadPartRequest() .withId(c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_ID))) .withMainUploadId( c.getInt(c @@ -532,7 +539,9 @@ public List getNonCompletedPartRequestsFromDB(int mainUploadI list.add(putPartRequest); } } finally { - c.close(); + if (c != null) { + c.close(); + } } return list; } @@ -558,7 +567,7 @@ public ContentValues generateContentValuesForMultiPartUpload(String bucket, String key, File file, long fileOffset, int partNumber, String uploadId, long bytesTotal, int isLastPart, ObjectMetadata metadata, CannedAccessControlList cannedAcl) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_TYPE, TransferType.UPLOAD.toString()); values.put(TransferTable.COLUMN_STATE, TransferState.WAITING.toString()); values.put(TransferTable.COLUMN_BUCKET_NAME, bucket); @@ -588,7 +597,7 @@ public ContentValues generateContentValuesForMultiPartUpload(String bucket, * @return the ContentValues */ private ContentValues generateContentValuesForObjectMetadata(ObjectMetadata metadata) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_USER_METADATA, JsonUtils.mapToString(metadata.getUserMetadata())); values.put(TransferTable.COLUMN_HEADER_CONTENT_TYPE, metadata.getContentType()); @@ -598,7 +607,7 @@ private ContentValues generateContentValuesForObjectMetadata(ObjectMetadata meta values.put(TransferTable.COLUMN_HEADER_CONTENT_DISPOSITION, metadata.getContentDisposition()); values.put(TransferTable.COLUMN_SSE_ALGORITHM, metadata.getSSEAlgorithm()); - values.put(TransferTable.COLUMN_SSE_KMS_KEY, metadata.getSSEKMSKeyId()); + values.put(TransferTable.COLUMN_SSE_KMS_KEY, metadata.getSSEAwsKmsKeyId()); values.put(TransferTable.COLUMN_EXPIRATION_TIME_RULE_ID, metadata.getExpirationTimeRuleId()); if (metadata.getHttpExpiresDate() != null) { values.put(TransferTable.COLUMN_HTTP_EXPIRES_DATE, @@ -624,15 +633,16 @@ private ContentValues generateContentValuesForObjectMetadata(ObjectMetadata meta private ContentValues generateContentValuesForSinglePartTransfer(TransferType type, String bucket, String key, File file, ObjectMetadata metadata, CannedAccessControlList cannedAcl) { - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put(TransferTable.COLUMN_TYPE, type.toString()); values.put(TransferTable.COLUMN_STATE, TransferState.WAITING.toString()); values.put(TransferTable.COLUMN_BUCKET_NAME, bucket); values.put(TransferTable.COLUMN_KEY, key); values.put(TransferTable.COLUMN_FILE, file.getAbsolutePath()); values.put(TransferTable.COLUMN_BYTES_CURRENT, 0l); - if (type.equals(TransferType.UPLOAD)) + if (type.equals(TransferType.UPLOAD)) { values.put(TransferTable.COLUMN_BYTES_TOTAL, file == null ? 0l : file.length()); + } values.put(TransferTable.COLUMN_IS_MULTIPART, 0); values.put(TransferTable.COLUMN_PART_NUM, 0); values.put(TransferTable.COLUMN_IS_ENCRYPTED, 0); @@ -692,15 +702,23 @@ public Uri getStateUri(TransferState state) { */ TransferRecord getTransferById(int id) { TransferRecord transfer = null; - Cursor c = queryTransferById(id); + Cursor c = null; try { + c = queryTransferById(id); if (c.moveToFirst()) { transfer = new TransferRecord(0); transfer.updateFromDB(c); } } finally { - c.close(); + if (c != null) { + c.close(); + } } return transfer; } + + static TransferDBBase getTransferDBBase() { + return transferDBBase; + } } + diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferObserver.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferObserver.java index 4cd468aedf..d54bbf4bbb 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferObserver.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferObserver.java @@ -81,6 +81,23 @@ public class TransferObserver { transferState = TransferState.WAITING; } + /** + * Constructs a TransferObserver and initializes fields with the given + * arguments. + * + * @param id The transfer id of the transfer to be observed. + * @param dbUtil an instance of database utility + * @param bucket bucket of the S3 object + * @param key key of the S3 object + * @param file a file associated with this transfer + * @param listener the listener for the transfer + */ + TransferObserver(int id, TransferDBUtil dbUtil, String bucket, String key, File file, + TransferListener listener) { + this(id, dbUtil, bucket, key, file); + this.setTransferListener(listener); + } + /** * Constructs a TransferObserver and initializes fields with the given * arguments. @@ -89,10 +106,9 @@ public class TransferObserver { * @param dbUtil an instance of database utility * @param c a cursor to read the state of the transfer from */ - TransferObserver(int id, TransferDBUtil dbUtil, Cursor c) { + TransferObserver(int id, TransferDBUtil dbUtil) { this.id = id; this.dbUtil = dbUtil; - updateFromDB(c); } /** @@ -100,13 +116,16 @@ public class TransferObserver { * TransferListener is set, then there's no need to call this method. */ public void refresh() { - final Cursor c = dbUtil.queryTransferById(id); + Cursor c = null; try { + c = dbUtil.queryTransferById(id); if (c.moveToFirst()) { updateFromDB(c); } } finally { - c.close(); + if (c != null) { + c.close(); + } } } @@ -115,7 +134,7 @@ public void refresh() { * * @param c a cursor to read the state of the transfer from */ - private void updateFromDB(Cursor c) { + protected void updateFromDB(Cursor c) { bucket = c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_BUCKET_NAME)); key = c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_KEY)); bytesTotal = c.getLong(c.getColumnIndexOrThrow(TransferTable.COLUMN_BYTES_TOTAL)); @@ -136,16 +155,18 @@ private void updateFromDB(Cursor c) { * @param listener A TransferListener used to receive notification. */ public void setTransferListener(TransferListener listener) { - synchronized (this) { - // Remove previous listener. - cleanTransferListener(); - - // One additional listener is attached so that the basic transfer - // info gets updated along side. - statusListener = new TransferStatusListener(); - TransferStatusUpdater.registerListener(id, statusListener); - transferListener = listener; - TransferStatusUpdater.registerListener(id, transferListener); + if (listener != null) { + synchronized (this) { + // Remove previous listener. + cleanTransferListener(); + + // One additional listener is attached so that the basic transfer + // info gets updated along side. + statusListener = new TransferStatusListener(); + TransferStatusUpdater.registerListener(id, statusListener); + transferListener = listener; + TransferStatusUpdater.registerListener(id, transferListener); + } } } @@ -249,3 +270,5 @@ public void onError(int id, Exception ex) { } } } + + diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferRecord.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferRecord.java index ce76c27c16..a91d6c49ef 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferRecord.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferRecord.java @@ -288,3 +288,4 @@ public String toString() { return sb.toString(); } } + diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferService.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferService.java index 8607092d3a..5ee556d42d 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferService.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferService.java @@ -118,7 +118,6 @@ public void onCreate() { super.onCreate(); Log.d(TAG, "Starting Transfer Service"); - dbUtil = new TransferDBUtil(getApplicationContext()); updater = new TransferStatusUpdater(dbUtil); @@ -206,6 +205,7 @@ public void onDestroy() { handlerThread.quit(); TransferThreadPool.closeThreadPool(); S3ClientReference.clear(); + dbUtil.closeDB(); super.onDestroy(); } @@ -367,9 +367,10 @@ private void removeCompletedTransfers() { */ void loadTransfersFromDB() { Log.d(TAG, "Loading transfers from database"); - final Cursor c = dbUtil.queryAllTransfersWithType(TransferType.ANY); + Cursor c = null; int count = 0; try { + c = dbUtil.queryAllTransfersWithType(TransferType.ANY); while (c.moveToNext()) { final int id = c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_ID)); final TransferState state = TransferState.getState(c.getString(c @@ -444,3 +445,4 @@ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { writer.flush(); } } + diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferUtility.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferUtility.java index 0f44731ca7..fe1832f525 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferUtility.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/TransferUtility.java @@ -112,7 +112,6 @@ public TransferUtility(AmazonS3 s3, Context context) { this.appContext = context.getApplicationContext(); this.dbUtil = new TransferDBUtil(appContext); } - /** * Starts downloading the S3 object specified by the bucket and the key to * the given file. The file must be a valid file. Directory isn't supported. @@ -124,6 +123,22 @@ public TransferUtility(AmazonS3 s3, Context context) { * @return A TransferObserver used to track download progress and state */ public TransferObserver download(String bucket, String key, File file) { + return download(bucket, key, file, null); + } + + /** + * Starts downloading the S3 object specified by the bucket and the key to + * the given file. The file must be a valid file. Directory isn't supported. + * Note that if the given file exists, it'll be overwritten. + * + * @param bucket The name of the bucket containing the object to download. + * @param key The key under which the object to download is stored. + * @param file The file to download the object's data to. + * @param listener a listener to attach to transfer observer. + * @return A TransferObserver used to track download progress and state + */ + public TransferObserver download(String bucket, String key, File file, + TransferListener listener) { if (file == null || file.isDirectory()) { throw new IllegalArgumentException("Invalid file: " + file); } @@ -136,7 +151,7 @@ public TransferObserver download(String bucket, String key, File file) { } sendIntent(TransferService.INTENT_ACTION_TRANSFER_ADD, recordId); - return new TransferObserver(recordId, dbUtil, bucket, key, file); + return new TransferObserver(recordId, dbUtil, bucket, key, file, listener); } /** @@ -200,6 +215,24 @@ public TransferObserver upload(String bucket, String key, File file, ObjectMetad */ public TransferObserver upload(String bucket, String key, File file, ObjectMetadata metadata, CannedAccessControlList cannedAcl) { + return upload(bucket, key, file, metadata, cannedAcl, null); + } + + /** + * Starts uploading the file to the given bucket, using the given key. The + * file must be a valid file. Directory isn't supported. + * + * @param bucket The name of the bucket to upload the new object to. + * @param key The key in the specified bucket by which to store the new + * object. + * @param file The file to upload. + * @param metadata The S3 metadata to associate with this object + * @param cannedAcl The canned ACL to associate with this object + * @param listener a listener to attach to transfer observer. + * @return A TransferObserver used to track upload progress and state + */ + public TransferObserver upload(String bucket, String key, File file, ObjectMetadata metadata, + CannedAccessControlList cannedAcl, TransferListener listener) { if (file == null || file.isDirectory() || !file.exists()) { throw new IllegalArgumentException("Invalid file: " + file); } @@ -214,9 +247,10 @@ public TransferObserver upload(String bucket, String key, File file, ObjectMetad } sendIntent(TransferService.INTENT_ACTION_TRANSFER_ADD, recordId); - return new TransferObserver(recordId, dbUtil, bucket, key, file); + return new TransferObserver(recordId, dbUtil, bucket, key, file, listener); } + /** * Gets a TransferObserver instance to track the record with the given id. * @@ -224,16 +258,21 @@ public TransferObserver upload(String bucket, String key, File file, ObjectMetad * @return The TransferObserver instance which is observing the record. */ public TransferObserver getTransferById(int id) { - final Cursor c = dbUtil.queryTransferById(id); + Cursor c = null; try { - if (c.moveToFirst()) { - return new TransferObserver(id, dbUtil, c); - } else { - return null; + c = dbUtil.queryTransferById(id); + if (c.moveToNext()) { + final TransferObserver to = new TransferObserver(id, dbUtil); + to.updateFromDB(c); + return to; } } finally { - c.close(); + if (c != null) { + c.close(); + } } + + return null; } /** @@ -245,14 +284,19 @@ public TransferObserver getTransferById(int id) { */ public List getTransfersWithType(TransferType type) { final List transferObservers = new ArrayList(); - final Cursor c = dbUtil.queryAllTransfersWithType(type); + Cursor c = null; try { + c = dbUtil.queryAllTransfersWithType(type); while (c.moveToNext()) { final int id = c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_ID)); - transferObservers.add(new TransferObserver(id, dbUtil, c)); + final TransferObserver to = new TransferObserver(id, dbUtil); + to.updateFromDB(c); + transferObservers.add(to); } } finally { - c.close(); + if (c != null) { + c.close(); + } } return transferObservers; } @@ -269,8 +313,9 @@ public List getTransfersWithType(TransferType type) { public List getTransfersWithTypeAndState(TransferType type, TransferState state) { final List transferObservers = new ArrayList(); - final Cursor c = dbUtil.queryTransfersWithTypeAndState(type, state); + Cursor c = null; try { + c = dbUtil.queryTransfersWithTypeAndState(type, state); while (c.moveToNext()) { final int partNum = c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_PART_NUM)); if (partNum != 0) { @@ -278,10 +323,14 @@ public List getTransfersWithTypeAndState(TransferType type, continue; } final int id = c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_ID)); - transferObservers.add(new TransferObserver(id, dbUtil, c)); + final TransferObserver to = new TransferObserver(id, dbUtil); + to.updateFromDB(c); + transferObservers.add(to); } } finally { - c.close(); + if (c != null) { + c.close(); + } } return transferObservers; } @@ -345,14 +394,17 @@ public boolean pause(int id) { * @param type The type of transfers */ public void pauseAllWithType(TransferType type) { - final Cursor c = dbUtil.queryAllTransfersWithType(type); + Cursor c = null; try { + c = dbUtil.queryAllTransfersWithType(type); while (c.moveToNext()) { final int id = c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_ID)); pause(id); } } finally { - c.close(); + if (c != null) { + c.close(); + } } } @@ -392,14 +444,17 @@ public boolean cancel(int id) { * @param type The type of transfers */ public void cancelAllWithType(TransferType type) { - final Cursor c = dbUtil.queryAllTransfersWithType(type); + Cursor c = null; try { + c = dbUtil.queryAllTransfersWithType(type); while (c.moveToNext()) { final int id = c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_ID)); cancel(id); } } finally { - c.close(); + if(c!=null) { + c.close(); + } } } @@ -455,4 +510,10 @@ static X appendMultipartTransferServiceUserA return request; } + TransferDBUtil getDbUtil() { + return dbUtil; + } + } + + diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/UploadTask.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/UploadTask.java index a6a92d0cb2..9cca9003d4 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/UploadTask.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/mobileconnectors/s3/transferutility/UploadTask.java @@ -27,8 +27,10 @@ import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PartETag; import com.amazonaws.services.s3.model.PutObjectRequest; +import com.amazonaws.services.s3.model.SSEAwsKeyManagementParams; import com.amazonaws.services.s3.model.UploadPartRequest; import com.amazonaws.services.s3.util.Mimetypes; +import com.google.gson.Gson; import java.io.File; import java.io.IOException; @@ -94,11 +96,11 @@ private Boolean uploadMultipartAndWaitForCompletion() throws ExecutionException */ long bytesAlreadyTransferrd = 0; if (upload.multipartId == null || upload.multipartId.isEmpty()) { - PutObjectRequest putObjectRequest = createPutObjectRequest(upload); + final PutObjectRequest putObjectRequest = createPutObjectRequest(upload); TransferUtility.appendMultipartTransferServiceUserAgentString(putObjectRequest); try { upload.multipartId = initiateMultipartUpload(putObjectRequest); - } catch (AmazonClientException ace) { + } catch (final AmazonClientException ace) { Log.e(TAG, "Error initiating multipart upload: " + upload.id + " due to " + ace.getMessage()); updater.throwError(upload.id, ace); @@ -119,11 +121,11 @@ private Boolean uploadMultipartAndWaitForCompletion() throws ExecutionException } updater.updateProgress(upload.id, bytesAlreadyTransferrd, upload.bytesTotal); - List requestList = dbUtil.getNonCompletedPartRequestsFromDB(upload.id, + final List requestList = dbUtil.getNonCompletedPartRequestsFromDB(upload.id, upload.multipartId); Log.d(TAG, "multipart upload " + upload.id + " in " + requestList.size() + " parts."); - ArrayList> futures = new ArrayList>(); - for (UploadPartRequest request : requestList) { + final ArrayList> futures = new ArrayList>(); + for (final UploadPartRequest request : requestList) { TransferUtility.appendMultipartTransferServiceUserAgentString(request); request.setGeneralProgressListener(updater.newProgressListener(upload.id)); futures.add(TransferThreadPool.submitTask(new UploadPartTask(request, s3, dbUtil))); @@ -134,30 +136,30 @@ private Boolean uploadMultipartAndWaitForCompletion() throws ExecutionException * Future.get() will block the current thread until the method * returns. */ - for (Future f : futures) { + for (final Future f : futures) { // UploadPartTask returns false when it's interrupted by user // and the state is set by caller - boolean b = f.get(); + final boolean b = f.get(); isSuccess &= b; } if (!isSuccess) { return false; } - } catch (InterruptedException e) { + } catch (final InterruptedException e) { /* * Future.get() will catch InterruptedException, but it's not a * failure, it may be caused by a pause operation from applications. */ - for (Future f : futures) { + for (final Future f : futures) { f.cancel(true); } // abort by user Log.d(TAG, "Transfer " + upload.id + " is interrupted by user"); return false; - } catch (ExecutionException ee) { + } catch (final ExecutionException ee) { // handle pause, cancel, etc if (ee.getCause() != null && ee.getCause() instanceof Exception) { - Exception e = (Exception) ee.getCause(); + final Exception e = (Exception) ee.getCause(); if (RetryUtils.isInterrupted(e)) { /* * thread is interrupted by user. don't update the state as @@ -182,7 +184,7 @@ private Boolean uploadMultipartAndWaitForCompletion() throws ExecutionException updater.updateProgress(upload.id, upload.bytesTotal, upload.bytesTotal); updater.updateState(upload.id, TransferState.COMPLETED); return true; - } catch (AmazonClientException ace) { + } catch (final AmazonClientException ace) { Log.e(TAG, "Failed to complete multipart: " + upload.id + " due to " + ace.getMessage()); updater.throwError(upload.id, ace); @@ -192,9 +194,9 @@ private Boolean uploadMultipartAndWaitForCompletion() throws ExecutionException } private Boolean uploadSinglePartAndWaitForCompletion() { - PutObjectRequest putObjectRequest = createPutObjectRequest(upload); + final PutObjectRequest putObjectRequest = createPutObjectRequest(upload); - long length = putObjectRequest.getFile().length(); + final long length = putObjectRequest.getFile().length(); TransferUtility.appendTransferServiceUserAgentString(putObjectRequest); updater.updateProgress(upload.id, 0, length); putObjectRequest.setGeneralProgressListener(updater.newProgressListener(upload.id)); @@ -204,7 +206,7 @@ private Boolean uploadSinglePartAndWaitForCompletion() { updater.updateProgress(upload.id, length, length); updater.updateState(upload.id, TransferState.COMPLETED); return true; - } catch (Exception e) { + } catch (final Exception e) { if (RetryUtils.isInterrupted(e)) { /* * thread is interrupted by user. don't update the state as it's @@ -227,8 +229,8 @@ private Boolean uploadSinglePartAndWaitForCompletion() { private void completeMultiPartUpload(int mainUploadId, String bucket, String key, String multipartId) throws AmazonClientException { - List partETags = dbUtil.queryPartETagsOfUpload(mainUploadId); - CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(bucket, + final List partETags = dbUtil.queryPartETagsOfUpload(mainUploadId); + final CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(bucket, key, multipartId, partETags); TransferUtility.appendMultipartTransferServiceUserAgentString(completeRequest); s3.completeMultipartUpload(completeRequest); @@ -246,10 +248,12 @@ private String initiateMultipartUpload(PutObjectRequest putObjectRequest) initiateMultipartUploadRequest = new InitiateMultipartUploadRequest( putObjectRequest.getBucketName(), putObjectRequest.getKey()) .withCannedACL(putObjectRequest.getCannedAcl()) - .withObjectMetadata(putObjectRequest.getMetadata()); + .withObjectMetadata(putObjectRequest.getMetadata()) + .withSSEAwsKeyManagementParams( + putObjectRequest.getSSEAwsKeyManagementParams()); TransferUtility .appendMultipartTransferServiceUserAgentString(initiateMultipartUploadRequest); - String uploadId = s3.initiateMultipartUpload(initiateMultipartUploadRequest).getUploadId(); + final String uploadId = s3.initiateMultipartUpload(initiateMultipartUploadRequest).getUploadId(); return uploadId; } @@ -261,11 +265,11 @@ private String initiateMultipartUpload(PutObjectRequest putObjectRequest) * @return Returns a PutObjectRequest with filled in metadata and parameters */ private PutObjectRequest createPutObjectRequest(TransferRecord upload) { - File file = new File(upload.file); - PutObjectRequest putObjectRequest = new PutObjectRequest(upload.bucketName, + final File file = new File(upload.file); + final PutObjectRequest putObjectRequest = new PutObjectRequest(upload.bucketName, upload.key, file); - ObjectMetadata om = new ObjectMetadata(); + final ObjectMetadata om = new ObjectMetadata(); om.setContentLength(file.length()); if (upload.headerCacheControl != null) { @@ -297,6 +301,10 @@ private PutObjectRequest createPutObjectRequest(TransferRecord upload) { if (upload.md5 != null) { om.setContentMD5(upload.md5); } + if (upload.sseKMSKey != null) { + putObjectRequest + .setSSEAwsKeyManagementParams(new SSEAwsKeyManagementParams(upload.sseKMSKey)); + } putObjectRequest.setMetadata(om); putObjectRequest.setCannedAcl(getCannedAclFromString(upload.cannedAcl)); @@ -307,7 +315,7 @@ private PutObjectRequest createPutObjectRequest(TransferRecord upload) { private static final Map cannedAclMap; static { cannedAclMap = new HashMap(); - for (CannedAccessControlList cannedAcl : CannedAccessControlList.values()) { + for (final CannedAccessControlList cannedAcl : CannedAccessControlList.values()) { cannedAclMap.put(cannedAcl.toString(), cannedAcl); } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3.java index bc01e928e0..c84771afd7 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3.java @@ -21,86 +21,11 @@ import com.amazonaws.ClientConfiguration; import com.amazonaws.HttpMethod; import com.amazonaws.services.s3.internal.Constants; -import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; -import com.amazonaws.services.s3.model.AccessControlList; -import com.amazonaws.services.s3.model.Bucket; -import com.amazonaws.services.s3.model.BucketAccelerateConfiguration; -import com.amazonaws.services.s3.model.BucketCrossOriginConfiguration; -import com.amazonaws.services.s3.model.BucketLifecycleConfiguration; -import com.amazonaws.services.s3.model.BucketLoggingConfiguration; -import com.amazonaws.services.s3.model.BucketNotificationConfiguration; -import com.amazonaws.services.s3.model.BucketPolicy; -import com.amazonaws.services.s3.model.BucketReplicationConfiguration; -import com.amazonaws.services.s3.model.BucketTaggingConfiguration; -import com.amazonaws.services.s3.model.BucketVersioningConfiguration; -import com.amazonaws.services.s3.model.BucketWebsiteConfiguration; -import com.amazonaws.services.s3.model.CannedAccessControlList; -import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; -import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; -import com.amazonaws.services.s3.model.CopyObjectRequest; -import com.amazonaws.services.s3.model.CopyObjectResult; -import com.amazonaws.services.s3.model.CopyPartRequest; -import com.amazonaws.services.s3.model.CopyPartResult; -import com.amazonaws.services.s3.model.CreateBucketRequest; -import com.amazonaws.services.s3.model.DeleteBucketCrossOriginConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketLifecycleConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketPolicyRequest; -import com.amazonaws.services.s3.model.DeleteBucketReplicationConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketRequest; -import com.amazonaws.services.s3.model.DeleteBucketTaggingConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketWebsiteConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteObjectRequest; -import com.amazonaws.services.s3.model.DeleteObjectsRequest; -import com.amazonaws.services.s3.model.DeleteObjectsResult; -import com.amazonaws.services.s3.model.DeleteVersionRequest; -import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; -import com.amazonaws.services.s3.model.GetBucketAccelerateConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketAclRequest; -import com.amazonaws.services.s3.model.GetBucketLocationRequest; -import com.amazonaws.services.s3.model.GetBucketPolicyRequest; -import com.amazonaws.services.s3.model.GetBucketReplicationConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketWebsiteConfigurationRequest; -import com.amazonaws.services.s3.model.GetObjectMetadataRequest; -import com.amazonaws.services.s3.model.GetObjectRequest; -import com.amazonaws.services.s3.model.GroupGrantee; -import com.amazonaws.services.s3.model.HeadBucketRequest; -import com.amazonaws.services.s3.model.HeadBucketResult; -import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; -import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; -import com.amazonaws.services.s3.model.ListBucketsRequest; -import com.amazonaws.services.s3.model.ListMultipartUploadsRequest; -import com.amazonaws.services.s3.model.ListObjectsRequest; -import com.amazonaws.services.s3.model.ListObjectsV2Request; -import com.amazonaws.services.s3.model.ListObjectsV2Result; -import com.amazonaws.services.s3.model.ListPartsRequest; -import com.amazonaws.services.s3.model.ListVersionsRequest; -import com.amazonaws.services.s3.model.MultiObjectDeleteException; -import com.amazonaws.services.s3.model.MultipartUploadListing; -import com.amazonaws.services.s3.model.ObjectListing; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.Owner; -import com.amazonaws.services.s3.model.PartListing; -import com.amazonaws.services.s3.model.Permission; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.PutObjectResult; -import com.amazonaws.services.s3.model.Region; -import com.amazonaws.services.s3.model.RestoreObjectRequest; -import com.amazonaws.services.s3.model.S3Object; -import com.amazonaws.services.s3.model.SetBucketAccelerateConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketAclRequest; -import com.amazonaws.services.s3.model.SetBucketCrossOriginConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketLifecycleConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketLoggingConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketNotificationConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketPolicyRequest; -import com.amazonaws.services.s3.model.SetBucketReplicationConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketTaggingConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketVersioningConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketWebsiteConfigurationRequest; -import com.amazonaws.services.s3.model.StorageClass; -import com.amazonaws.services.s3.model.UploadPartRequest; -import com.amazonaws.services.s3.model.UploadPartResult; -import com.amazonaws.services.s3.model.VersionListing; +import com.amazonaws.services.s3.internal.S3DirectSpi; +import com.amazonaws.services.s3.model.*; +import com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration; +import com.amazonaws.services.s3.model.inventory.InventoryConfiguration; +import com.amazonaws.services.s3.model.metrics.MetricsConfiguration; import java.io.File; import java.io.InputStream; @@ -129,7 +54,7 @@ * href="http://aws.amazon.com/s3"> http://aws.amazon.com/s3 *

*/ -public interface AmazonS3 { +public interface AmazonS3 extends S3DirectSpi { /** *

@@ -425,6 +350,44 @@ public ListObjectsV2Result listObjectsV2(ListObjectsV2Request listObjectsV2Reque public ObjectListing listNextBatchOfObjects(ObjectListing previousObjectListing) throws AmazonClientException, AmazonServiceException; + /** + *

+ * Provides an easy way to continue a truncated object listing and retrieve + * the next page of results. + *

+ *

+ * To continue the object listing and retrieve the next page of results, + * call the initial {@link ObjectListing} from one of the + * listObjects methods. If truncated (indicated when + * {@link ObjectListing#isTruncated()} returns true), pass the + * ObjectListing back into this method in order to retrieve the + * next page of results. Continue using this method to retrieve more results + * until the returned ObjectListing indicates that it is not + * truncated. + *

+ * + * @param listNextBatchOfObjectsRequest The request object for listing next + * batch of objects using the previous truncated + * ObjectListing. If a non-truncated + * ObjectListing is passed in by the request object, + * an empty ObjectListing is returned without ever + * contacting Amazon S3. + * @return The next set of ObjectListing results, beginning + * immediately after the last result in the specified previous + * ObjectListing. + * @throws AmazonClientException If any errors are encountered in the client + * while making the request or handling the response. + * @throws AmazonServiceException If any errors occurred in Amazon S3 while + * processing the request. + * @see AmazonS3Client#listObjects(String) + * @see AmazonS3Client#listObjects(String, String) + * @see AmazonS3Client#listObjects(ListObjectsRequest) + * @see AmazonS3Client#listNextBatchOfObjects(ObjectListing) + */ + public ObjectListing listNextBatchOfObjects( + ListNextBatchOfObjectsRequest listNextBatchOfObjectsRequest) + throws AmazonClientException, AmazonServiceException; + /** *

* Returns a list of summary information about the versions in the specified @@ -511,6 +474,49 @@ public VersionListing listVersions(String bucketName, String prefix) public VersionListing listNextBatchOfVersions(VersionListing previousVersionListing) throws AmazonClientException, AmazonServiceException; + /** + *

+ * Provides an easy way to continue a truncated {@link VersionListing} and + * retrieve the next page of results. + *

+ *

+ * Obtain the initial VersionListing from one of the + * listVersions methods. If the result is truncated (indicated + * when {@link VersionListing#isTruncated()} returns true), + * pass the VersionListing back into this method in order to + * retrieve the next page of results. From there, continue using this method + * to retrieve more results until the returned VersionListing + * indicates that it is not truncated. + *

+ *

+ * For more information about enabling versioning for a bucket, see + * {@link #setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)} + * . + *

+ * + * @param listNextBatchOfVersionsRequest The request object for listing next + * batch of versions using the previous truncated + * VersionListing. If a non-truncated + * VersionListing is passed in by the request + * object, an empty VersionListing is returned + * without ever contacting Amazon S3. + * @return The next set of VersionListing results, beginning + * immediately after the last result in the specified previous + * VersionListing. + * @throws AmazonClientException If any errors are encountered in the client + * while making the request or handling the response. + * @throws AmazonServiceException If any errors occurred in Amazon S3 while + * processing the request. + * @see AmazonS3Client#listVersions(String, String) + * @see AmazonS3Client#listVersions(ListVersionsRequest) + * @see AmazonS3Client#listVersions(String, String, String, String, String, + * Integer) + * @see AmazonS3Client#listNextBatchOfVersions(VersionListing) + */ + public VersionListing listNextBatchOfVersions( + ListNextBatchOfVersionsRequest listNextBatchOfVersionsRequest) + throws AmazonClientException, AmazonServiceException; + /** *

* Returns a list of summary information about the versions in the specified @@ -734,6 +740,28 @@ public VersionListing listVersions(ListVersionsRequest listVersionsRequest) public Owner getS3AccountOwner() throws AmazonClientException, AmazonServiceException; + /** + *

+ * Gets the current owner of the AWS account that the authenticated sender + * of the request is using. + *

+ *

+ * The caller must authenticate with a valid AWS Access Key ID that + * is registered with AWS. + *

+ * + * @param getS3AccountOwnerRequest The request object for retrieving the S3 + * account owner. + * @return The account of the authenticated sender + * @throws AmazonClientException If any errors are encountered in the client + * while making the request or handling the response. + * @throws AmazonServiceException If any errors occurred in Amazon S3 while + * processing the request. + * @see AmazonS3#getS3AccountOwner() + */ + public Owner getS3AccountOwner(GetS3AccountOwnerRequest getS3AccountOwnerRequest) + throws AmazonClientException, AmazonServiceException; + /** * Checks if the specified bucket exists. Amazon S3 buckets are named in a * global namespace; use this method to determine if a specified bucket name @@ -1200,6 +1228,33 @@ public AccessControlList getObjectAcl(String bucketName, String key) public AccessControlList getObjectAcl(String bucketName, String key, String versionId) throws AmazonClientException, AmazonServiceException; + /** + *

+ * Gets the {@link AccessControlList} (ACL) for the specified object in + * Amazon S3. + *

+ *

+ * Each bucket and object in Amazon S3 has an ACL that defines its access + * control policy. When a request is made, Amazon S3 authenticates the + * request using its standard authentication procedure and then checks the + * ACL to verify the sender was granted access to the bucket or object. If + * the sender is approved, the request proceeds. Otherwise, Amazon S3 + * returns an error. + *

+ * + * @param getObjectAclRequest the request object containing all the + * information needed for retrieving the object ACL. + * @return The AccessControlList for the specified Amazon S3 + * object. + * @throws AmazonClientException If any errors are encountered in the client + * while making the request or handling the response. + * @throws AmazonServiceException If any errors occurred in Amazon S3 while + * processing the request. + * @see AmazonS3#getObjectAcl(String, String, String) + */ + public AccessControlList getObjectAcl(GetObjectAclRequest getObjectAclRequest) + throws AmazonClientException, AmazonServiceException; + /** *

* Sets the {@link AccessControlList} for the specified object in Amazon S3. @@ -1361,9 +1416,34 @@ public void setObjectAcl(String bucketName, String key, String versionId, throws AmazonClientException, AmazonServiceException; /** + * Sets the {@link AccessControlList} for the specified Amazon S3 object + * with an optional version ID. *

- * Gets the {@link AccessControlList} (ACL) for the specified Amazon S3 - * bucket. + * Each bucket and object in Amazon S3 has an ACL that defines its access + * control policy. When a request is made, Amazon S3 authenticates the + * request using its standard authentication procedure and then checks the + * ACL to verify the sender was granted access to the bucket or object. If + * the sender is approved, the request proceeds. Otherwise, Amazon S3 + * returns an error. + *

+ * When constructing a custom AccessControlList, callers + * typically retrieve the existing AccessControlList for a + * bucket ({@link AmazonS3Client#getObjectAcl(String, String)}), modify it + * as necessary, and then use this method to upload the new ACL. + * + * @param setObjectAclRequest The request object containing the S3 object to + * modify and the ACL to set. + * @throws AmazonClientException If any errors are encountered in the client + * while making the request or handling the response. + * @throws AmazonServiceException If any errors occurred in Amazon S3 while + * processing the request. + */ + public void setObjectAcl(SetObjectAclRequest setObjectAclRequest) + throws AmazonClientException, AmazonServiceException; + + /** + *

+ * Gets the {@link AccessControlList} (ACL) for the specified Amazon S3 bucket. *

*

* Each bucket and object in Amazon S3 has an ACL that defines its access @@ -1574,6 +1654,14 @@ public ObjectMetadata getObjectMetadata(GetObjectMetadataRequest getObjectMetada * object's content, or placing constraints on when the object should be * downloaded) callers can use {@link #getObject(GetObjectRequest)}. *

+ *

+ * If you are accessing AWS + * KMS-encrypted objects, you need to specify the correct region of the + * bucket on your client and configure AWS Signature Version 4 for added + * security. For more information on how to do this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

* * @param bucketName The name of the bucket containing the desired object. * @param key The key under which the desired object is stored. @@ -1640,6 +1728,7 @@ public S3Object getObject(String bucketName, String key) throws AmazonClientExce * @see AmazonS3#getObject(String, String) * @see AmazonS3#getObject(GetObjectRequest, File) */ + @Override public S3Object getObject(GetObjectRequest getObjectRequest) throws AmazonClientException, AmazonServiceException; @@ -1668,6 +1757,20 @@ public S3Object getObject(GetObjectRequest getObjectRequest) * prepared to handle this method returning null if the * provided constraints aren't met when Amazon S3 receives the request. *

+ *

+ * If you are accessing AWS + * KMS-encrypted objects, you need to specify the correct region of the + * bucket on your client and configure AWS Signature Version 4 for added + * security. For more information on how to do this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

+ * @param getObjectRequest + * The request object containing all the options on how to + * download the Amazon S3 object content. + * @param destinationFile + * Indicates the file (which might already exist) where + * to save the object content being downloading from Amazon S3. * * @param getObjectRequest The request object containing all the options on * how to download the Amazon S3 object content. @@ -1684,14 +1787,60 @@ public S3Object getObject(GetObjectRequest getObjectRequest) * @see AmazonS3#getObject(String, String) * @see AmazonS3#getObject(GetObjectRequest) */ + @Override public ObjectMetadata getObject(GetObjectRequest getObjectRequest, File destinationFile) throws AmazonClientException, AmazonServiceException; /** *

- * Deletes the specified bucket. All objects (and all object versions, if - * versioning was ever enabled) in the bucket must be deleted before the - * bucket itself can be deleted. + * Retrieves and decodes the contents of an S3 object to a String. + *

+ * + * @param bucketName + * The name of the bucket containing the object to retrieve. + * @param key + * The key of the object to retrieve. + * @return contents of the object as a String + */ + String getObjectAsString(String bucketName, String key) + throws AmazonServiceException, AmazonClientException; + + /** + * Returns the tags for the specified object. + * + * @param getObjectTaggingRequest + * The request object containing all the options on how to + * retrieve the Amazon S3 object tags. + * @return The tags for the specified object. + */ + public GetObjectTaggingResult getObjectTagging(GetObjectTaggingRequest getObjectTaggingRequest); + + /** + * Set the tags for the specified object. + * + * @param setObjectTaggingRequest + * The request object containing all the options for setting the + * tags for the specified object. + */ + public SetObjectTaggingResult setObjectTagging(SetObjectTaggingRequest setObjectTaggingRequest); + + /** + * Remove the tags for the specified object. + * + * @param deleteObjectTaggingRequest + * The request object containing all the options for deleting + * the tags for the specified object. + * + * @return a {@link DeleteObjectTaggingResult} object containing the + * information returned by S3 for the the tag deletion. + */ + public DeleteObjectTaggingResult deleteObjectTagging(DeleteObjectTaggingRequest deleteObjectTaggingRequest); + + /** + *

+ * Deletes the specified bucket. All objects (and all object versions, if versioning + * was ever enabled) in the bucket must be deleted before the bucket itself + * can be deleted. *

*

* Only the owner of a bucket can delete it, regardless of the bucket's @@ -1742,6 +1891,15 @@ public void deleteBucket(String bucketName) * wasn't thrown, the entire object was stored. *

*

+ * If you are uploading or accessing AWS KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

+ *

* Depending on whether a file or input stream is being uploaded, this * method has slightly different behavior. *

@@ -1804,6 +1962,7 @@ public void deleteBucket(String bucketName) * @see AmazonS3#putObject(String, String, File) * @see AmazonS3#putObject(String, String, InputStream, ObjectMetadata) */ + @Override public PutObjectResult putObject(PutObjectRequest putObjectRequest) throws AmazonClientException, AmazonServiceException; @@ -1817,8 +1976,13 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) * wasn't thrown, the entire object was stored. *

*

- * The client automatically computes a checksum of the file. Amazon S3 uses - * checksums to validate the data in each file. + * If you are uploading or accessing AWS KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version *

*

* Using the file extension, Amazon S3 attempts to determine the correct @@ -1876,10 +2040,13 @@ public PutObjectResult putObject(String bucketName, String key, File file) * wasn't thrown, the entire object was stored. *

*

- * The client automatically computes a checksum of the file. This checksum - * is verified against another checksum that is calculated once the data - * reaches Amazon S3, ensuring the data has not corrupted in transit over - * the network. + * If you are uploading or accessing AWS KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version *

*

* Using the file extension, Amazon S3 attempts to determine the correct @@ -1939,19 +2106,44 @@ public PutObjectResult putObject( String bucketName, String key, InputStream input, ObjectMetadata metadata) throws AmazonClientException, AmazonServiceException; + /** + *

+ * Encodes a String into the contents of an S3 object. + *

+ *

+ * String will be encoded to bytes with UTF-8 encoding. + *

+ * + * @param bucketName + * The name of the bucket to place the new object in. + * @param key + * The key of the object to create. + * @param content + * The String to encode + */ + public PutObjectResult putObject(String bucketName, String key, String content) + throws AmazonServiceException, AmazonClientException; + /** *

* Copies a source object to a new destination in Amazon S3. *

*

- * By default, all object metadata for the source object are copied to the - * new destination object. The Amazon S3 AcccessControlList - * (ACL) is not copied to the new object; the new object will have - * the default Amazon S3 ACL, {@link CannedAccessControlList#Private}. + * By default, all object metadata for the source object except + * server-side-encryption, storage-class and + * website-redirect-location are copied to the new destination + * object, unless new object metadata in the specified + * {@link CopyObjectRequest} is provided. *

*

- * To copy an object, the caller's account must have read access to the - * source object and write access to the destination bucket + * The Amazon S3 Acccess Control List (ACL) is not copied to the new + * object. The new object will have the default Amazon S3 ACL, + * {@link CannedAccessControlList#Private}, unless one is explicitly + * provided in the specified {@link CopyObjectRequest}. + *

+ *

+ * To copy an object, the caller's account must have read access to the source object and + * write access to the destination bucket *

*

* This method only exposes the basic options for copying an Amazon S3 @@ -1960,6 +2152,15 @@ public PutObjectResult putObject( * conditional constraints for copying objects, setting ACLs, overwriting * object metadata, etc. *

+ *

+ * If you are copying AWS KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

* * @param sourceBucketName The name of the bucket containing the source * object to copy. @@ -1987,8 +2188,10 @@ public CopyObjectResult copyObject(String sourceBucketName, String sourceKey, * Copies a source object to a new destination in Amazon S3. *

*

- * By default, all object metadata for the source object are copied to the - * new destination object, unless new object metadata in the specified + * By default, all object metadata for the source object except + * server-side-encryption, storage-class and + * website-redirect-location are copied to the new destination + * object, unless new object metadata in the specified * {@link CopyObjectRequest} is provided. *

*

@@ -2013,6 +2216,15 @@ public CopyObjectResult copyObject(String sourceBucketName, String sourceKey, * object. For simple needs, use the * {@link AmazonS3Client#copyObject(String, String, String, String)} method. *

+ *

+ * If you are copying AWS KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

* * @param copyObjectRequest The request object containing all the options * for copying an Amazon S3 object. @@ -2040,6 +2252,15 @@ public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) * null. This method returns a non-null result under all other * circumstances. *

+ *

+ * If you are copying AWS KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

* * @param copyPartRequest The request object containing all the options for * copying an Amazon S3 object. @@ -2054,6 +2275,7 @@ public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) * @see AmazonS3Client#copyObject(CopyObjectRequest) * @see AmazonS3Client#initiateMultipartUpload(InitiateMultipartUploadRequest) */ + @Override public CopyPartResult copyPart(CopyPartRequest copyPartRequest) throws AmazonClientException, AmazonServiceException; @@ -2193,6 +2415,27 @@ public void deleteVersion(String bucketName, String key, String versionId) public void deleteVersion(DeleteVersionRequest deleteVersionRequest) throws AmazonClientException, AmazonServiceException; + /** + *

+ * Gets the logging configuration for the specified bucket. The bucket + * logging configuration object indicates if server access logging is + * enabled for the specified bucket, the destination bucket where server + * access logs are delivered, and the optional log file prefix. + *

+ * + * @param bucketName The name of the bucket whose bucket logging + * configuration is being retrieved. + * @return The bucket logging configuration for the specified bucket. + * @throws AmazonClientException If any errors are encountered in the client + * while making the request or handling the response. + * @throws AmazonServiceException If any errors occurred in Amazon S3 while + * processing the request. + * @see AmazonS3#setBucketLoggingConfiguration(SetBucketLoggingConfigurationRequest) + * @see AmazonS3#getBucketLoggingConfiguration(GetBucketLoggingConfigurationRequest) + */ + public BucketLoggingConfiguration getBucketLoggingConfiguration(String bucketName) + throws AmazonClientException, AmazonServiceException; + /** *

* Gets the logging configuration for the specified bucket. The bucket @@ -2209,8 +2452,10 @@ public void deleteVersion(DeleteVersionRequest deleteVersionRequest) * @throws AmazonServiceException If any errors occurred in Amazon S3 while * processing the request. * @see AmazonS3#setBucketLoggingConfiguration(SetBucketLoggingConfigurationRequest) + * @see AmazonS3#getBucketLoggingConfiguration(String) */ - public BucketLoggingConfiguration getBucketLoggingConfiguration(String bucketName) + public BucketLoggingConfiguration getBucketLoggingConfiguration( + GetBucketLoggingConfigurationRequest getBucketLoggingConfigurationRequest) throws AmazonClientException, AmazonServiceException; /** @@ -2260,9 +2505,9 @@ public void setBucketLoggingConfiguration( * A bucket's versioning configuration can be in one of three possible * states: *

    - *
  • {@link BucketVersioningConfiguration#OFF} - *
  • {@link BucketVersioningConfiguration#ENABLED} - *
  • {@link BucketVersioningConfiguration#SUSPENDED} + *
  • {@link BucketVersioningConfiguration#OFF}
  • + *
  • {@link BucketVersioningConfiguration#ENABLED}
  • + *
  • {@link BucketVersioningConfiguration#SUSPENDED}
  • *
*

*

@@ -2282,8 +2527,10 @@ public void setBucketLoggingConfiguration( * operation. It can never be overwritten. Additionally, the * PutObject API guarantees that, if versioning is enabled for * a bucket the request, no other object will be overwritten by that - * request. Refer to the documentation sections for each API for information - * on how versioning status affects the semantics of that particular API. + * request. Refer to the + * documentation sections for each API for information on how versioning + * status affects the semantics of that particular API. *

*

* Amazon S3 is eventually consistent. It can take time for the versioning @@ -2298,10 +2545,64 @@ public void setBucketLoggingConfiguration( * @throws AmazonServiceException If any errors occurred in Amazon S3 while * processing the request. * @see AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest) + * @see AmazonS3#getBucketVersioningConfiguration(GetBucketVersioningConfigurationRequest) */ public BucketVersioningConfiguration getBucketVersioningConfiguration(String bucketName) throws AmazonClientException, AmazonServiceException; + /** + *

+ * Returns the versioning configuration for the specified bucket. + *

+ *

+ * A bucket's versioning configuration can be in one of three possible + * states: + *

    + *
  • {@link BucketVersioningConfiguration#OFF} + *
  • {@link BucketVersioningConfiguration#ENABLED} + *
  • {@link BucketVersioningConfiguration#SUSPENDED} + *
+ *

+ *

+ * By default, new buckets are in the + * {@link BucketVersioningConfiguration#OFF off} state. Once versioning is + * enabled for a bucket the status can never be reverted to + * {@link BucketVersioningConfiguration#OFF off}. + *

+ *

+ * The versioning configuration of a bucket has different implications for + * each operation performed on that bucket or for objects within that + * bucket. For example, when versioning is enabled a PutObject + * operation creates a unique object version-id for the object being + * uploaded. The The PutObject API guarantees that, if + * versioning is enabled for a bucket at the time of the request, the new + * object can only be permanently deleted using a DeleteVersion + * operation. It can never be overwritten. Additionally, the + * PutObject API guarantees that, if versioning is enabled for + * a bucket the request, no other object will be overwritten by that + * request. Refer to the + * documentation sections for each API for information on how versioning + * status affects the semantics of that particular API. + *

+ *

+ * Amazon S3 is eventually consistent. It can take time for the versioning + * status of a bucket to be propagated throughout the system. + *

+ * + * @param getBucketVersioningConfigurationRequest The request object for + * retrieving the bucket versioning configuration. + * @return The bucket versioning configuration for the specified bucket. + * @throws AmazonClientException If any errors are encountered in the client + * while making the request or handling the response. + * @throws AmazonServiceException If any errors occurred in Amazon S3 while + * processing the request. + * @see AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest) + * @see AmazonS3#getBucketVersioningConfiguration(String) + */ + public BucketVersioningConfiguration getBucketVersioningConfiguration(GetBucketVersioningConfigurationRequest getBucketVersioningConfigurationRequest) + throws AmazonClientException, AmazonServiceException; + /** *

* Sets the versioning configuration for the specified bucket. @@ -2361,81 +2662,114 @@ public void setBucketVersioningConfiguration( throws AmazonClientException, AmazonServiceException; /** - * Gets the lifecycle configuration for the specified bucket, or null if no + * Gets the lifecycle configuration for the specified bucket, or null if + * the specified bucket does not exists, or an empty list if no * configuration has been established. * * @param bucketName The name of the bucket for which to retrieve lifecycle * configuration. + * + * @see AmazonS3#getBucketLifecycleConfiguration(GetBucketLifecycleConfigurationRequest) */ public BucketLifecycleConfiguration getBucketLifecycleConfiguration(String bucketName); /** - * Sets the lifecycle configuration for the specified bucket. + * Gets the lifecycle configuration for the specified bucket, or null if + * the specified bucket does not exists, or an empty list if no + * configuration has been established. * - * @param bucketName The name of the bucket for which to set the lifecycle - * configuration. - * @param bucketLifecycleConfiguration The new lifecycle configuration for - * this bucket, which completely replaces any existing + * @param getBucketLifecycleConfigurationRequest + * The request object for retrieving the bucket lifecycle * configuration. + * + * @see AmazonS3#getBucketLifecycleConfiguration(String) */ - public void setBucketLifecycleConfiguration(String bucketName, - BucketLifecycleConfiguration bucketLifecycleConfiguration); + public BucketLifecycleConfiguration getBucketLifecycleConfiguration( + GetBucketLifecycleConfigurationRequest getBucketLifecycleConfigurationRequest); /** * Sets the lifecycle configuration for the specified bucket. * - * @param setBucketLifecycleConfigurationRequest The request object - * containing all options for setting the bucket lifecycle + * @param bucketName + * The name of the bucket for which to set the lifecycle * configuration. + * @param bucketLifecycleConfiguration + * The new lifecycle configuration for this bucket, which + * completely replaces any existing configuration. */ - public void setBucketLifecycleConfiguration( - SetBucketLifecycleConfigurationRequest setBucketLifecycleConfigurationRequest); + public void setBucketLifecycleConfiguration(String bucketName, BucketLifecycleConfiguration bucketLifecycleConfiguration); + + /** + * Sets the lifecycle configuration for the specified bucket. + * + * @param setBucketLifecycleConfigurationRequest + * The request object containing all options for setting the + * bucket lifecycle configuration. + */ + public void setBucketLifecycleConfiguration(SetBucketLifecycleConfigurationRequest setBucketLifecycleConfigurationRequest); /** * Removes the lifecycle configuration for the bucket specified. * - * @param bucketName The name of the bucket for which to remove the - * lifecycle configuration. + * @param bucketName + * The name of the bucket for which to remove the lifecycle + * configuration. */ public void deleteBucketLifecycleConfiguration(String bucketName); /** * Removes the lifecycle configuration for the bucket specified. * - * @param deleteBucketLifecycleConfigurationRequest The request object - * containing all options for removing the bucket lifecycle + * @param deleteBucketLifecycleConfigurationRequest + * The request object containing all options for removing the + * bucket lifecycle configuration. + */ + public void deleteBucketLifecycleConfiguration(DeleteBucketLifecycleConfigurationRequest deleteBucketLifecycleConfigurationRequest); + + /** + * Gets the cross origin configuration for the specified bucket, or null if + * the specified bucket does not exists, or an empty list if no + * configuration has been established. + * + * @param bucketName + * The name of the bucket for which to retrieve cross origin * configuration. + * + * @see AmazonS3#getBucketCrossOriginConfiguration(GetBucketCrossOriginConfigurationRequest) */ - public void deleteBucketLifecycleConfiguration( - DeleteBucketLifecycleConfigurationRequest deleteBucketLifecycleConfigurationRequest); + public BucketCrossOriginConfiguration getBucketCrossOriginConfiguration(String bucketName); /** * Gets the cross origin configuration for the specified bucket, or null if * no configuration has been established. * - * @param bucketName The name of the bucket for which to retrieve cross - * origin configuration. + * @param getBucketCrossOriginConfigurationRequest + * The request object for retrieving the bucket cross origin + * configuration. + * + * @see AmazonS3#getBucketCrossOriginConfiguration(String) */ - public BucketCrossOriginConfiguration getBucketCrossOriginConfiguration(String bucketName); + public BucketCrossOriginConfiguration getBucketCrossOriginConfiguration( + GetBucketCrossOriginConfigurationRequest getBucketCrossOriginConfigurationRequest); /** * Sets the cross origin configuration for the specified bucket. * - * @param bucketName The name of the bucket for which to retrieve cross - * origin configuration. - * @param bucketCrossOriginConfiguration The new cross origin configuration - * for this bucket, which completely replaces any existing + * @param bucketName + * The name of the bucket for which to retrieve cross origin * configuration. + * @param bucketCrossOriginConfiguration + * The new cross origin configuration for this bucket, which + * completely replaces any existing configuration. */ - public void setBucketCrossOriginConfiguration(String bucketName, - BucketCrossOriginConfiguration bucketCrossOriginConfiguration); + public void setBucketCrossOriginConfiguration(String bucketName, BucketCrossOriginConfiguration bucketCrossOriginConfiguration); /** * Sets the cross origin configuration for the specified bucket. * - * @param setBucketCrossOriginConfigurationRequest The request object - * containing all options for setting the bucket cross origin - * configuration. + * @param setBucketCrossOriginConfigurationRequest + * The request object containing all options for setting the + * bucket cross origin configuration. */ public void setBucketCrossOriginConfiguration( SetBucketCrossOriginConfigurationRequest setBucketCrossOriginConfigurationRequest); @@ -2459,14 +2793,31 @@ public void deleteBucketCrossOriginConfiguration( DeleteBucketCrossOriginConfigurationRequest deleteBucketCrossOriginConfigurationRequest); /** - * Gets the tagging configuration for the specified bucket, or null if no + * Gets the tagging configuration for the specified bucket, or null if + * the specified bucket does not exists, or an empty list if no * configuration has been established. * * @param bucketName The name of the bucket for which to retrieve tagging * configuration. + * + * @see AmazonS3#getBucketTaggingConfiguration(GetBucketTaggingConfigurationRequest) */ public BucketTaggingConfiguration getBucketTaggingConfiguration(String bucketName); + /** + * Gets the tagging configuration for the specified bucket, or null if + * the specified bucket does not exists, or an empty list if no + * configuration has been established. + * + * @param getBucketTaggingConfigurationRequest + * The request object for retrieving the bucket tagging + * configuration. + * + * @see AmazonS3#getBucketTaggingConfiguration(String) + */ + public BucketTaggingConfiguration getBucketTaggingConfiguration( + GetBucketTaggingConfigurationRequest getBucketTaggingConfigurationRequest); + /** * Sets the tagging configuration for the specified bucket. * @@ -2530,10 +2881,40 @@ public void deleteBucketTaggingConfiguration( * while making the request or handling the response. * @throws AmazonServiceException If any errors occurred in Amazon S3 while * processing the request. + * @see AmazonS3#getBucketNotificationConfiguration(GetBucketNotificationConfigurationRequest) */ public BucketNotificationConfiguration getBucketNotificationConfiguration(String bucketName) throws AmazonClientException, AmazonServiceException; + /** + * Gets the notification configuration for the specified bucket. + *

+ * By default, new buckets have no notification configuration. + *

+ * The notification configuration of a bucket provides near realtime + * notifications of events the user is interested in, using SNS as the + * delivery service. Notification is turned on by enabling configuration on + * a bucket, specifying the events and the SNS topic. This configuration can + * only be turned on by the bucket owner. If a notification configuration + * already exists for the specified bucket, the new notification + * configuration will replace the existing notification configuration. To + * remove the notification configuration pass in an empty request. + * Currently, buckets may only have a single event and topic configuration. + *

+ * S3 is eventually consistent. It may take time for the notification status + * of a bucket to be propagated throughout the system. + * + * @param getBucketNotificationConfigurationRequest The bucket notification configuration request. + * @return The bucket notification configuration for the specified bucket. + * @throws AmazonClientException If any errors are encountered on the client + * while making the request or handling the response. + * @throws AmazonServiceException If any errors occurred in Amazon S3 while + * processing the request. + */ + public BucketNotificationConfiguration getBucketNotificationConfiguration( + GetBucketNotificationConfigurationRequest getBucketNotificationConfigurationRequest) + throws AmazonClientException, AmazonServiceException; + /** * Sets the notification configuration for the specified bucket. *

@@ -2983,11 +3364,20 @@ public void deleteBucketPolicy(DeleteBucketPolicyRequest deleteBucketPolicyReque *

*

* For example, an application may need remote users to upload files to the - * application owner's Amazon S3 bucket, but doesn't need to ship the AWS - * security credentials with the application. A pre-signed URL to PUT an - * object into the owner's bucket can be generated from a remote location - * with the owner's AWS security credentials, then the pre-signed URL can be - * passed to the end user's application to use. + * application owner's Amazon S3 bucket, but doesn't need to ship the + * AWS security credentials with the application. A pre-signed URL + * to PUT an object into the owner's bucket can be generated from a remote + * location with the owner's AWS security credentials, then the pre-signed + * URL can be passed to the end user's application to use. + *

+ *

+ * If you are generating presigned url for AWS KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version *

* * @param bucketName The name of the bucket containing the desired object. @@ -3023,11 +3413,20 @@ public URL generatePresignedUrl(String bucketName, String key, Date expiration) *

*

* For example, an application may need remote users to upload files to the - * application owner's Amazon S3 bucket, but doesn't need to ship the AWS - * security credentials with the application. A pre-signed URL to PUT an - * object into the owner's bucket can be generated from a remote location - * with the owner's AWS security credentials, then the pre-signed URL can be - * passed to the end user's application to use. + * application owner's Amazon S3 bucket, but doesn't need to ship the + * AWS security credentials with the application. A pre-signed URL + * to PUT an object into the owner's bucket can be generated from a remote + * location with the owner's AWS security credentials, then the pre-signed + * URL can be passed to the end user's application to use. + *

+ *

+ * If you are generating presigned url for AWS KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version *

* * @param bucketName The name of the bucket containing the desired object. @@ -3078,6 +3477,15 @@ public URL generatePresignedUrl(String bucketName, String key, Date expiration, * >this blog post. That method is only suitable for POSTs from HTML * forms by browsers. *

+ *

+ * If you are generating presigned url for AWS KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

* * @param generatePresignedUrlRequest The request object containing all the * options for generating a pre-signed URL (bucket name, key, @@ -3106,6 +3514,16 @@ public URL generatePresignedUrl(GeneratePresignedUrlRequest generatePresignedUrl * stop getting charged for storage of the uploaded parts. Once you complete * or abort the multipart upload Amazon S3 will release the stored parts and * stop charging you for their storage. + *

+ *

+ * If you are initiating a multipart upload for AWS KMS-encrypted objects, you need + * to specify the correct region of the bucket on your client and configure + * AWS Signature Version 4 for added security. For more information on how + * to do this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

* * @param request The InitiateMultipartUploadRequest object that specifies * all the parameters of this operation. @@ -3115,6 +3533,7 @@ public URL generatePresignedUrl(GeneratePresignedUrlRequest generatePresignedUrl * @throws AmazonServiceException If any errors occurred in Amazon S3 while * processing the request. */ + @Override public InitiateMultipartUploadResult initiateMultipartUpload( InitiateMultipartUploadRequest request) throws AmazonClientException, AmazonServiceException; @@ -3149,6 +3568,16 @@ public InitiateMultipartUploadResult initiateMultipartUpload( * stop getting charged for storage of the uploaded parts. Once you complete * or abort the multipart upload Amazon S3 will release the stored parts and * stop charging you for their storage. + *

+ *

+ * If you are performing upload part for + * AWS KMS-encrypted objects, you + * need to specify the correct region of the bucket on your client and + * configure AWS Signature Version 4 for added security. For more + * information on how to do this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

* * @param request The UploadPartRequest object that specifies all the * parameters of this operation. @@ -3159,6 +3588,7 @@ public InitiateMultipartUploadResult initiateMultipartUpload( * @throws AmazonServiceException If any errors occurred in Amazon S3 while * processing the request. */ + @Override public UploadPartResult uploadPart(UploadPartRequest request) throws AmazonClientException, AmazonServiceException; @@ -3203,6 +3633,7 @@ public PartListing listParts(ListPartsRequest request) * @throws AmazonServiceException If any errors occurred in Amazon S3 while * processing the request. */ + @Override public void abortMultipartUpload(AbortMultipartUploadRequest request) throws AmazonClientException, AmazonServiceException; @@ -3220,6 +3651,16 @@ public void abortMultipartUpload(AbortMultipartUploadRequest request) *

* Processing of a CompleteMultipartUpload request may take several minutes * to complete. + *

+ *

+ * If you are perfoming a complete multipart upload for AWS KMS-encrypted objects, you need + * to specify the correct region of the bucket on your client and configure + * AWS Signature Version 4 for added security. For more information on how + * to do this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html# + * specify-signature-version + *

* * @param request The CompleteMultipartUploadRequest object that specifies * all the parameters of this operation. @@ -3230,6 +3671,7 @@ public void abortMultipartUpload(AbortMultipartUploadRequest request) * @throws AmazonServiceException If any errors occurred in Amazon S3 while * processing the request. */ + @Override public CompleteMultipartUploadResult completeMultipartUpload( CompleteMultipartUploadRequest request) throws AmazonClientException, AmazonServiceException; @@ -3565,4 +4007,281 @@ public void setBucketAccelerateConfiguration(String bucketName, public void setBucketAccelerateConfiguration( SetBucketAccelerateConfigurationRequest setBucketAccelerateConfigurationRequest) throws AmazonServiceException, AmazonClientException; + + /** + * Deletes a metrics configuration (specified by the metrics configuration ID) from the bucket. + * + * @param bucketName + * The name of the bucket from which the metrics configuration is to be deleted + * @param id + * The ID of the metrics configuration to delete. + */ + public DeleteBucketMetricsConfigurationResult deleteBucketMetricsConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException; + + /** + * Deletes a metrics configuration (specified by the metrics configuration ID) from the bucket. + * + * @param deleteBucketMetricsConfigurationRequest + * The request object to delete the metrics configuration. + */ + public DeleteBucketMetricsConfigurationResult deleteBucketMetricsConfiguration( + DeleteBucketMetricsConfigurationRequest deleteBucketMetricsConfigurationRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Gets a metrics configuration (specified by the metrics configuration ID) from the bucket. + * + * @param bucketName + * The name of the bucket to get the metrics configuration from. + * @param id + * The ID of the metrics configuration to get. + * @return + * The result containing the requested metrics configuration. + */ + public GetBucketMetricsConfigurationResult getBucketMetricsConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException; + + /** + * Gets a metrics configuration (specified by the metrics configuration ID) from the bucket. + * + * @param getBucketMetricsConfigurationRequest + * The request object to retrieve the metrics configuration. + * @return + * The result containing the requested metrics configuration. + */ + public GetBucketMetricsConfigurationResult getBucketMetricsConfiguration( + GetBucketMetricsConfigurationRequest getBucketMetricsConfigurationRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Sets a metrics configuration (specified by the metrics configuration ID) for the bucket. + * + * @param bucketName + * The name of the bucket to set the metrics configuration. + * @param metricsConfiguration + * The metrics configuration to set. + */ + public SetBucketMetricsConfigurationResult setBucketMetricsConfiguration( + String bucketName, MetricsConfiguration metricsConfiguration) + throws AmazonServiceException, AmazonClientException; + + /** + * Sets a metrics configuration (specified by the metrics configuration ID) for the bucket. + * + * @param setBucketMetricsConfigurationRequest + * The request object to set the metrics configuration. + */ + public SetBucketMetricsConfigurationResult setBucketMetricsConfiguration( + SetBucketMetricsConfigurationRequest setBucketMetricsConfigurationRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Lists the metrics configurations for the bucket. + * + * @param listBucketMetricsConfigurationsRequest + * The request object to list all the metrics configurations for a bucket. + * @return + * The result containing the list of all the metrics configurations for the bucket. + */ + public ListBucketMetricsConfigurationsResult listBucketMetricsConfigurations( + ListBucketMetricsConfigurationsRequest listBucketMetricsConfigurationsRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Deletes an analytics configuration for the bucket (specified by the analytics configuration ID). + * + * @param bucketName + * The name of the bucket from which the analytics configuration is to be deleted + * @param id + * The ID of the analytics configuration to delete. + */ + public DeleteBucketAnalyticsConfigurationResult deleteBucketAnalyticsConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException; + + /** + * Deletes an analytics configuration for the bucket (specified by the analytics configuration ID). + * + * @param deleteBucketAnalyticsConfigurationRequest + * The request object to delete the analytics configuration. + */ + public DeleteBucketAnalyticsConfigurationResult deleteBucketAnalyticsConfiguration( + DeleteBucketAnalyticsConfigurationRequest deleteBucketAnalyticsConfigurationRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Gets an analytics configuration for the bucket (specified by the analytics configuration ID). + * + * @param bucketName + * The name of the bucket to get the analytics configuration from. + * @param id + * The ID of the analytics configuration to get. + * @return + * The result containing the requested analytics configuration. + */ + public GetBucketAnalyticsConfigurationResult getBucketAnalyticsConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException; + + /** + * Gets an analytics configuration for the bucket (specified by the analytics configuration ID). + * + * @param getBucketAnalyticsConfigurationRequest + * The request object to retrieve the analytics configuration. + * @return + * The result containing the requested analytics configuration. + */ + public GetBucketAnalyticsConfigurationResult getBucketAnalyticsConfiguration( + GetBucketAnalyticsConfigurationRequest getBucketAnalyticsConfigurationRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Sets an analytics configuration for the bucket (specified by the analytics configuration ID). + * + * @param bucketName + * The name of the bucket to set the analytics configuration. + * @param analyticsConfiguration + * The analytics configuration to set. + */ + public SetBucketAnalyticsConfigurationResult setBucketAnalyticsConfiguration( + String bucketName, AnalyticsConfiguration analyticsConfiguration) + throws AmazonServiceException, AmazonClientException; + + /** + * Sets an analytics configuration for the bucket (specified by the analytics configuration ID). + * + * @param setBucketAnalyticsConfigurationRequest + * The request object to set the analytics configuration. + */ + public SetBucketAnalyticsConfigurationResult setBucketAnalyticsConfiguration( + SetBucketAnalyticsConfigurationRequest setBucketAnalyticsConfigurationRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Lists the analytics configurations for the bucket. + * + * @param listBucketAnalyticsConfigurationsRequest + * The request object to list all the analytics configurations for a bucket. + * + * @return All the analytics configurations for the bucket. + */ + public ListBucketAnalyticsConfigurationsResult listBucketAnalyticsConfigurations( + ListBucketAnalyticsConfigurationsRequest listBucketAnalyticsConfigurationsRequest) + throws AmazonServiceException, AmazonClientException; + + + /** + * Deletes an inventory configuration (identified by the inventory ID) from the bucket. + * + * @param bucketName + * The name of the bucket from which the inventory configuration is to be deleted. + * @param id + * The ID of the inventory configuration to delete. + */ + public DeleteBucketInventoryConfigurationResult deleteBucketInventoryConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException; + + /** + * Deletes an inventory configuration (identified by the inventory ID) from the bucket. + * + * @param deleteBucketInventoryConfigurationRequest + * The request object for deleting an inventory configuration. + */ + public DeleteBucketInventoryConfigurationResult deleteBucketInventoryConfiguration( + DeleteBucketInventoryConfigurationRequest deleteBucketInventoryConfigurationRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Returns an inventory configuration (identified by the inventory ID) from the bucket. + * + * @param bucketName + * The name of the bucket to get the inventory configuration from. + * @param id + * The ID of the inventory configuration to delete. + * @return + * An {@link GetBucketInventoryConfigurationResult} object containing the inventory configuration. + */ + public GetBucketInventoryConfigurationResult getBucketInventoryConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException; + + /** + * Returns an inventory configuration (identified by the inventory ID) from the bucket. + * + * @param getBucketInventoryConfigurationRequest + * The request object to retreive an inventory configuration. + * @return + * An {@link GetBucketInventoryConfigurationResult} object containing the inventory configuration. + */ + public GetBucketInventoryConfigurationResult getBucketInventoryConfiguration( + GetBucketInventoryConfigurationRequest getBucketInventoryConfigurationRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Sets an inventory configuration (identified by the inventory ID) to the bucket. + * + * @param bucketName + * The name of the bucket to set the inventory configuration to. + * @param inventoryConfiguration + * The inventory configuration to set. + */ + public SetBucketInventoryConfigurationResult setBucketInventoryConfiguration( + String bucketName, InventoryConfiguration inventoryConfiguration) + throws AmazonServiceException, AmazonClientException; + + /** + * Sets an inventory configuration (identified by the inventory ID) to the bucket. + * + * @param setBucketInventoryConfigurationRequest + * The request object for setting an inventory configuration. + */ + public SetBucketInventoryConfigurationResult setBucketInventoryConfiguration( + SetBucketInventoryConfigurationRequest setBucketInventoryConfigurationRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Returns the list of inventory configurations for the bucket. + * + * @param listBucketInventoryConfigurationsRequest + * The request object to list the inventory configurations in a bucket. + * @return + * An {@link ListBucketInventoryConfigurationsResult} object containing the list of {@link InventoryConfiguration}s. + */ + public ListBucketInventoryConfigurationsResult listBucketInventoryConfigurations( + ListBucketInventoryConfigurationsRequest listBucketInventoryConfigurationsRequest) + throws AmazonServiceException, AmazonClientException; + + /** + * Returns the region with which the client is configured. + * + * @return The region this client will communicate with. + */ + Region getRegion(); + + /** + * Returns a string representation of the region with which this + * client is configured + * + * @return String value representing the region this client will + * communicate with + */ + String getRegionName(); + + /** + * Returns an URL for the object stored in the specified bucket and + * key. + *

+ * If the object identified by the given bucket and key has public read + * permissions (ex: {@link CannedAccessControlList#PublicRead}), then this + * URL can be directly accessed to retrieve the object's data. + * + * @param bucketName + * The name of the bucket containing the object whose URL is + * being requested. + * @param key + * The key under which the object whose URL is being requested is + * stored. + * + * @return A unique URL for the object stored in the specified bucket and + * key. + */ + URL getUrl(String bucketName, String key); } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3Client.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3Client.java index 1a5c60b715..d8c60d130b 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3Client.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3Client.java @@ -17,6 +17,9 @@ import static com.amazonaws.util.LengthCheckInputStream.EXCLUDE_SKIPPED_BYTES; import static com.amazonaws.util.LengthCheckInputStream.INCLUDE_SKIPPED_BYTES; +import static com.amazonaws.util.ValidationUtils.assertNotNull; +import static com.amazonaws.util.ValidationUtils.assertParameterNotNull; +import static com.amazonaws.util.ValidationUtils.assertStringNotEmpty; import com.amazonaws.AbortedException; import com.amazonaws.AmazonClientException; @@ -30,7 +33,6 @@ import com.amazonaws.HttpMethod; import com.amazonaws.Request; import com.amazonaws.Response; -import com.amazonaws.SDKGlobalConfiguration; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; @@ -52,11 +54,16 @@ import com.amazonaws.metrics.AwsSdkMetrics; import com.amazonaws.metrics.RequestMetricCollector; import com.amazonaws.regions.RegionUtils; +import com.amazonaws.retry.PredefinedRetryPolicies; +import com.amazonaws.retry.RetryPolicy; import com.amazonaws.services.s3.internal.AWSS3V4Signer; import com.amazonaws.services.s3.internal.BucketNameUtils; +import com.amazonaws.services.s3.internal.CompleteMultipartUploadRetryCondition; import com.amazonaws.services.s3.internal.Constants; +import com.amazonaws.services.s3.internal.DeleteObjectTaggingHeaderHandler; import com.amazonaws.services.s3.internal.DeleteObjectsResponse; import com.amazonaws.services.s3.internal.DigestValidationInputStream; +import com.amazonaws.services.s3.internal.GetObjectTaggingResponseHeaderHandler; import com.amazonaws.services.s3.internal.InputSubstream; import com.amazonaws.services.s3.internal.MD5DigestCalculatingInputStream; import com.amazonaws.services.s3.internal.ObjectExpirationHeaderHandler; @@ -67,111 +74,27 @@ import com.amazonaws.services.s3.internal.S3MetadataResponseHandler; import com.amazonaws.services.s3.internal.S3ObjectResponseHandler; import com.amazonaws.services.s3.internal.S3QueryStringSigner; +import com.amazonaws.services.s3.internal.S3RequesterChargedHeaderHandler; import com.amazonaws.services.s3.internal.S3Signer; import com.amazonaws.services.s3.internal.S3StringResponseHandler; import com.amazonaws.services.s3.internal.S3VersionHeaderHandler; import com.amazonaws.services.s3.internal.S3XmlResponseHandler; import com.amazonaws.services.s3.internal.ServerSideEncryptionHeaderHandler; import com.amazonaws.services.s3.internal.ServiceUtils; +import com.amazonaws.services.s3.internal.SetObjectTaggingResponseHeaderHandler; import com.amazonaws.services.s3.internal.XmlWriter; import com.amazonaws.services.s3.metrics.S3ServiceMetric; -import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; -import com.amazonaws.services.s3.model.AccessControlList; -import com.amazonaws.services.s3.model.AmazonS3Exception; -import com.amazonaws.services.s3.model.Bucket; -import com.amazonaws.services.s3.model.BucketAccelerateConfiguration; -import com.amazonaws.services.s3.model.BucketCrossOriginConfiguration; -import com.amazonaws.services.s3.model.BucketLifecycleConfiguration; -import com.amazonaws.services.s3.model.BucketLoggingConfiguration; -import com.amazonaws.services.s3.model.BucketNotificationConfiguration; -import com.amazonaws.services.s3.model.BucketPolicy; -import com.amazonaws.services.s3.model.BucketReplicationConfiguration; -import com.amazonaws.services.s3.model.BucketTaggingConfiguration; -import com.amazonaws.services.s3.model.BucketVersioningConfiguration; -import com.amazonaws.services.s3.model.BucketWebsiteConfiguration; -import com.amazonaws.services.s3.model.CannedAccessControlList; -import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; -import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; -import com.amazonaws.services.s3.model.CopyObjectRequest; -import com.amazonaws.services.s3.model.CopyObjectResult; -import com.amazonaws.services.s3.model.CopyPartRequest; -import com.amazonaws.services.s3.model.CopyPartResult; -import com.amazonaws.services.s3.model.CreateBucketRequest; -import com.amazonaws.services.s3.model.DeleteBucketCrossOriginConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketLifecycleConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketPolicyRequest; -import com.amazonaws.services.s3.model.DeleteBucketReplicationConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketRequest; -import com.amazonaws.services.s3.model.DeleteBucketTaggingConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketWebsiteConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteObjectRequest; -import com.amazonaws.services.s3.model.DeleteObjectsRequest; -import com.amazonaws.services.s3.model.DeleteObjectsResult; -import com.amazonaws.services.s3.model.DeleteVersionRequest; -import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; -import com.amazonaws.services.s3.model.GenericBucketRequest; -import com.amazonaws.services.s3.model.GetBucketAccelerateConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketAclRequest; -import com.amazonaws.services.s3.model.GetBucketLocationRequest; -import com.amazonaws.services.s3.model.GetBucketPolicyRequest; -import com.amazonaws.services.s3.model.GetBucketReplicationConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketWebsiteConfigurationRequest; -import com.amazonaws.services.s3.model.GetObjectMetadataRequest; -import com.amazonaws.services.s3.model.GetObjectRequest; -import com.amazonaws.services.s3.model.GetRequestPaymentConfigurationRequest; -import com.amazonaws.services.s3.model.Grant; -import com.amazonaws.services.s3.model.Grantee; -import com.amazonaws.services.s3.model.GroupGrantee; -import com.amazonaws.services.s3.model.HeadBucketRequest; -import com.amazonaws.services.s3.model.HeadBucketResult; -import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; -import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; -import com.amazonaws.services.s3.model.ListBucketsRequest; -import com.amazonaws.services.s3.model.ListMultipartUploadsRequest; -import com.amazonaws.services.s3.model.ListObjectsRequest; -import com.amazonaws.services.s3.model.ListObjectsV2Request; -import com.amazonaws.services.s3.model.ListObjectsV2Result; -import com.amazonaws.services.s3.model.ListPartsRequest; -import com.amazonaws.services.s3.model.ListVersionsRequest; -import com.amazonaws.services.s3.model.MultiFactorAuthentication; -import com.amazonaws.services.s3.model.MultiObjectDeleteException; -import com.amazonaws.services.s3.model.MultipartUploadListing; -import com.amazonaws.services.s3.model.ObjectListing; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.Owner; -import com.amazonaws.services.s3.model.PartListing; -import com.amazonaws.services.s3.model.Permission; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.PutObjectResult; -import com.amazonaws.services.s3.model.Region; -import com.amazonaws.services.s3.model.RequestPaymentConfiguration; +import com.amazonaws.services.s3.model.*; import com.amazonaws.services.s3.model.RequestPaymentConfiguration.Payer; -import com.amazonaws.services.s3.model.ResponseHeaderOverrides; -import com.amazonaws.services.s3.model.RestoreObjectRequest; -import com.amazonaws.services.s3.model.S3AccelerateUnsupported; -import com.amazonaws.services.s3.model.S3Object; -import com.amazonaws.services.s3.model.S3ObjectInputStream; -import com.amazonaws.services.s3.model.SSECustomerKey; -import com.amazonaws.services.s3.model.SetBucketAccelerateConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketAclRequest; -import com.amazonaws.services.s3.model.SetBucketCrossOriginConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketLifecycleConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketLoggingConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketNotificationConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketPolicyRequest; -import com.amazonaws.services.s3.model.SetBucketReplicationConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketTaggingConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketVersioningConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketWebsiteConfigurationRequest; -import com.amazonaws.services.s3.model.SetRequestPaymentConfigurationRequest; -import com.amazonaws.services.s3.model.StorageClass; -import com.amazonaws.services.s3.model.UploadPartRequest; -import com.amazonaws.services.s3.model.UploadPartResult; -import com.amazonaws.services.s3.model.VersionListing; +import com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration; +import com.amazonaws.services.s3.model.inventory.InventoryConfiguration; +import com.amazonaws.services.s3.model.metrics.MetricsConfiguration; import com.amazonaws.services.s3.model.transform.AclXmlFactory; import com.amazonaws.services.s3.model.transform.BucketConfigurationXmlFactory; +import com.amazonaws.services.s3.model.transform.BucketNotificationConfigurationStaxUnmarshaller; import com.amazonaws.services.s3.model.transform.HeadBucketResultHandler; import com.amazonaws.services.s3.model.transform.MultiObjectDeleteXmlFactory; +import com.amazonaws.services.s3.model.transform.ObjectTaggingXmlFactory; import com.amazonaws.services.s3.model.transform.RequestPaymentConfigurationXmlFactory; import com.amazonaws.services.s3.model.transform.RequestXmlFactory; import com.amazonaws.services.s3.model.transform.Unmarshallers; @@ -186,8 +109,10 @@ import com.amazonaws.util.BinaryUtils; import com.amazonaws.util.DateUtils; import com.amazonaws.util.HttpUtils; +import com.amazonaws.util.IOUtils; import com.amazonaws.util.LengthCheckInputStream; import com.amazonaws.util.Md5Utils; +import com.amazonaws.util.RuntimeHttpUtils; import com.amazonaws.util.ServiceClientHolderInputStream; import com.amazonaws.util.StringUtils; @@ -206,8 +131,10 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -236,6 +163,7 @@ * href="http://aws.amazon.com/s3"> http://aws.amazon.com/s3 *

*/ +@SuppressWarnings("deprecation") public class AmazonS3Client extends AmazonWebServiceClient implements AmazonS3 { public static final String S3_SERVICE_NAME = "s3"; @@ -285,6 +213,24 @@ public class AmazonS3Client extends AmazonWebServiceClient implements AmazonS3 { */ volatile String clientRegion; + private static final int BUCKET_REGION_CACHE_SIZE = 300; + + private static final Map bucketRegionCache = Collections.synchronizedMap( + new LinkedHashMap(BUCKET_REGION_CACHE_SIZE, 1.1f, true) { + private static final long serialVersionUID = 23453L; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > BUCKET_REGION_CACHE_SIZE; + } + }); + + static Map getBucketRegionCache() { + return bucketRegionCache; + } + + private final CompleteMultipartUploadRetryCondition completeMultipartUploadRetryCondition = new CompleteMultipartUploadRetryCondition(); + /** * Constructs a new client to invoke service methods on Amazon S3. A * credentials provider chain will be used that searches for credentials in @@ -478,7 +424,7 @@ private void init() { // calling this.setEndpoint(...) will also modify the signer accordingly setEndpoint(Constants.S3_HOSTNAME); - HandlerChainFactory chainFactory = new HandlerChainFactory(); + final HandlerChainFactory chainFactory = new HandlerChainFactory(); requestHandler2s.addAll(chainFactory.newRequestHandlerChain( "/com/amazonaws/services/s3/request.handlers")); requestHandler2s.addAll(chainFactory.newRequestHandler2Chain( @@ -536,12 +482,23 @@ public void setS3ClientOptions(S3ClientOptions clientOptions) { @Override public VersionListing listNextBatchOfVersions(VersionListing previousVersionListing) throws AmazonClientException, AmazonServiceException { + return listNextBatchOfVersions(new ListNextBatchOfVersionsRequest(previousVersionListing)); + } + + @Override + public VersionListing listNextBatchOfVersions( + ListNextBatchOfVersionsRequest listNextBatchOfVersionsRequest) + throws AmazonClientException, AmazonServiceException { + assertParameterNotNull( - previousVersionListing, - "The previous version listing parameter must be specified when listing the next batch of versions in a bucket"); + listNextBatchOfVersionsRequest, + "The request object parameter must be specified when listing the next batch of versions in a bucket"); + + final VersionListing previousVersionListing = listNextBatchOfVersionsRequest + .getPreviousVersionListing(); if (!previousVersionListing.isTruncated()) { - VersionListing emptyListing = new VersionListing(); + final VersionListing emptyListing = new VersionListing(); emptyListing.setBucketName(previousVersionListing.getBucketName()); emptyListing.setDelimiter(previousVersionListing.getDelimiter()); emptyListing.setKeyMarker(previousVersionListing.getNextKeyMarker()); @@ -554,14 +511,7 @@ public VersionListing listNextBatchOfVersions(VersionListing previousVersionList return emptyListing; } - return listVersions(new ListVersionsRequest( - previousVersionListing.getBucketName(), - previousVersionListing.getPrefix(), - previousVersionListing.getNextKeyMarker(), - previousVersionListing.getNextVersionIdMarker(), - previousVersionListing.getDelimiter(), - new Integer(previousVersionListing.getMaxKeys())) - .withEncodingType(previousVersionListing.getEncodingType())); + return listVersions(listNextBatchOfVersionsRequest.toListVersionsRequest()); } /* @@ -586,7 +536,7 @@ public VersionListing listVersions(String bucketName, String prefix, String keyM String versionIdMarker, String delimiter, Integer maxKeys) throws AmazonClientException, AmazonServiceException { - ListVersionsRequest request = new ListVersionsRequest() + final ListVersionsRequest request = new ListVersionsRequest() .withBucketName(bucketName) .withPrefix(prefix) .withDelimiter(delimiter) @@ -608,25 +558,36 @@ public VersionListing listVersions(ListVersionsRequest listVersionsRequest) assertParameterNotNull(listVersionsRequest.getBucketName(), "The bucket name parameter must be specified when listing versions in a bucket"); - Request request = createRequest(listVersionsRequest.getBucketName(), - null, listVersionsRequest, HttpMethodName.GET); + /** + * This flag shows whether we need to url decode S3 key names. This flag + * is enabled only when the customers don't explicitly call + * {@link listVersionsRequest#setEncodingType(String)}, otherwise, it + * will be disabled for maintaining backwards compatibility. + */ + final boolean shouldSDKDecodeResponse = listVersionsRequest.getEncodingType() == null; + + final Request request = createRequest( + listVersionsRequest.getBucketName(), null, listVersionsRequest, HttpMethodName.GET); request.addParameter("versions", null); - if (listVersionsRequest.getPrefix() != null) - request.addParameter("prefix", listVersionsRequest.getPrefix()); - if (listVersionsRequest.getKeyMarker() != null) - request.addParameter("key-marker", listVersionsRequest.getKeyMarker()); - if (listVersionsRequest.getVersionIdMarker() != null) - request.addParameter("version-id-marker", listVersionsRequest.getVersionIdMarker()); - if (listVersionsRequest.getDelimiter() != null) - request.addParameter("delimiter", listVersionsRequest.getDelimiter()); + addParameterIfNotNull(request, "prefix", + shouldSDKDecodeResponse ? HttpUtils.urlEncode(listVersionsRequest.getPrefix(), true) + : listVersionsRequest.getPrefix()); + addParameterIfNotNull(request, "key-marker", listVersionsRequest.getKeyMarker()); + addParameterIfNotNull(request, "version-id-marker", + listVersionsRequest.getVersionIdMarker()); + addParameterIfNotNull(request, "delimiter", + shouldSDKDecodeResponse + ? HttpUtils.urlEncode(listVersionsRequest.getDelimiter(), true) + : listVersionsRequest.getDelimiter()); + if (listVersionsRequest.getMaxResults() != null - && listVersionsRequest.getMaxResults().intValue() >= 0) + && listVersionsRequest.getMaxResults().intValue() >= 0) { request.addParameter("max-keys", listVersionsRequest.getMaxResults().toString()); - if (listVersionsRequest.getEncodingType() != null) - request.addParameter("encoding-type", listVersionsRequest.getEncodingType()); + } + addParameterIfNotNull(request, "encoding-type", listVersionsRequest.getEncodingType()); - return invoke(request, new Unmarshallers.VersionListUnmarshaller(), + return invoke(request, new Unmarshallers.VersionListUnmarshaller(shouldSDKDecodeResponse), listVersionsRequest.getBucketName(), null); } @@ -663,21 +624,33 @@ public ObjectListing listObjects(ListObjectsRequest listObjectsRequest) assertParameterNotNull(listObjectsRequest.getBucketName(), "The bucket name parameter must be specified when listing objects in a bucket"); - Request request = createRequest(listObjectsRequest.getBucketName(), - null, listObjectsRequest, HttpMethodName.GET); - if (listObjectsRequest.getPrefix() != null) - request.addParameter("prefix", listObjectsRequest.getPrefix()); - if (listObjectsRequest.getMarker() != null) - request.addParameter("marker", listObjectsRequest.getMarker()); - if (listObjectsRequest.getDelimiter() != null) - request.addParameter("delimiter", listObjectsRequest.getDelimiter()); + /** + * This flag shows whether we need to url decode S3 key names. This flag + * is enabled only when the customers don't explicitly call + * {@link ListObjectsRequest#setEncodingType(String)}, otherwise, it + * will be disabled for maintaining backwards compatibility. + */ + final boolean shouldSDKDecodeResponse = listObjectsRequest.getEncodingType() == null; + + // if the url is encoded, the prefix also has to be encoded. + final Request request = createRequest( + listObjectsRequest.getBucketName(), null, listObjectsRequest, HttpMethodName.GET); + + addParameterIfNotNull(request, "prefix", shouldSDKDecodeResponse + ? HttpUtils.urlEncode(listObjectsRequest.getPrefix(), true) + : listObjectsRequest.getPrefix()); + addParameterIfNotNull(request, "marker", listObjectsRequest.getMarker()); + addParameterIfNotNull(request, "delimiter", + shouldSDKDecodeResponse + ? HttpUtils.urlEncode(listObjectsRequest.getDelimiter(), true) + : listObjectsRequest.getDelimiter()); + addParameterIfNotNull(request, "encoding-type", listObjectsRequest.getEncodingType()); + if (listObjectsRequest.getMaxKeys() != null - && listObjectsRequest.getMaxKeys().intValue() >= 0) + && listObjectsRequest.getMaxKeys().intValue() >= 0) { request.addParameter("max-keys", listObjectsRequest.getMaxKeys().toString()); - if (listObjectsRequest.getEncodingType() != null) - request.addParameter("encoding-type", listObjectsRequest.getEncodingType()); - - return invoke(request, new Unmarshallers.ListObjectsUnmarshaller(), + } + return invoke(request, new Unmarshallers.ListObjectsUnmarshaller(shouldSDKDecodeResponse), listObjectsRequest.getBucketName(), null); } @@ -699,33 +672,31 @@ public ListObjectsV2Result listObjectsV2(ListObjectsV2Request listObjectsV2Reque throws AmazonClientException, AmazonServiceException { assertParameterNotNull(listObjectsV2Request.getBucketName(), "The bucket name parameter must be specified when listing objects in a bucket"); - Request request = createRequest(listObjectsV2Request.getBucketName(), - null, listObjectsV2Request, HttpMethodName.GET); - + final Request request = createRequest( + listObjectsV2Request.getBucketName(), null, listObjectsV2Request, + HttpMethodName.GET); /** * List type '2' is required to opt-in to listObjectsV2. */ request.addParameter("list-type", "2"); - if (listObjectsV2Request.getStartAfter() != null) - request.addParameter("start-after", listObjectsV2Request.getStartAfter()); - if (listObjectsV2Request.getContinuationToken() != null) - request.addParameter("continuation-token", + addParameterIfNotNull(request, "start-after", listObjectsV2Request.getStartAfter()); + addParameterIfNotNull(request, "continuation-token", listObjectsV2Request.getContinuationToken()); - if (listObjectsV2Request.getDelimiter() != null) - request.addParameter("delimiter", listObjectsV2Request.getDelimiter()); - if (listObjectsV2Request.getMaxKeys() != null - && listObjectsV2Request.getMaxKeys().intValue() >= 0) - request.addParameter("max-keys", listObjectsV2Request.getMaxKeys().toString()); - if (listObjectsV2Request.getPrefix() != null) - request.addParameter("prefix", listObjectsV2Request.getPrefix()); - if (listObjectsV2Request.getEncodingType() != null) - request.addParameter("encoding-type", listObjectsV2Request.getEncodingType()); - if (listObjectsV2Request.isFetchOwner()) - request.addParameter("fetch-owner", - Boolean.toString(listObjectsV2Request.isFetchOwner())); - - return invoke(request, new Unmarshallers.ListObjectsV2Unmarshaller(), + addParameterIfNotNull(request, "delimiter", listObjectsV2Request.getDelimiter()); + addParameterIfNotNull(request, "max-keys", listObjectsV2Request.getMaxKeys()); + addParameterIfNotNull(request, "prefix", listObjectsV2Request.getPrefix()); + addParameterIfNotNull(request, "encoding-type", listObjectsV2Request.getEncodingType()); + request.addParameter("fetch-owner", Boolean.toString(listObjectsV2Request.isFetchOwner())); + + /** + * If URL encoding has been requested from S3 we'll automatically decode + * the response. + */ + final boolean shouldSDKDecodeResponse = listObjectsV2Request + .getEncodingType() == Constants.URL_ENCODING; + + return invoke(request, new Unmarshallers.ListObjectsV2Unmarshaller(shouldSDKDecodeResponse), listObjectsV2Request.getBucketName(), null); } @@ -742,8 +713,20 @@ public ObjectListing listNextBatchOfObjects(ObjectListing previousObjectListing) previousObjectListing, "The previous object listing parameter must be specified when listing the next batch of objects in a bucket"); + return listNextBatchOfObjects(new ListNextBatchOfObjectsRequest(previousObjectListing)); + + } + + + @Override + public ObjectListing listNextBatchOfObjects( + ListNextBatchOfObjectsRequest listNextBatchOfObjectsRequest) + throws AmazonClientException, AmazonServiceException { + + final ObjectListing previousObjectListing = listNextBatchOfObjectsRequest.getPreviousObjectListing(); + if (!previousObjectListing.isTruncated()) { - ObjectListing emptyListing = new ObjectListing(); + final ObjectListing emptyListing = new ObjectListing(); emptyListing.setBucketName(previousObjectListing.getBucketName()); emptyListing.setDelimiter(previousObjectListing.getDelimiter()); emptyListing.setMarker(previousObjectListing.getNextMarker()); @@ -754,16 +737,10 @@ public ObjectListing listNextBatchOfObjects(ObjectListing previousObjectListing) return emptyListing; } - - return listObjects(new ListObjectsRequest( - previousObjectListing.getBucketName(), - previousObjectListing.getPrefix(), - previousObjectListing.getNextMarker(), - previousObjectListing.getDelimiter(), - new Integer(previousObjectListing.getMaxKeys())) - .withEncodingType(previousObjectListing.getEncodingType())); + return listObjects(listNextBatchOfObjectsRequest.toListObjectsRequest()); } + /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#getS3AccountOwner() @@ -771,7 +748,19 @@ public ObjectListing listNextBatchOfObjects(ObjectListing previousObjectListing) @Override public Owner getS3AccountOwner() throws AmazonClientException, AmazonServiceException { - Request request = createRequest(null, null, new ListBucketsRequest(), + + return getS3AccountOwner(new GetS3AccountOwnerRequest()); + + } + + + @Override + public Owner getS3AccountOwner(GetS3AccountOwnerRequest getS3AccountOwnerRequest) + throws AmazonClientException, AmazonServiceException { + assertParameterNotNull(getS3AccountOwnerRequest, + "The request object parameter getS3AccountOwnerRequest must be specified."); + final Request request = createRequest(null, null, + new ListBucketsRequest(), HttpMethodName.GET); return invoke(request, new Unmarshallers.ListBucketsOwnerUnmarshaller(), null, null); } @@ -783,7 +772,7 @@ public Owner getS3AccountOwner() @Override public List listBuckets(ListBucketsRequest listBucketsRequest) throws AmazonClientException, AmazonServiceException { - Request request = createRequest(null, null, listBucketsRequest, + final Request request = createRequest(null, null, listBucketsRequest, HttpMethodName.GET); return invoke(request, new Unmarshallers.ListBucketsUnmarshaller(), null, null); } @@ -809,11 +798,11 @@ public String getBucketLocation(GetBucketLocationRequest getBucketLocationReques throws AmazonClientException, AmazonServiceException { assertParameterNotNull(getBucketLocationRequest, "The request parameter must be specified when requesting a bucket's location"); - String bucketName = getBucketLocationRequest.getBucketName(); + final String bucketName = getBucketLocationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting a bucket's location"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, getBucketLocationRequest, HttpMethodName.GET); request.addParameter("location", null); @@ -880,11 +869,12 @@ public Bucket createBucket(CreateBucketRequest createBucketRequest) assertParameterNotNull(bucketName, "The bucket name parameter must be specified when creating a bucket"); - if (bucketName != null) + if (bucketName != null) { bucketName = bucketName.trim(); + } BucketNameUtils.validateBucketName(bucketName); - Request request = createRequest(bucketName, null, createBucketRequest, + final Request request = createRequest(bucketName, null, createBucketRequest, HttpMethodName.PUT); if (createBucketRequest.getAccessControlList() != null) { @@ -905,7 +895,7 @@ public Bucket createBucket(CreateBucketRequest createBucketRequest) region = RegionUtils .getRegionByEndpoint(this.endpoint.getHost()) .getName(); - } catch (IllegalArgumentException exception) { + } catch (final IllegalArgumentException exception) { // Endpoint does not correspond to a known region; send the // request with no location constraint and hope for the best. } @@ -917,12 +907,12 @@ public Bucket createBucket(CreateBucketRequest createBucketRequest) * creating a bucket in the US region. */ if (region != null && !StringUtils.upperCase(region).equals(Region.US_Standard.toString())) { - XmlWriter xml = new XmlWriter(); + final XmlWriter xml = new XmlWriter(); xml.start("CreateBucketConfiguration", "xmlns", Constants.XML_NAMESPACE); xml.start("LocationConstraint").value(region).end(); xml.end(); - byte[] bytes = xml.getBytes(); + final byte[] bytes = xml.getBytes(); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); } @@ -940,7 +930,7 @@ public Bucket createBucket(CreateBucketRequest createBucketRequest) @Override public AccessControlList getObjectAcl(String bucketName, String key) throws AmazonClientException, AmazonServiceException { - return getObjectAcl(bucketName, key, null); + return getObjectAcl(new GetObjectAclRequest(bucketName, key)); } /* @@ -951,12 +941,22 @@ public AccessControlList getObjectAcl(String bucketName, String key) @Override public AccessControlList getObjectAcl(String bucketName, String key, String versionId) throws AmazonClientException, AmazonServiceException { - assertParameterNotNull(bucketName, + return getObjectAcl(new GetObjectAclRequest(bucketName, key, versionId)); + } + + @Override + public AccessControlList getObjectAcl(GetObjectAclRequest getObjectAclRequest) + throws AmazonClientException, AmazonServiceException { + assertParameterNotNull(getObjectAclRequest, + "The request parameter must be specified when requesting an object's ACL"); + assertParameterNotNull(getObjectAclRequest.getBucketName(), "The bucket name parameter must be specified when requesting an object's ACL"); - assertParameterNotNull(key, + assertParameterNotNull(getObjectAclRequest.getKey(), "The key parameter must be specified when requesting an object's ACL"); - return getAcl(bucketName, key, versionId, null); + return getAcl(getObjectAclRequest.getBucketName(), getObjectAclRequest.getKey(), + getObjectAclRequest.getVersionId(), getObjectAclRequest.isRequesterPays(), + getObjectAclRequest); } @Override @@ -974,7 +974,7 @@ public void setObjectAcl(String bucketName, String key, CannedAccessControlList @Override public void setObjectAcl(String bucketName, String key, String versionId, AccessControlList acl) throws AmazonClientException, AmazonServiceException { - setObjectAcl0(bucketName, key, versionId, acl, null); + setObjectAcl(new SetObjectAclRequest(bucketName, key, versionId, acl)); } /** @@ -984,29 +984,15 @@ public void setObjectAcl(String bucketName, String key, String versionId, Access public void setObjectAcl(String bucketName, String key, String versionId, AccessControlList acl, RequestMetricCollector requestMetricCollector) throws AmazonClientException, AmazonServiceException { - setObjectAcl0(bucketName, key, versionId, acl, requestMetricCollector); - } - - private void setObjectAcl0(String bucketName, String key, String versionId, - AccessControlList acl, RequestMetricCollector requestMetricCollector) - throws AmazonClientException, AmazonServiceException { - assertParameterNotNull(bucketName, - "The bucket name parameter must be specified when setting an object's ACL"); - assertParameterNotNull(key, - "The key parameter must be specified when setting an object's ACL"); - assertParameterNotNull(acl, - "The ACL parameter must be specified when setting an object's ACL"); - - setAcl(bucketName, key, versionId, acl, - new GenericBucketRequest(bucketName) - .withRequestMetricCollector(requestMetricCollector)); + setObjectAcl(new SetObjectAclRequest(bucketName, key, versionId, acl) + . withRequestMetricCollector(requestMetricCollector)); } @Override public void setObjectAcl(String bucketName, String key, String versionId, CannedAccessControlList acl) throws AmazonClientException, AmazonServiceException { - setObjectAcl0(bucketName, key, versionId, acl, null); + setObjectAcl(new SetObjectAclRequest(bucketName, key, versionId, acl)); } /** @@ -1017,22 +1003,46 @@ public void setObjectAcl(String bucketName, String key, String versionId, public void setObjectAcl(String bucketName, String key, String versionId, CannedAccessControlList acl, RequestMetricCollector requestMetricCollector) { - setObjectAcl0(bucketName, key, versionId, acl, requestMetricCollector); + setObjectAcl(new SetObjectAclRequest(bucketName, key, versionId, acl) + . withRequestMetricCollector(requestMetricCollector)); } - private void setObjectAcl0(String bucketName, String key, String versionId, - CannedAccessControlList acl, RequestMetricCollector requestMetricCollector) + @Override + public void setObjectAcl(SetObjectAclRequest setObjectAclRequest) throws AmazonClientException, AmazonServiceException { - assertParameterNotNull(bucketName, + + assertParameterNotNull(setObjectAclRequest, + "The request must not be null."); + assertParameterNotNull(setObjectAclRequest.getBucketName(), "The bucket name parameter must be specified when setting an object's ACL"); - assertParameterNotNull(key, + assertParameterNotNull(setObjectAclRequest.getKey(), "The key parameter must be specified when setting an object's ACL"); - assertParameterNotNull(acl, - "The ACL parameter must be specified when setting an object's ACL"); - setAcl(bucketName, key, versionId, acl, - new GenericBucketRequest(bucketName) - .withRequestMetricCollector(requestMetricCollector)); + if (setObjectAclRequest.getAcl() != null && setObjectAclRequest.getCannedAcl() != null) { + throw new IllegalArgumentException( + "Only one of the ACL and CannedACL parameters can be specified, not both."); + } + + if (setObjectAclRequest.getAcl() != null) { + setAcl(setObjectAclRequest.getBucketName(), + setObjectAclRequest.getKey(), + setObjectAclRequest.getVersionId(), + setObjectAclRequest.getAcl(), + setObjectAclRequest.isRequesterPays(), + setObjectAclRequest); + + } else if (setObjectAclRequest.getCannedAcl() != null) { + setAcl(setObjectAclRequest.getBucketName(), + setObjectAclRequest.getKey(), + setObjectAclRequest.getVersionId(), + setObjectAclRequest.getCannedAcl(), + setObjectAclRequest.isRequesterPays(), + setObjectAclRequest); + + } else { + throw new IllegalArgumentException( + "At least one of the ACL and CannedACL parameters should be specified"); + } } /* @@ -1045,7 +1055,7 @@ public AccessControlList getBucketAcl(String bucketName) assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting a bucket's ACL"); - return getAcl(bucketName, null, null, null); + return getAcl(bucketName, null, null, false, null); } /* @@ -1057,11 +1067,11 @@ public AccessControlList getBucketAcl(String bucketName) @Override public AccessControlList getBucketAcl(GetBucketAclRequest getBucketAclRequest) throws AmazonClientException, AmazonServiceException { - String bucketName = getBucketAclRequest.getBucketName(); + final String bucketName = getBucketAclRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting a bucket's ACL"); - return getAcl(bucketName, null, null, getBucketAclRequest); + return getAcl(bucketName, null, null, false, getBucketAclRequest); } @Override @@ -1086,7 +1096,7 @@ private void setBucketAcl0(String bucketName, AccessControlList acl, assertParameterNotNull(acl, "The ACL parameter must be specified when setting a bucket's ACL"); - setAcl(bucketName, null, null, acl, + setAcl(bucketName, null, null, acl, false, new GenericBucketRequest(bucketName) .withRequestMetricCollector(requestMetricCollector)); } @@ -1094,16 +1104,16 @@ private void setBucketAcl0(String bucketName, AccessControlList acl, @Override public void setBucketAcl(SetBucketAclRequest setBucketAclRequest) throws AmazonClientException, AmazonServiceException { - String bucketName = setBucketAclRequest.getBucketName(); - AccessControlList acl = setBucketAclRequest.getAcl(); - CannedAccessControlList cannedAcl = setBucketAclRequest.getCannedAcl(); + final String bucketName = setBucketAclRequest.getBucketName(); + final AccessControlList acl = setBucketAclRequest.getAcl(); + final CannedAccessControlList cannedAcl = setBucketAclRequest.getCannedAcl(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting a bucket's ACL"); if (acl != null) { - setAcl(bucketName, null, null, acl, setBucketAclRequest); + setAcl(bucketName, null, null, acl, false, setBucketAclRequest); } else if (cannedAcl != null) { - setAcl(bucketName, null, null, cannedAcl, setBucketAclRequest); + setAcl(bucketName, null, null, cannedAcl, false, setBucketAclRequest); } else { assertParameterNotNull(null, "The ACL parameter must be specified when setting a bucket's ACL"); @@ -1134,7 +1144,7 @@ private void setBucketAcl0(String bucketName, CannedAccessControlList acl, assertParameterNotNull(acl, "The ACL parameter must be specified when setting a bucket's ACL"); - setAcl(bucketName, null, null, acl, + setAcl(bucketName, null, null, acl, false, new GenericBucketRequest(bucketName) .withRequestMetricCollector(col)); } @@ -1163,21 +1173,25 @@ public ObjectMetadata getObjectMetadata(GetObjectMetadataRequest getObjectMetada assertParameterNotNull(getObjectMetadataRequest, "The GetObjectMetadataRequest parameter must be specified when requesting an object's metadata"); - String bucketName = getObjectMetadataRequest.getBucketName(); - String key = getObjectMetadataRequest.getKey(); - String versionId = getObjectMetadataRequest.getVersionId(); + final String bucketName = getObjectMetadataRequest.getBucketName(); + final String key = getObjectMetadataRequest.getKey(); + final String versionId = getObjectMetadataRequest.getVersionId(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting an object's metadata"); assertParameterNotNull(key, "The key parameter must be specified when requesting an object's metadata"); - Request request = createRequest(bucketName, key, + final Request request = createRequest(bucketName, key, getObjectMetadataRequest, HttpMethodName.HEAD); - if (versionId != null) + if (versionId != null) { request.addParameter("versionId", versionId); + } + + populateRequesterPaysHeader(request, getObjectMetadataRequest.isRequesterPays()); + addPartNumberIfNotNull(request, getObjectMetadataRequest.getPartNumber()); - populateSseCpkRequestParameters(request, getObjectMetadataRequest.getSSECustomerKey()); + populateSSE_C(request, getObjectMetadataRequest.getSSECustomerKey()); return invoke(request, new S3MetadataResponseHandler(), bucketName, key); } @@ -1204,7 +1218,7 @@ public boolean doesBucketExist(String bucketName) try { headBucket(new HeadBucketRequest(bucketName)); return true; - } catch (AmazonServiceException ase) { + } catch (final AmazonServiceException ase) { // A redirect error or a forbidden error means the bucket exists. So // returning true. if ((ase.getStatusCode() == Constants.BUCKET_REDIRECT_STATUS_CODE) @@ -1225,7 +1239,7 @@ public boolean doesObjectExist(String bucketName, String objectName) try { getObjectMetadata(bucketName, objectName); return true; - } catch (AmazonS3Exception e) { + } catch (final AmazonS3Exception e) { if (e.getStatusCode() == 404) { return false; } @@ -1237,12 +1251,12 @@ public boolean doesObjectExist(String bucketName, String objectName) public HeadBucketResult headBucket(HeadBucketRequest headBucketRequest) throws AmazonClientException, AmazonServiceException { - String bucketName = headBucketRequest.getBucketName(); + final String bucketName = headBucketRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucketName parameter must be specified."); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, headBucketRequest, HttpMethodName.HEAD); return invoke(request, new HeadBucketResultHandler(), bucketName, null); } @@ -1303,7 +1317,7 @@ public S3Object getObject(GetObjectRequest getObjectRequest) assertParameterNotNull(getObjectRequest.getKey(), "The key parameter must be specified when requesting an object"); - Request request = createRequest(getObjectRequest.getBucketName(), + final Request request = createRequest(getObjectRequest.getBucketName(), getObjectRequest.getKey(), getObjectRequest, HttpMethodName.GET); if (getObjectRequest.getVersionId() != null) { @@ -1311,7 +1325,7 @@ public S3Object getObject(GetObjectRequest getObjectRequest) } // Range - long[] range = getObjectRequest.getRange(); + final long[] range = getObjectRequest.getRange(); if (range != null) { String rangeHeader = "bytes=" + Long.toString(range[0]) + "-"; if (range[1] >= 0) { @@ -1325,10 +1339,7 @@ public S3Object getObject(GetObjectRequest getObjectRequest) request.addHeader(Headers.RANGE, rangeHeader); } - if (getObjectRequest.isRequesterPays()) { - request.addHeader(Headers.REQUESTER_PAYS_HEADER, - Constants.REQUESTER_PAYS); - } + populateRequesterPaysHeader(request, getObjectRequest.isRequesterPays()); addResponseHeaderParameters(request, getObjectRequest.getResponseHeaders()); @@ -1342,19 +1353,19 @@ public S3Object getObject(GetObjectRequest getObjectRequest) getObjectRequest.getNonmatchingETagConstraints()); // Populate the SSE-CPK parameters to the request header - populateSseCpkRequestParameters(request, getObjectRequest.getSSECustomerKey()); + populateSSE_C(request, getObjectRequest.getSSECustomerKey()); /* * This is compatible with progress listener set by either the legacy * method GetObjectRequest#setProgressListener or the new method * GetObjectRequest#setGeneralProgressListener. */ - ProgressListener progressListener = getObjectRequest.getGeneralProgressListener(); - ProgressListenerCallbackExecutor progressListenerCallbackExecutor = ProgressListenerCallbackExecutor + final ProgressListener progressListener = getObjectRequest.getGeneralProgressListener(); + final ProgressListenerCallbackExecutor progressListenerCallbackExecutor = ProgressListenerCallbackExecutor .wrapListener(progressListener); try { - S3Object s3Object = invoke(request, new S3ObjectResponseHandler(), + final S3Object s3Object = invoke(request, new S3ObjectResponseHandler(), getObjectRequest.getBucketName(), getObjectRequest.getKey()); /* @@ -1376,6 +1387,7 @@ public S3Object getObject(GetObjectRequest getObjectRequest) // stream in a filter that will trigger progress reports. if (progressListenerCallbackExecutor != null) { @SuppressWarnings("resource") + final ProgressReportingInputStream progressReportingInputStream = new ProgressReportingInputStream( input, progressListenerCallbackExecutor); progressReportingInputStream.setFireCompletedEvent(true); @@ -1391,16 +1403,16 @@ public S3Object getObject(GetObjectRequest getObjectRequest) if (!ServiceUtils.skipMd5CheckPerRequest(getObjectRequest) && !ServiceUtils.skipMd5CheckPerResponse(s3Object.getObjectMetadata())) { byte[] serverSideHash = null; - String etag = s3Object.getObjectMetadata().getETag(); + final String etag = s3Object.getObjectMetadata().getETag(); if (etag != null && ServiceUtils.isMultipartUploadETag(etag) == false) { serverSideHash = BinaryUtils.fromHex(s3Object.getObjectMetadata().getETag()); try { // No content length check is performed when the // MD5 check is enabled, since a correct MD5 check would // imply a correct content length. - MessageDigest digest = MessageDigest.getInstance("MD5"); + final MessageDigest digest = MessageDigest.getInstance("MD5"); input = new DigestValidationInputStream(input, digest, serverSideHash); - } catch (NoSuchAlgorithmException e) { + } catch (final NoSuchAlgorithmException e) { log.warn("No MD5 digest algorithm available. Unable to calculate " + "checksum and verify data integrity.", e); } @@ -1421,7 +1433,7 @@ public S3Object getObject(GetObjectRequest getObjectRequest) s3Object.setObjectContent(new S3ObjectInputStream(input)); return s3Object; - } catch (AmazonS3Exception ase) { + } catch (final AmazonS3Exception ase) { /* * If the request failed because one of the specified constraints * was not met (ex: matching ETag, modified since date, etc.), then @@ -1456,7 +1468,7 @@ public ObjectMetadata getObject(final GetObjectRequest getObjectRequest, File de if (getObjectRequest.getRange() != null && getObjectRequest.getRange()[0] > 0) { mode = ServiceUtils.APPEND_MODE; } - S3Object s3Object = ServiceUtils.retryableDownloadS3ObjectToFile(destinationFile, + final S3Object s3Object = ServiceUtils.retryableDownloadS3ObjectToFile(destinationFile, new ServiceUtils.RetryableS3DownloadTask() { @Override @@ -1471,8 +1483,9 @@ public boolean needIntegrityCheck() { }, mode); // getObject can return null if constraints were specified but not met - if (s3Object == null) + if (s3Object == null) { return null; + } return s3Object.getObjectMetadata(); } @@ -1499,13 +1512,14 @@ public void deleteBucket(DeleteBucketRequest deleteBucketRequest) assertParameterNotNull(deleteBucketRequest, "The DeleteBucketRequest parameter must be specified when deleting a bucket"); - String bucketName = deleteBucketRequest.getBucketName(); + final String bucketName = deleteBucketRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting a bucket"); - Request request = createRequest(bucketName, null, deleteBucketRequest, + final Request request = createRequest(bucketName, null, deleteBucketRequest, HttpMethodName.DELETE); invoke(request, voidResponseHandler, bucketName, null); + bucketRegionCache.remove(bucketName); } /* @@ -1545,8 +1559,8 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) assertParameterNotNull(putObjectRequest, "The PutObjectRequest parameter must be specified when uploading an object"); - String bucketName = putObjectRequest.getBucketName(); - String key = putObjectRequest.getKey(); + final String bucketName = putObjectRequest.getBucketName(); + final String key = putObjectRequest.getKey(); ObjectMetadata metadata = putObjectRequest.getMetadata(); InputStream input = putObjectRequest.getInputStream(); @@ -1555,12 +1569,13 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) * method PutObjectRequest#setProgressListener or the new method * PutObjectRequest#setGeneralProgressListener. */ - ProgressListener progressListener = putObjectRequest.getGeneralProgressListener(); - ProgressListenerCallbackExecutor progressListenerCallbackExecutor = ProgressListenerCallbackExecutor + final ProgressListener progressListener = putObjectRequest.getGeneralProgressListener(); + final ProgressListenerCallbackExecutor progressListenerCallbackExecutor = ProgressListenerCallbackExecutor .wrapListener(progressListener); - if (metadata == null) + if (metadata == null) { metadata = new ObjectMetadata(); + } assertParameterNotNull(bucketName, "The bucket name parameter must be specified when uploading an object"); @@ -1572,7 +1587,7 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) // If a file is specified for upload, we need to pull some additional // information from it to auto-configure a few options if (putObjectRequest.getFile() != null) { - File file = putObjectRequest.getFile(); + final File file = putObjectRequest.getFile(); // Always set the content length, even if it's already set metadata.setContentLength(file.length()); @@ -1585,9 +1600,9 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) if (calculateMD5 && !skipContentMd5Check) { try { - String contentMd5_b64 = Md5Utils.md5AsBase64(file); + final String contentMd5_b64 = Md5Utils.md5AsBase64(file); metadata.setContentMD5(contentMd5_b64); - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException( "Unable to calculate MD5 hash: " + e.getMessage(), e); } @@ -1595,12 +1610,12 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) try { input = new RepeatableFileInputStream(file); - } catch (FileNotFoundException fnfe) { + } catch (final FileNotFoundException fnfe) { throw new AmazonClientException("Unable to find file to upload", fnfe); } } - Request request = createRequest(bucketName, key, putObjectRequest, + final Request request = createRequest(bucketName, key, putObjectRequest, HttpMethodName.PUT); if (putObjectRequest.getAccessControlList() != null) { @@ -1622,7 +1637,7 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) } // Populate the SSE-CPK parameters to the request header - populateSseCpkRequestParameters(request, putObjectRequest.getSSECustomerKey()); + populateSSE_C(request, putObjectRequest.getSSECustomerKey()); // Use internal interface to differentiate 0 from unset. final Long contentLength = (Long) metadata.getRawMetadataValue(Headers.CONTENT_LENGTH); @@ -1638,11 +1653,12 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) log.warn("No content length specified for stream data. " + "Stream contents will be buffered in memory and could result in " + "out of memory errors."); - ByteArrayInputStream bais = toByteArray(input); + final ByteArrayInputStream bais = toByteArray(input); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bais.available())); + request.setStreaming(true); input = bais; } else { - long len = calculateContentLength(input); + final long len = calculateContentLength(input); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(len)); } } else { @@ -1654,6 +1670,7 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) // plain-text data stream which in turn may have been wrapped // with it's own length check input stream.) @SuppressWarnings("resource") + final LengthCheckInputStream lcis = new LengthCheckInputStream( input, expectedLength, // expected data length to be uploaded @@ -1689,6 +1706,8 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) } populateRequestMetadata(request, metadata); + populateSSE_KMS(request, putObjectRequest.getSSEAwsKeyManagementParams()); + request.setContent(input); /* * Enable 100-continue support for PUT operations, since this is where @@ -1697,19 +1716,22 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) * do this for all operations since it will cause extra latency in the * network interaction. */ - request.addHeader("Expect", "100-continue"); - + /* + * HttpUrlConnection seems to be buggy in terms of implementation of + * expect continue. + */ + // request.addHeader("Expect", "100-continue"); ObjectMetadata returnedMetadata = null; try { returnedMetadata = invoke(request, new S3MetadataResponseHandler(), bucketName, key); - } catch (AmazonClientException ace) { + } catch (final AmazonClientException ace) { fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.FAILED_EVENT_CODE); throw ace; } finally { try { input.close(); - } catch (AbortedException ignore) { - } catch (Exception e) { + } catch (final AbortedException ignore) { + } catch (final Exception e) { log.debug("Unable to cleanly close input stream: " + e.getMessage(), e); } } @@ -1720,8 +1742,8 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) } if (returnedMetadata != null && contentMd5 != null && !skipContentMd5Check) { - byte[] clientSideHash = BinaryUtils.fromBase64(contentMd5); - byte[] serverSideHash = BinaryUtils.fromHex(returnedMetadata.getETag()); + final byte[] clientSideHash = BinaryUtils.fromBase64(contentMd5); + final byte[] serverSideHash = BinaryUtils.fromHex(returnedMetadata.getETag()); if (!Arrays.equals(clientSideHash, serverSideHash)) { fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.FAILED_EVENT_CODE); @@ -1737,16 +1759,16 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.COMPLETED_EVENT_CODE); - PutObjectResult result = new PutObjectResult(); - result.setETag(returnedMetadata.getETag()); + final PutObjectResult result = new PutObjectResult(); result.setVersionId(returnedMetadata.getVersionId()); result.setSSEAlgorithm(returnedMetadata.getSSEAlgorithm()); - result.setSSEKMSKeyId(returnedMetadata.getSSEKMSKeyId()); result.setSSECustomerAlgorithm(returnedMetadata.getSSECustomerAlgorithm()); result.setSSECustomerKeyMd5(returnedMetadata.getSSECustomerKeyMd5()); result.setExpirationTime(returnedMetadata.getExpirationTime()); result.setExpirationTimeRuleId(returnedMetadata.getExpirationTimeRuleId()); - result.setContentMd5(contentMd5); + result.setETag(returnedMetadata.getETag()); + result.setMetadata(returnedMetadata); + result.setRequesterCharged(returnedMetadata.isRequesterCharged()); return result; } @@ -1759,7 +1781,7 @@ public PutObjectResult putObject(PutObjectRequest putObjectRequest) */ private long calculateContentLength(InputStream is) { long len = 0; - byte[] buf = new byte[8 * 1024]; + final byte[] buf = new byte[8 * 1024]; int read; is.mark(-1); try { @@ -1767,7 +1789,7 @@ private long calculateContentLength(InputStream is) { len += read; } is.reset(); - } catch (IOException ioe) { + } catch (final IOException ioe) { throw new AmazonClientException("Could not calculate content length.", ioe); } return len; @@ -1778,24 +1800,25 @@ private long calculateContentLength(InputStream is) { */ private static void addAclHeaders(Request request, AccessControlList acl) { - Set grants = acl.getGrants(); - Map> grantsByPermission = new HashMap>(); - for (Grant grant : grants) { + final Set grants = acl.getGrants(); + final Map> grantsByPermission = new HashMap>(); + for (final Grant grant : grants) { if (!grantsByPermission.containsKey(grant.getPermission())) { grantsByPermission.put(grant.getPermission(), new LinkedList()); } grantsByPermission.get(grant.getPermission()).add(grant.getGrantee()); } - for (Permission permission : Permission.values()) { + for (final Permission permission : Permission.values()) { if (grantsByPermission.containsKey(permission)) { - Collection grantees = grantsByPermission.get(permission); + final Collection grantees = grantsByPermission.get(permission); boolean seenOne = false; - StringBuilder granteeString = new StringBuilder(); - for (Grantee grantee : grantees) { - if (!seenOne) + final StringBuilder granteeString = new StringBuilder(); + for (final Grantee grantee : grantees) { + if (!seenOne) { seenOne = true; - else + } else { granteeString.append(", "); + } granteeString.append(grantee.getTypeIdentifier()).append("=").append("\"") .append(grantee.getIdentifier()).append("\""); } @@ -1835,13 +1858,17 @@ public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) assertParameterNotNull(copyObjectRequest.getDestinationKey(), "The destination object key must be specified when copying an object"); - String destinationKey = copyObjectRequest.getDestinationKey(); - String destinationBucketName = copyObjectRequest.getDestinationBucketName(); + final String destinationKey = copyObjectRequest.getDestinationKey(); + final String destinationBucketName = copyObjectRequest.getDestinationBucketName(); - Request request = createRequest(destinationBucketName, destinationKey, + final Request request = createRequest(destinationBucketName, destinationKey, copyObjectRequest, HttpMethodName.PUT); populateRequestWithCopyObjectParameters(request, copyObjectRequest); + + // Populate the SSE AWS KMS parameters to the request header + populateSSE_KMS(request, + copyObjectRequest.getSSEAwsKeyManagementParams()); /* * We can't send a non-zero length Content-Length header if the user * specified it, otherwise it messes up the HTTP connection when the @@ -1851,16 +1878,17 @@ public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) CopyObjectResultHandler copyObjectResultHandler = null; try { @SuppressWarnings("unchecked") + final ResponseHeaderHandlerChain handler = new ResponseHeaderHandlerChain( // xml payload unmarshaller new Unmarshallers.CopyObjectUnmarshaller(), // header handlers new ServerSideEncryptionHeaderHandler(), - new S3VersionHeaderHandler(), - new ObjectExpirationHeaderHandler()); - copyObjectResultHandler = invoke(request, handler, destinationBucketName, - destinationKey); - } catch (AmazonS3Exception ase) { + new S3VersionHeaderHandler(), + new ObjectExpirationHeaderHandler(), + new S3RequesterChargedHeaderHandler()); + copyObjectResultHandler = invoke(request, handler, destinationBucketName, destinationKey); + } catch (final AmazonS3Exception ase) { /* * If the request failed because one of the specified constraints * was not met (ex: matching ETag, modified since date, etc.), then @@ -1886,12 +1914,12 @@ public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) * it's probably overkill for this one special case. */ if (copyObjectResultHandler.getErrorCode() != null) { - String errorCode = copyObjectResultHandler.getErrorCode(); - String errorMessage = copyObjectResultHandler.getErrorMessage(); - String requestId = copyObjectResultHandler.getErrorRequestId(); - String hostId = copyObjectResultHandler.getErrorHostId(); + final String errorCode = copyObjectResultHandler.getErrorCode(); + final String errorMessage = copyObjectResultHandler.getErrorMessage(); + final String requestId = copyObjectResultHandler.getErrorRequestId(); + final String hostId = copyObjectResultHandler.getErrorHostId(); - AmazonS3Exception ase = new AmazonS3Exception(errorMessage); + final AmazonS3Exception ase = new AmazonS3Exception(errorMessage); ase.setErrorCode(errorCode); ase.setErrorType(ErrorType.Service); ase.setRequestId(requestId); @@ -1904,16 +1932,16 @@ public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) // TODO: Might be nice to create this in our custom // S3VersionHeaderHandler - CopyObjectResult copyObjectResult = new CopyObjectResult(); + final CopyObjectResult copyObjectResult = new CopyObjectResult(); copyObjectResult.setETag(copyObjectResultHandler.getETag()); copyObjectResult.setLastModifiedDate(copyObjectResultHandler.getLastModified()); copyObjectResult.setVersionId(copyObjectResultHandler.getVersionId()); copyObjectResult.setSSEAlgorithm(copyObjectResultHandler.getSSEAlgorithm()); - copyObjectResult.setSSEKMSKeyId(copyObjectResultHandler.getSSEKMSKeyId()); copyObjectResult.setSSECustomerAlgorithm(copyObjectResultHandler.getSSECustomerAlgorithm()); copyObjectResult.setSSECustomerKeyMd5(copyObjectResultHandler.getSSECustomerKeyMd5()); copyObjectResult.setExpirationTime(copyObjectResultHandler.getExpirationTime()); copyObjectResult.setExpirationTimeRuleId(copyObjectResultHandler.getExpirationTimeRuleId()); + copyObjectResult.setRequesterCharged(copyObjectResultHandler.isRequesterCharged()); return copyObjectResult; } @@ -1958,10 +1986,10 @@ public CopyPartResult copyPart(CopyPartRequest copyPartRequest) { assertParameterNotNull(copyPartRequest.getPartNumber(), "The part number must be specified when copying a part"); - String destinationKey = copyPartRequest.getDestinationKey(); - String destinationBucketName = copyPartRequest.getDestinationBucketName(); + final String destinationKey = copyPartRequest.getDestinationKey(); + final String destinationBucketName = copyPartRequest.getDestinationBucketName(); - Request request = createRequest(destinationBucketName, destinationKey, + final Request request = createRequest(destinationBucketName, destinationKey, copyPartRequest, HttpMethodName.PUT); @@ -1979,15 +2007,16 @@ public CopyPartResult copyPart(CopyPartRequest copyPartRequest) { CopyObjectResultHandler copyObjectResultHandler = null; try { @SuppressWarnings("unchecked") + final ResponseHeaderHandlerChain handler = new ResponseHeaderHandlerChain( // xml payload unmarshaller new Unmarshallers.CopyObjectUnmarshaller(), // header handlers new ServerSideEncryptionHeaderHandler(), - new S3VersionHeaderHandler()); + new S3VersionHeaderHandler()); copyObjectResultHandler = invoke(request, handler, destinationBucketName, destinationKey); - } catch (AmazonS3Exception ase) { + } catch (final AmazonS3Exception ase) { /* * If the request failed because one of the specified constraints * was not met (ex: matching ETag, modified since date, etc.), then @@ -2013,12 +2042,12 @@ public CopyPartResult copyPart(CopyPartRequest copyPartRequest) { * it's probably overkill for this one special case. */ if (copyObjectResultHandler.getErrorCode() != null) { - String errorCode = copyObjectResultHandler.getErrorCode(); - String errorMessage = copyObjectResultHandler.getErrorMessage(); - String requestId = copyObjectResultHandler.getErrorRequestId(); - String hostId = copyObjectResultHandler.getErrorHostId(); + final String errorCode = copyObjectResultHandler.getErrorCode(); + final String errorMessage = copyObjectResultHandler.getErrorMessage(); + final String requestId = copyObjectResultHandler.getErrorRequestId(); + final String hostId = copyObjectResultHandler.getErrorHostId(); - AmazonS3Exception ase = new AmazonS3Exception(errorMessage); + final AmazonS3Exception ase = new AmazonS3Exception(errorMessage); ase.setErrorCode(errorCode); ase.setErrorType(ErrorType.Service); ase.setRequestId(requestId); @@ -2029,7 +2058,7 @@ public CopyPartResult copyPart(CopyPartRequest copyPartRequest) { throw ase; } - CopyPartResult copyPartResult = new CopyPartResult(); + final CopyPartResult copyPartResult = new CopyPartResult(); copyPartResult.setETag(copyObjectResultHandler.getETag()); copyPartResult.setPartNumber(copyPartRequest.getPartNumber()); copyPartResult.setLastModifiedDate(copyObjectResultHandler.getLastModified()); @@ -2069,7 +2098,7 @@ public void deleteObject(DeleteObjectRequest deleteObjectRequest) assertParameterNotNull(deleteObjectRequest.getKey(), "The key must be specified when deleting an object"); - Request request = createRequest(deleteObjectRequest.getBucketName(), + final Request request = createRequest(deleteObjectRequest.getBucketName(), deleteObjectRequest.getKey(), deleteObjectRequest, HttpMethodName.DELETE); invoke(request, voidResponseHandler, deleteObjectRequest.getBucketName(), deleteObjectRequest.getKey()); @@ -2083,7 +2112,7 @@ public void deleteObject(DeleteObjectRequest deleteObjectRequest) */ @Override public DeleteObjectsResult deleteObjects(DeleteObjectsRequest deleteObjectsRequest) { - Request request = createRequest(deleteObjectsRequest.getBucketName(), + final Request request = createRequest(deleteObjectsRequest.getBucketName(), null, deleteObjectsRequest, HttpMethodName.POST); request.addParameter("delete", null); @@ -2091,31 +2120,49 @@ public DeleteObjectsResult deleteObjects(DeleteObjectsRequest deleteObjectsReque populateRequestWithMfaDetails(request, deleteObjectsRequest.getMfa()); } - byte[] content = new MultiObjectDeleteXmlFactory() + populateRequesterPaysHeader(request, deleteObjectsRequest.isRequesterPays()); + + final byte[] content = new MultiObjectDeleteXmlFactory() .convertToXmlByteArray(deleteObjectsRequest); request.addHeader("Content-Length", String.valueOf(content.length)); request.addHeader("Content-Type", "application/xml"); request.setContent(new ByteArrayInputStream(content)); try { - byte[] md5 = Md5Utils.computeMD5Hash(content); - String md5Base64 = BinaryUtils.toBase64(md5); + final byte[] md5 = Md5Utils.computeMD5Hash(content); + final String md5Base64 = BinaryUtils.toBase64(md5); request.addHeader("Content-MD5", md5Base64); - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } - DeleteObjectsResponse response = invoke(request, + @SuppressWarnings("unchecked") + final + ResponseHeaderHandlerChain responseHandler = new ResponseHeaderHandlerChain( new Unmarshallers.DeleteObjectsResultUnmarshaller(), - deleteObjectsRequest.getBucketName(), null); + new S3RequesterChargedHeaderHandler()); + + final DeleteObjectsResponse response = invoke(request, responseHandler, deleteObjectsRequest.getBucketName(), null); /* * If the result was only partially successful, throw an exception */ - if (!response.getErrors().isEmpty()) { - throw new MultiObjectDeleteException(response.getErrors(), response.getDeletedObjects()); + if ( !response.getErrors().isEmpty() ) { + final Map headers = responseHandler.getResponseHeaders(); + + final MultiObjectDeleteException ex = new MultiObjectDeleteException( + response.getErrors(), + response.getDeletedObjects()); + + ex.setStatusCode(200); + ex.setRequestId(headers.get(Headers.REQUEST_ID)); + ex.setExtendedRequestId(headers.get(Headers.EXTENDED_REQUEST_ID)); + ex.setCloudFrontId(headers.get(Headers.CLOUD_FRONT_ID)); + + throw ex; } + final DeleteObjectsResult result = new DeleteObjectsResult(response.getDeletedObjects(), response.isRequesterCharged()); - return new DeleteObjectsResult(response.getDeletedObjects()); + return result; } /* @@ -2142,9 +2189,9 @@ public void deleteVersion(DeleteVersionRequest deleteVersionRequest) assertParameterNotNull(deleteVersionRequest, "The delete version request object must be specified when deleting a version"); - String bucketName = deleteVersionRequest.getBucketName(); - String key = deleteVersionRequest.getKey(); - String versionId = deleteVersionRequest.getVersionId(); + final String bucketName = deleteVersionRequest.getBucketName(); + final String key = deleteVersionRequest.getKey(); + final String versionId = deleteVersionRequest.getVersionId(); assertParameterNotNull(bucketName, "The bucket name must be specified when deleting a version"); @@ -2152,10 +2199,11 @@ public void deleteVersion(DeleteVersionRequest deleteVersionRequest) assertParameterNotNull(versionId, "The version ID must be specified when deleting a version"); - Request request = createRequest(bucketName, key, + final Request request = createRequest(bucketName, key, deleteVersionRequest, HttpMethodName.DELETE); - if (versionId != null) + if (versionId != null) { request.addParameter("versionId", versionId); + } if (deleteVersionRequest.getMfa() != null) { populateRequestWithMfaDetails(request, deleteVersionRequest.getMfa()); @@ -2178,8 +2226,8 @@ public void setBucketVersioningConfiguration( setBucketVersioningConfigurationRequest, "The SetBucketVersioningConfigurationRequest object must be specified when setting versioning configuration"); - String bucketName = setBucketVersioningConfigurationRequest.getBucketName(); - BucketVersioningConfiguration versioningConfiguration = setBucketVersioningConfigurationRequest + final String bucketName = setBucketVersioningConfigurationRequest.getBucketName(); + final BucketVersioningConfiguration versioningConfiguration = setBucketVersioningConfigurationRequest .getVersioningConfiguration(); assertParameterNotNull(bucketName, @@ -2192,7 +2240,7 @@ public void setBucketVersioningConfiguration( "The MFA parameter must be specified when changing MFA Delete status in the versioning configuration"); } - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, setBucketVersioningConfigurationRequest, HttpMethodName.PUT); request.addParameter("versioning", null); @@ -2203,7 +2251,7 @@ public void setBucketVersioningConfiguration( } } - byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(versioningConfiguration); + final byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(versioningConfiguration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); @@ -2219,11 +2267,22 @@ public void setBucketVersioningConfiguration( @Override public BucketVersioningConfiguration getBucketVersioningConfiguration(String bucketName) throws AmazonClientException, AmazonServiceException { + return getBucketVersioningConfiguration( + new GetBucketVersioningConfigurationRequest(bucketName)); + } + + @Override + public BucketVersioningConfiguration getBucketVersioningConfiguration( + GetBucketVersioningConfigurationRequest getBucketVersioningConfigurationRequest) + throws AmazonClientException, AmazonServiceException { + assertParameterNotNull(getBucketVersioningConfigurationRequest, + "The request object parameter getBucketVersioningConfigurationRequest must be specified."); + final String bucketName = getBucketVersioningConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when querying versioning configuration"); - Request request = createRequest(bucketName, null, - new GenericBucketRequest(bucketName), HttpMethodName.GET); + final Request request = createRequest(bucketName, + null, getBucketVersioningConfigurationRequest, HttpMethodName.GET); request.addParameter("versioning", null); return invoke(request, new Unmarshallers.BucketVersioningConfigurationUnmarshaller(), @@ -2252,12 +2311,12 @@ public BucketWebsiteConfiguration getBucketWebsiteConfiguration(String bucketNam public BucketWebsiteConfiguration getBucketWebsiteConfiguration( GetBucketWebsiteConfigurationRequest getBucketWebsiteConfigurationRequest) throws AmazonClientException, AmazonServiceException { - String bucketName = getBucketWebsiteConfigurationRequest.getBucketName(); + final String bucketName = getBucketWebsiteConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting a bucket's website configuration"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, getBucketWebsiteConfigurationRequest, HttpMethodName.GET); request.addParameter("website", null); request.addHeader("Content-Type", "application/xml"); @@ -2265,9 +2324,10 @@ public BucketWebsiteConfiguration getBucketWebsiteConfiguration( try { return invoke(request, new Unmarshallers.BucketWebsiteConfigurationUnmarshaller(), bucketName, null); - } catch (AmazonServiceException ase) { - if (ase.getStatusCode() == 404) + } catch (final AmazonServiceException ase) { + if (ase.getStatusCode() == 404) { return null; + } throw ase; } } @@ -2280,14 +2340,27 @@ public BucketWebsiteConfiguration getBucketWebsiteConfiguration( */ @Override public BucketLifecycleConfiguration getBucketLifecycleConfiguration(String bucketName) { - Request request = createRequest(bucketName, null, - new GenericBucketRequest(bucketName), HttpMethodName.GET); + return getBucketLifecycleConfiguration( + new GetBucketLifecycleConfigurationRequest(bucketName)); + } + + @Override + public BucketLifecycleConfiguration getBucketLifecycleConfiguration( + GetBucketLifecycleConfigurationRequest getBucketLifecycleConfigurationRequest) { + assertParameterNotNull(getBucketLifecycleConfigurationRequest, + "The request object pamameter getBucketLifecycleConfigurationRequest must be specified."); + final String bucketName = getBucketLifecycleConfigurationRequest.getBucketName(); + assertParameterNotNull(bucketName, + "The bucket name must be specifed when retrieving the bucket lifecycle configuration."); + + final Request request = createRequest(bucketName, + null, getBucketLifecycleConfigurationRequest, HttpMethodName.GET); request.addParameter("lifecycle", null); try { return invoke(request, new Unmarshallers.BucketLifecycleConfigurationUnmarshaller(), bucketName, null); - } catch (AmazonServiceException ase) { + } catch (final AmazonServiceException ase) { switch (ase.getStatusCode()) { case 404: return null; @@ -2323,8 +2396,8 @@ public void setBucketLifecycleConfiguration( assertParameterNotNull(setBucketLifecycleConfigurationRequest, "The set bucket lifecycle configuration request object must be specified."); - String bucketName = setBucketLifecycleConfigurationRequest.getBucketName(); - BucketLifecycleConfiguration bucketLifecycleConfiguration = setBucketLifecycleConfigurationRequest + final String bucketName = setBucketLifecycleConfigurationRequest.getBucketName(); + final BucketLifecycleConfiguration bucketLifecycleConfiguration = setBucketLifecycleConfigurationRequest .getLifecycleConfiguration(); assertParameterNotNull(bucketName, @@ -2333,20 +2406,20 @@ public void setBucketLifecycleConfiguration( bucketLifecycleConfiguration, "The lifecycle configuration parameter must be specified when setting bucket lifecycle configuration."); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, setBucketLifecycleConfigurationRequest, HttpMethodName.PUT); request.addParameter("lifecycle", null); - byte[] content = new BucketConfigurationXmlFactory() + final byte[] content = new BucketConfigurationXmlFactory() .convertToXmlByteArray(bucketLifecycleConfiguration); request.addHeader("Content-Length", String.valueOf(content.length)); request.addHeader("Content-Type", "application/xml"); request.setContent(new ByteArrayInputStream(content)); try { - byte[] md5 = Md5Utils.computeMD5Hash(content); - String md5Base64 = BinaryUtils.toBase64(md5); + final byte[] md5 = Md5Utils.computeMD5Hash(content); + final String md5Base64 = BinaryUtils.toBase64(md5); request.addHeader("Content-MD5", md5Base64); - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } @@ -2377,11 +2450,11 @@ public void deleteBucketLifecycleConfiguration( assertParameterNotNull(deleteBucketLifecycleConfigurationRequest, "The delete bucket lifecycle configuration request object must be specified."); - String bucketName = deleteBucketLifecycleConfigurationRequest.getBucketName(); + final String bucketName = deleteBucketLifecycleConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting bucket lifecycle configuration."); - Request request = createRequest(bucketName, + final Request request = createRequest(bucketName, null, deleteBucketLifecycleConfigurationRequest, HttpMethodName.DELETE); request.addParameter("lifecycle", null); @@ -2396,14 +2469,27 @@ public void deleteBucketLifecycleConfiguration( */ @Override public BucketCrossOriginConfiguration getBucketCrossOriginConfiguration(String bucketName) { - Request request = createRequest(bucketName, null, - new GenericBucketRequest(bucketName), HttpMethodName.GET); + return getBucketCrossOriginConfiguration( + new GetBucketCrossOriginConfigurationRequest(bucketName)); + } + + @Override + public BucketCrossOriginConfiguration getBucketCrossOriginConfiguration( + GetBucketCrossOriginConfigurationRequest getBucketCrossOriginConfigurationRequest) { + assertParameterNotNull(getBucketCrossOriginConfigurationRequest, + "The request object parameter getBucketCrossOriginConfigurationRequest must be specified."); + final String bucketName = getBucketCrossOriginConfigurationRequest.getBucketName(); + assertParameterNotNull(bucketName, + "The bucket name must be specified when retrieving the bucket cross origin configuration."); + + final Request request = createRequest(bucketName, + null, getBucketCrossOriginConfigurationRequest, HttpMethodName.GET); request.addParameter("cors", null); try { return invoke(request, new Unmarshallers.BucketCrossOriginConfigurationUnmarshaller(), bucketName, null); - } catch (AmazonServiceException ase) { + } catch (final AmazonServiceException ase) { switch (ase.getStatusCode()) { case 404: return null; @@ -2413,6 +2499,7 @@ public BucketCrossOriginConfiguration getBucketCrossOriginConfiguration(String b } } + /* * (non-Javadoc) * @see @@ -2439,8 +2526,8 @@ public void setBucketCrossOriginConfiguration( assertParameterNotNull(setBucketCrossOriginConfigurationRequest, "The set bucket cross origin configuration request object must be specified."); - String bucketName = setBucketCrossOriginConfigurationRequest.getBucketName(); - BucketCrossOriginConfiguration bucketCrossOriginConfiguration = setBucketCrossOriginConfigurationRequest + final String bucketName = setBucketCrossOriginConfigurationRequest.getBucketName(); + final BucketCrossOriginConfiguration bucketCrossOriginConfiguration = setBucketCrossOriginConfigurationRequest .getCrossOriginConfiguration(); assertParameterNotNull(bucketName, @@ -2449,20 +2536,20 @@ public void setBucketCrossOriginConfiguration( bucketCrossOriginConfiguration, "The cross origin configuration parameter must be specified when setting bucket cross origin configuration."); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, setBucketCrossOriginConfigurationRequest, HttpMethodName.PUT); request.addParameter("cors", null); - byte[] content = new BucketConfigurationXmlFactory() + final byte[] content = new BucketConfigurationXmlFactory() .convertToXmlByteArray(bucketCrossOriginConfiguration); request.addHeader("Content-Length", String.valueOf(content.length)); request.addHeader("Content-Type", "application/xml"); request.setContent(new ByteArrayInputStream(content)); try { - byte[] md5 = Md5Utils.computeMD5Hash(content); - String md5Base64 = BinaryUtils.toBase64(md5); + final byte[] md5 = Md5Utils.computeMD5Hash(content); + final String md5Base64 = BinaryUtils.toBase64(md5); request.addHeader("Content-MD5", md5Base64); - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } @@ -2494,11 +2581,11 @@ public void deleteBucketCrossOriginConfiguration( assertParameterNotNull(deleteBucketCrossOriginConfigurationRequest, "The delete bucket cross origin configuration request object must be specified."); - String bucketName = deleteBucketCrossOriginConfigurationRequest.getBucketName(); + final String bucketName = deleteBucketCrossOriginConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting bucket cross origin configuration."); - Request request = createRequest(bucketName, + final Request request = createRequest(bucketName, null, deleteBucketCrossOriginConfigurationRequest, HttpMethodName.DELETE); request.addParameter("cors", null); invoke(request, voidResponseHandler, bucketName, null); @@ -2512,14 +2599,26 @@ public void deleteBucketCrossOriginConfiguration( */ @Override public BucketTaggingConfiguration getBucketTaggingConfiguration(String bucketName) { - Request request = createRequest(bucketName, null, - new GenericBucketRequest(bucketName), HttpMethodName.GET); + return getBucketTaggingConfiguration(new GetBucketTaggingConfigurationRequest(bucketName)); + } + + @Override + public BucketTaggingConfiguration getBucketTaggingConfiguration( + GetBucketTaggingConfigurationRequest getBucketTaggingConfigurationRequest) { + assertParameterNotNull(getBucketTaggingConfigurationRequest, + "The request object parameter getBucketTaggingConfigurationRequest must be specifed."); + final String bucketName = getBucketTaggingConfigurationRequest.getBucketName(); + assertParameterNotNull(bucketName, + "The bucket name must be specified when retrieving the bucket tagging configuration."); + + final Request request = createRequest(bucketName, + null, getBucketTaggingConfigurationRequest, HttpMethodName.GET); request.addParameter("tagging", null); try { return invoke(request, new Unmarshallers.BucketTaggingConfigurationUnmarshaller(), bucketName, null); - } catch (AmazonServiceException ase) { + } catch (final AmazonServiceException ase) { switch (ase.getStatusCode()) { case 404: return null; @@ -2529,6 +2628,7 @@ public BucketTaggingConfiguration getBucketTaggingConfiguration(String bucketNam } } + /* * (non-Javadoc) * @see @@ -2555,8 +2655,8 @@ public void setBucketTaggingConfiguration( assertParameterNotNull(setBucketTaggingConfigurationRequest, "The set bucket tagging configuration request object must be specified."); - String bucketName = setBucketTaggingConfigurationRequest.getBucketName(); - BucketTaggingConfiguration bucketTaggingConfiguration = setBucketTaggingConfigurationRequest + final String bucketName = setBucketTaggingConfigurationRequest.getBucketName(); + final BucketTaggingConfiguration bucketTaggingConfiguration = setBucketTaggingConfigurationRequest .getTaggingConfiguration(); assertParameterNotNull(bucketName, @@ -2564,20 +2664,20 @@ public void setBucketTaggingConfiguration( assertParameterNotNull(bucketTaggingConfiguration, "The tagging configuration parameter must be specified when setting bucket tagging configuration."); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, setBucketTaggingConfigurationRequest, HttpMethodName.PUT); request.addParameter("tagging", null); - byte[] content = new BucketConfigurationXmlFactory() + final byte[] content = new BucketConfigurationXmlFactory() .convertToXmlByteArray(bucketTaggingConfiguration); request.addHeader("Content-Length", String.valueOf(content.length)); request.addHeader("Content-Type", "application/xml"); request.setContent(new ByteArrayInputStream(content)); try { - byte[] md5 = Md5Utils.computeMD5Hash(content); - String md5Base64 = BinaryUtils.toBase64(md5); + final byte[] md5 = Md5Utils.computeMD5Hash(content); + final String md5Base64 = BinaryUtils.toBase64(md5); request.addHeader("Content-MD5", md5Base64); - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } @@ -2607,11 +2707,11 @@ public void deleteBucketTaggingConfiguration( assertParameterNotNull(deleteBucketTaggingConfigurationRequest, "The delete bucket tagging configuration request object must be specified."); - String bucketName = deleteBucketTaggingConfigurationRequest.getBucketName(); + final String bucketName = deleteBucketTaggingConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting bucket tagging configuration."); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, deleteBucketTaggingConfigurationRequest, HttpMethodName.DELETE); request.addParameter("tagging", null); @@ -2642,8 +2742,8 @@ public void setBucketWebsiteConfiguration(String bucketName, public void setBucketWebsiteConfiguration( SetBucketWebsiteConfigurationRequest setBucketWebsiteConfigurationRequest) throws AmazonClientException, AmazonServiceException { - String bucketName = setBucketWebsiteConfigurationRequest.getBucketName(); - BucketWebsiteConfiguration configuration = setBucketWebsiteConfigurationRequest + final String bucketName = setBucketWebsiteConfigurationRequest.getBucketName(); + final BucketWebsiteConfiguration configuration = setBucketWebsiteConfigurationRequest .getConfiguration(); assertParameterNotNull(bucketName, @@ -2657,12 +2757,12 @@ public void setBucketWebsiteConfiguration( "The bucket website configuration parameter must specify the index document suffix when setting a bucket's website configuration"); } - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, setBucketWebsiteConfigurationRequest, HttpMethodName.PUT); request.addParameter("website", null); request.addHeader("Content-Type", "application/xml"); - byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(configuration); + final byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(configuration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); @@ -2691,12 +2791,12 @@ public void deleteBucketWebsiteConfiguration(String bucketName) public void deleteBucketWebsiteConfiguration( DeleteBucketWebsiteConfigurationRequest deleteBucketWebsiteConfigurationRequest) throws AmazonClientException, AmazonServiceException { - String bucketName = deleteBucketWebsiteConfigurationRequest.getBucketName(); + final String bucketName = deleteBucketWebsiteConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting a bucket's website configuration"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, deleteBucketWebsiteConfigurationRequest, HttpMethodName.DELETE); request.addParameter("website", null); request.addHeader("Content-Type", "application/xml"); @@ -2734,8 +2834,8 @@ public void setBucketNotificationConfiguration( assertParameterNotNull(setBucketNotificationConfigurationRequest, "The set bucket notification configuration request object must be specified."); - String bucketName = setBucketNotificationConfigurationRequest.getBucketName(); - BucketNotificationConfiguration bucketNotificationConfiguration = setBucketNotificationConfigurationRequest + final String bucketName = setBucketNotificationConfigurationRequest.getBucketName(); + final BucketNotificationConfiguration bucketNotificationConfiguration = setBucketNotificationConfigurationRequest .getNotificationConfiguration(); assertParameterNotNull(bucketName, @@ -2744,11 +2844,11 @@ public void setBucketNotificationConfiguration( bucketNotificationConfiguration, "The notification configuration parameter must be specified when setting bucket notification configuration."); - Request request = createRequest(bucketName, + final Request request = createRequest(bucketName, null, setBucketNotificationConfigurationRequest, HttpMethodName.PUT); request.addParameter("notification", null); - byte[] bytes = bucketConfigurationXmlFactory + final byte[] bytes = bucketConfigurationXmlFactory .convertToXmlByteArray(bucketNotificationConfiguration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); @@ -2756,6 +2856,15 @@ public void setBucketNotificationConfiguration( invoke(request, voidResponseHandler, bucketName, null); } + @Override + public BucketNotificationConfiguration getBucketNotificationConfiguration(String bucketName) + throws AmazonClientException, AmazonServiceException { + assertParameterNotNull(bucketName, + "The bucket name parameter must be specified when querying notification configuration"); + return getBucketNotificationConfiguration( + new GetBucketNotificationConfigurationRequest(bucketName)); + } + /* * (non-Javadoc) * @see @@ -2763,16 +2872,19 @@ public void setBucketNotificationConfiguration( * (java.lang.String) */ @Override - public BucketNotificationConfiguration getBucketNotificationConfiguration(String bucketName) + public BucketNotificationConfiguration getBucketNotificationConfiguration( + GetBucketNotificationConfigurationRequest getBucketNotificationConfigurationRequest) throws AmazonClientException, AmazonServiceException { + + final String bucketName = getBucketNotificationConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, - "The bucket name parameter must be specified when querying notification configuration"); + "The bucket request must specify a bucket name when querying notification configuration"); - Request request = createRequest(bucketName, null, - new GenericBucketRequest(bucketName), HttpMethodName.GET); + final Request request = createRequest(bucketName, + null, getBucketNotificationConfigurationRequest, HttpMethodName.GET); request.addParameter("notification", null); - return invoke(request, new Unmarshallers.BucketNotificationConfigurationUnmarshaller(), + return invoke(request, BucketNotificationConfigurationStaxUnmarshaller.getInstance(), bucketName, null); } @@ -2788,12 +2900,22 @@ public BucketLoggingConfiguration getBucketLoggingConfiguration(String bucketNam assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting a bucket's logging status"); - Request request = createRequest(bucketName, null, - new GenericBucketRequest(bucketName), HttpMethodName.GET); - request.addParameter("logging", null); + return getBucketLoggingConfiguration(new GetBucketLoggingConfigurationRequest(bucketName)); + } - return invoke(request, new Unmarshallers.BucketLoggingConfigurationnmarshaller(), - bucketName, null); + @Override + public BucketLoggingConfiguration getBucketLoggingConfiguration( + GetBucketLoggingConfigurationRequest getBucketLoggingConfigurationRequest) + throws AmazonClientException, AmazonServiceException { + assertParameterNotNull(getBucketLoggingConfigurationRequest, + "The bucket logging configuration"); + final String bucketName = getBucketLoggingConfigurationRequest.getBucketName(); + final Request request = createRequest(bucketName, + null, getBucketLoggingConfigurationRequest, HttpMethodName.GET); + request.addParameter("logging", null); + return invoke(request, + new Unmarshallers.BucketLoggingConfigurationnmarshaller(), + getBucketLoggingConfigurationRequest.getBucketName(), null); } /* @@ -2810,8 +2932,8 @@ public void setBucketLoggingConfiguration( setBucketLoggingConfigurationRequest, "The set bucket logging configuration request object must be specified when enabling server access logging"); - String bucketName = setBucketLoggingConfigurationRequest.getBucketName(); - BucketLoggingConfiguration loggingConfiguration = setBucketLoggingConfigurationRequest + final String bucketName = setBucketLoggingConfigurationRequest.getBucketName(); + final BucketLoggingConfiguration loggingConfiguration = setBucketLoggingConfigurationRequest .getLoggingConfiguration(); assertParameterNotNull(bucketName, @@ -2819,11 +2941,11 @@ public void setBucketLoggingConfiguration( assertParameterNotNull(loggingConfiguration, "The logging configuration parameter must be specified when enabling server access logging"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, setBucketLoggingConfigurationRequest, HttpMethodName.PUT); request.addParameter("logging", null); - byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(loggingConfiguration); + final byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(loggingConfiguration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); @@ -2845,11 +2967,11 @@ public BucketAccelerateConfiguration getBucketAccelerateConfiguration( assertParameterNotNull(getBucketAccelerateConfigurationRequest, "getBucketAccelerateConfigurationRequest must be specified."); - String bucketName = getBucketAccelerateConfigurationRequest.getBucketName(); + final String bucketName = getBucketAccelerateConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when querying accelerate configuration"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, getBucketAccelerateConfigurationRequest, HttpMethodName.GET); request.addParameter("accelerate", null); @@ -2873,8 +2995,8 @@ public void setBucketAccelerateConfiguration( assertParameterNotNull(setBucketAccelerateConfigurationRequest, "setBucketAccelerateConfigurationRequest must be specified"); - String bucketName = setBucketAccelerateConfigurationRequest.getBucketName(); - BucketAccelerateConfiguration accelerateConfiguration = setBucketAccelerateConfigurationRequest + final String bucketName = setBucketAccelerateConfigurationRequest.getBucketName(); + final BucketAccelerateConfiguration accelerateConfiguration = setBucketAccelerateConfigurationRequest .getAccelerateConfiguration(); assertParameterNotNull(bucketName, @@ -2884,12 +3006,12 @@ public void setBucketAccelerateConfiguration( assertParameterNotNull(accelerateConfiguration.getStatus(), "The status parameter must be specified when updating bucket accelerate configuration."); - Request request = createRequest( + final Request request = createRequest( bucketName, null, setBucketAccelerateConfigurationRequest, HttpMethodName.PUT); request.addParameter("accelerate", null); - byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(accelerateConfiguration); + final byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(accelerateConfiguration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); @@ -2919,10 +3041,10 @@ public void setBucketPolicy(String bucketName, String policyText) assertParameterNotNull(policyText, "The policy text must be specified when setting a bucket policy"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, new GenericBucketRequest(bucketName), HttpMethodName.PUT); request.addParameter("policy", null); - byte[] bytes = ServiceUtils.toByteArray(policyText); + final byte[] bytes = ServiceUtils.toByteArray(policyText); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); @@ -2953,28 +3075,29 @@ public BucketPolicy getBucketPolicy( assertParameterNotNull(getBucketPolicyRequest, "The request object must be specified when getting a bucket policy"); - String bucketName = getBucketPolicyRequest.getBucketName(); + final String bucketName = getBucketPolicyRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name must be specified when getting a bucket policy"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, getBucketPolicyRequest, HttpMethodName.GET); request.addParameter("policy", null); - BucketPolicy result = new BucketPolicy(); + final BucketPolicy result = new BucketPolicy(); try { - String policyText = invoke(request, new S3StringResponseHandler(), bucketName, null); + final String policyText = invoke(request, new S3StringResponseHandler(), bucketName, null); result.setPolicyText(policyText); return result; - } catch (AmazonServiceException ase) { + } catch (final AmazonServiceException ase) { /* * If we receive an error response telling us that no policy has * been set for this bucket, then instead of forcing the user to * deal with the exception, we'll just return an empty result. Any * other exceptions will be rethrown for the user to handle. */ - if (ase.getErrorCode().equals("NoSuchBucketPolicy")) + if (ase.getErrorCode().equals("NoSuchBucketPolicy")) { return result; + } throw ase; } } @@ -2991,18 +3114,18 @@ public void setBucketPolicy(SetBucketPolicyRequest setBucketPolicyRequest) assertParameterNotNull(setBucketPolicyRequest, "The request object must be specified when setting a bucket policy"); - String bucketName = setBucketPolicyRequest.getBucketName(); - String policyText = setBucketPolicyRequest.getPolicyText(); + final String bucketName = setBucketPolicyRequest.getBucketName(); + final String policyText = setBucketPolicyRequest.getPolicyText(); assertParameterNotNull(bucketName, "The bucket name must be specified when setting a bucket policy"); assertParameterNotNull(policyText, "The policy text must be specified when setting a bucket policy"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, setBucketPolicyRequest, HttpMethodName.PUT); request.addParameter("policy", null); - byte[] bytes = ServiceUtils.toByteArray(policyText); + final byte[] bytes = ServiceUtils.toByteArray(policyText); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); @@ -3022,11 +3145,11 @@ public void deleteBucketPolicy( assertParameterNotNull(deleteBucketPolicyRequest, "The request object must be specified when deleting a bucket policy"); - String bucketName = deleteBucketPolicyRequest.getBucketName(); + final String bucketName = deleteBucketPolicyRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name must be specified when deleting a bucket policy"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, deleteBucketPolicyRequest, HttpMethodName.DELETE); request.addParameter("policy", null); @@ -3055,7 +3178,7 @@ public URL generatePresignedUrl(String bucketName, String key, Date expiration) public URL generatePresignedUrl(String bucketName, String key, Date expiration, HttpMethod method) throws AmazonClientException { - GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, key, + final GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, key, method); request.setExpiration(expiration); @@ -3074,8 +3197,8 @@ public URL generatePresignedUrl(GeneratePresignedUrlRequest generatePresignedUrl assertParameterNotNull(generatePresignedUrlRequest, "The request parameter must be specified when generating a pre-signed URL"); - String bucketName = generatePresignedUrlRequest.getBucketName(); - String key = generatePresignedUrlRequest.getKey(); + final String bucketName = generatePresignedUrlRequest.getBucketName(); + final String key = generatePresignedUrlRequest.getKey(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when generating a pre-signed URL"); @@ -3087,7 +3210,7 @@ public URL generatePresignedUrl(GeneratePresignedUrlRequest generatePresignedUrl new Date(System.currentTimeMillis() + 1000 * 60 * 15)); } - HttpMethodName httpMethod = HttpMethodName.valueOf(generatePresignedUrlRequest.getMethod() + final HttpMethodName httpMethod = HttpMethodName.valueOf(generatePresignedUrlRequest.getMethod() .toString()); // If the key starts with a slash character itself, the following method @@ -3095,10 +3218,13 @@ public URL generatePresignedUrl(GeneratePresignedUrlRequest generatePresignedUrl // the HttpClient mistakenly treating the slash as a path delimiter. // For presigned request, we need to remember to remove this extra slash // before generating the URL. - Request request = createRequest(bucketName, key, + final Request request = createRequest(bucketName, key, generatePresignedUrlRequest, httpMethod); + if (generatePresignedUrlRequest.isZeroByteContent()) { + request.setContent(new ByteArrayInputStream(new byte[0])); + } - for (Entry entry : generatePresignedUrlRequest.getRequestParameters() + for (final Entry entry : generatePresignedUrlRequest.getRequestParameters() .entrySet()) { request.addParameter(entry.getKey(), entry.getValue()); } @@ -3111,11 +3237,19 @@ public URL generatePresignedUrl(GeneratePresignedUrlRequest generatePresignedUrl request.addHeader(Headers.CONTENT_MD5, generatePresignedUrlRequest.getContentMd5()); } - populateSseCpkRequestParameters(request, generatePresignedUrlRequest.getSSECustomerKey()); + // SSE-C + populateSSE_C(request, generatePresignedUrlRequest.getSSECustomerKey()); + // SSE + addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION, + generatePresignedUrlRequest.getSSEAlgorithm()); + // SSE-KMS + addHeaderIfNotNull(request, + Headers.SERVER_SIDE_ENCRYPTION_KMS_KEY_ID, + generatePresignedUrlRequest.getKmsCmkId()); addResponseHeaderParameters(request, generatePresignedUrlRequest.getResponseHeaders()); - Signer signer = createSigner(request, bucketName, key); + final Signer signer = createSigner(request, bucketName, key); if (signer instanceof Presigner) { // If we have a signer which knows how to presign requests, @@ -3159,12 +3293,13 @@ public void abortMultipartUpload(AbortMultipartUploadRequest abortMultipartUploa assertParameterNotNull(abortMultipartUploadRequest.getUploadId(), "The upload ID parameter must be specified when aborting a multipart upload"); - String bucketName = abortMultipartUploadRequest.getBucketName(); - String key = abortMultipartUploadRequest.getKey(); + final String bucketName = abortMultipartUploadRequest.getBucketName(); + final String key = abortMultipartUploadRequest.getKey(); - Request request = createRequest(bucketName, key, + final Request request = createRequest(bucketName, key, abortMultipartUploadRequest, HttpMethodName.DELETE); request.addParameter("uploadId", abortMultipartUploadRequest.getUploadId()); + populateRequesterPaysHeader(request, abortMultipartUploadRequest.isRequesterPays()); invoke(request, voidResponseHandler, bucketName, key); } @@ -3182,9 +3317,9 @@ public CompleteMultipartUploadResult completeMultipartUpload( assertParameterNotNull(completeMultipartUploadRequest, "The request parameter must be specified when completing a multipart upload"); - String bucketName = completeMultipartUploadRequest.getBucketName(); - String key = completeMultipartUploadRequest.getKey(); - String uploadId = completeMultipartUploadRequest.getUploadId(); + final String bucketName = completeMultipartUploadRequest.getBucketName(); + final String key = completeMultipartUploadRequest.getKey(); + final String uploadId = completeMultipartUploadRequest.getUploadId(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when completing a multipart upload"); assertParameterNotNull(key, @@ -3194,34 +3329,57 @@ public CompleteMultipartUploadResult completeMultipartUpload( assertParameterNotNull(completeMultipartUploadRequest.getPartETags(), "The part ETags parameter must be specified when completing a multipart upload"); - Request request = createRequest(bucketName, key, - completeMultipartUploadRequest, HttpMethodName.POST); - request.addParameter("uploadId", uploadId); + int retries = 0; + CompleteMultipartUploadHandler handler; + do { + final Request request = createRequest(bucketName, key, completeMultipartUploadRequest, HttpMethodName.POST); + request.addParameter("uploadId", uploadId); + + populateRequesterPaysHeader(request, completeMultipartUploadRequest.isRequesterPays()); - byte[] xml = RequestXmlFactory.convertToXmlByteArray(completeMultipartUploadRequest - .getPartETags()); - request.addHeader("Content-Type", "text/plain"); - request.addHeader("Content-Length", String.valueOf(xml.length)); + final byte[] xml = RequestXmlFactory.convertToXmlByteArray(completeMultipartUploadRequest.getPartETags()); + request.addHeader("Content-Type", "application/xml"); + request.addHeader("Content-Length", String.valueOf(xml.length)); request.setContent(new ByteArrayInputStream(xml)); - @SuppressWarnings("unchecked") - ResponseHeaderHandlerChain responseHandler = new ResponseHeaderHandlerChain( - // xml payload unmarshaller - new Unmarshallers.CompleteMultipartUploadResultUnmarshaller(), - // header handlers - new ServerSideEncryptionHeaderHandler(), - new ObjectExpirationHeaderHandler()); - CompleteMultipartUploadHandler handler = invoke(request, responseHandler, bucketName, key); - if (handler.getCompleteMultipartUploadResult() != null) { - String versionId = responseHandler.getResponseHeaders().get(Headers.S3_VERSION_ID); - handler.getCompleteMultipartUploadResult().setVersionId(versionId); - return handler.getCompleteMultipartUploadResult(); - } else { - throw handler.getAmazonS3Exception(); - } + @SuppressWarnings("unchecked") + final + ResponseHeaderHandlerChain responseHandler = new ResponseHeaderHandlerChain( + // xml payload unmarshaller + new Unmarshallers.CompleteMultipartUploadResultUnmarshaller(), + // header handlers + new ServerSideEncryptionHeaderHandler(), + new ObjectExpirationHeaderHandler(), + new S3VersionHeaderHandler(), + new S3RequesterChargedHeaderHandler()); + handler = invoke(request, responseHandler, bucketName, key); + if (handler.getCompleteMultipartUploadResult() != null) { + return handler.getCompleteMultipartUploadResult(); + } + } while (shouldRetryCompleteMultipartUpload(completeMultipartUploadRequest, + handler.getAmazonS3Exception(), retries++)); + + throw handler.getAmazonS3Exception(); } + private boolean shouldRetryCompleteMultipartUpload(AmazonWebServiceRequest originalRequest, + AmazonS3Exception exception, + int retriesAttempted) { + + final RetryPolicy retryPolicy = clientConfiguration.getRetryPolicy(); + + if (retryPolicy == null || retryPolicy.getRetryCondition() == null) { + return false; + } + + if (retryPolicy == PredefinedRetryPolicies.NO_RETRY_POLICY) { + return false; + } + + return completeMultipartUploadRetryCondition.shouldRetry + (originalRequest, exception, retriesAttempted); + } /* * (non-Javadoc) * @see @@ -3240,15 +3398,16 @@ public InitiateMultipartUploadResult initiateMultipartUpload( assertParameterNotNull(initiateMultipartUploadRequest.getKey(), "The key parameter must be specified when initiating a multipart upload"); - Request request = createRequest( + final Request request = createRequest( initiateMultipartUploadRequest.getBucketName(), initiateMultipartUploadRequest.getKey(), initiateMultipartUploadRequest, HttpMethodName.POST); request.addParameter("uploads", null); - if (initiateMultipartUploadRequest.getStorageClass() != null) + if (initiateMultipartUploadRequest.getStorageClass() != null) { request.addHeader(Headers.STORAGE_CLASS, initiateMultipartUploadRequest .getStorageClass().toString()); + } if (initiateMultipartUploadRequest.getRedirectLocation() != null) { request.addHeader(Headers.REDIRECT_LOCATION, @@ -3266,8 +3425,14 @@ public InitiateMultipartUploadResult initiateMultipartUpload( populateRequestMetadata(request, initiateMultipartUploadRequest.objectMetadata); } - // Populate the SSE-CPK parameters to the request header - populateSseCpkRequestParameters(request, initiateMultipartUploadRequest.getSSECustomerKey()); + populateRequesterPaysHeader(request, initiateMultipartUploadRequest.isRequesterPays()); + + // Populate the SSE-C parameters to the request header + populateSSE_C(request, initiateMultipartUploadRequest.getSSECustomerKey()); + + // Populate the SSE AWS KMS parameters to the request header + populateSSE_KMS(request, + initiateMultipartUploadRequest.getSSEAwsKeyManagementParams()); // Be careful that we don't send the object's total size as the content // length for the InitiateMultipartUpload request. @@ -3279,6 +3444,7 @@ public InitiateMultipartUploadResult initiateMultipartUpload( request.setContent(new ByteArrayInputStream(new byte[0])); @SuppressWarnings("unchecked") + final ResponseHeaderHandlerChain responseHandler = new ResponseHeaderHandlerChain( // xml payload unmarshaller new Unmarshallers.InitiateMultipartUploadResultUnmarshaller(), @@ -3305,25 +3471,31 @@ public MultipartUploadListing listMultipartUploads( assertParameterNotNull(listMultipartUploadsRequest.getBucketName(), "The bucket name parameter must be specified when listing multipart uploads"); - Request request = createRequest( + final Request request = createRequest( listMultipartUploadsRequest.getBucketName(), null, listMultipartUploadsRequest, HttpMethodName.GET); request.addParameter("uploads", null); - if (listMultipartUploadsRequest.getKeyMarker() != null) + if (listMultipartUploadsRequest.getKeyMarker() != null) { request.addParameter("key-marker", listMultipartUploadsRequest.getKeyMarker()); - if (listMultipartUploadsRequest.getMaxUploads() != null) + } + if (listMultipartUploadsRequest.getMaxUploads() != null) { request.addParameter("max-uploads", listMultipartUploadsRequest.getMaxUploads() .toString()); - if (listMultipartUploadsRequest.getUploadIdMarker() != null) + } + if (listMultipartUploadsRequest.getUploadIdMarker() != null) { request.addParameter("upload-id-marker", listMultipartUploadsRequest.getUploadIdMarker()); - if (listMultipartUploadsRequest.getDelimiter() != null) + } + if (listMultipartUploadsRequest.getDelimiter() != null) { request.addParameter("delimiter", listMultipartUploadsRequest.getDelimiter()); - if (listMultipartUploadsRequest.getPrefix() != null) + } + if (listMultipartUploadsRequest.getPrefix() != null) { request.addParameter("prefix", listMultipartUploadsRequest.getPrefix()); - if (listMultipartUploadsRequest.getEncodingType() != null) + } + if (listMultipartUploadsRequest.getEncodingType() != null) { request.addParameter("encoding-type", listMultipartUploadsRequest.getEncodingType()); + } return invoke(request, new Unmarshallers.ListMultipartUploadsResultUnmarshaller(), listMultipartUploadsRequest.getBucketName(), null); @@ -3348,18 +3520,23 @@ public PartListing listParts(ListPartsRequest listPartsRequest) assertParameterNotNull(listPartsRequest.getUploadId(), "The upload ID parameter must be specified when listing parts"); - Request request = createRequest(listPartsRequest.getBucketName(), + final Request request = createRequest(listPartsRequest.getBucketName(), listPartsRequest.getKey(), listPartsRequest, HttpMethodName.GET); request.addParameter("uploadId", listPartsRequest.getUploadId()); - if (listPartsRequest.getMaxParts() != null) + if (listPartsRequest.getMaxParts() != null) { request.addParameter("max-parts", listPartsRequest.getMaxParts().toString()); - if (listPartsRequest.getPartNumberMarker() != null) + } + if (listPartsRequest.getPartNumberMarker() != null) { request.addParameter("part-number-marker", listPartsRequest.getPartNumberMarker() .toString()); - if (listPartsRequest.getEncodingType() != null) + } + if (listPartsRequest.getEncodingType() != null) { request.addParameter("encoding-type", listPartsRequest.getEncodingType()); + } + + populateRequesterPaysHeader(request, listPartsRequest.isRequesterPays()); return invoke(request, new Unmarshallers.ListPartsResultUnmarshaller(), listPartsRequest.getBucketName(), listPartsRequest.getKey()); } @@ -3376,11 +3553,11 @@ public UploadPartResult uploadPart(UploadPartRequest uploadPartRequest) assertParameterNotNull(uploadPartRequest, "The request parameter must be specified when uploading a part"); - String bucketName = uploadPartRequest.getBucketName(); - String key = uploadPartRequest.getKey(); - String uploadId = uploadPartRequest.getUploadId(); - int partNumber = uploadPartRequest.getPartNumber(); - long partSize = uploadPartRequest.getPartSize(); + final String bucketName = uploadPartRequest.getBucketName(); + final String key = uploadPartRequest.getKey(); + final String uploadId = uploadPartRequest.getUploadId(); + final int partNumber = uploadPartRequest.getPartNumber(); + final long partSize = uploadPartRequest.getPartSize(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when uploading a part"); @@ -3393,17 +3570,26 @@ public UploadPartResult uploadPart(UploadPartRequest uploadPartRequest) assertParameterNotNull(partSize, "The part size parameter must be specified when uploading a part"); - Request request = createRequest(bucketName, key, uploadPartRequest, + final Request request = createRequest(bucketName, key, uploadPartRequest, HttpMethodName.PUT); request.addParameter("uploadId", uploadId); request.addParameter("partNumber", Integer.toString(partNumber)); + final ObjectMetadata objectMetadata = uploadPartRequest.getObjectMetadata(); + if (objectMetadata != null) { + populateRequestMetadata(request, objectMetadata); + } + addHeaderIfNotNull(request, Headers.CONTENT_MD5, uploadPartRequest.getMd5Digest()); request.addHeader(Headers.CONTENT_LENGTH, Long.toString(partSize)); - request.addHeader("Expect", "100-continue"); - + /* + * HttpUrlConnection seems to be buggy in terms of implementation of + * expect continue. + */ + // request.addHeader("Expect", "100-continue"); + populateRequesterPaysHeader(request, uploadPartRequest.isRequesterPays()); // Populate the SSE-CPK parameters to the request header - populateSseCpkRequestParameters(request, uploadPartRequest.getSSECustomerKey()); + populateSSE_C(request, uploadPartRequest.getSSECustomerKey()); InputStream inputStream = null; if (uploadPartRequest.getInputStream() != null) { @@ -3413,7 +3599,7 @@ public UploadPartResult uploadPart(UploadPartRequest uploadPartRequest) inputStream = new InputSubstream(new RepeatableFileInputStream( uploadPartRequest.getFile()), uploadPartRequest.getFileOffset(), partSize, true); - } catch (FileNotFoundException e) { + } catch (final FileNotFoundException e) { throw new IllegalArgumentException("The specified file doesn't exist", e); } } else { @@ -3438,8 +3624,8 @@ public UploadPartResult uploadPart(UploadPartRequest uploadPartRequest) * method UploadPartRequest#setProgressListener or the new method * UploadPartRequest#setGeneralProgressListener. */ - ProgressListener progressListener = uploadPartRequest.getGeneralProgressListener(); - ProgressListenerCallbackExecutor progressListenerCallbackExecutor = ProgressListenerCallbackExecutor + final ProgressListener progressListener = uploadPartRequest.getGeneralProgressListener(); + final ProgressListenerCallbackExecutor progressListenerCallbackExecutor = ProgressListenerCallbackExecutor .wrapListener(progressListener); if (progressListenerCallbackExecutor != null) { @@ -3451,13 +3637,13 @@ public UploadPartResult uploadPart(UploadPartRequest uploadPartRequest) try { request.setContent(inputStream); - ObjectMetadata metadata = invoke(request, new S3MetadataResponseHandler(), bucketName, + final ObjectMetadata metadata = invoke(request, new S3MetadataResponseHandler(), bucketName, key); if (metadata != null && md5DigestStream != null && !ServiceUtils.skipMd5CheckPerResponse(metadata)) { - byte[] clientSideHash = md5DigestStream.getMd5Digest(); - byte[] serverSideHash = BinaryUtils.fromHex(metadata.getETag()); + final byte[] clientSideHash = md5DigestStream.getMd5Digest(); + final byte[] serverSideHash = BinaryUtils.fromHex(metadata.getETag()); if (!Arrays.equals(clientSideHash, serverSideHash)) { throw new AmazonClientException( @@ -3472,15 +3658,15 @@ public UploadPartResult uploadPart(UploadPartRequest uploadPartRequest) fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.PART_COMPLETED_EVENT_CODE); - UploadPartResult result = new UploadPartResult(); + final UploadPartResult result = new UploadPartResult(); result.setETag(metadata.getETag()); result.setPartNumber(partNumber); result.setSSEAlgorithm(metadata.getSSEAlgorithm()); - result.setSSEKMSKeyId(metadata.getSSEKMSKeyId()); result.setSSECustomerAlgorithm(metadata.getSSECustomerAlgorithm()); result.setSSECustomerKeyMd5(metadata.getSSECustomerKeyMd5()); + result.setRequesterCharged(metadata.isRequesterCharged()); return result; - } catch (AmazonClientException ace) { + } catch (final AmazonClientException ace) { fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.PART_FAILED_EVENT_CODE); @@ -3495,7 +3681,7 @@ public UploadPartResult uploadPart(UploadPartRequest uploadPartRequest) if (inputStream != null) { try { inputStream.close(); - } catch (Exception e) { + } catch (final Exception e) { } } } @@ -3521,10 +3707,10 @@ public S3ResponseMetadata getCachedResponseMetadata(AmazonWebServiceRequest requ @Override public void restoreObject(RestoreObjectRequest restoreObjectRequest) throws AmazonServiceException { - String bucketName = restoreObjectRequest.getBucketName(); - String key = restoreObjectRequest.getKey(); - String versionId = restoreObjectRequest.getVersionId(); - int expirationIndays = restoreObjectRequest.getExpirationInDays(); + final String bucketName = restoreObjectRequest.getBucketName(); + final String key = restoreObjectRequest.getKey(); + final String versionId = restoreObjectRequest.getVersionId(); + final int expirationIndays = restoreObjectRequest.getExpirationInDays(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when copying a glacier object"); @@ -3535,22 +3721,24 @@ public void restoreObject(RestoreObjectRequest restoreObjectRequest) "The expiration in days parameter must be specified when copying a glacier object"); } - Request request = createRequest(bucketName, key, + final Request request = createRequest(bucketName, key, restoreObjectRequest, HttpMethodName.POST); request.addParameter("restore", null); if (versionId != null) { request.addParameter("versionId", versionId); } - byte[] content = RequestXmlFactory.convertToXmlByteArray(restoreObjectRequest); + populateRequesterPaysHeader(request, restoreObjectRequest.isRequesterPays()); + + final byte[] content = RequestXmlFactory.convertToXmlByteArray(restoreObjectRequest); request.addHeader("Content-Length", String.valueOf(content.length)); request.addHeader("Content-Type", "application/xml"); request.setContent(new ByteArrayInputStream(content)); try { - byte[] md5 = Md5Utils.computeMD5Hash(content); - String md5Base64 = BinaryUtils.toBase64(md5); + final byte[] md5 = Md5Utils.computeMD5Hash(content); + final String md5Base64 = BinaryUtils.toBase64(md5); request.addHeader("Content-MD5", md5Base64); - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } @@ -3574,22 +3762,6 @@ public void restoreObject(String bucketName, String key, int expirationInDays) * Private Interface */ - /** - *

- * Asserts that the specified parameter value is not null and - * if it is, throws an IllegalArgumentException with the - * specified error message. - *

- * - * @param parameterValue The parameter value being checked. - * @param errorMessage The error message to include in the - * IllegalArgumentException if the specified parameter is null. - */ - private void assertParameterNotNull(Object parameterValue, String errorMessage) { - if (parameterValue == null) - throw new IllegalArgumentException(errorMessage); - } - /** * Fires a progress event with the specified event type to the specified * listener. @@ -3600,9 +3772,10 @@ private void assertParameterNotNull(Object parameterValue, String errorMessage) private void fireProgressEvent( final ProgressListenerCallbackExecutor progressListenerCallbackExecutor, final int eventType) { - if (progressListenerCallbackExecutor == null) + if (progressListenerCallbackExecutor == null) { return; - ProgressEvent event = new ProgressEvent(0); + } + final ProgressEvent event = new ProgressEvent(0); event.setEventCode(eventType); progressListenerCallbackExecutor.progressChanged(event); } @@ -3625,15 +3798,18 @@ private void fireProgressEvent( * @return The S3 ACL for the specified resource. */ private AccessControlList getAcl(String bucketName, String key, String versionId, - AmazonWebServiceRequest originalRequest) { - if (originalRequest == null) + boolean isRequesterPays, AmazonWebServiceRequest originalRequest) { + if (originalRequest == null) { originalRequest = new GenericBucketRequest(bucketName); + } - Request request = createRequest(bucketName, key, originalRequest, + final Request request = createRequest(bucketName, key, originalRequest, HttpMethodName.GET); request.addParameter("acl", null); - if (versionId != null) + if (versionId != null) { request.addParameter("versionId", versionId); + } + populateRequesterPaysHeader(request, isRequesterPays); return invoke(request, new Unmarshallers.AccessControlListUnmarshaller(), bucketName, key); } @@ -3654,16 +3830,20 @@ private AccessControlList getAcl(String bucketName, String key, String versionId * @param originalRequest The original, user facing request object. */ private void setAcl(String bucketName, String key, String versionId, - CannedAccessControlList cannedAcl, AmazonWebServiceRequest originalRequest) { - if (originalRequest == null) + CannedAccessControlList cannedAcl, boolean isRequesterPays, + AmazonWebServiceRequest originalRequest) { + if (originalRequest == null) { originalRequest = new GenericBucketRequest(bucketName); + } - Request request = createRequest(bucketName, key, originalRequest, + final Request request = createRequest(bucketName, key, originalRequest, HttpMethodName.PUT); request.addParameter("acl", null); request.addHeader(Headers.S3_CANNED_ACL, cannedAcl.toString()); - if (versionId != null) + if (versionId != null) { request.addParameter("versionId", versionId); + } + populateRequesterPaysHeader(request, isRequesterPays); invoke(request, voidResponseHandler, bucketName, key); } @@ -3673,28 +3853,36 @@ private void setAcl(String bucketName, String key, String versionId, * specified, the ACL will be applied to the bucket, otherwise if bucketName * and key are specified, the ACL will be applied to the object. * - * @param bucketName The name of the bucket containing the specified key, or - * if no key is listed, the bucket whose ACL will be set. - * @param key The optional object key within the specified bucket whose ACL + * @param bucketName + * The name of the bucket containing the specified key, or if no + * key is listed, the bucket whose ACL will be set. + * @param key + * The optional object key within the specified bucket whose ACL * will be set. If not specified, the bucket ACL will be set. - * @param versionId The version ID of the object version whose ACL is being - * set. - * @param acl The ACL to apply to the resource. - * @param originalRequest The original, user facing request object. + * @param versionId + * The version ID of the object version whose ACL is being set. + * @param acl + * The ACL to apply to the resource. + * @param originalRequest + * The original, user facing request object. */ private void setAcl(String bucketName, String key, String versionId, AccessControlList acl, + boolean isRequesterPays, AmazonWebServiceRequest originalRequest) { - if (originalRequest == null) + if (originalRequest == null) { originalRequest = new GenericBucketRequest(bucketName); + } - Request request = createRequest(bucketName, key, originalRequest, + final Request request = createRequest(bucketName, key, originalRequest, HttpMethodName.PUT); request.addParameter("acl", null); - if (versionId != null) + if (versionId != null) { request.addParameter("versionId", versionId); + } + populateRequesterPaysHeader(request, isRequesterPays); - byte[] aclAsXml = new AclXmlFactory().convertToXmlByteArray(acl); - request.addHeader("Content-Type", "text/plain"); + final byte[] aclAsXml = new AclXmlFactory().convertToXmlByteArray(acl); + request.addHeader("Content-Type", "application/xml"); request.addHeader("Content-Length", String.valueOf(aclAsXml.length)); request.setContent(new ByteArrayInputStream(aclAsXml)); @@ -3708,84 +3896,105 @@ private void setAcl(String bucketName, String key, String versionId, AccessContr protected Signer createSigner(final Request request, final String bucketName, final String key) { - - Signer signer = getSigner(); - - if (upgradeToSigV4(request) && !(signer instanceof AWSS3V4Signer)) { - - AWSS3V4Signer v4Signer = new AWSS3V4Signer(); - - // Always set the service name; if the user has overridden it via - // setEndpoint(String, String, String), this will return the right - // value. Otherwise it will return "s3", which is an appropriate - // default. - v4Signer.setServiceName(getServiceNameIntern()); - - // If the user has set an authentication region override, pass it - // to the signer. Otherwise leave it null - the signer will parse - // region from the request endpoint. - - String regionOverride = getSignerRegionOverride(); - if (regionOverride == null) { - regionOverride = clientRegion; + // Instead of using request.getEndpoint() for this parameter, we use + // endpoint which is because + // in accelerate mode, the endpoint in request is regionless. We need + // the client-wide endpoint + // to fetch the region information and pick the correct signer. + final URI uri = clientOptions.isAccelerateModeEnabled() ? endpoint : request.getEndpoint(); + final Signer signer = getSignerByURI(uri); + + if (!isSignerOverridden()) { + if ((signer instanceof AWSS3V4Signer) && noExplicitRegionProvided(request)) { + + final String region = clientRegion == null ? bucketRegionCache.get(bucketName) + : clientRegion; + if (region != null) { + // If cache contains the region for the bucket, create an endpoint for the region and + // update the request with that endpoint. + resolveRequestEndpoint(request, bucketName, key, RuntimeHttpUtils.toUri(RegionUtils.getRegion(region).getServiceEndpoint(S3_SERVICE_NAME), clientConfiguration)); + + final AWSS3V4Signer v4Signer = (AWSS3V4Signer) signer; + v4Signer.setServiceName(getServiceNameIntern()); + v4Signer.setRegionName(region); + return v4Signer; + } else if (request.getOriginalRequest() instanceof GeneratePresignedUrlRequest) { + return createSigV2Signer(request, bucketName, key); + } } - if (regionOverride == null) { - throw new AmazonClientException( - "Signature Version 4 requires knowing the region of " - + "the bucket you're trying to access. You can " - + "configure a region by calling AmazonS3Client." - + "setRegion(Region) or AmazonS3Client.setEndpoint(" - + "String) with a region-specific endpoint such as " - + "\"s3-us-west-2.amazonaws.com\"."); - } else { + // use the signer override if provided, else see if you can get the + // signer from bucketreqion cache. + final String regionOverride = getSignerRegionOverride() == null + ? (clientRegion == null ? bucketRegionCache.get(bucketName) : clientRegion) + : getSignerRegionOverride(); + if (regionOverride != null) { + final AWSS3V4Signer v4Signer = new AWSS3V4Signer(); + v4Signer.setServiceName(getServiceNameIntern()); v4Signer.setRegionName(regionOverride); + return v4Signer; } - - return v4Signer; - } if (signer instanceof S3Signer) { - // The old S3Signer needs a method and path passed to its // constructor; if that's what we should use, getSigner() // will return a dummy instance and we need to create a // new one with the appropriate values for this request. - - String resourcePath = - "/" + - ((bucketName != null) ? bucketName + "/" : "") + - ((key != null) ? key : ""); - - return new S3Signer(request.getHttpMethod().toString(), - resourcePath); + return createSigV2Signer(request, bucketName, key); } return signer; } - private boolean upgradeToSigV4(Request request) { - - // User has said to always use SigV4 - this will fail if the user - // attempts to read from or write to a non-US-Standard bucket without - // explicitly setting the region. - - if (System.getProperty(SDKGlobalConfiguration - .ENFORCE_S3_SIGV4_SYSTEM_PROPERTY) != null) { - - return true; - } + /** + * Has signer been explicitly overriden in the configuration? + */ + private boolean isSignerOverridden() { + return clientConfiguration != null + && clientConfiguration.getSignerOverride() != null; + } - // Upgrade to Sigv4 if using non-default region. - if (!endpoint.getHost().endsWith(Constants.S3_HOSTNAME)) { - return true; + /** + *

+ * Returns true if the region required for signing could not be computed + * from the client or the request. + *

+ *

+ * This is the case when the standard endpoint is in use and neither an + * explicit region nor a signer override have been provided by the user. + *

+ */ + private boolean noExplicitRegionProvided(final Request request) { + return isStandardEndpoint(request.getEndpoint()) && + getSignerRegion() == null; + } + /** + * Return the region string that should be used for signing requests sent by + * this client. This method can only return null if both of the following + * are true: + * (a) the user has never specified a region via setRegion/configureRegion/setSignerRegionOverride + * (b) the user has specified a client endpoint that is known to be a global S3 endpoint + */ + private String getSignerRegion() { + String region = getSignerRegionOverride(); + if (region == null) { + region = clientRegion; } + return region; + } - // Go with the default (SigV4 only if we know we're talking to an - // endpoint that requires SigV4). + private boolean isStandardEndpoint(URI endpoint) { + return endpoint.getHost().endsWith(Constants.S3_HOSTNAME); + } - return false; + private S3Signer createSigV2Signer(final Request request, + final String bucketName, + final String key) { + final String resourcePath = "/" + + ((bucketName != null) ? bucketName + "/" : "") + + ((key != null) ? key : ""); + return new S3Signer(request.getHttpMethod().toString(), resourcePath); } /** @@ -3823,7 +4032,7 @@ protected void presignRequest(Request request, HttpMethod methodName, resourcePath = resourcePath.replaceAll("(?<=/)/", "%2F"); AWSCredentials credentials = awsCredentialsProvider.getCredentials(); - AmazonWebServiceRequest originalRequest = request.getOriginalRequest(); + final AmazonWebServiceRequest originalRequest = request.getOriginalRequest(); if (originalRequest != null && originalRequest.getRequestCredentials() != null) { credentials = originalRequest.getRequestCredentials(); } @@ -3837,7 +4046,7 @@ protected void presignRequest(Request request, HttpMethod methodName, // travels along // with the pre-signed URL when it's sent back to Amazon S3. if (request.getHeaders().containsKey(Headers.SECURITY_TOKEN)) { - String value = request.getHeaders().get(Headers.SECURITY_TOKEN); + final String value = request.getHeaders().get(Headers.SECURITY_TOKEN); request.addParameter(Headers.SECURITY_TOKEN, value); request.getHeaders().remove(Headers.SECURITY_TOKEN); } @@ -3845,7 +4054,7 @@ protected void presignRequest(Request request, HttpMethod methodName, private void beforeRequest(Request request) { if (requestHandler2s != null) { - for (RequestHandler2 requestHandler2 : requestHandler2s) { + for (final RequestHandler2 requestHandler2 : requestHandler2s) { requestHandler2.beforeRequest(request); } } @@ -3861,11 +4070,11 @@ private void beforeRequest(Request request) { * @return A new URI, creating from the current service endpoint URI and the * specified bucket. */ - private URI convertToVirtualHostEndpoint(String bucketName) { + private URI convertToVirtualHostEndpoint(URI endpoint, String bucketName) { try { return new URI(endpoint.getScheme() + "://" + bucketName + "." + endpoint.getAuthority()); - } catch (URISyntaxException e) { + } catch (final URISyntaxException e) { throw new IllegalArgumentException("Invalid bucket name: " + bucketName, e); } } @@ -3881,7 +4090,7 @@ private URI convertToVirtualHostEndpoint(String bucketName) { * in the request. */ protected static void populateRequestMetadata(Request request, ObjectMetadata metadata) { - Map rawMetadata = metadata.getRawMetadata(); + final Map rawMetadata = metadata.getRawMetadata(); if (rawMetadata.get(Headers.SERVER_SIDE_ENCRYPTION_KMS_KEY_ID) != null && !ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION.equals(rawMetadata @@ -3891,25 +4100,27 @@ protected static void populateRequestMetadata(Request request, ObjectMetadata } if (rawMetadata != null) { - for (Entry entry : rawMetadata.entrySet()) { + for (final Entry entry : rawMetadata.entrySet()) { request.addHeader(entry.getKey(), entry.getValue().toString()); } } - Date httpExpiresDate = metadata.getHttpExpiresDate(); + final Date httpExpiresDate = metadata.getHttpExpiresDate(); if (httpExpiresDate != null) { request.addHeader(Headers.EXPIRES, DateUtils.formatRFC822Date(httpExpiresDate)); } - Map userMetadata = metadata.getUserMetadata(); + final Map userMetadata = metadata.getUserMetadata(); if (userMetadata != null) { - for (Entry entry : userMetadata.entrySet()) { + for (final Entry entry : userMetadata.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); - if (key != null) + if (key != null) { key = key.trim(); - if (value != null) + } + if (value != null) { value = value.trim(); + } request.addHeader(Headers.S3_USER_METADATA_PREFIX + key, value); } } @@ -3928,12 +4139,13 @@ protected static void populateRequestMetadata(Request request, ObjectMetadata * @param mfa The Multi-Factor Authentication information. */ private void populateRequestWithMfaDetails(Request request, MultiFactorAuthentication mfa) { - if (mfa == null) + if (mfa == null) { return; + } - String endpoint = request.getEndpoint().toString(); + final String endpoint = request.getEndpoint().toString(); if (endpoint.startsWith("http://")) { - String httpsEndpoint = endpoint.replace("http://", "https://"); + final String httpsEndpoint = endpoint.replace("http://", "https://"); request.setEndpoint(URI.create(httpsEndpoint)); log.info("Overriding current endpoint to use HTTPS " + "as required by S3 for requests containing an MFA header"); @@ -3990,15 +4202,17 @@ private static void populateRequestWithCopyObjectParameters( request.addHeader(Headers.REDIRECT_LOCATION, copyObjectRequest.getRedirectLocation()); } - ObjectMetadata newObjectMetadata = copyObjectRequest.getNewObjectMetadata(); + populateRequesterPaysHeader(request, copyObjectRequest.isRequesterPays()); + + final ObjectMetadata newObjectMetadata = copyObjectRequest.getNewObjectMetadata(); if (newObjectMetadata != null) { request.addHeader(Headers.METADATA_DIRECTIVE, "REPLACE"); populateRequestMetadata(request, newObjectMetadata); } - // Populate the SSE-CPK parameters for the destination object - populateSourceSseCpkRequestParameters(request, copyObjectRequest.getSourceSSECustomerKey()); - populateSseCpkRequestParameters(request, copyObjectRequest.getDestinationSSECustomerKey()); + // Populate the SSE-C parameters for the destination object + populateSourceSSE_C(request, copyObjectRequest.getSourceSSECustomerKey()); + populateSSE_C(request, copyObjectRequest.getDestinationSSECustomerKey()); } /** @@ -4033,14 +4247,14 @@ private static void populateRequestWithCopyPartParameters(Request request, copyPartRequest.getNonmatchingETagConstraints()); if (copyPartRequest.getFirstByte() != null && copyPartRequest.getLastByte() != null) { - String range = "bytes=" + copyPartRequest.getFirstByte() + "-" + final String range = "bytes=" + copyPartRequest.getFirstByte() + "-" + copyPartRequest.getLastByte(); request.addHeader(Headers.COPY_PART_RANGE, range); } - // Populate the SSE-CPK parameters for the destination object - populateSourceSseCpkRequestParameters(request, copyPartRequest.getSourceSSECustomerKey()); - populateSseCpkRequestParameters(request, copyPartRequest.getDestinationSSECustomerKey()); + // Populate the SSE-C parameters for the destination object + populateSourceSSE_C(request, copyPartRequest.getSourceSSECustomerKey()); + populateSSE_C(request, copyPartRequest.getDestinationSSECustomerKey()); } /** @@ -4056,9 +4270,10 @@ private static void populateRequestWithCopyPartParameters(Request request, * @param sseCpkRequest The request object for an S3 operation that allows * server-side encryption using customer-provided keys. */ - private static void populateSseCpkRequestParameters(Request request, SSECustomerKey sseKey) { - if (sseKey == null) + private static void populateSSE_C(Request request, SSECustomerKey sseKey) { + if (sseKey == null) { return; + } addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM, sseKey.getAlgorithm()); @@ -4070,17 +4285,17 @@ private static void populateSseCpkRequestParameters(Request request, SSECusto // header, if the user didn't specify it in the metadata if (sseKey.getKey() != null && sseKey.getMd5() == null) { - String encryptionKey_b64 = sseKey.getKey(); - byte[] encryptionKey = Base64.decode(encryptionKey_b64); + final String encryptionKey_b64 = sseKey.getKey(); + final byte[] encryptionKey = Base64.decode(encryptionKey_b64); request.addHeader(Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, Md5Utils.md5AsBase64(encryptionKey)); } } - private static void populateSourceSseCpkRequestParameters(Request request, - SSECustomerKey sseKey) { - if (sseKey == null) + private static void populateSourceSSE_C(Request request, SSECustomerKey sseKey) { + if (sseKey == null) { return; + } // Populate the SSE-CPK parameters for the source object addHeaderIfNotNull(request, Headers.COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM, @@ -4093,20 +4308,49 @@ private static void populateSourceSseCpkRequestParameters(Request request, // header, if the user didn't specify it in the metadata if (sseKey.getKey() != null && sseKey.getMd5() == null) { - String encryptionKey_b64 = sseKey.getKey(); - byte[] encryptionKey = Base64.decode(encryptionKey_b64); + final String encryptionKey_b64 = sseKey.getKey(); + final byte[] encryptionKey = Base64.decode(encryptionKey_b64); request.addHeader(Headers.COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, Md5Utils.md5AsBase64(encryptionKey)); } } - /** + private static void populateSSE_KMS(Request request, + SSEAwsKeyManagementParams sseParams) { + + if (sseParams != null) { + addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION, + sseParams.getEncryption()); + addHeaderIfNotNull(request, + Headers.SERVER_SIDE_ENCRYPTION_KMS_KEY_ID, + sseParams.getAwsKmsKeyId()); + } + } + + /** + * Adds the part number to the specified request, if partNumber is not null. + * + * @param request + * The request to add the partNumber to. + * @param partNumber + * The part number to be added. + */ + private void addPartNumberIfNotNull(Request request, Integer partNumber) { + if (partNumber != null) { + request.addParameter("partNumber", partNumber.toString()); + } + } + + /** * Adds the specified header to the specified request, if the header value * is not null. * - * @param request The request to add the header to. - * @param header The header name. - * @param value The header value. + * @param request + * The request to add the header to. + * @param header + * The header name. + * @param value + * The header value. */ private static void addHeaderIfNotNull(Request request, String header, String value) { if (value != null) { @@ -4114,6 +4358,42 @@ private static void addHeaderIfNotNull(Request request, String header, String } } + /** + * Adds the specified parameter to the specified request, if the parameter + * value is not null. + * + * @param request + * The request to add the parameter to. + * @param paramName + * The parameter name. + * @param paramValue + * The parameter value. + */ + private static void addParameterIfNotNull(Request request, String paramName, + Integer paramValue) { + if (paramValue != null) { + addParameterIfNotNull(request, paramName, paramValue.toString()); + } + } + + /** + * Adds the specified parameter to the specified request, if the parameter + * value is not null. + * + * @param request + * The request to add the parameter to. + * @param paramName + * The parameter name. + * @param paramValue + * The parameter value. + */ + private static void addParameterIfNotNull(Request request, String paramName, + String paramValue) { + if (paramValue != null) { + request.addParameter(paramName, paramValue); + } + } + /** *

* Adds the specified date header in RFC 822 date format to the specified @@ -4195,7 +4475,7 @@ private static void addResponseHeaderParameters(Request request, public String getResourceUrl(String bucketName, String key) { try { return getUrl(bucketName, key).toString(); - } catch (Exception e) { + } catch (final Exception e) { return null; } } @@ -4214,18 +4494,20 @@ public String getResourceUrl(String bucketName, String key) { * @return A unique URL for the object stored in the specified bucket and * key. */ + @Override public URL getUrl(String bucketName, String key) { - Request request = new DefaultRequest(Constants.S3_SERVICE_NAME); - configRequest(request, bucketName, key); + final Request request = new DefaultRequest(Constants.S3_SERVICE_DISPLAY_NAME); + resolveRequestEndpoint(request, bucketName, key); return ServiceUtils.convertRequestToUrl(request); } + @Override public Region getRegion() { - String authority = super.endpoint.getAuthority(); + final String authority = super.endpoint.getAuthority(); if (Constants.S3_HOSTNAME.equals(authority)) { return Region.US_Standard; } - Matcher m = Region.S3_REGIONAL_ENDPOINT_PATTERN.matcher(authority); + final Matcher m = Region.S3_REGIONAL_ENDPOINT_PATTERN.matcher(authority); if (m.matches()) { return Region.fromValue(m.group(1)); } else { @@ -4254,75 +4536,30 @@ public Region getRegion() { */ protected Request createRequest(String bucketName, String key, X originalRequest, HttpMethodName httpMethod) { - Request request = new DefaultRequest(originalRequest, Constants.S3_SERVICE_NAME); - request.setHttpMethod(httpMethod); - configRequest(request, bucketName, key); - return request; + return createRequest(bucketName, key, originalRequest, httpMethod, null); } - /** - * Configure the given request with the specified bucket name and key. - * - * @return the request configured - */ - private void configRequest( - Request request, String bucketName, String key) { - /* - * If the underlying AmazonS3Client has enabled accelerate mode and the - * original request operation is accelerate mode supported, then the - * request will use the s3-accelerate endpoint to perform the - * operations. - */ + protected Request createRequest(String bucketName, + String key, X originalRequest, HttpMethodName httpMethod, URI endpoint) { + final Request request = new DefaultRequest(originalRequest, + Constants.S3_SERVICE_DISPLAY_NAME); + // If the underlying AmazonS3Client has enabled accelerate mode and the + // original request operation is accelerate mode supported, then the request + // will use the s3-accelerate endpoint to performs the operations. if (clientOptions.isAccelerateModeEnabled() - && !(request.getOriginalRequest() instanceof S3AccelerateUnsupported) - && BucketNameUtils.isDNSBucketName(bucketName)) { - request.setEndpoint(URI.create(clientConfiguration.getProtocol() + "://" - + bucketName + "." + Constants.S3_ACCELERATE_HOSTNAME)); - request.setResourcePath(key != null && key.startsWith("/") ? "/" + key : key); - } else if (!clientOptions.isPathStyleAccess() - && BucketNameUtils.isDNSBucketName(bucketName) - && !validIP(endpoint.getHost())) { - request.setEndpoint(convertToVirtualHostEndpoint(bucketName)); - /* - * If the key name starts with a slash character, in order to - * prevent it being treated as a path delimiter, we need to add - * another slash before the key name. {@see - * com.amazonaws.http.HttpRequestFactory#createHttpRequest} - */ - if (key != null && key.startsWith("/")) { - key = "/" + key; - } - request.setResourcePath(key); - } else { - request.setEndpoint(endpoint); - - if (bucketName != null) { - request.setResourcePath(bucketName + "/" + (key != null ? key : "")); + && !(request.getOriginalRequest() instanceof S3AccelerateUnsupported)) { + if (clientOptions.isDualstackEnabled()) { + endpoint = RuntimeHttpUtils.toUri(Constants.S3_ACCELERATE_DUALSTACK_HOSTNAME, + clientConfiguration); + } else { + endpoint = RuntimeHttpUtils.toUri(Constants.S3_ACCELERATE_HOSTNAME, + clientConfiguration); } } - } - private boolean validIP(String IP) { - if (IP == null) { - return false; - } - String[] tokens = IP.split("\\."); - if (tokens.length != 4) { - return false; - } - for (String token : tokens) { - int tokenInt; - try { - tokenInt = Integer.parseInt(token); - } catch (NumberFormatException ase) { - return false; - } - if (tokenInt < 0 || tokenInt > 255) { - return false; - } - - } - return true; + request.setHttpMethod(httpMethod); + resolveRequestEndpoint(request, bucketName, key, endpoint); + return request; } private X invoke(Request request, @@ -4334,16 +4571,16 @@ private X invoke(Request request, @Override protected final ExecutionContext createExecutionContext(AmazonWebServiceRequest req) { - boolean isMetricsEnabled = isRequestMetricsEnabled(req) || isProfilingEnabled(); + final boolean isMetricsEnabled = isRequestMetricsEnabled(req) || isProfilingEnabled(); return new S3ExecutionContext(requestHandler2s, isMetricsEnabled, this); } private X invoke(Request request, HttpResponseHandler> responseHandler, String bucket, String key) { - AmazonWebServiceRequest originalRequest = request.getOriginalRequest(); - ExecutionContext executionContext = createExecutionContext(originalRequest); - AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics(); + final AmazonWebServiceRequest originalRequest = request.getOriginalRequest(); + final ExecutionContext executionContext = createExecutionContext(originalRequest); + final AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics(); // Binds the request metrics to the current request. request.setAWSRequestMetrics(awsRequestMetrics); // Having the ClientExecuteTime defined here is not ideal (for the @@ -4364,8 +4601,17 @@ private X invoke(Request request, */ if (!request.getHeaders().containsKey(Headers.CONTENT_TYPE)) { request.addHeader(Headers.CONTENT_TYPE, - "application/x-www-form-urlencoded; charset=utf-8"); + "application/octet-stream"); } + + // Update the bucketRegionCache if we can't find region for the + // request + if (bucket != null + && !(request.getOriginalRequest() instanceof CreateBucketRequest) + && noExplicitRegionProvided(request)) { + fetchRegionFromCache(bucket); + } + AWSCredentials credentials = awsCredentialsProvider.getCredentials(); if (originalRequest.getRequestCredentials() != null) { credentials = originalRequest.getRequestCredentials(); @@ -4375,6 +4621,24 @@ private X invoke(Request request, response = client.execute(request, responseHandler, errorResponseHandler, executionContext); return response.getAwsResponse(); + }catch(final AmazonS3Exception ase){ + /** + * This is to handle the edge case: when the bucket is deleted and recreated in a different region, + * the cache still has the old region info. + * If region is not specified, the first request to this newly created bucket will fail because it used + * the outdated region present in cache. Here we update the cache with correct region. The subsequent + * requests will succeed. + * The recommended practice for any request is to provide region info always. + */ + if (ase.getStatusCode() == 301) { + if (ase.getAdditionalDetails() != null) { + final String region = ase.getAdditionalDetails().get(Headers.S3_BUCKET_REGION); + bucketRegionCache.put(bucket, region); + ase.setErrorMessage("The bucket is in this region: " + region + + ". Please use this region to retry the request"); + } + } + throw ase; } finally { endClientExecution(awsRequestMetrics, request, response); } @@ -4387,7 +4651,7 @@ private X invoke(Request request, */ @Override public void enableRequesterPays(String bucketName) { - RequestPaymentConfiguration configuration = new RequestPaymentConfiguration( + final RequestPaymentConfiguration configuration = new RequestPaymentConfiguration( Payer.Requester); setBucketRequestPayment(new SetRequestPaymentConfigurationRequest( @@ -4401,7 +4665,7 @@ public void enableRequesterPays(String bucketName) { */ @Override public void disableRequesterPays(String bucketName) { - RequestPaymentConfiguration configuration = new RequestPaymentConfiguration( + final RequestPaymentConfiguration configuration = new RequestPaymentConfiguration( Payer.BucketOwner); setBucketRequestPayment(new SetRequestPaymentConfigurationRequest( @@ -4416,7 +4680,7 @@ public void disableRequesterPays(String bucketName) { */ @Override public boolean isRequesterPaysEnabled(String bucketName) { - RequestPaymentConfiguration configuration = getBucketRequestPayment(new GetRequestPaymentConfigurationRequest( + final RequestPaymentConfiguration configuration = getBucketRequestPayment(new GetRequestPaymentConfigurationRequest( bucketName)); return (configuration.getPayer() == Payer.Requester); } @@ -4433,9 +4697,9 @@ public boolean isRequesterPaysEnabled(String bucketName) { private void setBucketRequestPayment( SetRequestPaymentConfigurationRequest setRequestPaymentConfigurationRequest) { - String bucketName = setRequestPaymentConfigurationRequest + final String bucketName = setRequestPaymentConfigurationRequest .getBucketName(); - RequestPaymentConfiguration configuration = setRequestPaymentConfigurationRequest + final RequestPaymentConfiguration configuration = setRequestPaymentConfigurationRequest .getConfiguration(); assertParameterNotNull(bucketName, @@ -4445,13 +4709,13 @@ private void setBucketRequestPayment( configuration, "The request payment configuration parameter must be specified when setting the Requester Pays."); - Request request = createRequest( + final Request request = createRequest( bucketName, null, setRequestPaymentConfigurationRequest, HttpMethodName.PUT); request.addParameter("requestPayment", null); request.addHeader("Content-Type", "application/xml"); - byte[] bytes = requestPaymentConfigurationXmlFactory + final byte[] bytes = requestPaymentConfigurationXmlFactory .convertToXmlByteArray(configuration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); @@ -4470,14 +4734,14 @@ private void setBucketRequestPayment( private RequestPaymentConfiguration getBucketRequestPayment( GetRequestPaymentConfigurationRequest getRequestPaymentConfigurationRequest) { - String bucketName = getRequestPaymentConfigurationRequest + final String bucketName = getRequestPaymentConfigurationRequest .getBucketName(); assertParameterNotNull( bucketName, "The bucket name parameter must be specified while getting the Request Payment Configuration."); - Request request = createRequest( + final Request request = createRequest( bucketName, null, getRequestPaymentConfigurationRequest, HttpMethodName.GET); request.addParameter("requestPayment", null); @@ -4505,8 +4769,8 @@ private void setZeroContentLength(Request req) { */ private ByteArrayInputStream toByteArray(InputStream is) { // 256k buffer - int size = 1024 * 256; - byte[] buf = new byte[size]; + final int size = 1024 * 256; + final byte[] buf = new byte[size]; int len = 0; try { int available = size; @@ -4519,7 +4783,7 @@ private ByteArrayInputStream toByteArray(InputStream is) { throw new AmazonClientException("Input stream exceeds 256k buffer."); } is.close(); - } catch (IOException ioe) { + } catch (final IOException ioe) { throw new AmazonClientException("Failed to read from inputstream", ioe); } return new ByteArrayInputStream(buf, 0, len); @@ -4553,7 +4817,7 @@ public void setBucketReplicationConfiguration( bucketReplicationConfiguration, "The replication configuration parameter must be specified when setting replication configuration."); - Request request = createRequest( + final Request request = createRequest( bucketName, null, setBucketReplicationConfigurationRequest, HttpMethodName.PUT); request.addParameter("replication", null); @@ -4568,7 +4832,7 @@ public void setBucketReplicationConfiguration( try { request.addHeader("Content-MD5", BinaryUtils.toBase64(Md5Utils.computeMD5Hash(bytes))); - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException( "Not able to compute MD5 of the replication rule configuration. Exception Message : " + e.getMessage(), @@ -4593,12 +4857,12 @@ public BucketReplicationConfiguration getBucketReplicationConfiguration( assertParameterNotNull( getBucketReplicationConfigurationRequest, "The bucket request parameter must be specified when retrieving replication configuration"); - String bucketName = getBucketReplicationConfigurationRequest.getBucketName(); + final String bucketName = getBucketReplicationConfigurationRequest.getBucketName(); assertParameterNotNull( bucketName, "The bucket request must specify a bucket name when retrieving replication configuration"); - Request request = createRequest(bucketName, null, + final Request request = createRequest(bucketName, null, getBucketReplicationConfigurationRequest, HttpMethodName.GET); request.addParameter("replication", null); @@ -4624,11 +4888,576 @@ public void deleteBucketReplicationConfiguration( bucketName, "The bucket name parameter must be specified when deleting replication configuration"); - Request request = createRequest(bucketName, + final Request request = createRequest(bucketName, null, deleteBucketReplicationConfigurationRequest, HttpMethodName.DELETE); request.addParameter("replication", null); invoke(request, voidResponseHandler, bucketName, null); } + + @Override + public DeleteBucketMetricsConfigurationResult deleteBucketMetricsConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException { + return deleteBucketMetricsConfiguration(new DeleteBucketMetricsConfigurationRequest(bucketName, id)); + } + + @Override + public DeleteBucketMetricsConfigurationResult deleteBucketMetricsConfiguration( + DeleteBucketMetricsConfigurationRequest deleteBucketMetricsConfigurationRequest) + throws AmazonServiceException, AmazonClientException { + + assertParameterNotNull(deleteBucketMetricsConfigurationRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty(deleteBucketMetricsConfigurationRequest.getBucketName(), "BucketName"); + final String id = assertStringNotEmpty(deleteBucketMetricsConfigurationRequest.getId(), "Metrics Id"); + + final Request request = + createRequest(bucketName, null, deleteBucketMetricsConfigurationRequest, HttpMethodName.DELETE); + request.addParameter("metrics", null); + request.addParameter("id", id); + + return invoke(request, new Unmarshallers.DeleteBucketMetricsConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public GetBucketMetricsConfigurationResult getBucketMetricsConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException { + return getBucketMetricsConfiguration(new GetBucketMetricsConfigurationRequest(bucketName, id)); + } + + @Override + public GetBucketMetricsConfigurationResult getBucketMetricsConfiguration( + GetBucketMetricsConfigurationRequest getBucketMetricsConfigurationRequest) + throws AmazonServiceException, AmazonClientException { + + assertParameterNotNull(getBucketMetricsConfigurationRequest, "The request cannot be null"); + final String bucketName = assertStringNotEmpty(getBucketMetricsConfigurationRequest.getBucketName(), "BucketName"); + final String id = assertStringNotEmpty(getBucketMetricsConfigurationRequest.getId(), "Metrics Id"); + + final Request request = + createRequest(bucketName, null, getBucketMetricsConfigurationRequest, HttpMethodName.GET); + request.addParameter("metrics", null); + request.addParameter("id", id); + + return invoke(request, new Unmarshallers.GetBucketMetricsConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public SetBucketMetricsConfigurationResult setBucketMetricsConfiguration( + String bucketName, MetricsConfiguration metricsConfiguration) + throws AmazonServiceException, AmazonClientException { + return setBucketMetricsConfiguration(new SetBucketMetricsConfigurationRequest(bucketName, metricsConfiguration)); + } + + @Override + public SetBucketMetricsConfigurationResult setBucketMetricsConfiguration( + SetBucketMetricsConfigurationRequest setBucketMetricsConfigurationRequest) + throws AmazonServiceException, AmazonClientException { + + new SetBucketMetricsConfigurationRequest(); + assertParameterNotNull(setBucketMetricsConfigurationRequest, "The request cannot be null"); + final String bucketName = assertStringNotEmpty(setBucketMetricsConfigurationRequest.getBucketName(), "BucketName"); + final MetricsConfiguration metricsConfiguration = assertNotNull( + setBucketMetricsConfigurationRequest.getMetricsConfiguration(), "Metrics Configuration"); + final String id = assertNotNull(metricsConfiguration.getId(), "Metrics Id"); + + final Request request = + createRequest(bucketName, null, setBucketMetricsConfigurationRequest, HttpMethodName.PUT); + request.addParameter("metrics", null); + request.addParameter("id", id); + + final byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(metricsConfiguration); + request.addHeader("Content-Length", String.valueOf(bytes.length)); + request.addHeader("Content-Type", "application/xml"); + request.setContent(new ByteArrayInputStream(bytes)); + + return invoke(request, new Unmarshallers.SetBucketMetricsConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public ListBucketMetricsConfigurationsResult listBucketMetricsConfigurations( + ListBucketMetricsConfigurationsRequest listBucketMetricsConfigurationsRequest) + throws AmazonServiceException, AmazonClientException { + + assertParameterNotNull(listBucketMetricsConfigurationsRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty(listBucketMetricsConfigurationsRequest.getBucketName(), "BucketName"); + + final Request request = + createRequest(bucketName, null, listBucketMetricsConfigurationsRequest, HttpMethodName.GET); + request.addParameter("metrics", null); + addParameterIfNotNull(request, "continuation-token", listBucketMetricsConfigurationsRequest.getContinuationToken()); + + return invoke(request, new Unmarshallers.ListBucketMetricsConfigurationsUnmarshaller(), bucketName, null); + } + + @Override + public DeleteBucketAnalyticsConfigurationResult deleteBucketAnalyticsConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException { + return deleteBucketAnalyticsConfiguration(new DeleteBucketAnalyticsConfigurationRequest(bucketName, id)); + } + + @Override + public DeleteBucketAnalyticsConfigurationResult deleteBucketAnalyticsConfiguration( + DeleteBucketAnalyticsConfigurationRequest deleteBucketAnalyticsConfigurationRequest) + throws AmazonServiceException, AmazonClientException { + + assertParameterNotNull(deleteBucketAnalyticsConfigurationRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty( + deleteBucketAnalyticsConfigurationRequest.getBucketName(), "BucketName"); + final String id = assertStringNotEmpty( + deleteBucketAnalyticsConfigurationRequest.getId(), "Analytics Id"); + + final Request request = + createRequest(bucketName, null, deleteBucketAnalyticsConfigurationRequest, HttpMethodName.DELETE); + request.addParameter("analytics", null); + request.addParameter("id", id); + + return invoke(request, new Unmarshallers.DeleteBucketAnalyticsConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public GetBucketAnalyticsConfigurationResult getBucketAnalyticsConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException { + return getBucketAnalyticsConfiguration(new GetBucketAnalyticsConfigurationRequest(bucketName, id)); + } + + @Override + public GetBucketAnalyticsConfigurationResult getBucketAnalyticsConfiguration( + GetBucketAnalyticsConfigurationRequest getBucketAnalyticsConfigurationRequest) + throws AmazonServiceException, AmazonClientException { + + assertParameterNotNull(getBucketAnalyticsConfigurationRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty( + getBucketAnalyticsConfigurationRequest.getBucketName(), "BucketName"); + final String id = assertStringNotEmpty( + getBucketAnalyticsConfigurationRequest.getId(), "Analytics Id"); + + final Request request = + createRequest(bucketName, null, getBucketAnalyticsConfigurationRequest, HttpMethodName.GET); + request.addParameter("analytics", null); + request.addParameter("id", id); + + return invoke(request, new Unmarshallers.GetBucketAnalyticsConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public SetBucketAnalyticsConfigurationResult setBucketAnalyticsConfiguration( + String bucketName, AnalyticsConfiguration analyticsConfiguration) + throws AmazonServiceException, AmazonClientException { + return setBucketAnalyticsConfiguration( + new SetBucketAnalyticsConfigurationRequest(bucketName, analyticsConfiguration)); + } + + @Override + public SetBucketAnalyticsConfigurationResult setBucketAnalyticsConfiguration( + SetBucketAnalyticsConfigurationRequest setBucketAnalyticsConfigurationRequest) + throws AmazonServiceException, AmazonClientException { + + assertParameterNotNull(setBucketAnalyticsConfigurationRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty( + setBucketAnalyticsConfigurationRequest.getBucketName(), "BucketName"); + final AnalyticsConfiguration analyticsConfiguration = assertNotNull( + setBucketAnalyticsConfigurationRequest.getAnalyticsConfiguration(), "Analytics Configuration"); + final String id = assertNotNull(analyticsConfiguration.getId(), "Analytics Id"); + + final Request request = + createRequest(bucketName, null, setBucketAnalyticsConfigurationRequest, HttpMethodName.PUT); + request.addParameter("analytics", null); + request.addParameter("id", id); + + final byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(analyticsConfiguration); + request.addHeader("Content-Length", String.valueOf(bytes.length)); + request.addHeader("Content-Type", "application/xml"); + request.setContent(new ByteArrayInputStream(bytes)); + + return invoke(request, new Unmarshallers.SetBucketAnalyticsConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public ListBucketAnalyticsConfigurationsResult listBucketAnalyticsConfigurations( + ListBucketAnalyticsConfigurationsRequest listBucketAnalyticsConfigurationsRequest) + throws AmazonServiceException, AmazonClientException { + + assertParameterNotNull(listBucketAnalyticsConfigurationsRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty( + listBucketAnalyticsConfigurationsRequest.getBucketName(), "BucketName"); + + final Request request = + createRequest(bucketName, null, listBucketAnalyticsConfigurationsRequest, HttpMethodName.GET); + request.addParameter("analytics", null); + addParameterIfNotNull(request, "continuation-token", listBucketAnalyticsConfigurationsRequest.getContinuationToken()); + + return invoke(request, new Unmarshallers.ListBucketAnalyticsConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public DeleteBucketInventoryConfigurationResult deleteBucketInventoryConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException { + return deleteBucketInventoryConfiguration( + new DeleteBucketInventoryConfigurationRequest(bucketName, id)); + } + + @Override + public DeleteBucketInventoryConfigurationResult deleteBucketInventoryConfiguration( + DeleteBucketInventoryConfigurationRequest deleteBucketInventoryConfigurationRequest) + throws AmazonServiceException, AmazonClientException { + assertParameterNotNull(deleteBucketInventoryConfigurationRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty(deleteBucketInventoryConfigurationRequest.getBucketName(), "BucketName"); + final String id = assertStringNotEmpty(deleteBucketInventoryConfigurationRequest.getId(), "Inventory id"); + + final Request request = createRequest(bucketName, null, deleteBucketInventoryConfigurationRequest, HttpMethodName.DELETE); + request.addParameter("inventory", null); + request.addParameter("id", id); + + return invoke(request, new Unmarshallers.DeleteBucketInventoryConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public GetBucketInventoryConfigurationResult getBucketInventoryConfiguration( + String bucketName, String id) throws AmazonServiceException, AmazonClientException { + return getBucketInventoryConfiguration( + new GetBucketInventoryConfigurationRequest(bucketName, id)); + } + + @Override + public GetBucketInventoryConfigurationResult getBucketInventoryConfiguration( + GetBucketInventoryConfigurationRequest getBucketInventoryConfigurationRequest) + throws AmazonServiceException, AmazonClientException { + assertParameterNotNull(getBucketInventoryConfigurationRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty(getBucketInventoryConfigurationRequest.getBucketName(), "BucketName"); + final String id = assertStringNotEmpty(getBucketInventoryConfigurationRequest.getId(), "Inventory id"); + + final Request request = createRequest(bucketName, null, getBucketInventoryConfigurationRequest, HttpMethodName.GET); + request.addParameter("inventory", null); + request.addParameter("id", id); + + return invoke(request, new Unmarshallers.GetBucketInventoryConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public SetBucketInventoryConfigurationResult setBucketInventoryConfiguration( + String bucketName, InventoryConfiguration inventoryConfiguration) + throws AmazonServiceException, AmazonClientException { + return setBucketInventoryConfiguration( + new SetBucketInventoryConfigurationRequest(bucketName, inventoryConfiguration)); + } + + @Override + public SetBucketInventoryConfigurationResult setBucketInventoryConfiguration( + SetBucketInventoryConfigurationRequest setBucketInventoryConfigurationRequest) + throws AmazonServiceException, AmazonClientException { + assertParameterNotNull(setBucketInventoryConfigurationRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty(setBucketInventoryConfigurationRequest.getBucketName(), "BucketName"); + final InventoryConfiguration inventoryConfiguration = assertNotNull(setBucketInventoryConfigurationRequest.getInventoryConfiguration(), + "InventoryConfiguration"); + final String id = assertNotNull(inventoryConfiguration.getId(), "Inventory id"); + + final Request request = createRequest(bucketName, null, setBucketInventoryConfigurationRequest, HttpMethodName.PUT); + request.addParameter("inventory", null); + request.addParameter("id", id); + + final byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(inventoryConfiguration); + request.addHeader("Content-Length", String.valueOf(bytes.length)); + request.addHeader("Content-Type", "application/xml"); + request.setContent(new ByteArrayInputStream(bytes)); + + return invoke(request, new Unmarshallers.SetBucketInventoryConfigurationUnmarshaller(), bucketName, null); + } + + @Override + public ListBucketInventoryConfigurationsResult listBucketInventoryConfigurations(ListBucketInventoryConfigurationsRequest listBucketInventoryConfigurationsRequest) + throws AmazonServiceException, AmazonClientException { + assertParameterNotNull(listBucketInventoryConfigurationsRequest, + "The request cannot be null"); + final String bucketName = assertStringNotEmpty(listBucketInventoryConfigurationsRequest.getBucketName(), "BucketName"); + + final Request request = createRequest(bucketName, null, listBucketInventoryConfigurationsRequest, HttpMethodName.GET); + request.addParameter("inventory", null); + addParameterIfNotNull(request, "continuation-token", listBucketInventoryConfigurationsRequest.getContinuationToken()); + + return invoke(request, new Unmarshallers.ListBucketInventoryConfigurationsUnmarshaller(), bucketName, null); + } + + private void setContent(Request request, byte[] content, String contentType, boolean setMd5) { + request.setContent(new ByteArrayInputStream(content)); + request.addHeader("Content-Length", Integer.toString(content.length)); + request.addHeader("Content-Type", contentType); + if (setMd5) { + try { + final byte[] md5 = Md5Utils.computeMD5Hash(content); + final String md5Base64 = BinaryUtils.toBase64(md5); + request.addHeader("Content-MD5", md5Base64); + } catch ( final Exception e ) { + throw new AmazonClientException("Couldn't compute md5 sum", e); + } + } + } + + + /** + *

+ * Populate the specified request with {@link Constants#REQUESTER_PAYS} to + * header {@link Headers#REQUESTER_PAYS_HEADER}, if isRequesterPays is true. + *

+ * + * @param request The specified request to populate. + * @param isRequesterPays The flag whether to populate the header or not. + */ + protected static void populateRequesterPaysHeader(Request request, boolean isRequesterPays) { + if (isRequesterPays) { + request.addHeader(Headers.REQUESTER_PAYS_HEADER, Constants.REQUESTER_PAYS); + } + } + + @Override + public String getObjectAsString(String bucketName, String key) + throws AmazonServiceException, AmazonClientException { + assertParameterNotNull(bucketName, "Bucket name must be provided"); + assertParameterNotNull(key, "Object key must be provided"); + + final S3Object object = getObject(bucketName, key); + try { + return IOUtils.toString(object.getObjectContent()); + } catch (final IOException e) { + throw new AmazonClientException("Error streaming content from S3 during download"); + } + } + + @Override + public GetObjectTaggingResult getObjectTagging( + GetObjectTaggingRequest getObjectTaggingRequest) { + assertParameterNotNull(getObjectTaggingRequest, + "The request parameter must be specified when getting the object tags"); + final String bucketName = assertStringNotEmpty(getObjectTaggingRequest.getBucketName(), + "BucketName"); + final String key = assertNotNull(getObjectTaggingRequest.getKey(), "Key"); + + final Request request = createRequest(bucketName, key, + getObjectTaggingRequest, HttpMethodName.GET); + request.addParameter("tagging", null); + addParameterIfNotNull(request, "versionId", getObjectTaggingRequest.getVersionId()); + + final ResponseHeaderHandlerChain handlerChain = new ResponseHeaderHandlerChain( + new Unmarshallers.GetObjectTaggingResponseUnmarshaller(), + new GetObjectTaggingResponseHeaderHandler()); + + return invoke(request, handlerChain, bucketName, key); + } + + @Override + public SetObjectTaggingResult setObjectTagging( + SetObjectTaggingRequest setObjectTaggingRequest) { + assertParameterNotNull(setObjectTaggingRequest, + "The request parameter must be specified setting the object tags"); + final String bucketName = assertStringNotEmpty(setObjectTaggingRequest.getBucketName(), + "BucketName"); + final String key = assertNotNull(setObjectTaggingRequest.getKey(), "Key"); + final ObjectTagging tagging = assertNotNull(setObjectTaggingRequest.getTagging(), + "ObjectTagging"); + + final Request request = createRequest(bucketName, key, + setObjectTaggingRequest, HttpMethodName.PUT); + request.addParameter("tagging", null); + addParameterIfNotNull(request, "versionId", setObjectTaggingRequest.getVersionId()); + final byte[] content = new ObjectTaggingXmlFactory().convertToXmlByteArray(tagging); + setContent(request, content, "application/xml", true); + + final ResponseHeaderHandlerChain handlerChain = new ResponseHeaderHandlerChain( + new Unmarshallers.SetObjectTaggingResponseUnmarshaller(), + new SetObjectTaggingResponseHeaderHandler()); + + return invoke(request, handlerChain, bucketName, key); + } + + @Override + public DeleteObjectTaggingResult deleteObjectTagging( + DeleteObjectTaggingRequest deleteObjectTaggingRequest) { + assertParameterNotNull(deleteObjectTaggingRequest, + "The request parameter must be specified when delete the object tags"); + final String bucketName = assertStringNotEmpty(deleteObjectTaggingRequest.getBucketName(), + "BucketName"); + final String key = assertStringNotEmpty(deleteObjectTaggingRequest.getKey(), "Key"); + + final Request request = createRequest(bucketName, key, + deleteObjectTaggingRequest, HttpMethodName.DELETE); + request.addParameter("tagging", null); + addParameterIfNotNull(request, "versionId", deleteObjectTaggingRequest.getVersionId()); + + final ResponseHeaderHandlerChain handlerChain = new ResponseHeaderHandlerChain( + new Unmarshallers.DeleteObjectTaggingResponseUnmarshaller(), + new DeleteObjectTaggingHeaderHandler()); + + return invoke(request, handlerChain, bucketName, key); + } + + @Override + public PutObjectResult putObject(String bucketName, String key, String content) + throws AmazonServiceException, AmazonClientException { + assertParameterNotNull(bucketName, "Bucket name must be provided"); + assertParameterNotNull(key, "Object key must be provided"); + assertParameterNotNull(content, "String content must be provided"); + + final byte[] contentBytes = content.getBytes(StringUtils.UTF8); + + final InputStream is = new ByteArrayInputStream(contentBytes); + final ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType("text/plain"); + metadata.setContentLength(contentBytes.length); + + return putObject(new PutObjectRequest(bucketName, key, is, metadata)); + } + + + + @Override + public String getRegionName() { + final String authority = super.endpoint.getAuthority(); + if(Constants.S3_HOSTNAME.equals(authority)) { + return "us-east-1"; + } + final Matcher m = Region.S3_REGIONAL_ENDPOINT_PATTERN.matcher(authority); + try { + m.matches(); + return RegionUtils.getRegion(m.group(1)).getName(); + } catch (final Exception e) { + throw new IllegalStateException("No valid region has been specified. Unable to return region name", e); + } + } + + /** + * Fetches the region of the bucket from the cache maintained. If the cache + * doesn't have an entry, fetches the region from Amazon S3 and updates the + * cache. + */ + private String fetchRegionFromCache(String bucketName) { + String bucketRegion = bucketRegionCache.get(bucketName); + if (bucketRegion == null) { + if (log.isDebugEnabled()) { + log.debug("Bucket region cache doesn't have an entry for " + bucketName + + ". Trying to get bucket region from Amazon S3."); + } + bucketRegion = getBucketRegionViaHeadRequest(bucketName); + if (bucketRegion != null) { + bucketRegionCache.put(bucketName, bucketRegion); + } + } + if (log.isDebugEnabled()) { + log.debug("Region for " + bucketName + " is " + bucketRegion); + } + return bucketRegion; + } + + /** + * Retrieves the region of the bucket by making a HeadBucket request to + * us-west-1 region. Currently S3 doesn't return region in a HEAD Bucket + * request if the bucket owner has enabled bucket to accept only SigV4 + * requests via bucket policies. + */ + private String getBucketRegionViaHeadRequest(String bucketName) { + String bucketRegion = null; + + try { + final String endpoint = "https://s3-us-west-1.amazonaws.com"; + final Request request = createRequest(bucketName, null, + new HeadBucketRequest(bucketName), HttpMethodName.HEAD, new URI(endpoint)); + + final HeadBucketResult result = invoke(request, new HeadBucketResultHandler(), + bucketName, null); + bucketRegion = result.getBucketRegion(); + } catch (final AmazonS3Exception exception) { + if (exception.getAdditionalDetails() != null) { + bucketRegion = exception.getAdditionalDetails().get( + Headers.S3_BUCKET_REGION); + } + } catch (final URISyntaxException e) { + log.warn("Error while creating URI"); + } + + if (bucketRegion == null && log.isDebugEnabled()) { + log.debug("Not able to derive region of the " + bucketName + + " from the HEAD Bucket requests."); + } + + return bucketRegion; + } + + /** + * Set the request's endpoint and resource path with the new region provided + * + * @param request Request to set endpoint for + * @param regionString New region to determine endpoint to hit + */ + public void resolveRequestEndpoint(Request request, String bucketName, + String key) { + resolveRequestEndpoint(request, bucketName, key, null); + } + + public void resolveRequestEndpoint(Request request, String bucketName, + String key, URI endpoint) { + final URI ep = endpoint == null ? this.endpoint : endpoint; + if (shouldUseVirtualAddressing(ep, bucketName)) { + request.setEndpoint(convertToVirtualHostEndpoint(ep, bucketName)); + request.setResourcePath(HttpUtils.urlEncode(getHostStyleResourcePath(key), true)); + } else { + request.setEndpoint(ep); + if (bucketName != null) { + request.setResourcePath( + HttpUtils.urlEncode(getPathStyleResourcePath(bucketName, key), true)); + } + } + } + + private boolean shouldUseVirtualAddressing(final URI endpoint, final String bucketName) { + return !clientOptions.isPathStyleAccess() && BucketNameUtils.isDNSBucketName(bucketName) + && !isValidIpV4Address(endpoint.getHost()); + } + + private String getHostStyleResourcePath(String key) { + String resourcePath = key; + /* + * If the key name starts with a slash character, in order to prevent it + * being treated as a path delimiter, we need to add another slash + * before the key name. {@see + * com.amazonaws.http.HttpRequestFactory#createHttpRequest} + */ + if (key != null && key.startsWith("/")) { + resourcePath = "/" + key; + } + return resourcePath; + } + + private String getPathStyleResourcePath(String bucketName, String key) { + return bucketName + "/" + (key != null ? key : ""); + } + + static boolean isValidIpV4Address(String ipAddr) { + if (ipAddr == null) { + return false; + } + final String[] tokens = ipAddr.split("\\."); + if (tokens.length != 4) { + return false; + } + for (final String token : tokens) { + try { + final int tokenInt = Integer.parseInt(token); + if (tokenInt < 0 || tokenInt > 255) { + return false; + } + } catch (final NumberFormatException ase) { + return false; + } + } + return true; + } + } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3Encryption.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3Encryption.java new file mode 100644 index 0000000000..5d9b1652b0 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3Encryption.java @@ -0,0 +1,22 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3; + +/** + * A marker interface used to check if an instance of S3 client is + * an S3 encryption client. + */ +public interface AmazonS3Encryption extends AmazonS3 { +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3EncryptionClient.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3EncryptionClient.java index a3b40b72cb..1c5e89e95d 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3EncryptionClient.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3EncryptionClient.java @@ -20,10 +20,15 @@ import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AnonymousAWSCredentials; import com.amazonaws.internal.StaticCredentialsProvider; +import com.amazonaws.metrics.RequestMetricCollector; +import com.amazonaws.regions.Region; +import com.amazonaws.services.kms.AWSKMSClient; +import com.amazonaws.services.s3.internal.MultiFileOutputStream; +import com.amazonaws.services.s3.internal.PartCreationEvent; import com.amazonaws.services.s3.internal.S3Direct; import com.amazonaws.services.s3.internal.crypto.CryptoModuleDispatcher; -import com.amazonaws.services.s3.internal.crypto.EncryptionUtils; import com.amazonaws.services.s3.internal.crypto.S3CryptoModule; import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; @@ -40,30 +45,51 @@ import com.amazonaws.services.s3.model.GroupGrantee; import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; +import com.amazonaws.services.s3.model.InstructionFileId; import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PartETag; import com.amazonaws.services.s3.model.Permission; +import com.amazonaws.services.s3.model.PutInstructionFileRequest; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.PutObjectResult; import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectId; import com.amazonaws.services.s3.model.StaticEncryptionMaterialsProvider; +import com.amazonaws.services.s3.model.UploadObjectRequest; import com.amazonaws.services.s3.model.UploadPartRequest; import com.amazonaws.services.s3.model.UploadPartResult; import com.amazonaws.util.VersionInfoUtils; import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; /** * Used to perform client-side encryption for storing data securely in S3. Data - * encryption is done using a one-time randomly generated content encryption key - * (CEK) per S3 object. + * encryption is done using a one-time randomly generated content encryption + * key (CEK) per S3 object. *

- * The encryption materials specified in the constructor will be used to protect - * the CEK which is then stored along side with the S3 object. + * The encryption materials specified in the constructor will be used to + * protect the CEK which is then stored along side with the S3 object. */ -public class AmazonS3EncryptionClient extends AmazonS3Client { +public class AmazonS3EncryptionClient extends AmazonS3Client implements + AmazonS3Encryption { public static final String USER_AGENT = AmazonS3EncryptionClient.class.getName() + "/" + VersionInfoUtils.getVersion(); private final S3CryptoModule crypto; + private final AWSKMSClient kms; + /** + * True if the a default KMS client is constructed, which will be shut down + * when this instance of S3 encryption client is shutdown. False otherwise, + * which means the users who provided the KMS client would be responsible + * to shut down the KMS client. + */ + private final boolean isKMSClientInternal; // ///////////////////// Constructors //////////////// /** @@ -380,20 +406,65 @@ public AmazonS3EncryptionClient( EncryptionMaterialsProvider kekMaterialsProvider, ClientConfiguration clientConfig, CryptoConfiguration cryptoConfig) { - super(credentialsProvider, clientConfig); + this(credentialsProvider, kekMaterialsProvider, clientConfig, + cryptoConfig, + null // request metric collector + ); + } + public AmazonS3EncryptionClient( + AWSCredentialsProvider credentialsProvider, + EncryptionMaterialsProvider kekMaterialsProvider, + ClientConfiguration clientConfig, + CryptoConfiguration cryptoConfig, + RequestMetricCollector requestMetricCollector) { + this(null, // KMS client + credentialsProvider, kekMaterialsProvider, clientConfig, + cryptoConfig, requestMetricCollector); + } + + public AmazonS3EncryptionClient(AWSKMSClient kms, + AWSCredentialsProvider credentialsProvider, + EncryptionMaterialsProvider kekMaterialsProvider, + ClientConfiguration clientConfig, + CryptoConfiguration cryptoConfig, + RequestMetricCollector requestMetricCollector) { + super(credentialsProvider, clientConfig, requestMetricCollector); assertParameterNotNull(kekMaterialsProvider, "EncryptionMaterialsProvider parameter must not be null."); assertParameterNotNull(cryptoConfig, "CryptoConfiguration parameter must not be null."); - this.crypto = new CryptoModuleDispatcher(new S3DirectImpl(), - credentialsProvider, kekMaterialsProvider, - clientConfig, cryptoConfig); + this.isKMSClientInternal = kms == null; + this.kms = isKMSClientInternal + ? newAWSKMSClient(credentialsProvider, clientConfig, cryptoConfig, + requestMetricCollector) + : kms; + this.crypto = new CryptoModuleDispatcher(this.kms, new S3DirectImpl(), + credentialsProvider, kekMaterialsProvider, cryptoConfig); + } + + /** + * Creates and returns a new instance of AWS KMS client in the case when + * an explicit AWS KMS client is not specified. + */ + private AWSKMSClient newAWSKMSClient( + AWSCredentialsProvider credentialsProvider, + ClientConfiguration clientConfig, + CryptoConfiguration cryptoConfig, + RequestMetricCollector requestMetricCollector + ) { + final AWSKMSClient kmsClient = new AWSKMSClient( + credentialsProvider, clientConfig, requestMetricCollector); + final Region kmsRegion = cryptoConfig.getAwsKmsRegion(); + if (kmsRegion != null) + kmsClient.setRegion(kmsRegion); + return kmsClient; } private void assertParameterNotNull(Object parameterValue, String errorMessage) { - if (parameterValue == null) + if (parameterValue == null) { throw new IllegalArgumentException(errorMessage); + } } /** @@ -409,7 +480,7 @@ private void assertParameterNotNull(Object parameterValue, */ @Override public PutObjectResult putObject(PutObjectRequest req) { - return crypto.putObjectSecurely(req); + return crypto.putObjectSecurely(req.clone()); } @Override @@ -428,8 +499,10 @@ public void deleteObject(DeleteObjectRequest req) { // Delete the object super.deleteObject(req); // If it exists, delete the instruction file. - DeleteObjectRequest instructionDeleteRequest = EncryptionUtils - .createInstructionDeleteObjectRequest(req); + InstructionFileId ifid = new S3ObjectId(req.getBucketName(), req.getKey()).instructionFileId(); + + DeleteObjectRequest instructionDeleteRequest = (DeleteObjectRequest) req.clone(); + instructionDeleteRequest.withBucketName(ifid.getBucket()).withKey(ifid.getKey()); super.deleteObject(instructionDeleteRequest); } @@ -453,7 +526,16 @@ public CompleteMultipartUploadResult completeMultipartUpload( @Override public InitiateMultipartUploadResult initiateMultipartUpload( InitiateMultipartUploadRequest req) { - return crypto.initiateMultipartUploadSecurely(req); + boolean isCreateEncryptionMaterial = true; + if (req instanceof EncryptedInitiateMultipartUploadRequest) { + EncryptedInitiateMultipartUploadRequest cryptoReq = + (EncryptedInitiateMultipartUploadRequest) req; + isCreateEncryptionMaterial = cryptoReq.isCreateEncryptionMaterial(); + } + return isCreateEncryptionMaterial + ? crypto.initiateMultipartUploadSecurely(req) + : super.initiateMultipartUpload(req) + ; } /** @@ -481,6 +563,36 @@ public void abortMultipartUpload(AbortMultipartUploadRequest req) { crypto.abortMultipartUploadSecurely(req); } + /** + * Creates a new crypto instruction file by re-encrypting the CEK of an + * existing encrypted S3 object with a new encryption material identifiable + * via a new set of material description. + *

+ * User of this method is responsible for explicitly deleting/updating the + * instruction file so created should the corresponding S3 object is + * deleted/created. + * + * @return the result of the put (instruction file) operation. + */ + public PutObjectResult putInstructionFile(PutInstructionFileRequest req) { + return crypto.putInstructionFileSecurely(req); + } + + /** + * {@inheritDoc} + *

+ * If the a default internal KMS client has been constructed, it will also be + * shut down by calling this method. + * Otherwise, users who provided the KMS client would be responsible to + * shut down the KMS client extrinsic to this method. + */ + @Override + public void shutdown() { + super.shutdown(); + if (isKMSClientInternal) + kms.shutdown(); + } + // /////////////////// Access to the methods in the super class ////////// /** * An internal implementation used to provide limited but direct access to @@ -531,4 +643,120 @@ public void abortMultipartUpload(AbortMultipartUploadRequest req) { AmazonS3EncryptionClient.super.abortMultipartUpload(req); } } + + /** + * Used to encrypt data first to disk with pipelined concurrent multi-part + * uploads to S3. This method enables significant speed-up of encrypting and + * uploading large payloads to Amazon S3 via pipelining and parallel uploads + * by consuming temporary disk space. + *

+ * There are many ways you can customize the behavior of this method, + * including + *

    + *
  • the configuration of your own custom thread pool
  • + *
  • the part size of each multi-part upload request; By default, a + * temporary ciphertext file is generated per part and gets uploaded + * immediately to S3
  • + *
  • the maximum temporary disk space that must not be exceeded by + * execution of this request; By default, the encryption will block upon + * hitting the limit and will only resume when the in-flight uploads catch + * up by releasing the temporary disk space upon successful uploads of the + * completed parts
  • + *
  • the configuration of your own {@link MultiFileOutputStream} for + * custom pipeline behavior
  • + *
  • the configuration of your own {@link UploadObjectObserver} for custom + * multi-part upload behavior
  • + *
+ *

+ * A request is handled with the following life cycle, calling the necessary + * Service Provider Interface: + *

    + *
  1. A thread pool is constructed (or retrieved from the request) for the + * execution of concurrent upload tasks to be submitted by the + * UploadObjectObserver
  2. + *
  3. An {@link UploadObjectObserver} is constructed (or retrieved from the + * request) for execution of concurrent uploads to S3
  4. + *
  5. Initialize the UploadObjectObserver
  6. + *
  7. Initialize a multi-part upload request to S3 by calling + * {@link UploadObjectObserver#onUploadInitiation(UploadObjectRequest)}
  8. + *
  9. A {@link MultiFileOutputStream} is constructed (or retrieved from the + * request) which serves as the pipeline for incremental (but serial) + * encryption to disk with concurrent multipart uploads to S3 whenever the + * parts on the disk are ready
  10. + *
  11. Initialize the MultiFileOutputStream
  12. + *
  13. Kicks off the pipeline for incremental encryption to disk with + * pipelined concurrent multi-part uploads to S3
  14. + *
  15. For every part encrypted into a temporary file on disk, it is + * uploaded by calling + * {@link UploadObjectObserver#onPartCreate(PartCreationEvent)}
  16. + *
  17. Finally, clean up and complete the multi-part upload by calling + * {@link UploadObjectObserver#onCompletion(List)}.
  18. + *
+ * + * @return the result of the completed muti-part uploads + * + * @throws IOException + * if the encryption to disk failed + * @throws InterruptedException + * if the current thread was interrupted while waiting + * @throws ExecutionException + * if the concurrent uploads threw an exception + */ + public CompleteMultipartUploadResult uploadObject(final UploadObjectRequest req) + throws IOException, InterruptedException, ExecutionException { + // Set up the pipeline for concurrent encrypt and upload + // Set up a thread pool for this pipeline + ExecutorService es = req.getExecutorService(); + final boolean defaultExecutorService = es == null; + if (es == null) + es = Executors.newFixedThreadPool(clientConfiguration.getMaxConnections()); + UploadObjectObserver observer = req.getUploadObjectObserver(); + if (observer == null) + observer = new UploadObjectObserver(); + // initialize the observer + observer.init(req, new S3DirectImpl(), this, es); + // Initiate upload + final String uploadId = observer.onUploadInitiation(req); + final List partETags = new ArrayList(); + MultiFileOutputStream mfos = req.getMultiFileOutputStream(); + if (mfos == null) + mfos = new MultiFileOutputStream(); + try { + // initialize the multi-file output stream + mfos.init(observer, req.getPartSize(), req.getDiskLimit()); + // Kicks off the encryption-upload pipeline; + // Note mfos is automatically closed upon method completion. + crypto.putLocalObjectSecurely(req, uploadId, mfos); + // block till all part have been uploaded + for (Future future: observer.getFutures()) { + UploadPartResult partResult = future.get(); + partETags.add(new PartETag(partResult.getPartNumber(), partResult.getETag())); + } + } catch(IOException ex) { + throw onAbort(observer, ex); + } catch(InterruptedException ex) { + throw onAbort(observer, ex); + } catch(ExecutionException ex) { + throw onAbort(observer, ex); + } catch(RuntimeException ex) { + throw onAbort(observer, ex); + } catch(Error ex) { + throw onAbort(observer, ex); + } finally { + if (defaultExecutorService) + es.shutdownNow(); // shut down the locally created thread pool + mfos.cleanup(); // delete left-over temp files + } + // Complete upload + return observer.onCompletion(partETags); + } + + /** + * Convenient method to notifies the observer to abort the multi-part + * upload, and returns the original exception. + */ + private T onAbort(UploadObjectObserver observer, T t) { + observer.onAbort(); + return t; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3URI.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3URI.java index efe9bf7915..24abd60274 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3URI.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/AmazonS3URI.java @@ -12,10 +12,11 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ - package com.amazonaws.services.s3; +import java.io.UnsupportedEncodingException; import java.net.URI; +import java.net.URLEncoder; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,20 +28,38 @@ public class AmazonS3URI { private static final Pattern ENDPOINT_PATTERN = Pattern.compile("^(.+\\.)?s3[.-]([a-z0-9-]+)\\."); + private static final Pattern VERSION_ID_PATTERN = Pattern.compile("[&;]"); + private final URI uri; private final boolean isPathStyle; private final String bucket; private final String key; + private final String versionId; private final String region; /** * Creates a new AmazonS3URI by parsing the given string. + * String will be URL encoded before generating the URI. * * @param str the URI to parse. */ public AmazonS3URI(final String str) { - this(URI.create(str)); + this(str, true); + } + + /** + * Creates a new AmazonS3URI by parsing the given string. + * String will optionally be URL encoded before generating the URI. + * URL encoding is recommended if you work with bucket names or object + * keys with special characters. This can be disabled if you pre-encode + * URI strings before passing them to this class. + * + * @param str the URI to parse. + * @param urlEncode true if string should be URL encoded + */ + public AmazonS3URI(final String str, final boolean urlEncode) { + this(URI.create(preprocessUrlStr(str, urlEncode)), urlEncode); } /** @@ -49,12 +68,39 @@ public AmazonS3URI(final String str) { * @param uri the URI to wrap */ public AmazonS3URI(final URI uri) { + this(uri, false); + } + + private AmazonS3URI(final URI uri, final boolean urlEncode) { if (uri == null) { throw new IllegalArgumentException("uri cannot be null"); } - this.uri = uri; + // s3://* + if ("s3".equalsIgnoreCase(uri.getScheme())) { + this.region = null; + this.versionId = null; + this.isPathStyle = false; + this.bucket = uri.getAuthority(); + + if (bucket == null) { + throw new IllegalArgumentException("Invalid S3 URI: no bucket: " + + uri); + } + + String path = uri.getPath(); + if (path.length() <= 1) { + // s3://bucket or s3://bucket/ + this.key = null; + } else { + // s3://bucket/key + // Remove the leading '/'. + this.key = uri.getPath().substring(1); + } + return; + } + String host = uri.getHost(); if (host == null) { throw new IllegalArgumentException("Invalid S3 URI: no hostname: " @@ -74,9 +120,9 @@ public AmazonS3URI(final URI uri) { // No bucket name in the authority; parse it from the path. this.isPathStyle = true; - // Grab the encoded path so we don't run afoul of '/'s in the - // bucket name. - String path = uri.getRawPath(); + // Use the raw path to avoid running afoul of '/'s in the + // bucket name if we have not performed full URL encoding + String path = urlEncode ? uri.getPath() : uri.getRawPath(); if ("/".equals(path)) { this.bucket = null; @@ -113,7 +159,8 @@ public AmazonS3URI(final URI uri) { // Remove the trailing '.' from the prefix to get the bucket. this.bucket = prefix.substring(0, prefix.length() - 1); - if ("/".equals(uri.getPath())) { + String path = uri.getPath(); + if (path == null || path.isEmpty() || "/".equals(uri.getPath())) { this.key = null; } else { // Remove the leading '/'. @@ -121,6 +168,8 @@ public AmazonS3URI(final URI uri) { } } + this.versionId = parseVersionId(uri.getRawQuery()); + if ("amazonaws".equals(matcher.group(2))) { // No region specified this.region = null; @@ -129,6 +178,25 @@ public AmazonS3URI(final URI uri) { } } + /** + * Attempts to parse a versionId parameter from the query + * string. + * + * @param query the query string to parse (possibly null) + * @return the versionId (possibly null) + */ + private static String parseVersionId(String query) { + if (query != null) { + String[] params = VERSION_ID_PATTERN.split(query); + for (String param : params) { + if (param.startsWith("versionId=")) { + return decode(param.substring(10)); + } + } + } + return null; + } + /** * @return the S3 URI being parsed */ @@ -159,6 +227,13 @@ public String getKey() { return key; } + /** + * @return the version id parsed from the URI (or null if no version specified) + */ + public String getVersionId() { + return versionId; + } + /** * @return the region parsed from the URI (or null if no region specified) */ @@ -172,8 +247,33 @@ public String toString() { } /** - * Percent-decodes the given string, with a fast path for strings that are - * not percent-encoded. + * URL encodes the given string. This allows us to pass special characters + * that would otherwise be rejected when building a URI instance. Because we + * need to retain the URI's path structure we subsequently need to replace + * percent encoded path delimiters back to their decoded counterparts. + * + * @param str the string to encode + * @return the encoded string + */ + private static String preprocessUrlStr(final String str, final boolean encode) { + if (encode) { + try { + return (URLEncoder.encode(str, "UTF-8") + .replace("%3A", ":") + .replace("%2F", "/") + .replace("+", "%20")); + } catch (UnsupportedEncodingException e) { + // This should never happen unless there is something + // fundamentally broken with the running JVM. + throw new RuntimeException(e); + } + } + return str; + } + + /** + * Percent-decodes the given string, with a fast path for strings that + * are not percent-encoded. * * @param str the string to decode * @return the decoded string @@ -226,8 +326,8 @@ private static String decode(final String str, final int firstPercent) { * @param index the index of the '%' character in the string */ private static void appendDecoded(final StringBuilder builder, - final String str, - final int index) { + final String str, + final int index) { if (index > str.length() - 3) { throw new IllegalStateException("Invalid percent-encoded string:" @@ -279,4 +379,30 @@ private static int fromHex(final char c) { "Invalid percent-encoded string: bad character '" + c + "' in " + "escape sequence."); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AmazonS3URI that = (AmazonS3URI) o; + + if (isPathStyle != that.isPathStyle) return false; + if (!uri.equals(that.uri)) return false; + if (bucket != null ? !bucket.equals(that.bucket) : that.bucket != null) return false; + if (key != null ? !key.equals(that.key) : that.key != null) return false; + if (versionId != null ? !versionId.equals(that.versionId) : that.versionId != null) return false; + return region != null ? region.equals(that.region) : that.region == null; + } + + @Override + public int hashCode() { + int result = uri.hashCode(); + result = 31 * result + (isPathStyle ? 1 : 0); + result = 31 * result + (bucket != null ? bucket.hashCode() : 0); + result = 31 * result + (key != null ? key.hashCode() : 0); + result = 31 * result + (versionId != null ? versionId.hashCode() : 0); + result = 31 * result + (region != null ? region.hashCode() : 0); + return result; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/Headers.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/Headers.java index bceada9bf1..ca971a48de 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/Headers.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/Headers.java @@ -31,10 +31,12 @@ public interface Headers { public static final String CONTENT_RANGE = "Content-Range"; public static final String CONTENT_MD5 = "Content-MD5"; public static final String CONTENT_TYPE = "Content-Type"; + public static final String CONTENT_LANGUAGE = "Content-Language"; public static final String DATE = "Date"; public static final String ETAG = "ETag"; public static final String LAST_MODIFIED = "Last-Modified"; public static final String SERVER = "Server"; + public static final String CONNECTION = "Connection"; /* * Amazon HTTP Headers @@ -224,9 +226,30 @@ public interface Headers { */ public static final String REQUESTER_CHARGED_HEADER = "x-amz-request-charged"; - /** - * Region where the bucket is located. This header is returned only in HEAD - * bucket and ListObjects response. - */ + + /** Header for the replication status of an Amazon S3 Object.*/ + public static final String OBJECT_REPLICATION_STATUS = "x-amz-replication-status"; + + /** Region where the request is being served */ + public static final String S3_SERVING_REGION = "x-amz-region"; + + /** Region where the bucket is located. This header is returned only in HEAD bucket and ListObjects response. */ public static final String S3_BUCKET_REGION = "x-amz-bucket-region"; + + /** Date when multipart upload will become eligible for abort operation by lifecycle. */ + public static final String ABORT_DATE = "x-amz-abort-date"; + + /** Id of the lifecycle rule that makes a multipart upload eligible for abort operation. */ + public static final String ABORT_RULE_ID = "x-amz-abort-rule-id"; + + /** S3 response header for a multipart object containing the number of parts in the object. */ + public static final String S3_PARTS_COUNT = "x-amz-mp-parts-count"; + + /** S3 request header for PUT object with a tag set */ + public static final String S3_TAGGING = "x-amz-tagging"; + + /** S3 response header the number of tags on an object */ + public static final String S3_TAGGING_COUNT = "x-amz-tagging-count"; + + public static final String TAGGING_DIRECTIVE = "x-amz-tagging-directive"; } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/KeyWrapException.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/KeyWrapException.java new file mode 100644 index 0000000000..b8120b8df8 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/KeyWrapException.java @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3; + +/** + * Key wrapping related security exception. + */ +public class KeyWrapException extends SecurityException { + private static final long serialVersionUID = 1L; + + public KeyWrapException() { + super(); + } + + public KeyWrapException(String s) { + super(s); + } + + public KeyWrapException(String message, Throwable cause) { + super(message, cause); + } + + public KeyWrapException(Throwable cause) { + super(cause); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/OnFileDelete.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/OnFileDelete.java new file mode 100644 index 0000000000..a49ba29292 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/OnFileDelete.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3; + +import com.amazonaws.services.s3.internal.FileDeletionEvent; + +/** + * A service provider interface (SPI) used to notify the event of a file + * deletion. + */ +public interface OnFileDelete { + /** + * Called upon a file deletion event. + *

+ * Implementation of this method should never block. + * + * @param event + * file deletion event + */ + public void onFileDelete(FileDeletionEvent event); +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/S3ClientOptions.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/S3ClientOptions.java index 9afc518ae6..c168d9694c 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/S3ClientOptions.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/S3ClientOptions.java @@ -12,7 +12,6 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ - package com.amazonaws.services.s3; /** @@ -22,12 +21,21 @@ public class S3ClientOptions { /** The default setting for use of path-style access */ public static final boolean DEFAULT_PATH_STYLE_ACCESS = false; + /** The default setting for use of chunked encoding */ + public static final boolean DEFAULT_CHUNKED_ENCODING_DISABLED = false; + /** The default setting for use of payload signing */ + public static final boolean DEFAULT_PAYLOAD_SIGNING_ENABLED = false; /** S3 accelerate is by default not enabled */ public static final boolean DEFAULT_ACCELERATE_MODE_ENABLED = false; + /** S3 dualstack endpoint is by default not enabled */ + public static final boolean DEFAULT_DUALSTACK_ENABLED = false; /** Flag for use of path-style access */ private boolean pathStyleAccess; - private boolean accelerateModeEnabled; + private final boolean chunkedEncodingDisabled; + private final boolean accelerateModeEnabled; + private final boolean payloadSigningEnabled; + private final boolean dualstackEnabled; /** * @return a new S3ClientOptions builder. @@ -38,13 +46,21 @@ public static Builder builder() { public static class Builder { private boolean pathStyleAccess = DEFAULT_PATH_STYLE_ACCESS; + /** Flag for user of chunked encoding */ + private boolean chunkedEncodingDisabled = DEFAULT_CHUNKED_ENCODING_DISABLED; private boolean accelerateModeEnabled = DEFAULT_ACCELERATE_MODE_ENABLED; + private boolean payloadSigningEnabled = DEFAULT_PAYLOAD_SIGNING_ENABLED; + private boolean dualstackEnabled = DEFAULT_DUALSTACK_ENABLED; private Builder() { } public S3ClientOptions build() { - return new S3ClientOptions(pathStyleAccess, accelerateModeEnabled); + return new S3ClientOptions(pathStyleAccess, + chunkedEncodingDisabled, + accelerateModeEnabled, + payloadSigningEnabled, + dualstackEnabled); } /** @@ -88,6 +104,66 @@ public Builder setAccelerateModeEnabled(boolean accelerateModeEnabled) { this.accelerateModeEnabled = accelerateModeEnabled; return this; } + + /** + *

+ * Configures the client to sign payloads in all situations. + *

+ *

+ * Payload signing is optional when chunked encoding is not used and requests are made + * against an HTTPS endpoint. Under these conditions the client will by default + * opt to not sign payloads to optimize performance. If this flag is set to true the + * client will instead always sign payloads. + *

+ *

+ * Note: Payload signing can be expensive, particularly if transferring + * large payloads in a single chunk. Enabling this option will result in a performance + * penalty. + *

+ * + * @param payloadSigningEnabled + * True to explicitly enable payload signing in all situations + */ + public Builder setPayloadSigningEnabled(boolean payloadSigningEnabled) { + this.payloadSigningEnabled = payloadSigningEnabled; + return this; + } + + /** + *

+ * Configures the client to disable chunked encoding for all requests. + *

+ *

+ * The default behavior is to enable chunked encoding automatically for PutObjectRequest and + * UploadPartRequest. Setting this flag will result in disabling chunked encoding for all + * requests. + *

+ *

+ * Note: Enabling this option has performance implications since the checksum for the + * payload will have to be pre-calculated before sending the data. If your payload is large this + * will affect the overall time required to upload an object. Using this option is recommended + * only if your endpoint does not implement chunked uploading. + *

+ * + * @return this Builder instance that can be used for method chaining + */ + public Builder disableChunkedEncoding() { + this.chunkedEncodingDisabled = true; + return this; + } + + /** + *

+ * Configures the client to use the dualstack endpoint for a region + *

+ * S3 supports dualstack endpoints which return both IPv6 and IPv4 values. + * Use of these endpoints is optional. + *

+ */ + public Builder enableDualstack() { + this.dualstackEnabled = true; + return this; + } } /** @@ -96,6 +172,11 @@ public Builder setAccelerateModeEnabled(boolean accelerateModeEnabled) { */ @Deprecated public S3ClientOptions() { + this.pathStyleAccess = DEFAULT_PATH_STYLE_ACCESS; + this.chunkedEncodingDisabled = DEFAULT_CHUNKED_ENCODING_DISABLED; + this.accelerateModeEnabled = DEFAULT_ACCELERATE_MODE_ENABLED; + this.payloadSigningEnabled = DEFAULT_PAYLOAD_SIGNING_ENABLED; + this.dualstackEnabled = DEFAULT_DUALSTACK_ENABLED; } /** @@ -105,12 +186,22 @@ public S3ClientOptions() { @Deprecated public S3ClientOptions(S3ClientOptions other) { this.pathStyleAccess = other.pathStyleAccess; + this.chunkedEncodingDisabled = other.chunkedEncodingDisabled; this.accelerateModeEnabled = other.accelerateModeEnabled; + this.payloadSigningEnabled = other.payloadSigningEnabled; + this.dualstackEnabled = other.dualstackEnabled; } - private S3ClientOptions(boolean pathStyleAccess, boolean accelerateModeEnabled) { + private S3ClientOptions(boolean pathStyleAccess, + boolean chunkedEncodingDisabled, + boolean accelerateModeEnabled, + boolean payloadSigningEnabled, + boolean dualstackEnabled) { this.pathStyleAccess = pathStyleAccess; + this.chunkedEncodingDisabled = chunkedEncodingDisabled; this.accelerateModeEnabled = accelerateModeEnabled; + this.payloadSigningEnabled = payloadSigningEnabled; + this.dualstackEnabled = dualstackEnabled; } /** @@ -135,6 +226,28 @@ public boolean isPathStyleAccess() { return pathStyleAccess; } + /** + *

+ * Returns whether the client has chunked encoding disabled for all requests. + *

+ *

+ * The default behavior is to enable chunked encoding automatically for PutObjectRequest and + * UploadPartRequest. Setting this flag will result in disabling chunked encoding for all + * requests. + *

+ *

+ * Note: Enabling this option has performance implications since the checksum for the + * payload will have to be pre-calculated before sending the data. If your payload is large this + * will affect the overall time required to upload an object. Using this option is recommended + * only if your endpoint does not implement chunked uploading. + *

+ * + * @return True if chunked encoding is explicitly disabled for all requests + */ + public boolean isChunkedEncodingDisabled() { + return chunkedEncodingDisabled; + } + /** *

* Returns whether the client has enabled accelerate mode for getting and @@ -153,6 +266,31 @@ public boolean isAccelerateModeEnabled() { return accelerateModeEnabled; } + + + /** + *

+ * Returns whether the client is configured to sign payloads in all situations. + *

+ *

+ * Payload signing is optional when chunked encoding is not used and requests are made + * against an HTTPS endpoint. Under these conditions the client will by default + * opt to not sign payloads to optimize performance. If this flag is set to true the + * client will instead always sign payloads. + *

+ *

+ * Note: Payload signing can be expensive, particularly if transferring + * large payloads in a single chunk. Enabling this option will result in a performance + * penalty. + *

+ * + * @return True if body signing is explicitly enabled for all requests + */ + public boolean isPayloadSigningEnabled() { + return payloadSigningEnabled; + } + + /** *

* Configures the client to use path-style access for all requests. @@ -176,6 +314,19 @@ public void setPathStyleAccess(boolean pathStyleAccess) { this.pathStyleAccess = pathStyleAccess; } + /** + *

+ * Returns whether the client is configured to use dualstack mode for + * accessing S3. + *

+ * + * @return True if the client will use the dualstack mode + */ + public boolean isDualstackEnabled() { + return dualstackEnabled; + } + + /** *

* Configures the client to use path-style access for all requests. diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/UploadObjectObserver.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/UploadObjectObserver.java new file mode 100644 index 0000000000..54d08ccbb7 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/UploadObjectObserver.java @@ -0,0 +1,294 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import org.apache.commons.logging.LogFactory; + +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.services.s3.internal.MultiFileOutputStream; +import com.amazonaws.services.s3.internal.PartCreationEvent; +import com.amazonaws.services.s3.internal.S3DirectSpi; +import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; +import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; +import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; +import com.amazonaws.services.s3.model.EncryptedInitiateMultipartUploadRequest; +import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; +import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; +import com.amazonaws.services.s3.model.PartETag; +import com.amazonaws.services.s3.model.UploadObjectRequest; +import com.amazonaws.services.s3.model.UploadPartRequest; +import com.amazonaws.services.s3.model.UploadPartResult; + +/** + * An observer that gets notified of ciphertext file creation for the purpose of + * pipelined parallel multi-part uploads of encrypted data to S3. This observer + * is responsible for uploading the files to S3 via multi-part upload, including + * the multi-part upload initiation, individual part uploads, and multi-part + * upload completion. + *

+ * This observer is designed for extension so that custom behavior can be + * provided. A customer observer can be configured via + * {@link UploadObjectRequest#withUploadObjectObserver(UploadObjectObserver)}. + * + * @see UploadObjectRequest + */ +public class UploadObjectObserver { + private final List> futures = new ArrayList>(); + private UploadObjectRequest req; + private String uploadId; + private S3DirectSpi s3direct; + private AmazonS3 s3; + private ExecutorService es; + + /** + * Used to initialized this observer. This method is an SPI (service + * provider interface) that is called from + * AmazonS3EncryptionClient. + *

+ * Implementation of this method should never block. + * + * @param req + * the upload object request + * @param s3direct + * used to perform non-encrypting s3 operation via the current + * instance of s3 (encryption) client + * @param s3 + * the current instance of s3 (encryption) client + * @param es + * the executor service to be used for concurrent uploads + * @return this object + */ + public UploadObjectObserver init(UploadObjectRequest req, + S3DirectSpi s3direct, AmazonS3 s3, ExecutorService es) { + this.req = req; + this.s3direct = s3direct; + this.s3 = s3; + this.es = es; + return this; + } + + protected InitiateMultipartUploadRequest newInitiateMultipartUploadRequest( + UploadObjectRequest req) { + return new EncryptedInitiateMultipartUploadRequest( + req.getBucketName(), req.getKey(), req.getMetadata()) + .withMaterialsDescription(req.getMaterialsDescription()) + .withRedirectLocation(req.getRedirectLocation()) + .withSSEAwsKeyManagementParams(req.getSSEAwsKeyManagementParams()) + .withSSECustomerKey(req.getSSECustomerKey()) + .withStorageClass(req.getStorageClass()) + .withAccessControlList(req.getAccessControlList()) + .withCannedACL(req.getCannedAcl()) + .withGeneralProgressListener(req.getGeneralProgressListener()) + .withRequestMetricCollector(req.getRequestMetricCollector()) + ; + } + + /** + * Notified from + * {@link AmazonS3EncryptionClient#uploadObject(UploadObjectRequest)} to + * initiate a multi-part upload. + * + * @param req + * the upload object request + * @return the initiated multi-part uploadId + */ + public String onUploadInitiation(UploadObjectRequest req) { + InitiateMultipartUploadResult res = + s3.initiateMultipartUpload(newInitiateMultipartUploadRequest(req)); + return this.uploadId = res.getUploadId(); + } + + /** + * Notified from {@link MultiFileOutputStream#fos()} when a part ready for + * upload has been successfully created on disk. By default, this method + * performs the following: + *

    + *
  1. calls {@link #newUploadPartRequest(PartCreationEvent, File)} to + * create an upload-part request for the newly created ciphertext file
  2. + *
  3. call {@link #appendUserAgent(AmazonWebServiceRequest, String)} to + * append the necessary user agent string to the request
  4. + *
  5. and finally submit a concurrent task, which calls the method + * {@link #uploadPart(UploadPartRequest)}, to be performed
  6. + *
+ *

+ * To enable parallel uploads, implementation of this method should never + * block. + * + * @param event + * to represent the completion of a ciphertext file creation + * which is ready for multipart upload to S3. + */ + public void onPartCreate(PartCreationEvent event) { + final File part = event.getPart(); + final UploadPartRequest reqUploadPart = + newUploadPartRequest(event, part); + final OnFileDelete fileDeleteObserver = event.getFileDeleteObserver(); + appendUserAgent(reqUploadPart, AmazonS3EncryptionClient.USER_AGENT); + futures.add(es.submit(new Callable() { + @Override public UploadPartResult call() { + // Upload the ciphertext directly via the non-encrypting + // s3 client + try { + return uploadPart(reqUploadPart); + } finally { + // clean up part already uploaded + if (!part.delete()) { + LogFactory.getLog(getClass()).debug( + "Ignoring failure to delete file " + part + + " which has already been uploaded"); + } else { + if (fileDeleteObserver != null) + fileDeleteObserver.onFileDelete(null); + } + } + } + })); + } + + /** + * Notified from + * {@link AmazonS3EncryptionClient#uploadObject(UploadObjectRequest)} when + * all parts have been successfully uploaded to S3. This method is + * responsible for finishing off the upload by making a complete multi-part + * upload request to S3 with the given list of etags. + * + * @param partETags + * all the etags returned from S3 for the previous part uploads. + * + * @return the completed multi-part upload result + */ + public CompleteMultipartUploadResult onCompletion(List partETags) { + return s3.completeMultipartUpload( + new CompleteMultipartUploadRequest( + req.getBucketName(), req.getKey(), uploadId, partETags)); + } + + /** + * Notified from + * {@link AmazonS3EncryptionClient#uploadObject(UploadObjectRequest)} when + * failed to upload any part. This method is responsible for cancelling + * ongoing uploads and aborting the multi-part upload request. + */ + public void onAbort() { + for (Future future : getFutures()) { + future.cancel(true); + } + if (uploadId != null) { + try { + s3.abortMultipartUpload(new AbortMultipartUploadRequest( + req.getBucketName(), req.getKey(), uploadId)); + } catch (Exception e) { + LogFactory.getLog(getClass()) + .debug("Failed to abort multi-part upload: " + uploadId, e); + } + } + } + /** + * Creates and returns an upload-part request corresponding to a ciphertext + * file upon a part-creation event. + * + * @param event + * the part-creation event of the ciphertxt file. + * @param part + * the created ciphertext file corresponding to the upload-part + */ + protected UploadPartRequest newUploadPartRequest(PartCreationEvent event, + final File part) { + final UploadPartRequest reqUploadPart = new UploadPartRequest() + .withBucketName(req.getBucketName()) + .withFile(part) + .withKey(req.getKey()) + .withPartNumber(event.getPartNumber()) + .withPartSize(part.length()) + .withLastPart(event.isLastPart()) + .withUploadId(uploadId) + .withObjectMetadata(req.getUploadPartMetadata()) + ; + return reqUploadPart; + } + + /** + * Uploads the ciphertext via the non-encrypting s3 client. + * @param reqUploadPart part upload request + * @return the result of the part upload when there is no exception + */ + protected UploadPartResult uploadPart(UploadPartRequest reqUploadPart) { + // Upload the ciphertext directly via the non-encrypting + // s3 client + return s3direct.uploadPart(reqUploadPart); + } + + /** + * Appends the given user agent to the given request. + * + * @return the given request. + */ + protected X appendUserAgent( + X request, String userAgent) { + request.getRequestClientOptions().appendUserAgent(userAgent); + return request; + } + + public List> getFutures() { + return futures; + } + + /** + * Returns the request initialized via + * {@link #init(UploadObjectRequest, S3DirectSpi, AmazonS3, ExecutorService)} + */ + protected UploadObjectRequest getRequest() { + return req; + } + + /** + * Returns the upload id after the multi-part upload has been initiated via + * {@link #onUploadInitiation(UploadObjectRequest)} + */ + protected String getUploadId() { + return uploadId; + } + + /** + * Returns the S3DirectSpi instance initialized via + * {@link #init(UploadObjectRequest, S3DirectSpi, AmazonS3, ExecutorService)} + */ + protected S3DirectSpi getS3DirectSpi() { + return s3direct; + } + + /** + * Returns the AmazonS3 instance initialized via + * {@link #init(UploadObjectRequest, S3DirectSpi, AmazonS3, ExecutorService)} + */ + protected AmazonS3 getAmazonS3() { + return s3; + } + + /** + * Returns the ExecutorService instance initialized via + * {@link #init(UploadObjectRequest, S3DirectSpi, AmazonS3, ExecutorService)} + */ + protected ExecutorService getExecutorService() { + return es; + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/AbstractS3ResponseHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/AbstractS3ResponseHandler.java index aeb7b2564b..06ed60093f 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/AbstractS3ResponseHandler.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/AbstractS3ResponseHandler.java @@ -15,6 +15,7 @@ package com.amazonaws.services.s3.internal; +import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonWebServiceResponse; import com.amazonaws.ResponseMetadata; import com.amazonaws.http.HttpResponse; @@ -55,6 +56,8 @@ public abstract class AbstractS3ResponseHandler ignoredHeaders.add(Headers.SERVER); ignoredHeaders.add(Headers.REQUEST_ID); ignoredHeaders.add(Headers.EXTENDED_REQUEST_ID); + ignoredHeaders.add(Headers.CLOUD_FRONT_ID); + ignoredHeaders.add(Headers.CONNECTION); } /** @@ -80,13 +83,15 @@ public boolean needsConnectionLeftOpen() { * the result to be plugged in. */ protected AmazonWebServiceResponse parseResponseMetadata(HttpResponse response) { - AmazonWebServiceResponse awsResponse = new AmazonWebServiceResponse(); - String awsRequestId = response.getHeaders().get(Headers.REQUEST_ID); - String hostId = response.getHeaders().get(Headers.EXTENDED_REQUEST_ID); + final AmazonWebServiceResponse awsResponse = new AmazonWebServiceResponse(); + final String awsRequestId = response.getHeaders().get(Headers.REQUEST_ID); + final String hostId = response.getHeaders().get(Headers.EXTENDED_REQUEST_ID); + final String cloudFrontId = response.getHeaders().get(Headers.CLOUD_FRONT_ID); - Map metadataMap = new HashMap(); + final Map metadataMap = new HashMap(); metadataMap.put(ResponseMetadata.AWS_REQUEST_ID, awsRequestId); metadataMap.put(S3ResponseMetadata.HOST_ID, hostId); + metadataMap.put(S3ResponseMetadata.CLOUD_FRONT_ID, cloudFrontId); awsResponse.setResponseMetadata(new S3ResponseMetadata(metadataMap)); return awsResponse; @@ -102,37 +107,49 @@ protected AmazonWebServiceResponse parseResponseMetadata(HttpResponse respons * headers. */ protected void populateObjectMetadata(HttpResponse response, ObjectMetadata metadata) { - for (Entry header : response.getHeaders().entrySet()) { + for (final Entry header : response.getHeaders().entrySet()) { String key = header.getKey(); if (key.startsWith(Headers.S3_USER_METADATA_PREFIX)) { key = key.substring(Headers.S3_USER_METADATA_PREFIX.length()); metadata.addUserMetadata(key, header.getValue()); } else if (ignoredHeaders.contains(key)) { // ignore... - } else if (key.equals(Headers.LAST_MODIFIED)) { + } else if (key.equalsIgnoreCase(Headers.LAST_MODIFIED)) { try { metadata.setHeader(key, ServiceUtils.parseRfc822Date(header.getValue())); - } catch (Exception pe) { + } catch (final Exception pe) { log.warn("Unable to parse last modified date: " + header.getValue(), pe); } - } else if (key.equals(Headers.CONTENT_LENGTH)) { + } else if (key.equalsIgnoreCase(Headers.CONTENT_LENGTH)) { try { metadata.setHeader(key, Long.parseLong(header.getValue())); - } catch (NumberFormatException nfe) { + } catch (final NumberFormatException nfe) { log.warn("Unable to parse content length: " + header.getValue(), nfe); } - } else if (key.equals(Headers.ETAG)) { + } else if (key.equalsIgnoreCase(Headers.ETAG)) { metadata.setHeader(key, ServiceUtils.removeQuotes(header.getValue())); - } else if (key.equals(Headers.EXPIRES)) { + } else if (key.equalsIgnoreCase(Headers.EXPIRES)) { try { metadata.setHttpExpiresDate(DateUtils.parseRFC822Date(header.getValue())); - } catch (Exception pe) { + } catch (final Exception pe) { log.warn("Unable to parse http expiration date: " + header.getValue(), pe); } - } else if (key.equals(Headers.EXPIRATION)) { + } else if (key.equalsIgnoreCase(Headers.EXPIRATION)) { new ObjectExpirationHeaderHandler().handle(metadata, response); - } else if (key.equals(Headers.RESTORE)) { + } else if (key.equalsIgnoreCase(Headers.RESTORE)) { new ObjectRestoreHeaderHandler().handle(metadata, response); + } else if (key.equalsIgnoreCase(Headers.REQUESTER_CHARGED_HEADER)) { + new S3RequesterChargedHeaderHandler().handle(metadata, + response); + } else if (key.equalsIgnoreCase(Headers.S3_PARTS_COUNT)) { + try { + metadata.setHeader(key, Integer.parseInt(header.getValue())); + } catch (final NumberFormatException nfe) { + throw new AmazonClientException( + "Unable to parse part count. Header x-amz-mp-parts-count has corrupted data" + + nfe.getMessage(), + nfe); + } } else { metadata.setHeader(key, header.getValue()); } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/BucketNameUtils.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/BucketNameUtils.java index 55c8eb9234..d8ce593550 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/BucketNameUtils.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/BucketNameUtils.java @@ -15,6 +15,8 @@ package com.amazonaws.services.s3.internal; +import java.util.regex.Pattern; + /** * Utilities for working with Amazon S3 bucket names, such as validation and * checked to see if they are compatible with DNS addressing. @@ -24,6 +26,8 @@ public enum BucketNameUtils { private static final int MIN_BUCKET_NAME_LENGTH = 3; private static final int MAX_BUCKET_NAME_LENGTH = 63; + private static final Pattern ipAddressPattern = Pattern.compile("(\\d+\\.){3}\\d+"); + /** * Validates that the specified bucket name is valid for Amazon S3 V2 naming * (i.e. DNS addressable in virtual host style). Throws an @@ -85,13 +89,20 @@ private static boolean isValidV2BucketName(final String bucketName, return exception( throwOnError, - "Bucket name should be between 3 and 63 characters long"); + "Bucket name should be between " + MIN_BUCKET_NAME_LENGTH + " and " + + MAX_BUCKET_NAME_LENGTH + " characters long"); + } + + if (ipAddressPattern.matcher(bucketName).matches()) { + return exception( + throwOnError, + "Bucket name must not be formatted as an IP Address"); } char previous = '\0'; for (int i = 0; i < bucketName.length(); ++i) { - char next = bucketName.charAt(i); + final char next = bucketName.charAt(i); if (next >= 'A' && next <= 'Z') { return exception( @@ -106,6 +117,11 @@ private static boolean isValidV2BucketName(final String bucketName, } if (next == '.') { + if (previous == '\0') { + return exception( + throwOnError, + "Bucket name should not begin with a period"); + } if (previous == '.') { return exception( throwOnError, @@ -116,18 +132,17 @@ private static boolean isValidV2BucketName(final String bucketName, throwOnError, "Bucket name should not contain dashes next to periods"); } - /** - * . in bucket name may cause host name verification failure - * when using virtual host style addressing. return false to - * fall back to path style addressing. - */ - return false; } else if (next == '-') { if (previous == '.') { return exception( throwOnError, "Bucket name should not contain dashes next to periods"); } + if (previous == '\0') { + return exception( + throwOnError, + "Bucket name should not begin with a '-'"); + } } else if ((next < '0') || (next > '9' && next < 'a') || (next > 'z')) { @@ -146,6 +161,12 @@ private static boolean isValidV2BucketName(final String bucketName, "Bucket name should not end with '-' or '.'"); } + // if it contains . then its not a valid dns name but its a valid bucket + // name + if (bucketName.contains(".")) { + return false; + } + return true; } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/CompleteMultipartUploadRetryCondition.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/CompleteMultipartUploadRetryCondition.java new file mode 100644 index 0000000000..c5a8f2dcad --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/CompleteMultipartUploadRetryCondition.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; + +import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.retry.RetryPolicy; +import com.amazonaws.services.s3.model.AmazonS3Exception; + +public class CompleteMultipartUploadRetryCondition implements RetryPolicy.RetryCondition { + + private static final int MAX_RETRY_ATTEMPTS = 3; + + private static final String ERROR_CODE = "InternalError"; + private static final String RETYABLE_ERROR_MESSAGE = "Please try again."; + + private final int maxCompleteMultipartUploadRetries; + + public CompleteMultipartUploadRetryCondition() { + this(MAX_RETRY_ATTEMPTS); + } + + /** + * For testing purposes. + */ + CompleteMultipartUploadRetryCondition( + int maxRetryAttempts) { + this.maxCompleteMultipartUploadRetries = maxRetryAttempts; + } + + @Override + public boolean shouldRetry(AmazonWebServiceRequest originalRequest, + AmazonClientException exception, int retriesAttempted) { + + if (exception instanceof AmazonS3Exception) { + return test((AmazonS3Exception) exception) + && retriesAttempted < maxCompleteMultipartUploadRetries; + } + return false; + } + + boolean test(AmazonS3Exception exception) { + if (exception == null || exception.getErrorCode() == null || + exception.getErrorMessage() == null) { + return false; + } + return exception.getErrorCode().contains(ERROR_CODE) && + exception.getErrorMessage().contains(RETYABLE_ERROR_MESSAGE); + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/Constants.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/Constants.java index 9f6c9ca9f8..573df0d239 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/Constants.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/Constants.java @@ -15,8 +15,10 @@ package com.amazonaws.services.s3.internal; +import com.amazonaws.RequestClientOptions; import com.amazonaws.SDKGlobalConfiguration; import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.SSEAlgorithm; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -27,15 +29,24 @@ public class Constants { /** Default hostname for the S3 service endpoint */ - public static String S3_HOSTNAME = "s3.amazonaws.com"; + public static final String S3_HOSTNAME = "s3.amazonaws.com"; + /** Hostname for the s3-external-1 service endpoint */ + public static final String S3_EXTERNAL_1_HOSTNAME = "s3-external-1.amazonaws.com"; /** Service hostname for accessing accelerated S3 buckets */ public static final String S3_ACCELERATE_HOSTNAME = "s3-accelerate.amazonaws.com"; + public static final String S3_ACCELERATE_DUALSTACK_HOSTNAME = "s3-accelerate.dualstack.amazonaws.com"; - /** Service name for Amazon S3 */ - public static String S3_SERVICE_NAME = "Amazon S3"; + /** Dualstack qualifier for S3 */ + public static final String S3_DUALSTACK_QUALIFIER = "dualstack"; + + /** Service display name for Amazon S3 (not to be used in SigV4 signing) */ + public static final String S3_SERVICE_DISPLAY_NAME = "Amazon S3"; /** Default encoding used for text data */ - public static String DEFAULT_ENCODING = "UTF-8"; + public static final String DEFAULT_ENCODING = "UTF-8"; + + /** URL encoding for s3 object keys */ + public static final String URL_ENCODING = "url"; /** HMAC/SHA1 Algorithm per RFC 2104, used when signing S3 requests */ public static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; @@ -69,30 +80,52 @@ public class Constants { * buffer of this size will be created and filled with the first bytes from * a stream being uploaded so that any transmit errors that occur in that * section of the data can be automatically retried without the caller's - * intervention. + * intervention. Add 1 to get around an implementation quirk when used + * against BufferedInputStream. */ - public static final int DEFAULT_STREAM_BUFFER_SIZE = 128 * KB; + public static final int DEFAULT_STREAM_BUFFER_SIZE = RequestClientOptions.DEFAULT_STREAM_BUFFER_SIZE; /** * Returns the buffer size override if it is specified in the system * property, otherwise returns the default value. */ + @Deprecated public static int getStreamBufferSize() { int streamBufferSize = DEFAULT_STREAM_BUFFER_SIZE; - String bufferSizeOverride = + final String bufferSizeOverride = System.getProperty(SDKGlobalConfiguration .DEFAULT_S3_STREAM_BUFFER_SIZE); if (bufferSizeOverride != null) { try { streamBufferSize = Integer.parseInt(bufferSizeOverride); - } catch (Exception e) { + } catch (final Exception e) { log.warn("Unable to parse buffer size override from value: " + bufferSizeOverride); } } return streamBufferSize; } + /** + * Returns the value of the system property + * {@link SDKGlobalConfiguration#DEFAULT_S3_STREAM_BUFFER_SIZE} as an + * Integer; or null if not set. This method exists for backward + * compatibility reasons. + */ + public static Integer getS3StreamBufferSize() { + final String s = + System.getProperty(SDKGlobalConfiguration.DEFAULT_S3_STREAM_BUFFER_SIZE); + if (s == null) { + return null; + } + try { + return Integer.valueOf(s); + } catch (final Exception e) { + log.warn("Unable to parse buffer size override from value: " + s); + } + return null; + } + /** Shared logger for client events */ private static Log log = LogFactory.getLog(AmazonS3Client.class); @@ -105,4 +138,7 @@ public static int getStreamBufferSize() { // Constant indicating the requester pays for data transfer cost for a // bucket. public static final String REQUESTER_PAYS = "requester"; + + public static final String SSE_AWS_KMS_ENCRYPTION_SCHEME = + SSEAlgorithm.KMS.getAlgorithm(); } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/DeleteObjectTaggingHeaderHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/DeleteObjectTaggingHeaderHandler.java new file mode 100644 index 0000000000..ce96e5459e --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/DeleteObjectTaggingHeaderHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; + +import com.amazonaws.http.HttpResponse; +import com.amazonaws.services.s3.Headers; +import com.amazonaws.services.s3.model.DeleteObjectTaggingResult; + +import java.util.Map; + +/** + * Handler for retrieving headers from the {@link + * com.amazonaws.services.s3.model.DeleteObjectTaggingRequest} response. + */ +public class DeleteObjectTaggingHeaderHandler implements HeaderHandler { + @Override + public void handle(DeleteObjectTaggingResult result, HttpResponse response) { + result.setVersionId(response.getHeaders().get(Headers.S3_VERSION_ID)); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/DeleteObjectsResponse.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/DeleteObjectsResponse.java index fee716ae11..8009be5743 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/DeleteObjectsResponse.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/DeleteObjectsResponse.java @@ -33,11 +33,17 @@ * @see MultiObjectDeleteException * @see AmazonS3Client#deleteObjects(com.amazonaws.services.s3.model.DeleteObjectsRequest) */ -public class DeleteObjectsResponse { +public class DeleteObjectsResponse implements S3RequesterChargedResult { private List deletedObjects; private List errors; + /** + * Indicate if the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterCharged; + public DeleteObjectsResponse() { this(new ArrayList(), new ArrayList()); } @@ -62,4 +68,14 @@ public List getErrors() { public void setErrors(List errors) { this.errors = errors; } + + @Override + public boolean isRequesterCharged() { + return isRequesterCharged; + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + this.isRequesterCharged = isRequesterCharged; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/FileDeletionEvent.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/FileDeletionEvent.java new file mode 100644 index 0000000000..a722ecdc7c --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/FileDeletionEvent.java @@ -0,0 +1,23 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; + +/** + * A file deletion event. + */ +public class FileDeletionEvent { + // currently only a placeholder so method signature won't be affected by + // future changes +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/GetObjectTaggingResponseHeaderHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/GetObjectTaggingResponseHeaderHandler.java new file mode 100644 index 0000000000..de6f5aeab6 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/GetObjectTaggingResponseHeaderHandler.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Portions copyright 2006-2009 James Murty. Please see LICENSE.txt + * for applicable license terms and NOTICE.txt for applicable notices. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; + +import com.amazonaws.http.HttpResponse; +import com.amazonaws.services.s3.Headers; +import com.amazonaws.services.s3.model.GetObjectTaggingResult; + +/** + * Handler for retrieving headers from the {@link + * com.amazonaws.services.s3.model.GetObjectTaggingRequest} response. + */ +public class GetObjectTaggingResponseHeaderHandler implements HeaderHandler { + @Override + public void handle(GetObjectTaggingResult result, HttpResponse response) { + result.setVersionId(response.getHeaders().get(Headers.S3_VERSION_ID)); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/MD5DigestCalculatingInputStream.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/MD5DigestCalculatingInputStream.java index bd5663c54b..ea2188ae35 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/MD5DigestCalculatingInputStream.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/MD5DigestCalculatingInputStream.java @@ -37,12 +37,24 @@ public class MD5DigestCalculatingInputStream extends SdkFilterInputStream { public MD5DigestCalculatingInputStream(InputStream in) { super(in); + digest = newMD5(); + } + + private MessageDigest newMD5() { try { - digest = MessageDigest.getInstance("MD5"); + return MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { // should never occur throw new IllegalStateException("unexpected", e); } } + + private MessageDigest cloneFrom(MessageDigest from) { + try { + return (MessageDigest)from.clone(); + } catch (CloneNotSupportedException e) { // should never occur + throw new IllegalStateException("unexpected", e); + } + } public byte[] getMd5Digest() { return digest.digest(); @@ -50,13 +62,9 @@ public byte[] getMd5Digest() { @Override public void mark(int readlimit) { - super.mark(readlimit); if (markSupported()) { - try { - digestLastMarked = (MessageDigest) digest.clone(); - } catch (CloneNotSupportedException e) { // should never occur - throw new IllegalStateException("unexpected", e); - } + super.mark(readlimit); + digestLastMarked = cloneFrom(digest); } } @@ -65,13 +73,15 @@ public void mark(int readlimit) { */ @Override public void reset() throws IOException { - super.reset(); - if (digestLastMarked != null) { - try { - digest = (MessageDigest) digestLastMarked.clone(); - } catch (CloneNotSupportedException e) { // should never occur - throw new IllegalStateException("unexpected", e); - } + if (markSupported()) { + super.reset(); + digest = (digestLastMarked == null) + // This is necessary so that should there be a reset without a + // preceding mark, the MD5 would still be computed correctly. + ? newMD5() + : cloneFrom(digestLastMarked); + } else { + throw new IOException("mark/reset not supported"); } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/MultiFileOutputStream.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/MultiFileOutputStream.java new file mode 100644 index 0000000000..113e1800a8 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/MultiFileOutputStream.java @@ -0,0 +1,297 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; + +import com.amazonaws.AbortedException; +import com.amazonaws.services.s3.OnFileDelete; +import com.amazonaws.services.s3.UploadObjectObserver; + +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; +import java.util.concurrent.Semaphore; + +/** + * Used to split an output stream into multiple files for purposes such as + * parallel uploads. + */ +public class MultiFileOutputStream extends OutputStream implements OnFileDelete { + static final int DEFAULT_PART_SIZE = 5 << 20; // 5MB + private final File root; + private final String namePrefix; + private int filesCreated; + private long partSize = DEFAULT_PART_SIZE; + private long diskLimit = Long.MAX_VALUE; + private UploadObjectObserver observer; + /** + * Number of bytes that have been written to the current file. + */ + private int currFileBytesWritten; + /** + * Total number of bytes written to all files so far. + */ + private long totalBytesWritten; + private FileOutputStream os; + private boolean closed; + + /** null means no blocking necessary. */ + private Semaphore diskPermits; + + /** + * Construct an instance to use the default temporary directory and temp + * file naming convention. The + * {@link #init(UploadObjectObserver, long, long)} must be called before + * this stream is considered fully initialized. + */ + public MultiFileOutputStream() { + root = new File(System.getProperty("java.io.tmpdir")); + namePrefix = yyMMdd_hhmmss() + "." + UUID.randomUUID(); + } + + /** + * Construct an instance to use the specified directory for temp file + * creations, and the specified prefix for temp file naming. The + * {@link #init(UploadObjectObserver, long, long)} must be called before + * this stream is considered fully initialized. + */ + public MultiFileOutputStream(File root, String namePrefix) { + if (root == null || !root.isDirectory() || !root.canWrite()) { + throw new IllegalArgumentException(root + + " must be a writable directory"); + } + if (namePrefix == null || namePrefix.trim().length() == 0) { + throw new IllegalArgumentException( + "Please specify a non-empty name prefix"); + } + this.root = root; + this.namePrefix = namePrefix; + } + + /** + * Used to initialized this stream. This method is an SPI (service provider + * interface) that is called from AmazonS3EncryptionClient. + *

+ * Implementation of this method should never block. + * + * @param observer + * the upload object observer + * @param partSize + * part size for multi-part upload + * @param diskLimit + * the maximum disk space to be used for this multi-part upload + * + * @return this object + */ + public MultiFileOutputStream init(UploadObjectObserver observer, + long partSize, long diskLimit) { + if (observer == null) { + throw new IllegalArgumentException("Observer must be specified"); + } + this.observer = observer; + if (diskLimit < partSize << 1) { + throw new IllegalArgumentException( + "Maximum temporary disk space must be at least twice as large as the part size: partSize=" + + partSize + ", diskSize=" + diskLimit); + } + this.partSize = partSize; + this.diskLimit = diskLimit; + final int max = (int)(diskLimit/partSize); + this.diskPermits = max < 0 ? null : new Semaphore(max); + return this; + } + + /** + * {@inheritDoc} + * + * This method would block as necessary if running out of disk space. + */ + @Override + public void write(int b) throws IOException { + fos().write(b); + currFileBytesWritten++; + totalBytesWritten++; + } + + /** + * {@inheritDoc} + * + * This method would block as necessary if running out of disk space. + */ + @Override + public void write(byte[] b) throws IOException { + if (b.length == 0) { + return; + } + fos().write(b); + currFileBytesWritten += b.length; + totalBytesWritten += b.length; + } + + /** + * {@inheritDoc} + * + * This method would block as necessary if running out of disk space. + */ + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (b.length == 0) { + return; + } + fos().write(b, off, len); + currFileBytesWritten += len; + totalBytesWritten += len; + } + + /** + * Returns the file output stream to be used for writing, blocking if + * necessary if running out of disk space. + * + * @throws InterruptedException if the running thread was interrupted + */ + private FileOutputStream fos() throws IOException { + if (closed) { + throw new IOException("Output stream is already closed"); + } + if (os == null || currFileBytesWritten >= partSize) { + if (os != null) { + os.close(); + // notify about the new file ready for processing + observer.onPartCreate(new PartCreationEvent( + getFile(filesCreated), filesCreated, false, this)); + } + currFileBytesWritten = 0; + filesCreated++; + blockIfNecessary(); + final File file = getFile(filesCreated); + file.deleteOnExit(); + os = new FileOutputStream(file); + } + return os; + } + + @Override + public void onFileDelete(FileDeletionEvent event) { + if (diskPermits != null) { + diskPermits.release(); + } + } + + /** + * Blocks the running thread if running out of disk space. + * + * @throws AbortedException + * if the running thread is interrupted while acquiring a + * semaphore + */ + private void blockIfNecessary() { + if (diskPermits == null || diskLimit == Long.MAX_VALUE) { + return; + } + try { + diskPermits.acquire(); + } catch (final InterruptedException e) { + // don't want to re-interrupt so it won't cause SDK stream to be + // closed in case the thread is reused for a different request + throw new AbortedException(e); + } + } + + @Override + public void flush() throws IOException { + if (os != null) { + os.flush(); + } + } + + @Override + public void close() throws IOException { + if (closed) { + return; + } + closed = true; + if (os != null) { + os.close(); + final File lastPart = getFile(filesCreated); + if (lastPart.length() == 0) { + if (!lastPart.delete()) { + LogFactory.getLog(getClass()).debug( + "Ignoring failure to delete empty file " + lastPart); + } + } else { + // notify about the new file ready for processing + observer.onPartCreate(new PartCreationEvent( + getFile(filesCreated), filesCreated, true, this)); + } + } + } + + public void cleanup() { + for (int i=0; i < getNumFilesWritten(); i++) { + final File f = getFile(i); + if (f.exists()) { + if (!f.delete()) { + LogFactory.getLog(getClass()).debug( + "Ignoring failure to delete file " + f); + } + } + } + } + + /** + * @return the number of files written with the specified prefix with the + * part number as the file extension. + */ + public int getNumFilesWritten() { + return filesCreated; + } + + public File getFile(int partNumber) { + return new File(root, namePrefix + "." + partNumber); + } + + public long getPartSize() { + return partSize; + } + + public File getRoot() { + return root; + } + + public String getNamePrefix() { + return namePrefix; + } + + public long getTotalBytesWritten() { + return totalBytesWritten; + } + + static String yyMMdd_hhmmss() { + return new SimpleDateFormat("yyMMdd-hhmmss").format(new Date()); + } + + public boolean isClosed() { + return closed; + } + + public long getDiskLimit() { + return diskLimit; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/ObjectRestoreHeaderHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/ObjectRestoreHeaderHandler.java index 5a90a38d63..d4c0eb9ce3 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/ObjectRestoreHeaderHandler.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/ObjectRestoreHeaderHandler.java @@ -54,7 +54,10 @@ public void handle(T result, HttpResponse response) { String restoreHeader = response.getHeaders().get(Headers.RESTORE); if (restoreHeader != null) { result.setRestoreExpirationTime(parseDate(restoreHeader)); - result.setOngoingRestore(parseBoolean(restoreHeader)); + Boolean onGoingRestore = parseBoolean(restoreHeader); + if (onGoingRestore != null) { + result.setOngoingRestore(onGoingRestore); + } } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/PartCreationEvent.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/PartCreationEvent.java new file mode 100644 index 0000000000..29bd8242a3 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/PartCreationEvent.java @@ -0,0 +1,58 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; + +import java.io.File; + +import com.amazonaws.services.s3.OnFileDelete; + +public class PartCreationEvent { + private final File part; + private final int partNumber; + private final boolean isLastPart; + private final OnFileDelete fileDeleteObserver; + + PartCreationEvent(File part, int partNumber, boolean isLastPart, + OnFileDelete fileDeleteObserver) { + if (part == null) + throw new IllegalArgumentException("part must not be specified"); + this.part = part; + this.partNumber = partNumber; + this.isLastPart = isLastPart; + this.fileDeleteObserver = fileDeleteObserver; + } + + /** + * Returns a non-null part (in the form of a file) for multi-part upload. + */ + public File getPart() { + return part; + } + + public int getPartNumber() { + return partNumber; + } + + public boolean isLastPart() { + return isLastPart; + } + + /** + * Returns an observer for file deletion; or null if there is none. + */ + public OnFileDelete getFileDeleteObserver() { + return fileDeleteObserver; + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/ProgressReportingInputStream.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/ProgressReportingInputStream.java index 5f51e430dc..9295e83f5c 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/ProgressReportingInputStream.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/ProgressReportingInputStream.java @@ -89,21 +89,34 @@ public boolean getFireCompletedEvent() { @Override public int read() throws IOException { - int data = super.read(); - if (data == -1) + final int data = super.read(); + if (data == -1) { notifyCompleted(); - if (data != -1) + } + if (data != -1) { notify(1); + } return data; } + @Override + public void reset() throws IOException { + super.reset(); + final ProgressEvent event = new ProgressEvent(unnotifiedByteCount); + event.setEventCode(ProgressEvent.RESET_EVENT_CODE); + listener.progressChanged(event); + unnotifiedByteCount = 0; + } + @Override public int read(byte[] b, int off, int len) throws IOException { - int bytesRead = super.read(b, off, len); - if (bytesRead == -1) + final int bytesRead = super.read(b, off, len); + if (bytesRead == -1) { notifyCompleted(); - if (bytesRead != -1) + } + if (bytesRead != -1) { notify(bytesRead); + } return bytesRead; } @@ -117,10 +130,11 @@ public void close() throws IOException { } private void notifyCompleted() { - if (fireCompletedEvent == false) + if (fireCompletedEvent == false) { return; + } - ProgressEvent event = new ProgressEvent(unnotifiedByteCount); + final ProgressEvent event = new ProgressEvent(unnotifiedByteCount); event.setEventCode(com.amazonaws.event.ProgressEvent.COMPLETED_EVENT_CODE); unnotifiedByteCount = 0; listener.progressChanged(event); diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/RestUtils.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/RestUtils.java index 8829c7a01b..ccd3834b40 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/RestUtils.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/RestUtils.java @@ -24,6 +24,7 @@ import com.amazonaws.util.StringUtils; import java.util.Arrays; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -40,8 +41,9 @@ public class RestUtils { */ private static final List SIGNED_PARAMETERS = Arrays.asList(new String[] { "acl", "torrent", "logging", "location", "policy", "requestPayment", "versioning", - "versions", "versionId", "notification", "uploadId", "uploads", "partNumber", - "website", "delete", "lifecycle", "tagging", "cors", "restore", "accelerate", + "versions", "versionId", "notification", "uploadId", "uploads", "partNumber", "website", + "delete", "lifecycle", "tagging", "cors", "restore", "replication", "accelerate", + "inventory", "analytics", "metrics", ResponseHeaderOverrides.RESPONSE_HEADER_CACHE_CONTROL, ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_DISPOSITION, ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_ENCODING, @@ -54,26 +56,51 @@ public class RestUtils { * Calculate the canonical string for a REST/HTTP request to S3. When * expires is non-null, it will be used instead of the Date header. */ - public static String makeS3CanonicalString(String method, String resource, - Request request, String expires) - { - StringBuilder buf = new StringBuilder(); + public static String makeS3CanonicalString(String method, + String resource, Request request, String expires) { + return makeS3CanonicalString(method, resource, request, expires, null); + } + + /** + * Calculate the canonical string for a REST/HTTP request to S3. + * + * @param method + * The HTTP verb. + * @param resource + * The HTTP-encoded resource path. + * @param request + * The request to be canonicalized. + * @param expires + * When expires is non-null, it will be used instead of the Date + * header. + * @param additionalQueryParamsToSign + * A collection of user-specified query parameters that should be + * included in the canonical request, in addition to those + * default parameters that are always signed. + * @return The canonical string representation for the given S3 request. + */ + public static String makeS3CanonicalString(String method, + String resource, Request request, String expires, + Collection additionalQueryParamsToSign) { + + final StringBuilder buf = new StringBuilder(); buf.append(method + "\n"); // Add all interesting headers to a list, then sort them. "Interesting" // is defined as Content-MD5, Content-Type, Date, and x-amz- - Map headersMap = request.getHeaders(); - SortedMap interestingHeaders = new TreeMap(); + final Map headersMap = request.getHeaders(); + final SortedMap interestingHeaders = new TreeMap(); if (headersMap != null && headersMap.size() > 0) { - Iterator> headerIter = headersMap.entrySet().iterator(); + final Iterator> headerIter = headersMap.entrySet().iterator(); while (headerIter.hasNext()) { - Map.Entry entry = headerIter.next(); - String key = entry.getKey(); - String value = entry.getValue(); + final Map.Entry entry = headerIter.next(); + final String key = entry.getKey(); + final String value = entry.getValue(); - if (key == null) + if (key == null) { continue; - String lk = StringUtils.lowerCase(key.toString()); + } + final String lk = StringUtils.lowerCase(key); // Ignore any headers that are not particularly interesting. if (lk.equals("content-type") || lk.equals("content-md5") || lk.equals("date") || @@ -107,18 +134,17 @@ public static String makeS3CanonicalString(String method, String resource, // Any parameters that are prefixed with "x-amz-" need to be included // in the headers section of the canonical string to sign - for (Map.Entry parameter : request.getParameters().entrySet()) { + for (final Map.Entry parameter : request.getParameters().entrySet()) { if (parameter.getKey().startsWith("x-amz-")) { interestingHeaders.put(parameter.getKey(), parameter.getValue()); } } // Add all the interesting headers (i.e.: all that startwith x-amz- ;-)) - for (Iterator> i = interestingHeaders.entrySet().iterator(); i - .hasNext();) { - Map.Entry entry = i.next(); - String key = entry.getKey(); - String value = entry.getValue(); + for (final Iterator> i = interestingHeaders.entrySet().iterator(); i.hasNext(); ) { + final Map.Entry entry = i.next(); + final String key = entry.getKey(); + final String value = entry.getValue(); if (key.startsWith(Headers.AMAZON_PREFIX)) { buf.append(key).append(':'); @@ -133,19 +159,26 @@ public static String makeS3CanonicalString(String method, String resource, // Add all the interesting parameters buf.append(resource); - String[] parameterNames = request.getParameters().keySet().toArray( + final String[] parameterNames = request.getParameters().keySet().toArray( new String[request.getParameters().size()]); Arrays.sort(parameterNames); char separator = '?'; - for (String parameterName : parameterNames) { + for (final String parameterName : parameterNames) { // Skip any parameters that aren't part of the canonical signed // string - if (SIGNED_PARAMETERS.contains(parameterName) == false) + if ( !SIGNED_PARAMETERS.contains(parameterName) + && + (additionalQueryParamsToSign == null || + !additionalQueryParamsToSign.contains(parameterName)) + ) { continue; + } + if(buf.length() == 0 ) { + buf.append(separator); + } - buf.append(separator); buf.append(parameterName); - String parameterValue = request.getParameters().get(parameterName); + final String parameterValue = request.getParameters().get(parameterName); if (parameterValue != null) { buf.append("=").append(parameterValue); } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3Direct.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3Direct.java index 5fce972a19..02629beeea 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3Direct.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3Direct.java @@ -1,4 +1,17 @@ - +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; @@ -22,7 +35,7 @@ * Used to provide direct access to the underlying/original S3 client methods * free of any added cryptographic functionalities. */ -public abstract class S3Direct { +public abstract class S3Direct implements S3DirectSpi { public abstract PutObjectResult putObject(PutObjectRequest req); public abstract S3Object getObject(GetObjectRequest req); diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3DirectSpi.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3DirectSpi.java new file mode 100644 index 0000000000..707a0ea34b --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3DirectSpi.java @@ -0,0 +1,56 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; + +import java.io.File; + +import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; +import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; +import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; +import com.amazonaws.services.s3.model.CopyPartRequest; +import com.amazonaws.services.s3.model.CopyPartResult; +import com.amazonaws.services.s3.model.GetObjectRequest; +import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; +import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.UploadPartRequest; +import com.amazonaws.services.s3.model.UploadPartResult; + +/** + * A Service Provider Interface that allows direct access to the underlying + * non-encrypting S3 client of an S3 encryption client instance. + */ +public interface S3DirectSpi { + public PutObjectResult putObject(PutObjectRequest req); + + public S3Object getObject(GetObjectRequest req); + + public ObjectMetadata getObject(GetObjectRequest req, File dest); + + public CompleteMultipartUploadResult completeMultipartUpload( + CompleteMultipartUploadRequest req); + + public InitiateMultipartUploadResult initiateMultipartUpload( + InitiateMultipartUploadRequest req); + + public UploadPartResult uploadPart(UploadPartRequest req); + + public CopyPartResult copyPart(CopyPartRequest req); + + public void abortMultipartUpload(AbortMultipartUploadRequest req); +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3ErrorResponseHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3ErrorResponseHandler.java index df156f41b0..54659e5371 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3ErrorResponseHandler.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3ErrorResponseHandler.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; import java.util.Map; /** @@ -61,9 +62,10 @@ public AmazonServiceException handle(HttpResponse errorResponse) throws IOExcept String content = ""; try { content = IOUtils.toString(is); - } catch (IOException ex) { - if (log.isDebugEnabled()) + } catch (final IOException ex) { + if (log.isDebugEnabled()) { log.debug("Failed in reading the error response", ex); + } return newAmazonS3Exception(errorResponse.getStatusText(), errorResponse); } try { // try to parse the error response as XML @@ -81,9 +83,10 @@ public AmazonServiceException handle(HttpResponse errorResponse) throws IOExcept ase.setExtendedRequestId(extendedRequestId); ase.setCloudFrontId(errorResponse.getHeaders().get(Headers.CLOUD_FRONT_ID)); return ase; - } catch (Exception ex) { - if (log.isDebugEnabled()) + } catch (final Exception ex) { + if (log.isDebugEnabled()) { log.debug("Failed in parsing the response as XML: " + content, ex); + } return newAmazonS3Exception(content, errorResponse); } } @@ -98,10 +101,14 @@ private AmazonS3Exception newAmazonS3Exception(String errmsg, HttpResponse httpR ase.setErrorCode(statusCode + " " + httpResponse.getStatusText()); ase.setStatusCode(statusCode); ase.setErrorType(errorTypeOf(statusCode)); - Map headers = httpResponse.getHeaders(); + final Map headers = httpResponse.getHeaders(); ase.setRequestId(headers.get(Headers.REQUEST_ID)); ase.setExtendedRequestId(headers.get(Headers.EXTENDED_REQUEST_ID)); ase.setCloudFrontId(headers.get(Headers.CLOUD_FRONT_ID)); + final Map additionalDetails = new HashMap(); + additionalDetails.put(Headers.S3_BUCKET_REGION, + headers.get(Headers.S3_BUCKET_REGION)); + ase.setAdditionalDetails(additionalDetails); return ase; } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3ObjectResponseHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3ObjectResponseHandler.java index e0007f896c..29fda955df 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3ObjectResponseHandler.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3ObjectResponseHandler.java @@ -37,8 +37,8 @@ public AmazonWebServiceResponse handle(HttpResponse response) throws E * TODO: It'd be nice to set the bucket name and key here, but the * information isn't easy to pull out of the response/request currently. */ - S3Object object = new S3Object(); - AmazonWebServiceResponse awsResponse = parseResponseMetadata(response); + final S3Object object = new S3Object(); + final AmazonWebServiceResponse awsResponse = parseResponseMetadata(response); if (response.getHeaders().get(Headers.REDIRECT_LOCATION) != null) { object.setRedirectLocation(response.getHeaders().get(Headers.REDIRECT_LOCATION)); } @@ -47,7 +47,12 @@ public AmazonWebServiceResponse handle(HttpResponse response) throws E if (response.getHeaders().get(Headers.REQUESTER_CHARGED_HEADER) != null) { object.setRequesterCharged(true); } - ObjectMetadata metadata = object.getObjectMetadata(); + + if (response.getHeaders().get(Headers.S3_TAGGING_COUNT) != null) { + object.setTaggingCount(Integer.parseInt(response.getHeaders().get(Headers.S3_TAGGING_COUNT))); + } + + final ObjectMetadata metadata = object.getObjectMetadata(); populateObjectMetadata(response, metadata); object.setObjectContent(new S3ObjectInputStream(response.getContent())); diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3RequesterChargedHeaderHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3RequesterChargedHeaderHandler.java new file mode 100644 index 0000000000..3bfe8add8a --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3RequesterChargedHeaderHandler.java @@ -0,0 +1,39 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; + +import com.amazonaws.http.HttpResponse; +import com.amazonaws.services.s3.Headers; + +/** + * Header handler to pull the {@link Headers#REQUESTER_CHARGED_HEADER} header + * out of the response. This header is required for requests with + * {@link Headers#REQUESTER_PAYS_HEADER} header. + */ +public class S3RequesterChargedHeaderHandler + implements HeaderHandler { + + /* + * (non-Javadoc) + * @see + * com.amazonaws.services.s3.internal.HeaderHandler#handle(java.lang.Object, + * com.amazonaws.http.HttpResponse) + */ + @Override + public void handle(T result, HttpResponse response) { + result.setRequesterCharged( + response.getHeaders().get(Headers.REQUESTER_CHARGED_HEADER) != null); + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3RequesterChargedResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3RequesterChargedResult.java new file mode 100644 index 0000000000..aa0ee1b2ca --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3RequesterChargedResult.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016-2017 Amazon Technologies, 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.internal; + +public interface S3RequesterChargedResult { + + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + *

+ * If a bucket is enabled for Requester Pays, then any attempt of operation + * from it without Requester Pays enabled will result in a 403 error and the + * bucket owner will be charged for the request. + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for conducting + * this operation from Requester Pays Bucket. + */ + public boolean isRequesterCharged(); + + /** + * Used for conducting this operation from a Requester Pays Bucket. If set + * the requester is charged for conducting the operation from the bucket. + *

+ * If a bucket is enabled for Requester Pays, then any attempt of operation + * from it without Requester Pays enabled will result in a 403 error and the + * bucket owner will be charged for the request. + * + * @param isRequesterCharged Indicates requester is charged for this + * operation. + */ + public void setRequesterCharged(boolean isRequesterCharged); +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3Signer.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3Signer.java index 14b80911d9..f3c268cf5f 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3Signer.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3Signer.java @@ -27,7 +27,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import java.util.Collection; +import java.util.Collections; import java.util.Date; +import java.util.HashSet; +import java.util.Set; /** * Implementation of the {@linkplain Signer} interface specific to S3's signing @@ -56,11 +60,26 @@ public class S3Signer extends AbstractAWSSigner { private final String resourcePath; /** - * Create a dummy instance of the S3Signer. + * The names of all the user-specified query parameters that should be + * included in the canonical request, in addition to those default + * parameters that are always signed. + * + * @see RestUtils#makeS3CanonicalString(String, String, Request, String) + */ + private final Set additionalQueryParamsToSign; + + /** + * Create a dummy instance of the S3Signer. This constructor will be invoked + * by internal config via reflection. */ public S3Signer() { + /* + * NOTE: don't delegate to the other ctors, otherwise an IAE will be + * thrown since the resourcePath is lazily initialized to null. + */ this.httpVerb = null; this.resourcePath = null; + this.additionalQueryParamsToSign = null; } /** @@ -73,11 +92,39 @@ public S3Signer() { * "//", or "//". */ public S3Signer(String httpVerb, String resourcePath) { + this(httpVerb, resourcePath, null); + } + + /** + * Constructs a new S3Signer to sign requests based on the AWS credentials, + * HTTP method and canonical S3 resource path. + * + * @param httpVerb + * The HTTP verb (GET, PUT, POST, HEAD, DELETE) the request is + * using. + * @param resourcePath + * The canonical S3 resource path (ex: "/", "//", or + * "//". + * @param additionalQueryParamsToSign + * A collection of user-specified query parameters that should be + * included in the canonical request, in addition to those + * default parameters that are always signed. + * + * @see RestUtils#makeS3CanonicalString(String, String, Request, String) + */ + public S3Signer(String httpVerb, String resourcePath, + Collection additionalQueryParamsToSign) { + if (resourcePath == null) { + throw new IllegalArgumentException( + "Parameter resourcePath is empty"); + } + this.httpVerb = httpVerb; this.resourcePath = resourcePath; - - if (resourcePath == null) - throw new IllegalArgumentException("Parameter resourcePath is empty"); + this.additionalQueryParamsToSign = additionalQueryParamsToSign == null + ? null + : Collections.unmodifiableSet(new HashSet( + additionalQueryParamsToSign)); } /** @@ -96,7 +143,7 @@ void sign(Request request, AWSCredentials credentials, Date overrideDate) { return; } - AWSCredentials sanitizedCredentials = sanitizeCredentials(credentials); + final AWSCredentials sanitizedCredentials = sanitizeCredentials(credentials); if (sanitizedCredentials instanceof AWSSessionCredentials) { addSessionCredentials(request, (AWSSessionCredentials) sanitizedCredentials); } @@ -108,10 +155,10 @@ void sign(Request request, AWSCredentials credentials, Date overrideDate) { * httpclient works, we need to do the same encoding here for the * resource path. */ - String encodedResourcePath = HttpUtils.appendUri(request.getEndpoint().getPath(), + final String encodedResourcePath = HttpUtils.appendUri(request.getEndpoint().getPath(), resourcePath, true); - int timeOffset = getTimeOffset(request); + final int timeOffset = getTimeOffset(request); Date date = getSignatureDate(timeOffset); if (overrideDate != null) { @@ -119,11 +166,11 @@ void sign(Request request, AWSCredentials credentials, Date overrideDate) { } request.addHeader(Headers.DATE, ServiceUtils.formatRfc822Date(date)); - String canonicalString = RestUtils.makeS3CanonicalString( - httpVerb, encodedResourcePath, request, null); + final String canonicalString = RestUtils.makeS3CanonicalString(httpVerb, + encodedResourcePath, request, null, additionalQueryParamsToSign); log.debug("Calculated string to sign:\n\"" + canonicalString + "\""); - String signature = super.signAndBase64Encode(canonicalString, + final String signature = super.signAndBase64Encode(canonicalString, sanitizedCredentials.getAWSSecretKey(), SigningAlgorithm.HmacSHA1); request.addHeader("Authorization", "AWS " + sanitizedCredentials.getAWSAccessKeyId() + ":" + signature); diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3VersionHeaderHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3VersionHeaderHandler.java index 0b879a9947..5890a426a2 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3VersionHeaderHandler.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/S3VersionHeaderHandler.java @@ -12,18 +12,16 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ - package com.amazonaws.services.s3.internal; import com.amazonaws.http.HttpResponse; import com.amazonaws.services.s3.Headers; -import com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.CopyObjectResultHandler; /** * Header handler to pull the S3_VERSION_ID header out of the response. This * header is required for the copyPart and copyObject api methods. */ -public class S3VersionHeaderHandler implements HeaderHandler { +public class S3VersionHeaderHandler implements HeaderHandler { /* * (non-Javadoc) @@ -32,7 +30,7 @@ public class S3VersionHeaderHandler implements HeaderHandler request, String urlString = request.getEndpoint() + urlPath; boolean firstParam = true; - for (String param : request.getParameters().keySet()) { + for (final String param : request.getParameters().keySet()) { if (firstParam) { urlString += "?"; firstParam = false; @@ -183,13 +187,13 @@ public static URL convertRequestToUrl(Request request, urlString += "&"; } - String value = request.getParameters().get(param); + final String value = request.getParameters().get(param); urlString += param + "=" + HttpUtils.urlEncode(value, false); } try { return new URL(urlString); - } catch (MalformedURLException e) { + } catch (final MalformedURLException e) { throw new AmazonClientException( "Unable to convert request to well formed URL: " + e.getMessage(), e); } @@ -208,9 +212,10 @@ public static String join(List strings) { String result = ""; boolean first = true; - for (String s : strings) { - if (!first) + for (final String s : strings) { + if (!first) { result += ", "; + } result += s; first = false; @@ -236,7 +241,7 @@ public static void downloadObjectToFile(S3Object s3Object, boolean appendData) { // attempt to create the parent if it doesn't exist - File parentDirectory = destinationFile.getParentFile(); + final File parentDirectory = destinationFile.getParentFile(); if (parentDirectory != null && !parentDirectory.exists()) { parentDirectory.mkdirs(); } @@ -245,23 +250,23 @@ public static void downloadObjectToFile(S3Object s3Object, try { outputStream = new BufferedOutputStream(new FileOutputStream( destinationFile, appendData)); - byte[] buffer = new byte[1024 * 10]; + final byte[] buffer = new byte[1024 * 10]; int bytesRead; while ((bytesRead = s3Object.getObjectContent().read(buffer)) > -1) { outputStream.write(buffer, 0, bytesRead); } - } catch (IOException e) { + } catch (final IOException e) { s3Object.getObjectContent().abort(); throw new AmazonClientException( "Unable to store object contents to disk: " + e.getMessage(), e); } finally { try { outputStream.close(); - } catch (Exception e) { + } catch (final Exception e) { } try { s3Object.getObjectContent().close(); - } catch (Exception e) { + } catch (final Exception e) { } } @@ -274,7 +279,7 @@ public static void downloadObjectToFile(S3Object s3Object, clientSideHash = Md5Utils.computeMD5Hash(new FileInputStream(destinationFile)); serverSideHash = BinaryUtils.fromHex(s3Object.getObjectMetadata().getETag()); } - } catch (Exception e) { + } catch (final Exception e) { log.warn("Unable to calculate MD5 hash to validate download: " + e.getMessage(), e); } @@ -332,16 +337,18 @@ public static S3Object retryableDownloadS3ObjectToFile(File file, do { needRetry = false; s3Object = retryableS3DownloadTask.getS3ObjectStream(); - if (s3Object == null) + if (s3Object == null) { return null; + } try { ServiceUtils.downloadObjectToFile(s3Object, file, retryableS3DownloadTask.needIntegrityCheck(), appendData); - } catch (AmazonClientException ace) { - if (!ace.isRetryable()) + } catch (final AmazonClientException ace) { + if (!ace.isRetryable()) { throw ace; + } // Determine whether an immediate retry is needed according to // the captured AmazonClientException. // (There are three cases when downloadObjectToFile() throws @@ -357,9 +364,9 @@ public static S3Object retryableDownloadS3ObjectToFile(File file, throw ace; } else { needRetry = true; - if (hasRetried) + if (hasRetried) { throw ace; - else { + } else { log.info("Retry the download of object " + s3Object.getKey() + " (bucket " + s3Object.getBucketName() + ")", ace); hasRetried = true; @@ -384,9 +391,10 @@ public static S3Object retryableDownloadS3ObjectToFile(File file, * the plaintext. */ public static boolean skipMd5CheckPerResponse(ObjectMetadata metadata) { - if (metadata == null) + if (metadata == null) { return false; - boolean sseKMS = (ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION.equals(metadata + } + final boolean sseKMS = (SSEAlgorithm.KMS.toString().equals(metadata .getSSEAlgorithm())); return (metadata.getSSECustomerAlgorithm() != null) || sseKMS; @@ -397,25 +405,28 @@ public static boolean skipMd5CheckPerResponse(ObjectMetadata metadata) { * requested object content. */ public static boolean skipMd5CheckPerRequest(AmazonWebServiceRequest request) { - if (System.getProperty("com.amazonaws.services.s3.disableGetObjectMD5Validation") != null) + if (System.getProperty("com.amazonaws.services.s3.disableGetObjectMD5Validation") != null) { return true; + } if (request instanceof GetObjectRequest) { - GetObjectRequest getObjectRequest = (GetObjectRequest) request; + final GetObjectRequest getObjectRequest = (GetObjectRequest) request; // Skip MD5 check for range get - if (getObjectRequest.getRange() != null) + if (getObjectRequest.getRange() != null) { return true; - if (getObjectRequest.getSSECustomerKey() != null) + } + if (getObjectRequest.getSSECustomerKey() != null) { return true; + } } else if (request instanceof PutObjectRequest) { - PutObjectRequest putObjectRequest = (PutObjectRequest) request; - ObjectMetadata om = putObjectRequest.getMetadata(); + final PutObjectRequest putObjectRequest = (PutObjectRequest) request; + final ObjectMetadata om = putObjectRequest.getMetadata(); if (om != null && om.getSSEAlgorithm() != null) { return true; } return putObjectRequest.getSSECustomerKey() != null; } else if (request instanceof UploadPartRequest) { - UploadPartRequest uploadPartRequest = (UploadPartRequest) request; + final UploadPartRequest uploadPartRequest = (UploadPartRequest) request; return uploadPartRequest.getSSECustomerKey() != null; } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/SetObjectTaggingResponseHeaderHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/SetObjectTaggingResponseHeaderHandler.java new file mode 100644 index 0000000000..3f90623e3a --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/SetObjectTaggingResponseHeaderHandler.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Portions copyright 2006-2009 James Murty. Please see LICENSE.txt + * for applicable license terms and NOTICE.txt for applicable notices. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal; + +import com.amazonaws.http.HttpResponse; +import com.amazonaws.services.s3.Headers; +import com.amazonaws.services.s3.model.SetObjectTaggingResult; + +/** + * Handler for retrieving headers from the {@link + * com.amazonaws.services.s3.model.SetObjectTaggingRequest} response. + */ +public class SetObjectTaggingResponseHeaderHandler implements HeaderHandler { + @Override + public void handle(SetObjectTaggingResult result, HttpResponse response) { + result.setVersionId(response.getHeaders().get(Headers.S3_VERSION_ID)); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CipherLite.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CipherLite.java index 592f4dc1a4..bd1bbe116f 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CipherLite.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CipherLite.java @@ -81,6 +81,23 @@ private CipherLite() { this.cipherMode = cipherMode; } + /** + * Recreates a new instance of CipherLite from the current one. + */ + CipherLite recreate() { + return scheme.createCipherLite(secreteKey, cipher.getIV(), + this.cipherMode, cipher.getProvider()); + } + + /** + * Creates a new instance of CipherLite from the current one, but using + * the given IV. + */ + CipherLite createUsingIV(byte[] iv) { + return scheme.createCipherLite(secreteKey, iv, this.cipherMode, + cipher.getProvider()); + } + /** * Returns an auxiliary {@link CipherLite} for partial plaintext * re-encryption (or re-decryption) purposes. diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CipherLiteInputStream.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CipherLiteInputStream.java index fbcd06282f..1228718fec 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CipherLiteInputStream.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CipherLiteInputStream.java @@ -24,14 +24,13 @@ import javax.crypto.IllegalBlockSizeException; /** - * @author Hanson Char * @see CipherLite * @see GCMCipherLite */ -public final class CipherLiteInputStream extends SdkFilterInputStream { +public class CipherLiteInputStream extends SdkFilterInputStream { private static final int MAX_RETRY = 1000; private static final int DEFAULT_IN_BUFFER_SIZE = 512; - private final CipherLite cipherLite; + private CipherLite cipherLite; /** * True if this input stream is currently involved in a multipart uploads; * false otherwise. For multipart uploads, the doFinal method if the @@ -48,7 +47,7 @@ public final class CipherLiteInputStream extends SdkFilterInputStream { */ private final boolean lastMultiPart; private boolean eof = false; - private byte[] bufin; + private final byte[] bufin; private byte[] bufout; private int curr_pos = 0; private int max_pos = 0; @@ -64,9 +63,10 @@ public CipherLiteInputStream(InputStream is, CipherLite c, int buffsize) { public CipherLiteInputStream(InputStream is, CipherLite c, int buffsize, boolean multipart, boolean lastMultiPart) { super(is); - if (lastMultiPart && !multipart) + if (lastMultiPart && !multipart) { throw new IllegalArgumentException( "lastMultiPart can only be true if multipart is true"); + } this.multipart = multipart; this.lastMultiPart = lastMultiPart; this.cipherLite = c; @@ -85,20 +85,23 @@ protected CipherLiteInputStream(InputStream is) { @Override public int read() throws IOException { if (curr_pos >= max_pos) { - if (eof) + if (eof) { return -1; + } int count = 0; int len; do { - if (count > MAX_RETRY) + if (count > MAX_RETRY) { throw new IOException( "exceeded maximum number of attempts to read next chunk of data"); + } len = nextChunk(); count++; } while (len == 0); - if (len == -1) + if (len == -1) { return -1; + } } return (bufout[curr_pos++] & 0xFF); }; @@ -112,26 +115,31 @@ public int read(byte b[]) throws IOException { public int read(byte buf[], int off, int target_len) throws IOException { if (curr_pos >= max_pos) { // all buffered data has been read, let's get some more - if (eof) + if (eof) { return -1; + } int count = 0; int len; do { - if (count > MAX_RETRY) + if (count > MAX_RETRY) { throw new IOException( "exceeded maximum number of attempts to read next chunk of data"); + } len = nextChunk(); count++; } while (len == 0); - if (len == -1) + if (len == -1) { return -1; + } } - if (target_len <= 0) + if (target_len <= 0) { return 0; + } int len = max_pos - curr_pos; - if (target_len < len) + if (target_len < len) { len = target_len; + } // if buf == null, will throw NPE as intended per javadoc System.arraycopy(bufout, curr_pos, buf, off, len); curr_pos += len; @@ -141,11 +149,13 @@ public int read(byte buf[], int off, int target_len) throws IOException { @Override public long skip(long n) throws IOException { abortIfNeeded(); - int available = max_pos - curr_pos; - if (n > available) + final int available = max_pos - curr_pos; + if (n > available) { n = available; - if (n < 0) + } + if (n < 0) { return 0; + } curr_pos += n; return n; } @@ -167,8 +177,8 @@ public void close() throws IOException { try { // simulate the RI: throw away the unprocessed data cipherLite.doFinal(); - } catch (BadPaddingException ex) { - } catch (IllegalBlockSizeException ex) { + } catch (final BadPaddingException ex) { + } catch (final IllegalBlockSizeException ex) { } } } @@ -194,6 +204,10 @@ public void reset() throws IOException { abortIfNeeded(); in.reset(); cipherLite.reset(); + resetInternal(); + } + + final void resetInternal() { if (markSupported()) { curr_pos = max_pos = 0; eof = false; @@ -211,10 +225,11 @@ public void reset() throws IOException { */ private int nextChunk() throws IOException { abortIfNeeded(); - if (eof) + if (eof) { return -1; + } bufout = null; - int len = in.read(bufin); + final int len = in.read(bufin); if (len == -1) { eof = true; // Skip doFinal if it's a multi-part upload but not the last part @@ -228,11 +243,12 @@ private int nextChunk() throws IOException { } curr_pos = 0; return max_pos = bufout.length; - } catch (IllegalBlockSizeException ignore) { + } catch (final IllegalBlockSizeException ignore) { // like the RI - } catch (BadPaddingException e) { - if (S3CryptoScheme.isAesGcm(cipherLite.getCipherAlgorithm())) + } catch (final BadPaddingException e) { + if (S3CryptoScheme.isAesGcm(cipherLite.getCipherAlgorithm())) { throw new SecurityException(e); + } } } return -1; @@ -242,4 +258,7 @@ private int nextChunk() throws IOException { return max_pos = (bufout == null ? 0 : bufout.length); } + void renewCipherLite() { + cipherLite = cipherLite.recreate(); + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/ContentCryptoMaterial.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/ContentCryptoMaterial.java index 2541068ad5..f0b54fa32f 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/ContentCryptoMaterial.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/ContentCryptoMaterial.java @@ -15,10 +15,24 @@ package com.amazonaws.services.s3.internal.crypto; +import static com.amazonaws.services.s3.internal.crypto.KMSSecuredCEK.isKMSKeyWrapped; +import static com.amazonaws.util.BinaryUtils.copyAllBytesFrom; + import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.services.kms.AWSKMSClient; +import com.amazonaws.services.kms.model.DecryptRequest; +import com.amazonaws.services.kms.model.DecryptResult; +import com.amazonaws.services.kms.model.EncryptRequest; +import com.amazonaws.services.kms.model.EncryptResult; import com.amazonaws.services.s3.Headers; +import com.amazonaws.services.s3.KeyWrapException; +import com.amazonaws.services.s3.model.CryptoMode; import com.amazonaws.services.s3.model.EncryptionMaterials; import com.amazonaws.services.s3.model.EncryptionMaterialsAccessor; +import com.amazonaws.services.s3.model.ExtraMaterialsDescription; +import com.amazonaws.services.s3.model.KMSEncryptionMaterials; +import com.amazonaws.services.s3.model.MaterialsDescriptionProvider; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.util.Base64; @@ -29,11 +43,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.ByteBuffer; import java.security.Key; import java.security.Provider; +import java.security.SecureRandom; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -66,11 +84,23 @@ final class ContentCryptoMaterial { /** * Returns the key wrapping algorithm, or null if the content key is not * secured via a key wrapping algorithm. + *

+ * Note if the returned value is + * {@value KMSSecuredCEK#KEY_PROTECTION_MECHANISM}, it means the key is + * protected via KMS rather than a direct key-wrapping algorithm. */ String getKeyWrappingAlgorithm() { return keyWrappingAlgorithm; } + /** + * Returns true if KMS is used for the content key protection; false + * otherwise. + */ + private boolean usesKMSKey() { + return isKMSKeyWrapped(keyWrappingAlgorithm); + } + /** * Returns the content crypto scheme. */ @@ -81,14 +111,23 @@ ContentCryptoScheme getContentCryptoScheme() { /** * Returns the given metadata updated with this content crypto material. */ - ObjectMetadata toObjectMetadata(ObjectMetadata metadata) { + ObjectMetadata toObjectMetadata(ObjectMetadata metadata, CryptoMode mode) { + return mode == CryptoMode.EncryptionOnly && !usesKMSKey() + ? toObjectMetadataEO(metadata) + : toObjectMetadata(metadata); + } + + /** + * Returns the metadata in the latest format. + */ + private ObjectMetadata toObjectMetadata(ObjectMetadata metadata) { // If we generated a symmetric key to encrypt the data, store it in the // object metadata. - byte[] encryptedCEK = getEncryptedCEK(); + final byte[] encryptedCEK = getEncryptedCEK(); metadata.addUserMetadata(Headers.CRYPTO_KEY_V2, Base64.encodeAsString(encryptedCEK)); // Put the cipher initialization vector (IV) into the object metadata - byte[] iv = cipherLite.getIV(); + final byte[] iv = cipherLite.getIV(); metadata.addUserMetadata(Headers.CRYPTO_IV, Base64.encodeAsString(iv)); // Put the materials description into the object metadata as JSON metadata.addUserMetadata(Headers.MATERIALS_DESCRIPTION, @@ -96,49 +135,94 @@ ObjectMetadata toObjectMetadata(ObjectMetadata metadata) { // The CRYPTO_CEK_ALGORITHM, CRYPTO_TAG_LENGTH and // CRYPTO_KEYWRAP_ALGORITHM were not available in the Encryption Only // (EO) implementation - ContentCryptoScheme scheme = getContentCryptoScheme(); + final ContentCryptoScheme scheme = getContentCryptoScheme(); metadata.addUserMetadata(Headers.CRYPTO_CEK_ALGORITHM, scheme.getCipherAlgorithm()); - int tagLen = scheme.getTagLengthInBits(); - if (tagLen > 0) + final int tagLen = scheme.getTagLengthInBits(); + if (tagLen > 0) { metadata.addUserMetadata(Headers.CRYPTO_TAG_LENGTH, String.valueOf(tagLen)); - String keyWrapAlgo = getKeyWrappingAlgorithm(); - if (keyWrapAlgo != null) + } + final String keyWrapAlgo = getKeyWrappingAlgorithm(); + if (keyWrapAlgo != null) { metadata.addUserMetadata(Headers.CRYPTO_KEYWRAP_ALGORITHM, keyWrapAlgo); + } return metadata; } + /** + * Returns the metadata in backward compatibility (old) format, so it can be + * read by older version of the AWS SDK. + */ + private ObjectMetadata toObjectMetadataEO(ObjectMetadata metadata) { + // If we generated a symmetric key to encrypt the data, store it in the + // object metadata. + final byte[] encryptedCEK = getEncryptedCEK(); + metadata.addUserMetadata(Headers.CRYPTO_KEY, + Base64.encodeAsString(encryptedCEK)); + // Put the cipher initialization vector (IV) into the object metadata + final byte[] iv = cipherLite.getIV(); + metadata.addUserMetadata(Headers.CRYPTO_IV, Base64.encodeAsString(iv)); + // Put the materials description into the object metadata as JSON + metadata.addUserMetadata(Headers.MATERIALS_DESCRIPTION, + kekMaterialDescAsJson()); + return metadata; + } + + /** + * Returns the json string in backward compatibility (old) format, so it can + * be read by older version of the AWS SDK. + */ + String toJsonString(CryptoMode mode) { + return mode == CryptoMode.EncryptionOnly && !usesKMSKey() + ? toJsonStringEO() : toJsonString(); + } + + /** + * Returns the json string in the latest format. + */ String toJsonString() { - Map map = new HashMap(); - byte[] encryptedCEK = getEncryptedCEK(); + final Map map = new HashMap(); + final byte[] encryptedCEK = getEncryptedCEK(); map.put(Headers.CRYPTO_KEY_V2, Base64.encodeAsString(encryptedCEK)); - byte[] iv = cipherLite.getIV(); + final byte[] iv = cipherLite.getIV(); map.put(Headers.CRYPTO_IV, Base64.encodeAsString(iv)); map.put(Headers.MATERIALS_DESCRIPTION, kekMaterialDescAsJson()); // The CRYPTO_CEK_ALGORITHM, CRYPTO_TAG_LENGTH and // CRYPTO_KEYWRAP_ALGORITHM were not available in the Encryption Only // (EO) implementation - ContentCryptoScheme scheme = getContentCryptoScheme(); + final ContentCryptoScheme scheme = getContentCryptoScheme(); map.put(Headers.CRYPTO_CEK_ALGORITHM, scheme.getCipherAlgorithm()); - int tagLen = scheme.getTagLengthInBits(); - if (tagLen > 0) + final int tagLen = scheme.getTagLengthInBits(); + if (tagLen > 0) { map.put(Headers.CRYPTO_TAG_LENGTH, String.valueOf(tagLen)); - String keyWrapAlgo = getKeyWrappingAlgorithm(); - if (keyWrapAlgo != null) + } + final String keyWrapAlgo = getKeyWrappingAlgorithm(); + if (keyWrapAlgo != null) { map.put(Headers.CRYPTO_KEYWRAP_ALGORITHM, keyWrapAlgo); + } return JsonUtils.mapToString(map); } + private String toJsonStringEO() { + final Map map = new HashMap(); + final byte[] encryptedCEK = getEncryptedCEK(); + map.put(Headers.CRYPTO_KEY, Base64.encodeAsString(encryptedCEK)); + final byte[] iv = cipherLite.getIV(); + map.put(Headers.CRYPTO_IV, Base64.encodeAsString(iv)); + map.put(Headers.MATERIALS_DESCRIPTION, kekMaterialDescAsJson()); + return JsonUtils.mapToString(map); + } /** * Returns the key-encrypting-key material description as a non-null json * string; */ private String kekMaterialDescAsJson() { Map kekMaterialDesc = getKEKMaterialsDescription(); - if (kekMaterialDesc == null) + if (kekMaterialDesc == null) { kekMaterialDesc = Collections.emptyMap(); + } return JsonUtils.mapToString(kekMaterialDesc); } @@ -146,41 +230,55 @@ private String kekMaterialDescAsJson() { * Returns the corresponding kek material description from the given json; * or null if the input is null. */ + @SuppressWarnings("unchecked") private static Map matdescFromJson(String json) { - if (json == null) { - return null; - } - Map map = JsonUtils.jsonToMap(json); + final Map map = JsonUtils.jsonToMap(json); return map == null ? null : Collections.unmodifiableMap(map); } /** - * Returns the content encrypting key unwrapped or decrypted. + * Returns the content encrypting key unwrapped or decrypted. Note if KMS + * is used for key protection, a remote call will be made to KMS to decrypt + * the ciphertext blob. * - * @param cekSecured the content encrypting key in wrapped or encrypted - * form; must not be null - * @param keyWrapAlgo key wrapping algorithm; or null if direct encryption - * instead of key wrapping is used - * @param materials the client key encrypting key material for the content + * @param cekSecured + * the content encrypting key in wrapped or encrypted form; must + * not be null + * @param keyWrapAlgo + * key wrapping algorithm; or null if direct encryption instead + * of key wrapping is used + * @param materials + * the client key encrypting key material for the content * encrypting key - * @param securityProvider security provider or null if the default security - * provider of the JCE is used + * @param securityProvider + * security provider or null if the default security provider of + * the JCE is used */ private static SecretKey cek(byte[] cekSecured, String keyWrapAlgo, - EncryptionMaterials materials, Provider securityProvider) { + EncryptionMaterials materials, Provider securityProvider, + ContentCryptoScheme contentCryptoScheme, AWSKMSClient kms) { + if (isKMSKeyWrapped(keyWrapAlgo)) { + return cekByKMS(cekSecured, keyWrapAlgo, materials, contentCryptoScheme, kms); + } Key kek; if (materials.getKeyPair() != null) { // Do envelope decryption with private key from key pair kek = materials.getKeyPair().getPrivate(); + if (kek == null) { + throw new AmazonClientException("Key encrypting key not available"); + } } else { // Do envelope decryption with symmetric key kek = materials.getSymmetricKey(); + if (kek == null) { + throw new AmazonClientException("Key encrypting key not available"); + } } try { if (keyWrapAlgo != null) { // Key wrapping specified - Cipher cipher = securityProvider == null ? Cipher + final Cipher cipher = securityProvider == null ? Cipher .getInstance(keyWrapAlgo) : Cipher.getInstance( keyWrapAlgo, securityProvider); cipher.init(Cipher.UNWRAP_MODE, kek); @@ -196,65 +294,128 @@ private static SecretKey cek(byte[] cekSecured, String keyWrapAlgo, cipher = Cipher.getInstance(kek.getAlgorithm()); } cipher.init(Cipher.DECRYPT_MODE, kek); - byte[] decryptedSymmetricKeyBytes = cipher.doFinal(cekSecured); + final byte[] decryptedSymmetricKeyBytes = cipher.doFinal(cekSecured); return new SecretKeySpec(decryptedSymmetricKeyBytes, JceEncryptionConstants.SYMMETRIC_KEY_ALGORITHM); - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException( - "Unable to decrypt symmetric key from object metadata : " - + e.getMessage(), e); + "Unable to decrypt symmetric key from object metadata", e); } } + /** + * Decrypts the secured CEK via KMS; involves network calls. + * + * @return the CEK (in plaintext). + */ + private static SecretKey cekByKMS(byte[] cekSecured, String keyWrapAlgo, + EncryptionMaterials materials, + ContentCryptoScheme contentCryptoScheme, AWSKMSClient kms) { + final DecryptRequest kmsreq = new DecryptRequest() + .withEncryptionContext(materials.getMaterialsDescription()) + .withCiphertextBlob(ByteBuffer.wrap(cekSecured)); + final DecryptResult result = kms.decrypt(kmsreq); + return new SecretKeySpec(copyAllBytesFrom(result.getPlaintext()), + contentCryptoScheme.getKeyGeneratorAlgorithm()); + } + + /** + * @return a non-null content crypto material. + */ + static ContentCryptoMaterial fromObjectMetadata( + ObjectMetadata metadata, + EncryptionMaterialsAccessor kekMaterialAccessor, + Provider securityProvider, + boolean keyWrapExpected, + AWSKMSClient kms) { + return fromObjectMetadata0(metadata, kekMaterialAccessor, + securityProvider, null, ExtraMaterialsDescription.NONE, keyWrapExpected, kms); + } + /** * Factory method to return the content crypto material from the S3 object * meta data, using the specified key encrypting key material accessor and * an optional security provider. + * + * @return a non-null content crypto material. */ static ContentCryptoMaterial fromObjectMetadata( ObjectMetadata metadata, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, - long[] range) { + long[] range, + ExtraMaterialsDescription extra, + boolean keyWrapExpected, + AWSKMSClient kms) { + return fromObjectMetadata0(metadata, kekMaterialAccessor, + securityProvider, range, extra, keyWrapExpected, kms); + } + + /** + * @return a non-null content crypto material. + */ + private static ContentCryptoMaterial fromObjectMetadata0( + ObjectMetadata metadata, + EncryptionMaterialsAccessor kekMaterialAccessor, + Provider securityProvider, + long[] range, + ExtraMaterialsDescription extra, + boolean keyWrapExpected, + AWSKMSClient kms) { // CEK and IV - Map userMeta = metadata.getUserMetadata(); + final Map userMeta = metadata.getUserMetadata(); String b64key = userMeta.get(Headers.CRYPTO_KEY_V2); if (b64key == null) { b64key = userMeta.get(Headers.CRYPTO_KEY); - if (b64key == null) - throw new AmazonClientException("Content encrypting key not found."); + if (b64key == null) { + throw new AmazonClientException( + "Content encrypting key not found."); + } } - byte[] cekWrapped = Base64.decode(b64key); + final byte[] cekWrapped = Base64.decode(b64key); byte[] iv = Base64.decode(userMeta.get(Headers.CRYPTO_IV)); if (cekWrapped == null || iv == null) { - throw new AmazonClientException("Content encrypting key or IV not found."); + throw new AmazonClientException( + "Content encrypting key or IV not found."); } // Material description - String matdescStr = userMeta.get(Headers.MATERIALS_DESCRIPTION); - Map matdesc = matdescFromJson(matdescStr); - EncryptionMaterials materials = kekMaterialAccessor == null + final String matdescStr = userMeta.get(Headers.MATERIALS_DESCRIPTION); + final String keyWrapAlgo = userMeta.get(Headers.CRYPTO_KEYWRAP_ALGORITHM); + final boolean isKMS = isKMSKeyWrapped(keyWrapAlgo); + final Map core = matdescFromJson(matdescStr); + final Map merged = isKMS || extra == null + ? core : extra.mergeInto(core); + final EncryptionMaterials materials; + if (isKMS) { + materials = new KMSEncryptionMaterials( + core.get(KMSEncryptionMaterials.CUSTOMER_MASTER_KEY_ID)); + materials.addDescriptions(core); + } else { + materials = kekMaterialAccessor == null ? null - : kekMaterialAccessor.getEncryptionMaterials(matdesc); - if (materials == null) { - throw new AmazonClientException( - "Unable to retrieve the client encryption materials"); + : kekMaterialAccessor.getEncryptionMaterials(merged) + ; + if (materials == null) { + throw new AmazonClientException( + "Unable to retrieve the client encryption materials"); + } } // CEK algorithm - String cekAlgo = userMeta.get(Headers.CRYPTO_CEK_ALGORITHM); - boolean isRangeGet = range != null; + final String cekAlgo = userMeta.get(Headers.CRYPTO_CEK_ALGORITHM); + final boolean isRangeGet = range != null; // The content crypto scheme may vary depending on whether // it is a range get operation - ContentCryptoScheme contentCryptoScheme = ContentCryptoScheme - .fromCEKAlgo(cekAlgo, isRangeGet); + final ContentCryptoScheme contentCryptoScheme = + ContentCryptoScheme.fromCEKAlgo(cekAlgo, isRangeGet); if (isRangeGet) { // Adjust the IV as needed iv = contentCryptoScheme.adjustIV(iv, range[0]); } else { // Validate the tag length supported - int tagLenExpected = contentCryptoScheme.getTagLengthInBits(); + final int tagLenExpected = contentCryptoScheme.getTagLengthInBits(); if (tagLenExpected > 0) { - String s = userMeta.get(Headers.CRYPTO_TAG_LENGTH); - int tagLenActual = Integer.parseInt(s); + final String s = userMeta.get(Headers.CRYPTO_TAG_LENGTH); + final int tagLenActual = Integer.parseInt(s); if (tagLenExpected != tagLenActual) { throw new AmazonClientException("Unsupported tag length: " + tagLenActual + ", expected: " + tagLenExpected); @@ -262,73 +423,119 @@ static ContentCryptoMaterial fromObjectMetadata( } } // Unwrap or decrypt the CEK - String keyWrapAlgo = userMeta.get(Headers.CRYPTO_KEYWRAP_ALGORITHM); - SecretKey cek = cek(cekWrapped, keyWrapAlgo, materials, - securityProvider); - return new ContentCryptoMaterial(matdesc, cekWrapped, keyWrapAlgo, + if (keyWrapExpected && keyWrapAlgo == null) { + throw newKeyWrapException(); + } + final SecretKey cek = cek(cekWrapped, keyWrapAlgo, materials, + securityProvider, contentCryptoScheme, kms); + return new ContentCryptoMaterial(merged, cekWrapped, keyWrapAlgo, contentCryptoScheme.createCipherLite(cek, iv, Cipher.DECRYPT_MODE, securityProvider)); } + private static KeyWrapException newKeyWrapException() { + return new KeyWrapException( + "Missing key-wrap for the content-encrypting-key"); + } + + /** + * @return a non-null content crypto material. + */ + static ContentCryptoMaterial fromInstructionFile( + Map instFile, + EncryptionMaterialsAccessor kekMaterialAccessor, + Provider securityProvider, + boolean keyWrapExpected, + AWSKMSClient kms) { + return fromInstructionFile0(instFile, kekMaterialAccessor, + securityProvider, null, ExtraMaterialsDescription.NONE, keyWrapExpected, kms); + } + /** * Factory method to return the content crypto material from the S3 * instruction file, using the specified key encrypting key material * accessor and an optional security provider. + * + * @return a non-null content crypto material. */ - static ContentCryptoMaterial fromInstructionFile(Map instFile, + static ContentCryptoMaterial fromInstructionFile( + Map instFile, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, - long[] range) { + long[] range, + ExtraMaterialsDescription extra, + boolean keyWrapExpected, + AWSKMSClient kms) { return fromInstructionFile0(instFile, kekMaterialAccessor, - securityProvider, range); + securityProvider, range, extra, keyWrapExpected, kms); } + /** + * @return a non-null content crypto material. + */ private static ContentCryptoMaterial fromInstructionFile0( - Map map, + Map instFile, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, - long[] range) { + long[] range, + ExtraMaterialsDescription extra, + boolean keyWrapExpected, + AWSKMSClient kms) { // CEK and IV - String b64key = map.get(Headers.CRYPTO_KEY_V2); + String b64key = instFile.get(Headers.CRYPTO_KEY_V2); if (b64key == null) { - b64key = map.get(Headers.CRYPTO_KEY); - if (b64key == null) - throw new AmazonClientException("Content encrypting key not found."); + b64key = instFile.get(Headers.CRYPTO_KEY); + if (b64key == null) { + throw new AmazonClientException( + "Content encrypting key not found."); + } } - byte[] cekWrapped = Base64.decode(b64key); - byte[] iv = Base64.decode(map.get(Headers.CRYPTO_IV)); + final byte[] cekWrapped = Base64.decode(b64key); + byte[] iv = Base64.decode(instFile.get(Headers.CRYPTO_IV)); if (cekWrapped == null || iv == null) { throw new AmazonClientException( "Necessary encryption info not found in the instruction file " - + map); + + instFile); } + final String keyWrapAlgo = instFile.get(Headers.CRYPTO_KEYWRAP_ALGORITHM); + final boolean isKMS = isKMSKeyWrapped(keyWrapAlgo); // Material description - String matdescStr = map.get(Headers.MATERIALS_DESCRIPTION); - Map matdesc = matdescFromJson(matdescStr); - EncryptionMaterials materials = kekMaterialAccessor == null + final String matdescStr = instFile.get(Headers.MATERIALS_DESCRIPTION); + final Map core = matdescFromJson(matdescStr); + final Map merged = extra == null || isKMS + ? core : extra.mergeInto(core); + EncryptionMaterials materials; + if (isKMS) { + materials = new KMSEncryptionMaterials( + core.get(KMSEncryptionMaterials.CUSTOMER_MASTER_KEY_ID)); + materials.addDescriptions(core); + } else { + materials = kekMaterialAccessor == null ? null - : kekMaterialAccessor.getEncryptionMaterials(matdesc); - if (materials == null) { - throw new AmazonClientException( + : kekMaterialAccessor.getEncryptionMaterials(merged); + if (materials == null) { + throw new AmazonClientException( "Unable to retrieve the encryption materials that originally " - + "encrypted object corresponding to instruction file " + map); + + "encrypted object corresponding to instruction file " + + instFile); + } } // CEK algorithm - String cekAlgo = map.get(Headers.CRYPTO_CEK_ALGORITHM); - boolean isRangeGet = range != null; + final String cekAlgo = instFile.get(Headers.CRYPTO_CEK_ALGORITHM); + final boolean isRangeGet = range != null; // The content crypto scheme may vary depending on whether // it is a range get operation - ContentCryptoScheme contentCryptoScheme = ContentCryptoScheme + final ContentCryptoScheme contentCryptoScheme = ContentCryptoScheme .fromCEKAlgo(cekAlgo, isRangeGet); if (isRangeGet) { // Adjust the IV as needed iv = contentCryptoScheme.adjustIV(iv, range[0]); } else { // Validate the tag length supported - int tagLenExpected = contentCryptoScheme.getTagLengthInBits(); + final int tagLenExpected = contentCryptoScheme.getTagLengthInBits(); if (tagLenExpected > 0) { - String s = map.get(Headers.CRYPTO_TAG_LENGTH); - int tagLenActual = Integer.parseInt(s); + final String s = instFile.get(Headers.CRYPTO_TAG_LENGTH); + final int tagLenActual = Integer.parseInt(s); if (tagLenExpected != tagLenActual) { throw new AmazonClientException("Unsupported tag length: " + tagLenActual + ", expected: " + tagLenExpected); @@ -336,9 +543,12 @@ private static ContentCryptoMaterial fromInstructionFile0( } } // Unwrap or decrypt the CEK - String keyWrapAlgo = map.get(Headers.CRYPTO_KEYWRAP_ALGORITHM); - SecretKey cek = cek(cekWrapped, keyWrapAlgo, materials, securityProvider); - return new ContentCryptoMaterial(matdesc, cekWrapped, keyWrapAlgo, + if (keyWrapExpected && keyWrapAlgo == null) { + throw newKeyWrapException(); + } + final SecretKey cek = cek(cekWrapped, keyWrapAlgo, materials, + securityProvider, contentCryptoScheme, kms); + return new ContentCryptoMaterial(merged, cekWrapped, keyWrapAlgo, contentCryptoScheme.createCipherLite(cek, iv, Cipher.DECRYPT_MODE, securityProvider)); } @@ -350,24 +560,25 @@ private static ContentCryptoMaterial fromInstructionFile0( static String parseInstructionFile(S3Object instructionFile) { try { return convertStreamToString(instructionFile.getObjectContent()); - } catch (Exception e) { - throw new AmazonClientException("Error parsing JSON instruction file: " - + e.getMessage()); + } catch (final Exception e) { + throw new AmazonClientException("Error parsing JSON instruction file", e); } } /** * Converts the contents of an input stream to a String */ - private static String convertStreamToString(InputStream inputStream) throws IOException { + private static String convertStreamToString(InputStream inputStream) + throws IOException { if (inputStream == null) { return ""; } else { - StringBuilder stringBuilder = new StringBuilder(); + final StringBuilder stringBuilder = new StringBuilder(); String line; try { - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, - StringUtils.UTF8)); + final BufferedReader reader = + new BufferedReader(new InputStreamReader(inputStream, + StringUtils.UTF8)); while ((line = reader.readLine()) != null) { stringBuilder.append(line); } @@ -403,4 +614,311 @@ Map getKEKMaterialsDescription() { byte[] getEncryptedCEK() { return this.encryptedCEK.clone(); } + + /** + * Recreates a new content crypto material from the current material given a + * new KEK material-descriptions. The purpose is to re-encrypt the CEK under + * a different KEK. + * + * Note network calls are involved if the CEK has been or is to be protected + * by KMS. + * + * @param newKEKMatDesc + * material descriptions for the new KEK; never null + * @param accessor + * used to retrieve the KEK given the corresponding material + * description + * @param targetScheme + * the target crypto scheme to be used for key wrapping, etc. + * @param p + * optional security provider; null means to use the default. + * @throws SecurityException + * if the old and new material description are the same; or if + * the old and new KEK are the same + */ + ContentCryptoMaterial recreate(Map newKEKMatDesc, + EncryptionMaterialsAccessor accessor, S3CryptoScheme targetScheme, + Provider p, AWSKMSClient kms, AmazonWebServiceRequest req) { + if (!usesKMSKey() && newKEKMatDesc.equals(kekMaterialsDescription)) { + throw new SecurityException( + "Material description of the new KEK must differ from the current one"); + } + final EncryptionMaterials origKEK; + if (usesKMSKey()) { + origKEK = new KMSEncryptionMaterials(kekMaterialsDescription.get( + KMSEncryptionMaterials.CUSTOMER_MASTER_KEY_ID)); + } else { + origKEK = accessor.getEncryptionMaterials(kekMaterialsDescription); + } + final EncryptionMaterials newKEK = accessor.getEncryptionMaterials(newKEKMatDesc); + if (newKEK == null) { + throw new AmazonClientException( + "No material available with the description " + + newKEKMatDesc + + " from the encryption material provider"); + } + final SecretKey cek = cek(encryptedCEK, keyWrappingAlgorithm, origKEK, p, + getContentCryptoScheme(), kms); + final ContentCryptoMaterial output = create(cek, cipherLite.getIV(), newKEK, + getContentCryptoScheme(), // must use same content crypto scheme + targetScheme, + p, kms, req); + if (Arrays.equals(output.encryptedCEK, encryptedCEK)) { + throw new SecurityException( + "The new KEK must differ from the original"); + } + return output; + } + + /** + * Recreates a new content crypto material from the current material given a + * new KEK encryption materials. The purpose is to re-encrypt the CEK under + * the new KEK. + * + * Note network calls are involved if the CEK has been or is to be protected + * by KMS. + * + * @param newKEK + * encryption materials for the new KEK; must not be null + * @param accessor + * used to retrieve the original KEK given the corresponding + * material description + * @param targetScheme + * the target crypto scheme to use for recreating the content + * crypto material + * @param p + * optional security provider; null means to use the default. + * @throws SecurityException + * if the old and new material description are the same; or if + * the old and new KEK are the same + */ + ContentCryptoMaterial recreate(EncryptionMaterials newKEK, + EncryptionMaterialsAccessor accessor, S3CryptoScheme targetScheme, + Provider p, AWSKMSClient kms, AmazonWebServiceRequest req) { + if (!usesKMSKey() + && newKEK.getMaterialsDescription().equals(kekMaterialsDescription)) { + throw new SecurityException( + "Material description of the new KEK must differ from the current one"); + } + final EncryptionMaterials origKEK; + if (usesKMSKey()) { + origKEK = new KMSEncryptionMaterials(kekMaterialsDescription.get( + KMSEncryptionMaterials.CUSTOMER_MASTER_KEY_ID)); + } else { + origKEK = accessor.getEncryptionMaterials(kekMaterialsDescription); + } + final SecretKey cek = cek(encryptedCEK, keyWrappingAlgorithm, origKEK, p, + getContentCryptoScheme(), kms); + final ContentCryptoMaterial output = + create(cek, cipherLite.getIV(), newKEK, + getContentCryptoScheme(), // must use same content crypto scheme + targetScheme, // target scheme used to recreate the content crypto material + p, kms, req); + if (Arrays.equals(output.encryptedCEK, encryptedCEK)) { + throw new SecurityException( + "The new KEK must differ from the original"); + } + return output; + } + + /** + * Returns a new instance of ContentCryptoMaterial for the + * input parameters using the specified content crypto scheme, and the key + * wrapping and secure randomness specified of the specified s3 crypto + * scheme. + * + * Note network calls are involved if the CEK is to be protected by KMS. + * + * @param cek + * content encrypting key; must not be null. + * @param iv + * initialization vector; must not be null. + * @param contentCryptoScheme + * content crypto scheme to be used + * @param targetScheme + * the target s3 crypto scheme to be used for recreating the + * content crypto material by providing the key wrapping scheme + * and mechanism for secure randomness + * @param provider + * optional security provider + */ + static ContentCryptoMaterial create(SecretKey cek, byte[] iv, + EncryptionMaterials kekMaterials, + ContentCryptoScheme contentCryptoScheme, + S3CryptoScheme targetScheme, + Provider provider, AWSKMSClient kms, + AmazonWebServiceRequest req) { + return doCreate(cek, iv, kekMaterials, contentCryptoScheme, + targetScheme, provider, kms, req); + } + + /** + * Returns a new instance of ContentCryptoMaterial + * for the input parameters using the specified s3 crypto scheme. + * Note network calls are involved if the CEK is to be protected by KMS. + * + * @param cek content encrypting key + * @param iv initialization vector + * @param kekMaterials kek encryption material used to secure the CEK; + * can be KMS enabled. + * @param scheme + * s3 crypto scheme to be used for the content crypto material by + * providing the content crypto scheme, key wrapping scheme and + * mechanism for secure randomness + * @param provider optional security provider + * @param kms reference to the KMS client + * @param req originating service request + */ + static ContentCryptoMaterial create(SecretKey cek, byte[] iv, + EncryptionMaterials kekMaterials, + S3CryptoScheme scheme, + Provider provider, AWSKMSClient kms, + AmazonWebServiceRequest req) { + return doCreate(cek, iv, kekMaterials, scheme.getContentCryptoScheme(), + scheme, provider, kms, req); + } + + /** + * Returns a new instance of ContentCryptoMaterial for the + * given input parameters by using the specified content crypto scheme, and + * S3 crypto scheme. + * + * Note network calls are involved if the CEK is to be protected by KMS. + * + * @param cek + * content encrypting key + * @param iv + * initialization vector + * @param kekMaterials + * kek encryption material used to secure the CEK; can be KMS + * enabled. + * @param contentCryptoScheme + * content crypto scheme to be used, which can differ from the + * one of targetS3CryptoScheme + * @param targetS3CryptoScheme + * the target s3 crypto scheme to be used for providing the key + * wrapping scheme and mechanism for secure randomness + * @param provider + * security provider + * @param kms + * reference to the KMS client + * @param req + * the originating AWS service request + */ + private static ContentCryptoMaterial doCreate(SecretKey cek, byte[] iv, + EncryptionMaterials kekMaterials, + ContentCryptoScheme contentCryptoScheme, + S3CryptoScheme targetS3CryptoScheme, + Provider provider, + AWSKMSClient kms, + AmazonWebServiceRequest req) { + // Secure the envelope symmetric key either by encryption, key wrapping + // or KMS. + final SecuredCEK cekSecured = secureCEK(cek, kekMaterials, + targetS3CryptoScheme.getKeyWrapScheme(), + targetS3CryptoScheme.getSecureRandom(), + provider, kms, req); + return wrap(cek, iv, contentCryptoScheme, provider, cekSecured); + } + + /** + * Returns a new instance of ContentCryptoMaterial by wrapping + * the input parameters, including the already secured CEK. No network calls + * are involved. + */ + public static ContentCryptoMaterial wrap( + SecretKey cek, byte[] iv, + ContentCryptoScheme contentCryptoScheme, + Provider provider, + SecuredCEK cekSecured) { + return new ContentCryptoMaterial( + cekSecured.getMaterialDescription(), + cekSecured.getEncrypted(), + cekSecured.getKeyWrapAlgorithm(), + contentCryptoScheme.createCipherLite + (cek, iv, Cipher.ENCRYPT_MODE, provider)); + } + + /** + * Secure the given CEK. Note network calls are involved if the CEK is to + * be protected by KMS. + * + * @param cek content encrypting key to be secured + * @param materials used to provide the key-encryption-key (KEK); or if + * it is KMS-enabled, the customer master key id and material description. + * @param contentCryptoScheme the content crypto scheme + * @param p optional security provider; can be null if the default is used. + * @return a secured CEK in the form of ciphertext or ciphertext blob. + */ + private static SecuredCEK secureCEK(SecretKey cek, + EncryptionMaterials materials, S3KeyWrapScheme kwScheme, + SecureRandom srand, Provider p, AWSKMSClient kms, + AmazonWebServiceRequest req) { + final Map matdesc; + + if (materials.isKMSEnabled()) { + matdesc = mergeMaterialDescriptions(materials, req); + final EncryptRequest encryptRequest = new EncryptRequest() + .withEncryptionContext(matdesc) + .withKeyId(materials.getCustomerMasterKeyId()) + .withPlaintext(ByteBuffer.wrap(cek.getEncoded())) + ; + encryptRequest + .withGeneralProgressListener(req.getGeneralProgressListener()) + .withRequestMetricCollector(req.getRequestMetricCollector()) + ; + final EncryptResult encryptResult = kms.encrypt(encryptRequest); + final byte[] keyBlob = copyAllBytesFrom(encryptResult.getCiphertextBlob()); + return new KMSSecuredCEK(keyBlob, matdesc); + } else { + matdesc = materials.getMaterialsDescription(); + } + Key kek; + if (materials.getKeyPair() != null) { + // Do envelope encryption with public key from key pair + kek = materials.getKeyPair().getPublic(); + } else { + // Do envelope encryption with symmetric key + kek = materials.getSymmetricKey(); + } + final String keyWrapAlgo = kwScheme.getKeyWrapAlgorithm(kek); + try { + if (keyWrapAlgo != null) { + final Cipher cipher = p == null ? Cipher + .getInstance(keyWrapAlgo) : Cipher.getInstance( + keyWrapAlgo, p); + cipher.init(Cipher.WRAP_MODE, kek, srand); + return new SecuredCEK(cipher.wrap(cek), keyWrapAlgo, matdesc); + } + // fall back to the Encryption Only (EO) key encrypting method + Cipher cipher; + final byte[] toBeEncryptedBytes = cek.getEncoded(); + final String algo = kek.getAlgorithm(); + if (p != null) { + cipher = Cipher.getInstance(algo, p); + } else { + cipher = Cipher.getInstance(algo); // Use default JCE Provider + } + cipher.init(Cipher.ENCRYPT_MODE, kek); + return new SecuredCEK(cipher.doFinal(toBeEncryptedBytes), null, matdesc); + } catch (final Exception e) { + throw new AmazonClientException("Unable to encrypt symmetric key", e); + } + } + + static Map mergeMaterialDescriptions( + EncryptionMaterials materials, + AmazonWebServiceRequest req) + { + Map matdesc = materials.getMaterialsDescription(); + if (req instanceof MaterialsDescriptionProvider) { + final MaterialsDescriptionProvider mdp = (MaterialsDescriptionProvider) req; + final Map matdesc_req = mdp.getMaterialsDescription(); + if (matdesc_req != null) { + matdesc = new TreeMap(matdesc); + matdesc.putAll(matdesc_req); // request takes precedence + } + } + return matdesc; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/ContentCryptoScheme.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/ContentCryptoScheme.java index e3faba57e6..6cafa4e9c7 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/ContentCryptoScheme.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/ContentCryptoScheme.java @@ -249,4 +249,11 @@ CipherLite createCipherLite(SecretKey cek, byte[] iv, int cipherMode) * limit. */ abstract long getMaxPlaintextSize(); + + /** + * A convenient method motivated by KMS. + */ + final String getKeySpec() { + return getKeyGeneratorAlgorithm() + "_" + getKeyLengthInBits(); + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CryptoModuleDispatcher.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CryptoModuleDispatcher.java index 5eb3d71b56..ff0f294af0 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CryptoModuleDispatcher.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CryptoModuleDispatcher.java @@ -15,10 +15,13 @@ package com.amazonaws.services.s3.internal.crypto; +import static com.amazonaws.services.s3.model.CryptoMode.AuthenticatedEncryption; +import static com.amazonaws.services.s3.model.CryptoMode.EncryptionOnly; + import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; -import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.services.kms.AWSKMSClient; import com.amazonaws.services.s3.internal.S3Direct; import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; @@ -32,13 +35,17 @@ import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutInstructionFileRequest; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.PutObjectResult; import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.UploadObjectRequest; import com.amazonaws.services.s3.model.UploadPartRequest; import com.amazonaws.services.s3.model.UploadPartResult; import java.io.File; +import java.io.IOException; +import java.io.OutputStream; /** * A proxy cryptographic module used to dispatch method calls to the appropriate @@ -51,52 +58,69 @@ public class CryptoModuleDispatcher extends S3CryptoModule * NOTE: Because the encryption process requires context from block * N-1 in order to encrypt block N, parts uploaded with the @@ -138,15 +166,36 @@ public InitiateMultipartUploadResult initiateMultipartUploadSecurely( @Override public UploadPartResult uploadPartSecurely(UploadPartRequest req) throws AmazonClientException, AmazonServiceException { - return defaultCryptoMode == CryptoMode.EncryptionOnly - ? eo.uploadPartSecurely(req) - : ae.uploadPartSecurely(req); + return defaultCryptoMode == EncryptionOnly + ? eo.uploadPartSecurely(req) + : ae.uploadPartSecurely(req) + ; } @Override public CopyPartResult copyPartSecurely(CopyPartRequest req) { - return defaultCryptoMode == CryptoMode.EncryptionOnly - ? eo.copyPartSecurely(req) - : ae.copyPartSecurely(req); + return defaultCryptoMode == EncryptionOnly + ? eo.copyPartSecurely(req) + : ae.copyPartSecurely(req) + ; + } + + @Override + public PutObjectResult putInstructionFileSecurely( + PutInstructionFileRequest req) { + return defaultCryptoMode == EncryptionOnly + ? eo.putInstructionFileSecurely(req) + : ae.putInstructionFileSecurely(req) + ; + } + + @Override + public void putLocalObjectSecurely(UploadObjectRequest req, + String uploadId, OutputStream os) throws IOException { + if (defaultCryptoMode == EncryptionOnly) { + eo.putLocalObjectSecurely(req, uploadId, os); + } else { + ae.putLocalObjectSecurely(req, uploadId, os); + } } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CryptoRuntime.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CryptoRuntime.java index a458fa341e..dc6fce4488 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CryptoRuntime.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/CryptoRuntime.java @@ -28,11 +28,14 @@ public class CryptoRuntime { static final String BOUNCY_CASTLE_PROVIDER = "BC"; private static final String BC_PROVIDER_FQCN = "org.bouncycastle.jce.provider.BouncyCastleProvider"; - public static boolean isBouncyCastleAvailable() { + public static synchronized boolean isBouncyCastleAvailable() { return Security.getProvider(BOUNCY_CASTLE_PROVIDER) != null; } - public static void enableBouncyCastle() { + public static synchronized void enableBouncyCastle() { + if (isBouncyCastleAvailable()) { + return; + } try { @SuppressWarnings("unchecked") Class c = (Class) Class.forName(BC_PROVIDER_FQCN); diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/JceEncryptionConstants.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/JceEncryptionConstants.java index 2ffc469e8c..98f9207d7f 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/JceEncryptionConstants.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/JceEncryptionConstants.java @@ -21,18 +21,18 @@ public class JceEncryptionConstants { /** Name of the symmetric encryption algorithm */ - public static String SYMMETRIC_KEY_ALGORITHM = "AES"; + public static final String SYMMETRIC_KEY_ALGORITHM = "AES"; /** * Name of the algorithm, mode, and padding we will use in the symmetric * cipher for encryption */ - public static String SYMMETRIC_CIPHER_METHOD = "AES/CBC/PKCS5Padding"; + public static final String SYMMETRIC_CIPHER_METHOD = "AES/CBC/PKCS5Padding"; /** Minimum length of the generated symmetric key */ - public static int SYMMETRIC_KEY_LENGTH = 256; + public static final int SYMMETRIC_KEY_LENGTH = 256; /** AES cipher block size */ - public static int SYMMETRIC_CIPHER_BLOCK_SIZE = 16; + public static final int SYMMETRIC_CIPHER_BLOCK_SIZE = 16; } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/KMSSecuredCEK.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/KMSSecuredCEK.java new file mode 100644 index 0000000000..2f01cfa44b --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/KMSSecuredCEK.java @@ -0,0 +1,36 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Portions copyright 2006-2009 James Murty. Please see LICENSE.txt + * for applicable license terms and NOTICE.txt for applicable notices. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal.crypto; + +import java.util.Map; + +final class KMSSecuredCEK extends SecuredCEK { + static final String KEY_PROTECTION_MECHANISM = "kms"; + + KMSSecuredCEK(byte[] encryptedKeyBlob, Map matdesc) { + super(encryptedKeyBlob, KEY_PROTECTION_MECHANISM, matdesc); + } + + /** + * Returns true if the specified key wrapping algorithm is + * {@value #KEY_PROTECTION_MECHANISM}; false otherwise. + */ + public static boolean isKMSKeyWrapped(String keyWrapAlgo) { + return KEY_PROTECTION_MECHANISM.equals(keyWrapAlgo); + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/MultipartUploadCbcContext.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/MultipartUploadCbcContext.java new file mode 100644 index 0000000000..8238812f81 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/MultipartUploadCbcContext.java @@ -0,0 +1,32 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal.crypto; + +final class MultipartUploadCbcContext extends MultipartUploadCryptoContext { + private byte[] nextIV; + + MultipartUploadCbcContext(String bucketName, String key, + ContentCryptoMaterial cekMaterial) { + super(bucketName, key, cekMaterial); + } + + public void setNextInitializationVector(byte[] nextIV) { + this.nextIV = nextIV; + } + + public byte[] getNextInitializationVector() { + return nextIV; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/MultipartUploadCryptoContext.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/MultipartUploadCryptoContext.java index 38c69731df..49643e0f1b 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/MultipartUploadCryptoContext.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/MultipartUploadCryptoContext.java @@ -15,8 +15,18 @@ package com.amazonaws.services.s3.internal.crypto; -final class MultipartUploadCryptoContext extends MultipartUploadContext { +import com.amazonaws.AmazonClientException; + +class MultipartUploadCryptoContext extends MultipartUploadContext { private final ContentCryptoMaterial cekMaterial; + /** + * Can be used to enforce serial uploads. + */ + private int partNumber; + /** + * True if a multi-part upload is currently in progress; false otherwise. + */ + private volatile boolean partUploadInProgress; MultipartUploadCryptoContext(String bucketName, String key, ContentCryptoMaterial cekMaterial) { @@ -39,4 +49,49 @@ CipherLite getCipherLite() { ContentCryptoMaterial getContentCryptoMaterial() { return cekMaterial; } + + /** + * Can be used to check the next part number must either be the same (if it + * was an retry) or increment by exactly 1 during a serial part uploads. + *

+ * As a side effect, the {@link #partUploadInProgress} will be set to true + * upon successful completion of this method. Caller of this method is + * responsible to call {@link #endPartUpload()} in a finally block once the + * respective part-upload is completed (either normally or abruptly). + * + * @see #endPartUpload() + * @throws AmazonClientException if parallel part upload is detected + */ + void beginPartUpload(final int nextPartNumber) + throws AmazonClientException { + if (nextPartNumber < 1) { + throw new IllegalArgumentException("part number must be at least 1"); + } + if (partUploadInProgress) { + throw new AmazonClientException( + "Parts are required to be uploaded in series"); + } + synchronized (this) { + if (nextPartNumber - partNumber <= 1) { + partNumber = nextPartNumber; + partUploadInProgress = true; + } else { + throw new AmazonClientException( + "Parts are required to be uploaded in series (partNumber=" + + partNumber + ", nextPartNumber=" + + nextPartNumber + ")"); + } + } + } + + /** + * Used to mark the completion of a part upload before the next. Should be + * invoked in a finally block, and must be preceded previously by a call to + * {@link #beginPartUpload(int)}. + * + * @see #beginPartUpload(int) + */ + void endPartUpload() { + partUploadInProgress = false; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/RenewableCipherLiteInputStream.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/RenewableCipherLiteInputStream.java new file mode 100644 index 0000000000..3d8333f857 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/RenewableCipherLiteInputStream.java @@ -0,0 +1,120 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal.crypto; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A specific kind of {@link CipherLiteInputStream} that supports partial + * mark-and-reset in the sense that, if the underlying input stream supports + * mark-and-reset, this input stream can then be marked at and get reset back to + * the very beginning of the stream (but not anywhere else). + */ +public final class RenewableCipherLiteInputStream extends CipherLiteInputStream { + private boolean hasBeenAccessed; + + public RenewableCipherLiteInputStream(InputStream is, CipherLite cipherLite) { + super(is, cipherLite); + } + + public RenewableCipherLiteInputStream(InputStream is, CipherLite c, + int buffsize) { + super(is, c, buffsize); + } + + public RenewableCipherLiteInputStream(InputStream is, CipherLite c, + int buffsize, boolean multipart, boolean lastMultiPart) { + super(is, c, buffsize, multipart, lastMultiPart); + } + + protected RenewableCipherLiteInputStream(InputStream is) { + super(is); + } + + /** + * Mark and reset is currently only partially supported, in the sense that, + * if the underlying input stream supports mark-and-reset, this input stream + * can then be marked at and get reset back to the very beginning of the + * stream (but not anywhere else). + */ + @Override + public boolean markSupported() { + abortIfNeeded(); + return in.markSupported(); + } + + /** + * Mark and reset is currently only partially supported, in the sense that, + * if the underlying input stream supports mark-and-reset, this input stream + * can then be marked at and get reset back to the very beginning of the + * stream (but not anywhere else). + * + * @throws UnsupportedOperationException + * if mark is called after this stream has been accessed. + */ + @Override + public void mark(final int readlimit) { + abortIfNeeded(); + if (hasBeenAccessed) { + throw new UnsupportedOperationException( + "Marking is only supported before your first call to " + + "read or skip."); + } + in.mark(readlimit); + } + + /** + * Resets back to the very beginning of the stream. + *

+ * Mark and reset is currently only partially supported, in the sense that, + * if the underlying input stream supports mark-and-reset, this input stream + * can then be marked at and get reset back to the very beginning of the + * stream (but not anywhere else). + */ + @Override + public void reset() throws IOException { + abortIfNeeded(); + in.reset(); + renewCipherLite(); + resetInternal(); + hasBeenAccessed = false; + } + + @Override + public int read() throws IOException { + hasBeenAccessed = true; + return super.read(); + } + + @Override + public int read(final byte[] b) throws IOException { + hasBeenAccessed = true; + return super.read(b); + } + + @Override + public int read(final byte[] b, final int off, final int len) + throws IOException { + hasBeenAccessed = true; + return super.read(b, off, len); + } + + @Override + public long skip(final long n) throws IOException { + hasBeenAccessed = true; + return super.skip(n); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModule.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModule.java index e2284bb457..0a0b2a34a1 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModule.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModule.java @@ -20,23 +20,32 @@ import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; import com.amazonaws.services.s3.model.CopyPartRequest; import com.amazonaws.services.s3.model.CopyPartResult; +import com.amazonaws.services.s3.model.CryptoMode; +import com.amazonaws.services.s3.model.EncryptedGetObjectRequest; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutInstructionFileRequest; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.PutObjectResult; import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.UploadObjectRequest; import com.amazonaws.services.s3.model.UploadPartRequest; import com.amazonaws.services.s3.model.UploadPartResult; import java.io.File; +import java.io.IOException; +import java.io.OutputStream; /** - * An internal SPI used to implement different cryptographic modules for use - * with the S3 encryption client. + * An internal SPI used to implement different cryptographic modules + * for use with the S3 encryption client. */ public abstract class S3CryptoModule { + /** + * @return the result of the putting the S3 object. + */ public abstract PutObjectResult putObjectSecurely(PutObjectRequest req); public abstract S3Object getObjectSecurely(GetObjectRequest req); @@ -55,4 +64,31 @@ public abstract InitiateMultipartUploadResult initiateMultipartUploadSecurely( public abstract CopyPartResult copyPartSecurely(CopyPartRequest req); public abstract void abortMultipartUploadSecurely(AbortMultipartUploadRequest req); + + /** + * @return the result of putting the instruction file in S3; or null if the + * specified S3 object doesn't exist. The S3 object can be + * subsequently retrieved using the new instruction file via the + * usual get operation by specifying a + * {@link EncryptedGetObjectRequest}. + * + * @throws IllegalArgumentException + * if the specified S3 object doesn't exist. + * @throws SecurityException + * if the protection level of the material in the new + * instruction file is lower than that of the original. + * Currently, this means if the original material has been + * secured via authenticated encryption, then the new + * instruction file cannot be created via an S3 encryption + * client configured with {@link CryptoMode#EncryptionOnly}. + */ + public abstract PutObjectResult putInstructionFileSecurely( + PutInstructionFileRequest req); + + /** + * @param uploadId multipart upload id + * @param os output stream which will be closed upon method completion. + */ + public abstract void putLocalObjectSecurely(UploadObjectRequest req, + String uploadId, OutputStream os) throws IOException; } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleAE.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleAE.java index 8aebfe51f9..e7bc7f3c41 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleAE.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleAE.java @@ -12,38 +12,32 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ - package com.amazonaws.services.s3.internal.crypto; import static com.amazonaws.services.s3.AmazonS3EncryptionClient.USER_AGENT; -import static com.amazonaws.services.s3.internal.crypto.EncryptionUtils.createInstructionGetRequest; -import static com.amazonaws.services.s3.internal.crypto.EncryptionUtils.getAdjustedCryptoRange; +import static com.amazonaws.services.s3.model.CryptoMode.AuthenticatedEncryption; +import static com.amazonaws.services.s3.model.CryptoMode.StrictAuthenticatedEncryption; +import static com.amazonaws.services.s3.model.ExtraMaterialsDescription.NONE; +import static com.amazonaws.util.IOUtils.closeQuietly; import com.amazonaws.AmazonClientException; -import com.amazonaws.AmazonServiceException; -import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.services.s3.internal.InputSubstream; -import com.amazonaws.services.s3.internal.RepeatableFileInputStream; +import com.amazonaws.internal.SdkFilterInputStream; +import com.amazonaws.services.kms.AWSKMSClient; import com.amazonaws.services.s3.internal.S3Direct; -import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; -import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; -import com.amazonaws.services.s3.model.CopyPartRequest; -import com.amazonaws.services.s3.model.CopyPartResult; import com.amazonaws.services.s3.model.CryptoConfiguration; -import com.amazonaws.services.s3.model.CryptoStorageMode; +import com.amazonaws.services.s3.model.CryptoMode; +import com.amazonaws.services.s3.model.EncryptedGetObjectRequest; import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; +import com.amazonaws.services.s3.model.ExtraMaterialsDescription; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; -import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.PutObjectResult; import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectId; import com.amazonaws.services.s3.model.S3ObjectInputStream; import com.amazonaws.services.s3.model.UploadPartRequest; -import com.amazonaws.services.s3.model.UploadPartResult; import com.amazonaws.util.json.JsonUtils; import java.io.BufferedOutputStream; @@ -53,27 +47,31 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Collections; import java.util.Map; /** - * Authenticated encryption (AE) cryptographic module for the S3 encryption - * client. + * Authenticated encryption (AE) cryptographic module for the S3 encryption client. */ class S3CryptoModuleAE extends S3CryptoModuleBase { static { // Enable bouncy castle if available CryptoRuntime.enableBouncyCastle(); } - private static final boolean IS_MULTI_PART = true; - - S3CryptoModuleAE(S3Direct s3, + /** + * @param cryptoConfig a read-only copy of the crypto configuration. + */ + S3CryptoModuleAE(AWSKMSClient kms, S3Direct s3, AWSCredentialsProvider credentialsProvider, EncryptionMaterialsProvider encryptionMaterialsProvider, - ClientConfiguration clientConfig, CryptoConfiguration cryptoConfig) { - super(s3, credentialsProvider, encryptionMaterialsProvider, - clientConfig, cryptoConfig, - new S3CryptoScheme(ContentCryptoScheme.AES_GCM)); + super(kms, s3, credentialsProvider, encryptionMaterialsProvider, + cryptoConfig); + final CryptoMode mode = cryptoConfig.getCryptoMode(); + if (mode != StrictAuthenticatedEncryption + && mode != AuthenticatedEncryption) { + throw new IllegalArgumentException(); + } } /** @@ -82,9 +80,17 @@ class S3CryptoModuleAE extends S3CryptoModuleBase S3CryptoModuleAE(S3Direct s3, EncryptionMaterialsProvider encryptionMaterialsProvider, CryptoConfiguration cryptoConfig) { - this(s3, new DefaultAWSCredentialsProviderChain(), - encryptionMaterialsProvider, new ClientConfiguration(), - cryptoConfig); + this(null, s3, new DefaultAWSCredentialsProviderChain(), + encryptionMaterialsProvider, cryptoConfig); + } + /** + * Used for testing purposes only. + */ + S3CryptoModuleAE(AWSKMSClient kms, S3Direct s3, + EncryptionMaterialsProvider encryptionMaterialsProvider, + CryptoConfiguration cryptoConfig) { + this(kms, s3, new DefaultAWSCredentialsProviderChain(), + encryptionMaterialsProvider, cryptoConfig); } /** @@ -95,180 +101,209 @@ protected boolean isStrict() { return false; } - /** - * @throws SecurityException if the crypto scheme used in the given content - * crypto material is not allowed in this crypto module. - */ - protected void securityCheck(ContentCryptoMaterial cekMaterial, - S3ObjectWrapper retrieved) { - // default is no-op. Sublcass may override. - } - @Override - public PutObjectResult putObjectSecurely(PutObjectRequest putObjectRequest) - throws AmazonClientException, AmazonServiceException { - appendUserAgent(putObjectRequest, USER_AGENT); - - if (this.cryptoConfig.getStorageMode() == CryptoStorageMode.InstructionFile) { - return putObjectUsingInstructionFile(putObjectRequest); - } else { - return putObjectUsingMetadata(putObjectRequest); - } - } - - private PutObjectResult putObjectUsingMetadata(PutObjectRequest req) - throws AmazonClientException, AmazonServiceException { - ContentCryptoMaterial cekMaterial = createContentCryptoMaterial(req); - // Wraps the object data with a cipher input stream - PutObjectRequest wrappedReq = wrapWithCipher(req, cekMaterial); - // Update the metadata - req.setMetadata(updateMetadataWithContentCryptoMaterial( - req.getMetadata(), req.getFile(), - cekMaterial)); - // Put the encrypted object into S3 - return s3.putObject(wrappedReq); - } - - @Override - public S3Object getObjectSecurely(GetObjectRequest req) - throws AmazonClientException, AmazonServiceException { + public S3Object getObjectSecurely(GetObjectRequest req) { appendUserAgent(req, USER_AGENT); - // Adjust the crypto range to retrieve all of the cipher blocks needed - // to contain the user's desired + // Adjust the crypto range to retrieve all of the cipher blocks needed to contain the user's desired // range of bytes. - long[] desiredRange = req.getRange(); - if (isStrict() && desiredRange != null) - throw new SecurityException("Range get is not allowed in strict crypto mode"); - long[] adjustedCryptoRange = getAdjustedCryptoRange(desiredRange); - if (adjustedCryptoRange != null) + final long[] desiredRange = req.getRange(); + if (isStrict() && (desiredRange != null || req.getPartNumber() != null)) { + throw new SecurityException("Range get and getting a part are not allowed in strict crypto mode"); + } + final long[] adjustedCryptoRange = getAdjustedCryptoRange(desiredRange); + if (adjustedCryptoRange != null) { req.setRange(adjustedCryptoRange[0], adjustedCryptoRange[1]); + } // Get the object from S3 - S3Object retrieved = s3.getObject(req); - // If the caller has specified constraints, it's possible that - // super.getObject(...) + final S3Object retrieved = s3.getObject(req); + // If the caller has specified constraints, it's possible that super.getObject(...) // would return null, so we simply return null as well. - if (retrieved == null) + if (retrieved == null) { return null; + } + String suffix = null; + if (req instanceof EncryptedGetObjectRequest) { + final EncryptedGetObjectRequest ereq = (EncryptedGetObjectRequest)req; + suffix = ereq.getInstructionFileSuffix(); + } try { - return decipher(req, desiredRange, adjustedCryptoRange, retrieved); - } catch (AmazonClientException ace) { + return suffix == null || suffix.trim().isEmpty() + ? decipher(req, desiredRange, adjustedCryptoRange, retrieved) + : decipherWithInstFileSuffix(req, + desiredRange, adjustedCryptoRange, retrieved, + suffix) + ; + } catch (final RuntimeException ex) { // If we're unable to set up the decryption, make sure we close the // HTTP connection - try { - retrieved.getObjectContent().close(); - } catch (Exception e) { - log.debug("Safely ignoring", e); - } - throw ace; + closeQuietly(retrieved, log); + throw ex; + } catch (final Error error) { + closeQuietly(retrieved, log); + throw error; } } private S3Object decipher(GetObjectRequest req, long[] desiredRange, long[] cryptoRange, S3Object retrieved) { - S3ObjectWrapper wrapped = new S3ObjectWrapper(retrieved); + final S3ObjectWrapper wrapped = new S3ObjectWrapper(retrieved, req.getS3ObjectId()); // Check if encryption info is in object metadata - if (wrapped.hasEncryptionInfo()) - return decipherWithMetadata(desiredRange, cryptoRange, wrapped); + if (wrapped.hasEncryptionInfo()) { + return decipherWithMetadata(req, desiredRange, cryptoRange, wrapped); + } // Check if encrypted info is in an instruction file - S3ObjectWrapper instructionFile = fetchInstructionFile(req); - if (instructionFile != null) { + final S3ObjectWrapper ifile = fetchInstructionFile(req.getS3ObjectId(), null); + if (ifile != null) { try { - if (instructionFile.isInstructionFile()) { - return decipherWithInstructionFile(desiredRange, cryptoRange, - wrapped, instructionFile); + if (ifile.isInstructionFile()) { + return decipherWithInstructionFile(req, desiredRange, + cryptoRange, wrapped, ifile); } } finally { - try { - instructionFile.getObjectContent().close(); - } catch (Exception ignore) { - } + closeQuietly(ifile, log); } } - if (isStrict()) { - try { - wrapped.close(); - } catch (IOException ignore) { - } - throw new SecurityException("S3 object with bucket name: " + + if (isStrict() || !cryptoConfig.isIgnoreMissingInstructionFile()) { + closeQuietly(wrapped, log); + throw new SecurityException("Instruction file not found for S3 object with bucket name: " + retrieved.getBucketName() + ", key: " - + retrieved.getKey() + " is not encrypted"); + + retrieved.getKey()); } - // The object was not encrypted to begin with. Return the object - // without decrypting it. + // To keep backward compatible: + // ignore the missing instruction file and treat the object as un-encrypted. log.warn(String.format( "Unable to detect encryption information for object '%s' in bucket '%s'. " + "Returning object without decryption.", retrieved.getKey(), retrieved.getBucketName())); // Adjust the output to the desired range of bytes. - S3ObjectWrapper adjusted = adjustToDesiredRange(wrapped, desiredRange, null); + final S3ObjectWrapper adjusted = adjustToDesiredRange(wrapped, desiredRange, null); return adjusted.getS3Object(); } - private S3Object decipherWithInstructionFile(long[] desiredRange, - long[] cryptoRange, S3ObjectWrapper retrieved, + /** + * Same as {@link #decipher(GetObjectRequest, long[], long[], S3Object)} + * but makes use of an instruction file with the specified suffix. + * @param instFileSuffix never null or empty (which is assumed to have been + * sanitized upstream.) + */ + private S3Object decipherWithInstFileSuffix(GetObjectRequest req, + long[] desiredRange, long[] cryptoRange, S3Object retrieved, + String instFileSuffix) { + final S3ObjectId id = req.getS3ObjectId(); + // Check if encrypted info is in an instruction file + final S3ObjectWrapper ifile = fetchInstructionFile(id, instFileSuffix); + if (ifile == null) { + throw new AmazonClientException("Instruction file with suffix " + + instFileSuffix + " is not found for " + retrieved); + } + try { + if (ifile.isInstructionFile()) { + return decipherWithInstructionFile(req, desiredRange, + cryptoRange, new S3ObjectWrapper(retrieved, id), ifile); + } else { + throw new AmazonClientException( + "Invalid Instruction file with suffix " + + instFileSuffix + " detected for " + retrieved); + } + } finally { + closeQuietly(ifile, log); + } + } + + private S3Object decipherWithInstructionFile(GetObjectRequest req, + long[] desiredRange, long[] cryptoRange, S3ObjectWrapper retrieved, S3ObjectWrapper instructionFile) { - String json = instructionFile.toJsonString(); - Map instruction = JsonUtils.jsonToMap(json); - ContentCryptoMaterial cekMaterial = + ExtraMaterialsDescription extraMatDesc = NONE; + boolean keyWrapExpected = isStrict(); + if (req instanceof EncryptedGetObjectRequest) { + final EncryptedGetObjectRequest ereq = (EncryptedGetObjectRequest)req; + extraMatDesc = ereq.getExtraMaterialDescription(); + if (!keyWrapExpected) { + keyWrapExpected = ereq.isKeyWrapExpected(); + } + } + final String json = instructionFile.toJsonString(); + @SuppressWarnings("unchecked") + final + Map matdesc = + Collections.unmodifiableMap(JsonUtils.jsonToMap(json)); + final ContentCryptoMaterial cekMaterial = ContentCryptoMaterial.fromInstructionFile( - instruction, - kekMaterialsProvider, - cryptoConfig.getCryptoProvider(), - cryptoRange // range is sometimes necessary to compute - // the adjusted IV - ); + matdesc, + kekMaterialsProvider, + cryptoConfig.getCryptoProvider(), + cryptoRange, // range is sometimes necessary to compute the adjusted IV + extraMatDesc, + keyWrapExpected, + kms + ); securityCheck(cekMaterial, retrieved); - S3ObjectWrapper decrypted = decrypt(retrieved, cekMaterial, cryptoRange); + final S3ObjectWrapper decrypted = decrypt(retrieved, cekMaterial, cryptoRange); // Adjust the output to the desired range of bytes. - S3ObjectWrapper adjusted = adjustToDesiredRange( - decrypted, desiredRange, instruction); + final S3ObjectWrapper adjusted = adjustToDesiredRange( + decrypted, desiredRange, matdesc); return adjusted.getS3Object(); } - private S3Object decipherWithMetadata(long[] desiredRange, + private S3Object decipherWithMetadata(GetObjectRequest req, + long[] desiredRange, long[] cryptoRange, S3ObjectWrapper retrieved) { - ContentCryptoMaterial cekMaterial = ContentCryptoMaterial - .fromObjectMetadata(retrieved.getObjectMetadata(), - kekMaterialsProvider, - cryptoConfig.getCryptoProvider(), - // range is sometimes necessary to compute the adjusted - // IV - cryptoRange - ); + ExtraMaterialsDescription extraMatDesc = NONE; + boolean keyWrapExpected = isStrict(); + if (req instanceof EncryptedGetObjectRequest) { + final EncryptedGetObjectRequest ereq = (EncryptedGetObjectRequest)req; + extraMatDesc = ereq.getExtraMaterialDescription(); + if (!keyWrapExpected) { + keyWrapExpected = ereq.isKeyWrapExpected(); + } + } + final ContentCryptoMaterial cekMaterial = ContentCryptoMaterial + .fromObjectMetadata(retrieved.getObjectMetadata(), + kekMaterialsProvider, + cryptoConfig.getCryptoProvider(), + // range is sometimes necessary to compute the adjusted IV + cryptoRange, + extraMatDesc, + keyWrapExpected, + kms + ); securityCheck(cekMaterial, retrieved); - S3ObjectWrapper decrypted = decrypt(retrieved, cekMaterial, cryptoRange); + final S3ObjectWrapper decrypted = decrypt(retrieved, cekMaterial, cryptoRange); // Adjust the output to the desired range of bytes. - S3ObjectWrapper adjusted = adjustToDesiredRange( + final S3ObjectWrapper adjusted = adjustToDesiredRange( decrypted, desiredRange, null); return adjusted.getS3Object(); } /** - * Adjusts the retrieved S3Object so that the object contents contain only - * the range of bytes desired by the user. Since encrypted contents can only - * be retrieved in CIPHER_BLOCK_SIZE (16 bytes) chunks, the S3Object - * potentially contains more bytes than desired, so this method adjusts the - * contents range. + * Adjusts the retrieved S3Object so that the object contents contain only the range of bytes + * desired by the user. Since encrypted contents can only be retrieved in CIPHER_BLOCK_SIZE + * (16 bytes) chunks, the S3Object potentially contains more bytes than desired, so this method + * adjusts the contents range. * - * @param s3object The S3Object retrieved from S3 that could possibly - * contain more bytes than desired by the user. - * @param range A two-element array of longs corresponding to the start and - * finish (inclusive) of a desired range of bytes. - * @param instruction Instruction file in JSON or null if no instruction - * file is involved - * @return The S3Object with adjusted object contents containing only the - * range desired by the user. If the range specified is invalid, - * then the S3Object is returned without any modifications. + * @param s3object + * The S3Object retrieved from S3 that could possibly contain more bytes than desired + * by the user. + * @param range + * A two-element array of longs corresponding to the start and finish (inclusive) of a desired + * range of bytes. + * @param instruction + * Instruction file in JSON or null if no instruction file is involved + * @return + * The S3Object with adjusted object contents containing only the range desired by the user. + * If the range specified is invalid, then the S3Object is returned without any modifications. */ protected final S3ObjectWrapper adjustToDesiredRange(S3ObjectWrapper s3object, - long[] range, Map instruction) { - if (range == null) + long[] range, Map instruction) { + if (range == null) { return s3object; + } // Figure out the original encryption scheme used, which can be // different from the crypto scheme used for decryption. - ContentCryptoScheme encryptionScheme = s3object.encryptionSchemeOf(instruction); + final ContentCryptoScheme encryptionScheme = s3object.encryptionSchemeOf(instruction); // range get on data encrypted using AES_GCM final long instanceLen = s3object.getObjectMetadata().getInstanceLength(); final long maxOffset = instanceLen - encryptionScheme.getTagLengthInBits() / 8 - 1; @@ -276,12 +311,9 @@ protected final S3ObjectWrapper adjustToDesiredRange(S3ObjectWrapper s3object, range[1] = maxOffset; if (range[0] > range[1]) { // Return empty content - try { // First let's close the existing input stream to - // avoid resource leakage - s3object.getObjectContent().close(); - } catch (IOException ignore) { - log.trace("", ignore); - } + // First let's close the existing input stream to avoid resource + // leakage + closeQuietly(s3object.getObjectContent(), log); s3object.setObjectContent(new ByteArrayInputStream(new byte[0])); return s3object; } @@ -291,207 +323,78 @@ protected final S3ObjectWrapper adjustToDesiredRange(S3ObjectWrapper s3object, return s3object; } try { - S3ObjectInputStream objectContent = s3object.getObjectContent(); - InputStream adjustedRangeContents = new AdjustedRangeInputStream(objectContent, - range[0], range[1]); - s3object.setObjectContent(new S3ObjectInputStream(adjustedRangeContents, objectContent - .getHttpRequest())); + final S3ObjectInputStream objectContent = s3object.getObjectContent(); + final InputStream adjustedRangeContents = new AdjustedRangeInputStream(objectContent, range[0], range[1]); + s3object.setObjectContent(new S3ObjectInputStream(adjustedRangeContents, objectContent.getHttpRequest())); return s3object; - } catch (IOException e) { - throw new AmazonClientException("Error adjusting output to desired byte range: " - + e.getMessage()); + } catch (final IOException e) { + throw new AmazonClientException("Error adjusting output to desired byte range: " + e.getMessage()); } } @Override - public ObjectMetadata getObjectSecurely(GetObjectRequest getObjectRequest, File destinationFile) - throws AmazonClientException, AmazonServiceException { + public ObjectMetadata getObjectSecurely(GetObjectRequest getObjectRequest, + File destinationFile) { assertParameterNotNull(destinationFile, - "The destination file parameter must be specified when downloading an object directly to a file"); + "The destination file parameter must be specified when downloading an object directly to a file"); - S3Object s3Object = getObjectSecurely(getObjectRequest); + final S3Object s3Object = getObjectSecurely(getObjectRequest); // getObject can return null if constraints were specified but not met - if (s3Object == null) + if (s3Object == null) { return null; + } OutputStream outputStream = null; try { outputStream = new BufferedOutputStream(new FileOutputStream(destinationFile)); - byte[] buffer = new byte[1024 * 10]; + final byte[] buffer = new byte[1024*10]; int bytesRead; while ((bytesRead = s3Object.getObjectContent().read(buffer)) > -1) { outputStream.write(buffer, 0, bytesRead); } - } catch (IOException e) { + } catch (final IOException e) { throw new AmazonClientException( "Unable to store object contents to disk: " + e.getMessage(), e); } finally { - try { - outputStream.close(); - } catch (Exception e) { - log.debug(e.getMessage()); - } - try { - s3Object.getObjectContent().close(); - } catch (Exception e) { - log.debug(e.getMessage()); - } + closeQuietly(outputStream, log); + closeQuietly(s3Object.getObjectContent(), log); } /* - * Unlike the standard Amazon S3 Client, the Amazon S3 Encryption Client - * does not do an MD5 check here because the contents stored in S3 and - * the contents we just retrieved are different. In S3, the stored - * contents are encrypted, and locally, the retrieved contents are - * decrypted. + * Unlike the standard Amazon S3 Client, the Amazon S3 Encryption Client does not do an MD5 check + * here because the contents stored in S3 and the contents we just retrieved are different. In + * S3, the stored contents are encrypted, and locally, the retrieved contents are decrypted. */ return s3Object.getObjectMetadata(); } @Override - public CompleteMultipartUploadResult completeMultipartUploadSecurely( - CompleteMultipartUploadRequest req) throws AmazonClientException, - AmazonServiceException { - appendUserAgent(req, USER_AGENT); - String uploadId = req.getUploadId(); - MultipartUploadCryptoContext uploadContext = multipartUploadContexts.get(uploadId); - - if (uploadContext.hasFinalPartBeenSeen() == false) { - throw new AmazonClientException( - "Unable to complete an encrypted multipart upload without being told which part was the last. " - + - "Without knowing which part was the last, the encrypted data in Amazon S3 is incomplete and corrupt."); - } - - CompleteMultipartUploadResult result = s3.completeMultipartUpload(req); - - // In InstructionFile mode, we want to write the instruction file only - // after the whole upload has completed correctly. - if (cryptoConfig.getStorageMode() == CryptoStorageMode.InstructionFile) { - // Put the instruction file into S3 - s3.putObject(createInstructionPutRequest( - uploadContext.getBucketName(), - uploadContext.getKey(), - uploadContext.getContentCryptoMaterial())); - } - multipartUploadContexts.remove(uploadId); - return result; + final MultipartUploadCryptoContext newUploadContext( + InitiateMultipartUploadRequest req, ContentCryptoMaterial cekMaterial) { + return new MultipartUploadCryptoContext( + req.getBucketName(), req.getKey(), cekMaterial); } + //// specific overrides for uploading parts. @Override - public InitiateMultipartUploadResult initiateMultipartUploadSecurely( - InitiateMultipartUploadRequest req) - throws AmazonClientException, AmazonServiceException { - appendUserAgent(req, USER_AGENT); - // Generate a one-time use symmetric key and initialize a cipher to - // encrypt object data - ContentCryptoMaterial cekMaterial = createContentCryptoMaterial(req); - if (cryptoConfig.getStorageMode() == CryptoStorageMode.ObjectMetadata) { - ObjectMetadata metadata = req.getObjectMetadata(); - if (metadata == null) - metadata = new ObjectMetadata(); - // Store encryption info in metadata - req.setObjectMetadata(updateMetadataWithContentCryptoMaterial( - metadata, null, cekMaterial)); - } - InitiateMultipartUploadResult result = s3.initiateMultipartUpload(req); - MultipartUploadCryptoContext uploadContext = new MultipartUploadCryptoContext( - req.getBucketName(), req.getKey(), cekMaterial); - multipartUploadContexts.put(result.getUploadId(), uploadContext); - return result; + final CipherLite cipherLiteForNextPart( + MultipartUploadCryptoContext uploadContext) { + return uploadContext.getCipherLite(); } - - /** - * {@inheritDoc} - *

- * NOTE: Because the encryption process requires context from - * previous blocks, parts uploaded with the AmazonS3EncryptionClient (as - * opposed to the normal AmazonS3Client) must be uploaded serially, and in - * order. Otherwise, the previous encryption context isn't available to use - * when encrypting the current part. - */ @Override - public UploadPartResult uploadPartSecurely(UploadPartRequest req) - throws AmazonClientException, AmazonServiceException { - appendUserAgent(req, USER_AGENT); - final int blockSize = contentCryptoScheme.getBlockSizeInBytes(); - final boolean isLastPart = req.isLastPart(); - final String uploadId = req.getUploadId(); - final long partSize = req.getPartSize(); - final boolean partSizeMultipleOfCipherBlockSize = 0 == (partSize % blockSize); - if (!isLastPart && !partSizeMultipleOfCipherBlockSize) { - throw new AmazonClientException( - "Invalid part size: part sizes for encrypted multipart uploads must be multiples " - + "of the cipher block size (" - + blockSize - + ") with the exception of the last part."); - } - - // Generate the envelope symmetric key and initialize a cipher to - // encrypt the object's data - MultipartUploadCryptoContext uploadContext = multipartUploadContexts.get(uploadId); - if (uploadContext == null) { - throw new AmazonClientException( - "No client-side information available on upload ID " + uploadId); - } - - CipherLite cipherLite = uploadContext.getCipherLite(); - req.setInputStream(newMultipartS3CipherInputStream(req, cipherLite)); - // Treat all encryption requests as input stream upload requests, not as - // file upload requests. - req.setFile(null); - req.setFileOffset(0); - // The last part of the multipart upload will contain an extra 16-byte - // mac - if (req.isLastPart()) { - // We only change the size of the last part - req.setPartSize(partSize + (contentCryptoScheme.getTagLengthInBits() / 8)); - if (uploadContext.hasFinalPartBeenSeen()) { - throw new AmazonClientException( - "This part was specified as the last part in a multipart upload, but a previous part was already marked as the last part. " - + "Only the last part of the upload should be marked as the last part."); - } - uploadContext.setHasFinalPartBeenSeen(true); - } - - UploadPartResult result = s3.uploadPart(req); - return result; + final SdkFilterInputStream wrapForMultipart( + CipherLiteInputStream is, long partSize) { + return is; } - - protected final CipherLiteInputStream newMultipartS3CipherInputStream( - UploadPartRequest req, CipherLite cipherLite) { - try { - InputStream is = req.getInputStream(); - if (req.getFile() != null) { - is = new InputSubstream( - new RepeatableFileInputStream( - req.getFile()), - req.getFileOffset(), - req.getPartSize(), - req.isLastPart()); - } - - return new CipherLiteInputStream(is, cipherLite, - DEFAULT_BUFFER_SIZE, - IS_MULTI_PART, req.isLastPart()); - } catch (Exception e) { - throw new AmazonClientException( - "Unable to create cipher input stream: " + e.getMessage(), - e); - } + @Override + final long computeLastPartSize(UploadPartRequest req) { + return req.getPartSize() + + (contentCryptoScheme.getTagLengthInBits() / 8); } - @Override - public CopyPartResult copyPartSecurely(CopyPartRequest copyPartRequest) { - String uploadId = copyPartRequest.getUploadId(); - MultipartUploadCryptoContext uploadContext = multipartUploadContexts.get(uploadId); - - if (!uploadContext.hasFinalPartBeenSeen()) { - uploadContext.setHasFinalPartBeenSeen(true); - } - - return s3.copyPart(copyPartRequest); + final void updateUploadContext(MultipartUploadCryptoContext uploadContext, + SdkFilterInputStream is) { } /* @@ -499,91 +402,45 @@ public CopyPartResult copyPartSecurely(CopyPartRequest copyPartRequest) { */ /** - * Puts an encrypted object into S3, and puts an instruction file into S3. - * Encryption info is stored in the instruction file. + * Returns an updated object where the object content input stream contains the decrypted contents. * - * @param putObjectRequest The request object containing all the parameters - * to upload a new object to Amazon S3. - * @return A {@link PutObjectResult} object containing the information - * returned by Amazon S3 for the new, created object. - * @throws AmazonClientException If any errors are encountered on the client - * while making the request or handling the response. - * @throws AmazonServiceException If any errors occurred in Amazon S3 while - * processing the request. - */ - private PutObjectResult putObjectUsingInstructionFile(PutObjectRequest putObjectRequest) - throws AmazonClientException, AmazonServiceException { - PutObjectRequest putInstFileRequest = putObjectRequest.clone(); - // Create instruction - ContentCryptoMaterial cekMaterial = createContentCryptoMaterial(putObjectRequest); - // Wraps the object data with a cipher input stream; note the metadata - // is mutated as a side effect. - PutObjectRequest req = wrapWithCipher(putObjectRequest, cekMaterial); - // Put the encrypted object into S3 - PutObjectResult result = s3.putObject(req); - // Put the instruction file into S3 - s3.putObject(upateInstructionPutRequest(putInstFileRequest, cekMaterial)); - // Return the result of the encrypted object PUT. - return result; - } - - /** - * Returns an updated object where the object content input stream contains - * the decrypted contents. - * - * @param wrapper The object whose contents are to be decrypted. - * @param cekMaterial The instruction that will be used to decrypt the - * object data. - * @return The updated object where the object content input stream contains - * the decrypted contents. + * @param wrapper + * The object whose contents are to be decrypted. + * @param cekMaterial + * The instruction that will be used to decrypt the object data. + * @return + * The updated object where the object content input stream contains the decrypted contents. */ private S3ObjectWrapper decrypt(S3ObjectWrapper wrapper, ContentCryptoMaterial cekMaterial, long[] range) { - S3ObjectInputStream objectContent = wrapper.getObjectContent(); + final S3ObjectInputStream objectContent = wrapper.getObjectContent(); wrapper.setObjectContent(new S3ObjectInputStream( - new CipherLiteInputStream(objectContent, cekMaterial - .getCipherLite(), DEFAULT_BUFFER_SIZE), objectContent - .getHttpRequest())); + new CipherLiteInputStream(objectContent, + cekMaterial.getCipherLite(), + DEFAULT_BUFFER_SIZE), + objectContent.getHttpRequest())); return wrapper; } - /** - * Retrieves an instruction file from S3. If no instruction file is found, - * returns null. - * - * @param getObjectRequest A GET request for an object in S3. The parameters - * from this request will be used to retrieve the corresponding - * instruction file. - * @return An instruction file, or null if no instruction file was found. - */ - private S3ObjectWrapper fetchInstructionFile(GetObjectRequest getObjectRequest) { - try { - S3Object o = s3.getObject(createInstructionGetRequest(getObjectRequest)); - return o == null ? null : new S3ObjectWrapper(o); - } catch (AmazonServiceException e) { - // If no instruction file is found, log a debug message, and return - // null. - log.debug("Unable to retrieve instruction file : " + e.getMessage()); - return null; - } - } - /** * Asserts that the specified parameter value is not null and if it is, * throws an IllegalArgumentException with the specified error message. * - * @param parameterValue The parameter value being checked. - * @param errorMessage The error message to include in the - * IllegalArgumentException if the specified parameter is null. + * @param parameterValue + * The parameter value being checked. + * @param errorMessage + * The error message to include in the IllegalArgumentException + * if the specified parameter is null. */ private void assertParameterNotNull(Object parameterValue, String errorMessage) { - if (parameterValue == null) + if (parameterValue == null) { throw new IllegalArgumentException(errorMessage); + } } @Override protected final long ciphertextLength(long originalContentLength) { // Add 16 bytes for the 128-bit tag length using AES/GCM - return originalContentLength + contentCryptoScheme.getTagLengthInBits() / 8; + return originalContentLength + contentCryptoScheme.getTagLengthInBits()/8; } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleAEStrict.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleAEStrict.java index 91c8331647..5a1d0e9e84 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleAEStrict.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleAEStrict.java @@ -15,8 +15,10 @@ package com.amazonaws.services.s3.internal.crypto; -import com.amazonaws.ClientConfiguration; +import static com.amazonaws.services.s3.model.CryptoMode.StrictAuthenticatedEncryption; + import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.services.kms.AWSKMSClient; import com.amazonaws.services.s3.internal.S3Direct; import com.amazonaws.services.s3.model.CryptoConfiguration; import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; @@ -26,29 +28,23 @@ * encryption client. */ class S3CryptoModuleAEStrict extends S3CryptoModuleAE { - S3CryptoModuleAEStrict(S3Direct s3, - AWSCredentialsProvider credentialsProvider, - EncryptionMaterialsProvider encryptionMaterialsProvider, - ClientConfiguration clientConfig, CryptoConfiguration cryptoConfig) { - super(s3, credentialsProvider, encryptionMaterialsProvider, - clientConfig, cryptoConfig); - } - /** - * Used for testing purposes only. + * @param cryptoConfig a read-only copy of the crypto configuration. */ - S3CryptoModuleAEStrict(S3Direct s3, + S3CryptoModuleAEStrict(AWSKMSClient kms, S3Direct s3, + AWSCredentialsProvider credentialsProvider, EncryptionMaterialsProvider encryptionMaterialsProvider, CryptoConfiguration cryptoConfig) { - super(s3, encryptionMaterialsProvider, cryptoConfig); + super(kms, s3, credentialsProvider, encryptionMaterialsProvider, + cryptoConfig); + if (cryptoConfig.getCryptoMode() != StrictAuthenticatedEncryption) + throw new IllegalArgumentException(); } - @Override protected final boolean isStrict() { return true; } - @Override protected void securityCheck(ContentCryptoMaterial cekMaterial, S3ObjectWrapper retrieved) { if (!ContentCryptoScheme.AES_GCM.equals(cekMaterial.getContentCryptoScheme())) { diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleBase.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleBase.java index 173f888256..232c4d7bb9 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleBase.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleBase.java @@ -15,34 +15,68 @@ package com.amazonaws.services.s3.internal.crypto; -import static com.amazonaws.services.s3.internal.crypto.EncryptionUtils.INSTRUCTION_SUFFIX; +import static com.amazonaws.services.s3.AmazonS3EncryptionClient.USER_AGENT; +import static com.amazonaws.services.s3.model.CryptoStorageMode.InstructionFile; +import static com.amazonaws.services.s3.model.CryptoStorageMode.ObjectMetadata; +import static com.amazonaws.services.s3.model.InstructionFileId.DEFAULT_INSTRUCTION_FILE_SUFFIX; +import static com.amazonaws.services.s3.model.InstructionFileId.DOT; +import static com.amazonaws.services.s3.model.S3DataSource.Utils.cleanupDataSource; +import static com.amazonaws.util.BinaryUtils.copyAllBytesFrom; +import static com.amazonaws.util.IOUtils.closeQuietly; import static com.amazonaws.util.LengthCheckInputStream.EXCLUDE_SKIPPED_BYTES; import static com.amazonaws.util.StringUtils.UTF8; import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonServiceException; import com.amazonaws.AmazonWebServiceRequest; -import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.internal.ReleasableInputStream; +import com.amazonaws.internal.ResettableInputStream; +import com.amazonaws.internal.SdkFilterInputStream; +import com.amazonaws.services.kms.AWSKMSClient; +import com.amazonaws.services.kms.model.GenerateDataKeyRequest; +import com.amazonaws.services.kms.model.GenerateDataKeyResult; import com.amazonaws.services.s3.Headers; -import com.amazonaws.services.s3.internal.RepeatableFileInputStream; +import com.amazonaws.services.s3.internal.InputSubstream; import com.amazonaws.services.s3.internal.S3Direct; import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; +import com.amazonaws.services.s3.model.AbstractPutObjectRequest; +import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; +import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; +import com.amazonaws.services.s3.model.CopyPartRequest; +import com.amazonaws.services.s3.model.CopyPartResult; import com.amazonaws.services.s3.model.CryptoConfiguration; +import com.amazonaws.services.s3.model.CryptoMode; import com.amazonaws.services.s3.model.EncryptionMaterials; +import com.amazonaws.services.s3.model.EncryptionMaterialsFactory; import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; +import com.amazonaws.services.s3.model.GetObjectRequest; +import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; +import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; +import com.amazonaws.services.s3.model.InstructionFileId; import com.amazonaws.services.s3.model.MaterialsDescriptionProvider; import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutInstructionFileRequest; import com.amazonaws.services.s3.model.PutObjectRequest; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectId; +import com.amazonaws.services.s3.model.UploadObjectRequest; +import com.amazonaws.services.s3.model.UploadPartRequest; +import com.amazonaws.services.s3.model.UploadPartResult; import com.amazonaws.services.s3.util.Mimetypes; +import com.amazonaws.util.IOUtils; import com.amazonaws.util.LengthCheckInputStream; +import com.amazonaws.util.json.JsonUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; import java.io.InputStream; -import java.security.Key; +import java.io.OutputStream; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.Provider; @@ -50,236 +84,535 @@ import java.util.HashMap; import java.util.Map; -import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; /** * Common implementation for different S3 cryptographic modules. */ -public abstract class S3CryptoModuleBase +public abstract class S3CryptoModuleBase extends S3CryptoModule { - protected static final int DEFAULT_BUFFER_SIZE = 1024 * 2; // 2K + private static final boolean IS_MULTI_PART = true; + protected static final int DEFAULT_BUFFER_SIZE = 1024*2; // 2K protected final EncryptionMaterialsProvider kekMaterialsProvider; - protected final CryptoConfiguration cryptoConfig; protected final Log log = LogFactory.getLog(getClass()); protected final S3CryptoScheme cryptoScheme; - protected final ContentCryptoScheme contentCryptoScheme; + /** A read-only copy of the crypto configuration. */ + protected final CryptoConfiguration cryptoConfig; /** Map of data about in progress encrypted multipart uploads. */ - protected final Map multipartUploadContexts = - Collections.synchronizedMap(new HashMap()); + protected final Map multipartUploadContexts = + Collections.synchronizedMap(new HashMap()); protected final S3Direct s3; + protected final AWSKMSClient kms; - protected S3CryptoModuleBase(S3Direct s3, + /** + * @param cryptoConfig a read-only copy of the crypto configuration. + */ + protected S3CryptoModuleBase(AWSKMSClient kms, S3Direct s3, AWSCredentialsProvider credentialsProvider, EncryptionMaterialsProvider kekMaterialsProvider, - ClientConfiguration clientConfig, - CryptoConfiguration cryptoConfig, - S3CryptoScheme cryptoScheme) { + CryptoConfiguration cryptoConfig) { + if (!cryptoConfig.isReadOnly()) { + throw new IllegalArgumentException("The cryto configuration parameter is required to be read-only"); + } this.kekMaterialsProvider = kekMaterialsProvider; + this.s3 = s3; this.cryptoConfig = cryptoConfig; + this.cryptoScheme = S3CryptoScheme.from(cryptoConfig.getCryptoMode()); + this.contentCryptoScheme = cryptoScheme.getContentCryptoScheme(); + this.kms = kms; + } + + /** + * For testing purposes only. + */ + protected S3CryptoModuleBase(S3Direct s3, + AWSCredentialsProvider credentialsProvider, + EncryptionMaterialsProvider kekMaterialsProvider, + CryptoConfiguration cryptoConfig) { + this.kekMaterialsProvider = kekMaterialsProvider; this.s3 = s3; - this.cryptoScheme = cryptoScheme; + this.cryptoConfig = cryptoConfig; + this.cryptoScheme = S3CryptoScheme.from(cryptoConfig.getCryptoMode()); this.contentCryptoScheme = cryptoScheme.getContentCryptoScheme(); + this.kms = null; } /** * Returns the length of the ciphertext computed from the length of the * plaintext. * - * @param plaintextLength a non-negative number + * @param plaintextLength + * a non-negative number * @return a non-negative number */ protected abstract long ciphertextLength(long plaintextLength); - // ////////////////////// Common Implementation //////////////////////// + //////////////////////// Common Implementation //////////////////////// + @Override + public PutObjectResult putObjectSecurely(PutObjectRequest req) { + // TODO: consider cloning req before proceeding further to reduce side + // effects + appendUserAgent(req, USER_AGENT); + return cryptoConfig.getStorageMode() == InstructionFile + ? putObjectUsingInstructionFile(req) + : putObjectUsingMetadata(req); + } + + private PutObjectResult putObjectUsingMetadata(PutObjectRequest req) { + final ContentCryptoMaterial cekMaterial = createContentCryptoMaterial(req); + // Wraps the object data with a cipher input stream + final File fileOrig = req.getFile(); + final InputStream isOrig = req.getInputStream(); + final PutObjectRequest wrappedReq = wrapWithCipher(req, cekMaterial); + // Update the metadata + req.setMetadata(updateMetadataWithContentCryptoMaterial( + req.getMetadata(), req.getFile(), cekMaterial)); + // Put the encrypted object into S3 + try { + return s3.putObject(wrappedReq); + } finally { + cleanupDataSource(req, fileOrig, isOrig, wrappedReq.getInputStream(), log); + } + } + + /** + * Puts an encrypted object into S3, and puts an instruction file into S3. + * Encryption info is stored in the instruction file. + * + * @param putObjectRequest + * The request object containing all the parameters to upload a + * new object to Amazon S3. + * @return A {@link PutObjectResult} object containing the information + * returned by Amazon S3 for the new, created object. + */ + private PutObjectResult putObjectUsingInstructionFile( + PutObjectRequest putObjectRequest) { + final File fileOrig = putObjectRequest.getFile(); + final InputStream isOrig = putObjectRequest.getInputStream(); + final PutObjectRequest putInstFileRequest = putObjectRequest.clone() + .withFile(null) + .withInputStream(null) + ; + putInstFileRequest.setKey(putInstFileRequest.getKey() + DOT + + DEFAULT_INSTRUCTION_FILE_SUFFIX); + // Create instruction + final ContentCryptoMaterial cekMaterial = createContentCryptoMaterial(putObjectRequest); + // Wraps the object data with a cipher input stream; note the metadata + // is mutated as a side effect. + final PutObjectRequest req = wrapWithCipher(putObjectRequest, cekMaterial); + // Put the encrypted object into S3 + final PutObjectResult result; + try { + result = s3.putObject(req); + } finally { + cleanupDataSource(putObjectRequest, fileOrig, isOrig, + req.getInputStream(), log); + } + // Put the instruction file into S3 + s3.putObject(updateInstructionPutRequest(putInstFileRequest, + cekMaterial)); + // Return the result of the encrypted object PUT. + return result; + } + @Override public final void abortMultipartUploadSecurely(AbortMultipartUploadRequest req) { s3.abortMultipartUpload(req); multipartUploadContexts.remove(req.getUploadId()); } + @Override + public final CopyPartResult copyPartSecurely(CopyPartRequest copyPartRequest) { + final String uploadId = copyPartRequest.getUploadId(); + final T uploadContext = multipartUploadContexts.get(uploadId); + final CopyPartResult result = s3.copyPart(copyPartRequest); + + if (uploadContext != null && !uploadContext.hasFinalPartBeenSeen()) { + uploadContext.setHasFinalPartBeenSeen(true); + } + return result; + } + + abstract T newUploadContext(InitiateMultipartUploadRequest req, + ContentCryptoMaterial cekMaterial); + + @Override + public InitiateMultipartUploadResult initiateMultipartUploadSecurely( + InitiateMultipartUploadRequest req) { + appendUserAgent(req, USER_AGENT); + // Generate a one-time use symmetric key and initialize a cipher to + // encrypt object data + final ContentCryptoMaterial cekMaterial = createContentCryptoMaterial(req); + if (cryptoConfig.getStorageMode() == ObjectMetadata) { + ObjectMetadata metadata = req.getObjectMetadata(); + if (metadata == null) { + metadata = new ObjectMetadata(); + } + // Store encryption info in metadata + req.setObjectMetadata(updateMetadataWithContentCryptoMaterial( + metadata, null, cekMaterial)); + } + final InitiateMultipartUploadResult result = s3.initiateMultipartUpload(req); + final T uploadContext = newUploadContext(req, cekMaterial); + if (req instanceof MaterialsDescriptionProvider) { + final MaterialsDescriptionProvider p = (MaterialsDescriptionProvider) req; + uploadContext.setMaterialsDescription(p.getMaterialsDescription()); + } + multipartUploadContexts.put(result.getUploadId(), uploadContext); + return result; + } + + //// specific crypto module behavior for uploading parts. + abstract CipherLite cipherLiteForNextPart(T uploadContext); + abstract long computeLastPartSize(UploadPartRequest req); + abstract SdkFilterInputStream wrapForMultipart( + I is, long partSize); + abstract void updateUploadContext(T uploadContext, SdkFilterInputStream is); + /** + * {@inheritDoc} + * + *

+ * NOTE: Because the encryption process requires context from + * previous blocks, parts uploaded with the AmazonS3EncryptionClient (as + * opposed to the normal AmazonS3Client) must be uploaded serially, and in + * order. Otherwise, the previous encryption context isn't available to use + * when encrypting the current part. + */ + @Override + public UploadPartResult uploadPartSecurely(UploadPartRequest req) { + appendUserAgent(req, USER_AGENT); + final int blockSize = contentCryptoScheme.getBlockSizeInBytes(); + final boolean isLastPart = req.isLastPart(); + final String uploadId = req.getUploadId(); + final long partSize = req.getPartSize(); + final boolean partSizeMultipleOfCipherBlockSize = 0 == (partSize % blockSize); + if (!isLastPart && !partSizeMultipleOfCipherBlockSize) { + throw new AmazonClientException( + "Invalid part size: part sizes for encrypted multipart uploads must be multiples " + + "of the cipher block size (" + + blockSize + + ") with the exception of the last part."); + } + final T uploadContext = multipartUploadContexts.get(uploadId); + if (uploadContext == null) { + throw new AmazonClientException( + "No client-side information available on upload ID " + uploadId); + } + final UploadPartResult result; + // Checks the parts are uploaded in series + uploadContext.beginPartUpload(req.getPartNumber()); + final CipherLite cipherLite = cipherLiteForNextPart(uploadContext); + final File fileOrig = req.getFile(); + final InputStream isOrig = req.getInputStream(); + SdkFilterInputStream isCurr = null; + try { + final CipherLiteInputStream clis = newMultipartS3CipherInputStream(req, cipherLite); + isCurr = clis; // so the clis will be closed (in the finally block below) upon + // unexpected failure should we opened a file undereath + isCurr = wrapForMultipart(clis, partSize); + req.setInputStream(isCurr); + // Treat all encryption requests as input stream upload requests, + // not as file upload requests. + req.setFile(null); + req.setFileOffset(0); + // The last part of the multipart upload will contain an extra + // 16-byte mac + if (isLastPart) { + // We only change the size of the last part + final long lastPartSize = computeLastPartSize(req); + if (lastPartSize > -1) { + req.setPartSize(lastPartSize); + } + if (uploadContext.hasFinalPartBeenSeen()) { + throw new AmazonClientException( + "This part was specified as the last part in a multipart upload, but a previous part was already marked as the last part. " + + "Only the last part of the upload should be marked as the last part."); + } + } + + result = s3.uploadPart(req); + } finally { + cleanupDataSource(req, fileOrig, isOrig, isCurr, log); + uploadContext.endPartUpload(); + } + if (isLastPart) { + uploadContext.setHasFinalPartBeenSeen(true); + } + updateUploadContext(uploadContext, isCurr); + return result; + } + + protected final CipherLiteInputStream newMultipartS3CipherInputStream( + UploadPartRequest req, CipherLite cipherLite) { + final File fileOrig = req.getFile(); + final InputStream isOrig = req.getInputStream(); + InputStream isCurr = null; + try { + if (fileOrig == null) { + if (isOrig == null) { + throw new IllegalArgumentException( + "A File or InputStream must be specified when uploading part"); + } + isCurr = isOrig; + } else { + isCurr = new ResettableInputStream(fileOrig); + } + isCurr = new InputSubstream(isCurr, + req.getFileOffset(), + req.getPartSize(), + req.isLastPart()); + return cipherLite.markSupported() + ? new CipherLiteInputStream(isCurr, cipherLite, + DEFAULT_BUFFER_SIZE, + IS_MULTI_PART, req.isLastPart()) + : new RenewableCipherLiteInputStream(isCurr, cipherLite, + DEFAULT_BUFFER_SIZE, + IS_MULTI_PART, req.isLastPart()); + } catch (final Exception e) { + cleanupDataSource(req, fileOrig, isOrig, isCurr, log); + throw new AmazonClientException("Unable to create cipher input stream", e); + } + } + + @Override + public CompleteMultipartUploadResult completeMultipartUploadSecurely( + CompleteMultipartUploadRequest req) { + appendUserAgent(req, USER_AGENT); + final String uploadId = req.getUploadId(); + final T uploadContext = multipartUploadContexts.get(uploadId); + + if (uploadContext != null && !uploadContext.hasFinalPartBeenSeen()) { + throw new AmazonClientException( + "Unable to complete an encrypted multipart upload without being told which part was the last. " + + "Without knowing which part was the last, the encrypted data in Amazon S3 is incomplete and corrupt."); + } + final CompleteMultipartUploadResult result = s3.completeMultipartUpload(req); + + // In InstructionFile mode, we want to write the instruction file only + // after the whole upload has completed correctly. + if (uploadContext != null + && cryptoConfig.getStorageMode() == InstructionFile) { + // Put the instruction file into S3 + s3.putObject(createInstructionPutRequest( + uploadContext.getBucketName(), uploadContext.getKey(), + uploadContext.getContentCryptoMaterial())); + } + multipartUploadContexts.remove(uploadId); + return result; + } + protected final ObjectMetadata updateMetadataWithContentCryptoMaterial( ObjectMetadata metadata, File file, ContentCryptoMaterial instruction) { - if (metadata == null) + if (metadata == null) { metadata = new ObjectMetadata(); + } if (file != null) { - Mimetypes mimetypes = Mimetypes.getInstance(); + final Mimetypes mimetypes = Mimetypes.getInstance(); metadata.setContentType(mimetypes.getMimetype(file)); } - return instruction.toObjectMetadata(metadata); + return instruction.toObjectMetadata(metadata, cryptoConfig.getCryptoMode()); } - protected final ContentCryptoMaterial createContentCryptoMaterial(AmazonWebServiceRequest req) { + /** + * Creates and returns a non-null content crypto material for the given + * request. + * + * @throws AmazonClientException if no encryption material can be found. + */ + protected final ContentCryptoMaterial createContentCryptoMaterial( + AmazonWebServiceRequest req) { + if (req instanceof EncryptionMaterialsFactory) { + // per request level encryption materials + final EncryptionMaterialsFactory f = (EncryptionMaterialsFactory)req; + final EncryptionMaterials materials = f.getEncryptionMaterials(); + if (materials != null) { + return buildContentCryptoMaterial(materials, + cryptoConfig.getCryptoProvider(), req); + } + } if (req instanceof MaterialsDescriptionProvider) { - return newContentCryptoMaterial(this.kekMaterialsProvider, - ((MaterialsDescriptionProvider) req).getMaterialsDescription(), - this.cryptoConfig.getCryptoProvider()); - } else { - return newContentCryptoMaterial(this.kekMaterialsProvider, - this.cryptoConfig.getCryptoProvider()); + // per request level material description + final MaterialsDescriptionProvider mdp = (MaterialsDescriptionProvider) req; + final Map matdesc_req = mdp.getMaterialsDescription(); + final ContentCryptoMaterial ccm = newContentCryptoMaterial( + kekMaterialsProvider, + matdesc_req, + cryptoConfig.getCryptoProvider(), req); + if (ccm != null) { + return ccm; + } + if (matdesc_req != null) { + // check to see if KMS is in use and if so we should fall thru + // to the s3 client level encryption material + final EncryptionMaterials material = + kekMaterialsProvider.getEncryptionMaterials(); + if (!material.isKMSEnabled()) { + throw new AmazonClientException( + "No material available from the encryption material provider for description " + + matdesc_req); + } + } + // if there is no material description, fall thru to use + // the per s3 client level encryption materials } + // per s3 client level encryption materials + return newContentCryptoMaterial(this.kekMaterialsProvider, + cryptoConfig.getCryptoProvider(), req); } /** - * Generates and returns the content encryption material with the given kek - * material, material description and security providers. + * Returns the content encryption material generated with the given kek + * material, material description and security providers; or null if + * the encryption material cannot be found for the specified description. */ private ContentCryptoMaterial newContentCryptoMaterial( EncryptionMaterialsProvider kekMaterialProvider, - Map materialsDescription, Provider provider) { - EncryptionMaterials kekMaterials = kekMaterialProvider - .getEncryptionMaterials(materialsDescription); - return buildContentCryptoMaterial(kekMaterials, provider); + Map materialsDescription, Provider provider, + AmazonWebServiceRequest req) { + final EncryptionMaterials kekMaterials = + kekMaterialProvider.getEncryptionMaterials(materialsDescription); + if (kekMaterials == null) { + return null; + } + return buildContentCryptoMaterial(kekMaterials, provider, req); } /** - * Generates and returns the content encryption material with the given kek - * material and security providers. + * Returns a non-null content encryption material generated with the given + * kek material and security providers. + * + * @throws AmazonClientException if no encryption material can be found from + * the given encryption material provider. */ private ContentCryptoMaterial newContentCryptoMaterial( EncryptionMaterialsProvider kekMaterialProvider, - Provider provider) { - EncryptionMaterials kekMaterials = kekMaterialProvider.getEncryptionMaterials(); - return buildContentCryptoMaterial(kekMaterials, provider); + Provider provider, AmazonWebServiceRequest req) { + final EncryptionMaterials kekMaterials = kekMaterialProvider.getEncryptionMaterials(); + if (kekMaterials == null) { + throw new AmazonClientException("No material available from the encryption material provider"); + } + return buildContentCryptoMaterial(kekMaterials, provider, req); + } + + @Override + public final void putLocalObjectSecurely(final UploadObjectRequest reqIn, + String uploadId, OutputStream os) throws IOException { + UploadObjectRequest req = reqIn.clone(); + + final File fileOrig = req.getFile(); + final InputStream isOrig = req.getInputStream(); + + final T uploadContext = multipartUploadContexts.get(uploadId); + final ContentCryptoMaterial cekMaterial = uploadContext.getContentCryptoMaterial(); + req = wrapWithCipher(req, cekMaterial); + + try { + IOUtils.copy(req.getInputStream(), os); + // so it won't crap out with a false negative at the end; (Not + // really relevant here) + uploadContext.setHasFinalPartBeenSeen(true); + } finally { + cleanupDataSource(req, fileOrig, isOrig, + req.getInputStream(), log); + IOUtils.closeQuietly(os, log); + } + return; } + /** + * @param materials a non-null encryption material + */ private ContentCryptoMaterial buildContentCryptoMaterial( - EncryptionMaterials kekMaterials, Provider provider) { - // Generate a one-time use symmetric key and initialize a cipher to - // encrypt object data - SecretKey cek = generateCEK(kekMaterials, provider); + EncryptionMaterials materials, Provider provider, + AmazonWebServiceRequest req) { // Randomly generate the IV - byte[] iv = new byte[contentCryptoScheme.getIVLengthInBytes()]; + final byte[] iv = new byte[contentCryptoScheme.getIVLengthInBytes()]; cryptoScheme.getSecureRandom().nextBytes(iv); - // Encrypt the envelope symmetric key - SecuredCEK cekSecured = secureCEK(cek, kekMaterials, provider); - // Return a new instruction with the appropriate fields. - return new ContentCryptoMaterial( - kekMaterials.getMaterialsDescription(), - cekSecured.encrypted, - cekSecured.keyWrapAlgorithm, - contentCryptoScheme.createCipherLite - (cek, iv, Cipher.ENCRYPT_MODE, provider)); + if (materials.isKMSEnabled()) { + final Map encryptionContext = + ContentCryptoMaterial.mergeMaterialDescriptions(materials, req); + final GenerateDataKeyRequest keyGenReq = new GenerateDataKeyRequest() + .withEncryptionContext(encryptionContext) + .withKeyId(materials.getCustomerMasterKeyId()) + .withKeySpec(contentCryptoScheme.getKeySpec()); + keyGenReq + .withGeneralProgressListener(req.getGeneralProgressListener()) + .withRequestMetricCollector(req.getRequestMetricCollector()) + ; + final GenerateDataKeyResult keyGenRes = kms.generateDataKey(keyGenReq); + final SecretKey cek = + new SecretKeySpec(copyAllBytesFrom(keyGenRes.getPlaintext()), + contentCryptoScheme.getKeyGeneratorAlgorithm()); + final byte[] keyBlob = copyAllBytesFrom(keyGenRes.getCiphertextBlob()); + return ContentCryptoMaterial.wrap(cek, iv, + contentCryptoScheme, provider, + new KMSSecuredCEK(keyBlob, encryptionContext)); + } else { + // Generate a one-time use symmetric key and initialize a cipher to encrypt object data + return ContentCryptoMaterial.create( + generateCEK(materials, provider), + iv, materials, cryptoScheme, provider, kms, req); + } } + /** + * @param kekMaterials non-null encryption materials + */ protected final SecretKey generateCEK( final EncryptionMaterials kekMaterials, final Provider providerIn) { - final String keygenAlgo = - contentCryptoScheme.getKeyGeneratorAlgorithm(); + final String keygenAlgo = contentCryptoScheme.getKeyGeneratorAlgorithm(); KeyGenerator generator; try { - generator = providerIn == null ? - KeyGenerator.getInstance(keygenAlgo) : - KeyGenerator.getInstance(keygenAlgo, providerIn); + generator = providerIn == null + ? KeyGenerator.getInstance(keygenAlgo) + : KeyGenerator.getInstance(keygenAlgo, providerIn); generator.init(contentCryptoScheme.getKeyLengthInBits(), cryptoScheme.getSecureRandom()); - // set to true iff key encryption involves BC's public key - boolean involvesBcPublicKey = false; - KeyPair keyPair = kekMaterials.getKeyPair(); - if (keyPair != null) { - String keyWrapAlgo = cryptoScheme - .getKeyWrapScheme() - .getKeyWrapAlgorithm(keyPair.getPublic()); + // Set to true iff the key encryption involves the use of BC's public key + boolean involvesBCPublicKey = false; + final KeyPair keypair = kekMaterials.getKeyPair(); + if (keypair != null) { + final String keyWrapAlgo = cryptoScheme.getKeyWrapScheme().getKeyWrapAlgorithm(keypair.getPublic()); if (keyWrapAlgo == null) { - Provider provider = generator.getProvider(); - String providerName = provider == null ? - null : - provider.getName(); - involvesBcPublicKey = CryptoRuntime.BOUNCY_CASTLE_PROVIDER - .equals(providerName); + final Provider provider = generator.getProvider(); + final String providerName = provider == null ? null : provider.getName(); + involvesBCPublicKey = CryptoRuntime.BOUNCY_CASTLE_PROVIDER.equals(providerName); } } - if (!involvesBcPublicKey) { - return generator.generateKey(); - } else { - for (int retry = 0; retry < 10; retry++) { - // generate the random key until leading byte is nonzero - // see https://github.com/aws/aws-sdk-android/issues/15 - SecretKey secretKey = generator.generateKey(); - if (secretKey.getEncoded()[0] != 0) { - return secretKey; - } + SecretKey secretKey = generator.generateKey(); + if (!involvesBCPublicKey || secretKey.getEncoded()[0] != 0) { + return secretKey; + } + for (int retry = 0; retry < 9; retry++) { + // Regenerate the random key due to a bug/feature in BC: + // https://github.com/aws/aws-sdk-android/issues/15 + secretKey = generator.generateKey(); + if (secretKey.getEncoded()[0] != 0) { + return secretKey; } - // if using public BC key, this happens with p = 2^-80 - throw new AmazonClientException( - "Failed to generate secret key"); } - } catch (NoSuchAlgorithmException e) { + // The probability of getting here is 2^80, which is impossible in practice. + throw new AmazonClientException("Failed to generate secret key"); + } catch (final NoSuchAlgorithmException e) { throw new AmazonClientException( "Unable to generate envelope symmetric key:" + e.getMessage(), e); } } - protected final SecuredCEK secureCEK(SecretKey toBeEncrypted, - EncryptionMaterials materials, Provider cryptoProvider) - { - Key kek; - if (materials.getKeyPair() != null) { - // Do envelope encryption with public key from key pair - kek = materials.getKeyPair().getPublic(); - } else { - // Do envelope encryption with symmetric key - kek = materials.getSymmetricKey(); - } - S3KeyWrapScheme kwScheme = cryptoScheme.getKeyWrapScheme(); - String keyWrapAlgo = kwScheme.getKeyWrapAlgorithm(kek); - try { - if (keyWrapAlgo != null) { - Cipher cipher = cryptoProvider == null - ? Cipher.getInstance(keyWrapAlgo) - : Cipher.getInstance(keyWrapAlgo, cryptoProvider); - cipher.init(Cipher.WRAP_MODE, kek, cryptoScheme.getSecureRandom()); - return new SecuredCEK(cipher.wrap(toBeEncrypted), keyWrapAlgo); - } - // fall back to the Encryption Only (EO) key encrypting method - Cipher cipher; - byte[] toBeEncryptedBytes = toBeEncrypted.getEncoded(); - String algo = kek.getAlgorithm(); - if (cryptoProvider != null) { - cipher = Cipher.getInstance(algo, cryptoProvider); - } else { - cipher = Cipher.getInstance(algo); // Use default JCE Provider - } - cipher.init(Cipher.ENCRYPT_MODE, kek); - return new SecuredCEK(cipher.doFinal(toBeEncryptedBytes), null); - } catch (Exception e) { - throw new AmazonClientException("Unable to encrypt symmetric key: " + e.getMessage(), e); - } - } - - /** - * Used to carry both the secured CEK and the key wrapping algorithm, if - * any. - */ - private static class SecuredCEK { - /** - * The encrypted CEK either via key wrapping or simple encryption. - */ - final byte[] encrypted; - /** - * The key wrapping algorithm used, or null if the CEK is not secured - * via key wrapping. - */ - final String keyWrapAlgorithm; - - SecuredCEK(byte[] encryptedKey, String keyWrapAlgorithm) { - this.encrypted = encryptedKey; - this.keyWrapAlgorithm = keyWrapAlgorithm; - } - } - /** - * Returns a request that has the content as input stream wrapped with a - * cipher, and configured with some meta data and user metadata. + * Returns the given PutObjectRequest but has the content as + * input stream wrapped with a cipher, and configured with some meta data + * and user metadata. */ - protected final PutObjectRequest wrapWithCipher( - PutObjectRequest request, ContentCryptoMaterial cekMaterial) { + protected final R wrapWithCipher( + final R request, ContentCryptoMaterial cekMaterial) { // Create a new metadata object if there is no metadata already. ObjectMetadata metadata = request.getMetadata(); if (metadata == null) { @@ -306,7 +639,7 @@ protected final PutObjectRequest wrapWithCipher( } request.setMetadata(metadata); request.setInputStream(newS3CipherLiteInputStream( - request, cekMaterial, plaintextLength)); + request, cekMaterial, plaintextLength)); // Treat all encryption requests as input stream upload requests, not as // file upload requests. request.setFile(null); @@ -314,12 +647,19 @@ protected final PutObjectRequest wrapWithCipher( } private CipherLiteInputStream newS3CipherLiteInputStream( - PutObjectRequest req, ContentCryptoMaterial cekMaterial, + AbstractPutObjectRequest req, ContentCryptoMaterial cekMaterial, long plaintextLength) { + final File fileOrig = req.getFile(); + final InputStream isOrig = req.getInputStream(); + InputStream isCurr = null; try { - InputStream is = req.getInputStream(); - if (req.getFile() != null) - is = new RepeatableFileInputStream(req.getFile()); + if (fileOrig == null) { + // When input is a FileInputStream, this wrapping enables + // unlimited mark-and-reset + isCurr = isOrig == null ? null : ReleasableInputStream.wrap(isOrig); + } else { + isCurr = new ResettableInputStream(fileOrig); + } if (plaintextLength > -1) { // S3 allows a single PUT to be no more than 5GB, which // therefore won't exceed the maximum length that can be @@ -327,16 +667,21 @@ private CipherLiteInputStream newS3CipherLiteInputStream( // This ensures the plain-text read from the underlying data // stream has the same length as the expected total. - is = new LengthCheckInputStream(is, plaintextLength, + isCurr = new LengthCheckInputStream(isCurr, plaintextLength, EXCLUDE_SKIPPED_BYTES); } - return new CipherLiteInputStream(is, - cekMaterial.getCipherLite(), - DEFAULT_BUFFER_SIZE); - } catch (Exception e) { - throw new AmazonClientException( - "Unable to create cipher input stream: " + e.getMessage(), - e); + final CipherLite cipherLite = cekMaterial.getCipherLite(); + + if (cipherLite.markSupported()) { + return new CipherLiteInputStream(isCurr, cipherLite, + DEFAULT_BUFFER_SIZE); + } else { + return new RenewableCipherLiteInputStream(isCurr, cipherLite, + DEFAULT_BUFFER_SIZE); + } + } catch (final Exception e) { + cleanupDataSource(req, fileOrig, isOrig, isCurr, log); + throw new AmazonClientException("Unable to create cipher input stream", e); } } @@ -344,7 +689,7 @@ private CipherLiteInputStream newS3CipherLiteInputStream( * Returns the plaintext length from the request and metadata; or -1 if * unknown. */ - protected final long plaintextLength(PutObjectRequest request, + protected final long plaintextLength(AbstractPutObjectRequest request, ObjectMetadata metadata) { if (request.getFile() != null) { return request.getFile().length(); @@ -362,50 +707,256 @@ public final S3CryptoScheme getS3CryptoScheme() { /** * Updates put request to store the specified instruction object in S3. * - * @param request The put request for the original object to be stored in - * S3. - * @param cekMaterial The instruction object to be stored in S3. + * @param req + * The put-instruction-file request for the instruction file to + * be stored in S3. + * @param cekMaterial + * The instruction object to be stored in S3. * @return A put request to store the specified instruction object in S3. */ - protected final PutObjectRequest upateInstructionPutRequest( - PutObjectRequest request, ContentCryptoMaterial cekMaterial) { - byte[] bytes = cekMaterial.toJsonString().getBytes(UTF8); - InputStream is = new ByteArrayInputStream(bytes); - ObjectMetadata metadata = request.getMetadata(); + protected final PutObjectRequest updateInstructionPutRequest( + PutObjectRequest req, ContentCryptoMaterial cekMaterial) { + final byte[] bytes = cekMaterial.toJsonString(cryptoConfig.getCryptoMode()) + .getBytes(UTF8); + ObjectMetadata metadata = req.getMetadata(); if (metadata == null) { metadata = new ObjectMetadata(); - request.setMetadata(metadata); + req.setMetadata(metadata); } // Set the content-length of the upload metadata.setContentLength(bytes.length); // Set the crypto instruction file header metadata.addUserMetadata(Headers.CRYPTO_INSTRUCTION_FILE, ""); // Update the instruction request - request.setKey(request.getKey() + INSTRUCTION_SUFFIX); - request.setMetadata(metadata); - request.setInputStream(is); - request.setFile(null); - return request; + req.setMetadata(metadata); + req.setInputStream(new ByteArrayInputStream(bytes)); + // the file attribute in the request is always null before calling this + // routine + return req; } protected final PutObjectRequest createInstructionPutRequest( String bucketName, String key, ContentCryptoMaterial cekMaterial) { - byte[] bytes = cekMaterial.toJsonString().getBytes(UTF8); - InputStream is = new ByteArrayInputStream(bytes); - ObjectMetadata metadata = new ObjectMetadata(); + final byte[] bytes = cekMaterial.toJsonString(cryptoConfig.getCryptoMode()) + .getBytes(UTF8); + final InputStream is = new ByteArrayInputStream(bytes); + final ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentLength(bytes.length); metadata.addUserMetadata(Headers.CRYPTO_INSTRUCTION_FILE, ""); - return new PutObjectRequest(bucketName, key + INSTRUCTION_SUFFIX, - is, metadata); + final InstructionFileId ifileId = new S3ObjectId(bucketName, key) + .instructionFileId(); + return new PutObjectRequest(ifileId.getBucket(), ifileId.getKey(), + is, metadata); } /** - * Appends a user agent to the request's USER_AGENT client marker. This - * method is intended only for internal use by the AWS SDK. + * Appends a user agent to the request's USER_AGENT client marker. + * This method is intended only for internal use by the AWS SDK. */ final X appendUserAgent( X request, String userAgent) { request.getRequestClientOptions().appendUserAgent(userAgent); return request; } + + /** + * Checks if the the crypto scheme used in the given content crypto material + * is allowed to be used in this crypto module. Default is no-op. Subclass + * may override. + * + * @throws SecurityException + * if the crypto scheme used in the given content crypto + * material is not allowed in this crypto module. + */ + protected void securityCheck(ContentCryptoMaterial cekMaterial, + S3ObjectWrapper retrieved) { + } + + /** + * Retrieves an instruction file from S3; or null if no instruction file is + * found. + * + * @param s3ObjectId + * the S3 object id (not the instruction file id) + * @param instFileSuffix + * suffix of the instruction file to be retrieved; or null to use + * the default suffix. + * @return an instruction file, or null if no instruction file is found. + */ + final S3ObjectWrapper fetchInstructionFile(S3ObjectId s3ObjectId, + String instFileSuffix) { + try { + final S3Object o = s3.getObject( + createInstructionGetRequest(s3ObjectId, instFileSuffix)); + return o == null ? null : new S3ObjectWrapper(o, s3ObjectId); + } catch (final AmazonServiceException e) { + // If no instruction file is found, log a debug message, and return + // null. + if (log.isDebugEnabled()) { + log.debug("Unable to retrieve instruction file : " + + e.getMessage()); + } + return null; + } + } + + @Override + public final PutObjectResult putInstructionFileSecurely( + PutInstructionFileRequest req) { + final S3ObjectId id = req.getS3ObjectId(); + final GetObjectRequest getreq = new GetObjectRequest(id); + appendUserAgent(getreq, USER_AGENT); + // Get the object from S3 + final S3Object retrieved = s3.getObject(getreq); + // We only need the meta-data already retrieved, not the data stream. + // So close it immediately to prevent resource leakage. + closeQuietly(retrieved, log); + if (retrieved == null) { + throw new IllegalArgumentException( + "The specified S3 object (" + id + ") doesn't exist."); + } + final S3ObjectWrapper wrapped = new S3ObjectWrapper(retrieved, id); + try { + final ContentCryptoMaterial origCCM = contentCryptoMaterialOf(wrapped); + if (ContentCryptoScheme.AES_GCM.equals(origCCM.getContentCryptoScheme()) + && cryptoConfig.getCryptoMode() == CryptoMode.EncryptionOnly) { + throw new SecurityException( + "Lowering the protection of encryption material is not allowed"); + } + securityCheck(origCCM, wrapped); + // Re-ecnrypt the CEK in a new content crypto material + final EncryptionMaterials newKEK = req.getEncryptionMaterials(); + final ContentCryptoMaterial newCCM; + if (newKEK == null) { + newCCM = origCCM.recreate(req.getMaterialsDescription(), + this.kekMaterialsProvider, + cryptoScheme, + cryptoConfig.getCryptoProvider(), kms, req); + } else { + newCCM = origCCM.recreate(newKEK, + this.kekMaterialsProvider, + cryptoScheme, + cryptoConfig.getCryptoProvider(), kms, req); + } + final PutObjectRequest putInstFileRequest = req.createPutObjectRequest(retrieved); + // Put the new instruction file into S3 + return s3.putObject(updateInstructionPutRequest(putInstFileRequest, newCCM)); + } catch (final RuntimeException ex) { + // If we're unable to set up the decryption, make sure we close the + // HTTP connection + closeQuietly(retrieved, log); + throw ex; + } catch (final Error error) { + closeQuietly(retrieved, log); + throw error; + } + } + + /** + * Returns the content crypto material of an existing S3 object. + * + * @param s3w + * an existing S3 object (wrapper) + * @param s3objectId + * the object id used to retrieve the existing S3 object + * + * @return a non-null content crypto material. + */ + private ContentCryptoMaterial contentCryptoMaterialOf(S3ObjectWrapper s3w) { + // Check if encryption info is in object metadata + if (s3w.hasEncryptionInfo()) { + return ContentCryptoMaterial + .fromObjectMetadata(s3w.getObjectMetadata(), + kekMaterialsProvider, + cryptoConfig.getCryptoProvider(), + false, // existing CEK not necessarily key-wrapped + kms + ); + } + final S3ObjectWrapper orig_ifile = + fetchInstructionFile(s3w.getS3ObjectId(), null); + if (orig_ifile == null) { + throw new IllegalArgumentException( + "S3 object is not encrypted: " + s3w); + } + if (!orig_ifile.isInstructionFile()) { + throw new AmazonClientException( + "Invalid instruction file for S3 object: " + s3w); + } + final String json = orig_ifile.toJsonString(); + return ccmFromJson(json); + } + + private ContentCryptoMaterial ccmFromJson(String json) { + @SuppressWarnings("unchecked") + final + Map instruction = Collections.unmodifiableMap( + JsonUtils.jsonToMap(json)); + return ContentCryptoMaterial.fromInstructionFile( + instruction, + kekMaterialsProvider, + cryptoConfig.getCryptoProvider(), + false, // existing CEK not necessarily key-wrapped + kms + ); + } + + /** + * Creates a get object request for an instruction file using + * the default instruction file suffix. + * + * @param id + * an S3 object id (not the instruction file id) + * @return + * A get request to retrieve an instruction file from S3. + */ + final GetObjectRequest createInstructionGetRequest(S3ObjectId id) { + return createInstructionGetRequest(id, null); + } + + /** + * Creates and return a get object request for an instruction file. + * + * @param s3objectId + * an S3 object id (not the instruction file id) + * @param instFileSuffix + * suffix of the specific instruction file to be used, or null if + * the default instruction file is to be used. + */ + final GetObjectRequest createInstructionGetRequest( + S3ObjectId s3objectId, String instFileSuffix) { + return new GetObjectRequest( + s3objectId.instructionFileId(instFileSuffix)); + } + + static long[] getAdjustedCryptoRange(long[] range) { + // If range is invalid, then return null. + if (range == null || range[0] > range[1]) { + return null; + } + final long[] adjustedCryptoRange = new long[2]; + adjustedCryptoRange[0] = getCipherBlockLowerBound(range[0]); + adjustedCryptoRange[1] = getCipherBlockUpperBound(range[1]); + return adjustedCryptoRange; + } + + private static long getCipherBlockLowerBound(long leftmostBytePosition) { + final long cipherBlockSize = JceEncryptionConstants.SYMMETRIC_CIPHER_BLOCK_SIZE; + final long offset = leftmostBytePosition % cipherBlockSize; + final long lowerBound = leftmostBytePosition - offset - cipherBlockSize; + return lowerBound < 0 ? 0 : lowerBound; + } + + /** + * Takes the position of the rightmost desired byte of a user specified + * range and returns the position of the end of the following cipher block; + * or {@value Long#MAX_VALUE} if the resultant position has a value that + * exceeds {@value Long#MAX_VALUE}. + */ + private static long getCipherBlockUpperBound(final long rightmostBytePosition) { + final long cipherBlockSize = JceEncryptionConstants.SYMMETRIC_CIPHER_BLOCK_SIZE; + final long offset = cipherBlockSize - (rightmostBytePosition % cipherBlockSize); + final long upperBound = rightmostBytePosition + offset + cipherBlockSize; + return upperBound < 0 ? Long.MAX_VALUE : upperBound; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleEO.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleEO.java index 9c6060a51a..a401c2d13f 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleEO.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3CryptoModuleEO.java @@ -12,59 +12,40 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ - package com.amazonaws.services.s3.internal.crypto; -import static com.amazonaws.services.s3.AmazonS3EncryptionClient.USER_AGENT; -import static com.amazonaws.services.s3.internal.crypto.EncryptionUtils.createSymmetricCipher; -import static com.amazonaws.services.s3.internal.crypto.EncryptionUtils.encryptRequestUsingInstruction; -import static com.amazonaws.services.s3.internal.crypto.EncryptionUtils.generateInstruction; -import static com.amazonaws.services.s3.internal.crypto.EncryptionUtils.generateOneTimeUseSymmetricKey; -import static com.amazonaws.services.s3.internal.crypto.EncryptionUtils.getEncryptedSymmetricKey; +import static com.amazonaws.services.s3.model.CryptoMode.EncryptionOnly; + +import java.io.File; -import com.amazonaws.AmazonClientException; -import com.amazonaws.AmazonServiceException; -import com.amazonaws.AmazonWebServiceRequest; -import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; +import com.amazonaws.internal.SdkFilterInputStream; +import com.amazonaws.services.kms.AWSKMSClient; import com.amazonaws.services.s3.internal.S3Direct; -import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; -import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; -import com.amazonaws.services.s3.model.CopyPartRequest; -import com.amazonaws.services.s3.model.CopyPartResult; import com.amazonaws.services.s3.model.CryptoConfiguration; -import com.amazonaws.services.s3.model.CryptoStorageMode; -import com.amazonaws.services.s3.model.EncryptedInitiateMultipartUploadRequest; -import com.amazonaws.services.s3.model.EncryptionMaterials; import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; -import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; -import com.amazonaws.services.s3.model.MaterialsDescriptionProvider; import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.PutObjectResult; import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.UploadPartRequest; -import com.amazonaws.services.s3.model.UploadPartResult; - -import java.io.File; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; /** * Encryption only (EO) cryptographic module for the S3 encryption client. */ -class S3CryptoModuleEO extends S3CryptoModuleBase { - S3CryptoModuleEO(S3Direct s3, +class S3CryptoModuleEO extends S3CryptoModuleBase { + /** + * @param cryptoConfig a read-only copy of the crypto configuration + */ + S3CryptoModuleEO(AWSKMSClient kms, S3Direct s3, AWSCredentialsProvider credentialsProvider, EncryptionMaterialsProvider encryptionMaterialsProvider, - ClientConfiguration clientConfig, CryptoConfiguration cryptoConfig) { - super(s3, credentialsProvider, encryptionMaterialsProvider, - clientConfig, cryptoConfig, - new S3CryptoScheme(ContentCryptoScheme.AES_CBC)); + CryptoConfiguration cryptoConfig) { + super(kms, s3, credentialsProvider, encryptionMaterialsProvider, + cryptoConfig); + if (cryptoConfig.getCryptoMode() != EncryptionOnly) + throw new IllegalArgumentException(); } /** @@ -73,314 +54,94 @@ class S3CryptoModuleEO extends S3CryptoModuleBase { S3CryptoModuleEO(S3Direct s3, EncryptionMaterialsProvider encryptionMaterialsProvider, CryptoConfiguration cryptoConfig) { - this(s3, new DefaultAWSCredentialsProviderChain(), - encryptionMaterialsProvider, new ClientConfiguration(), - cryptoConfig); + this(null, s3, new DefaultAWSCredentialsProviderChain(), + encryptionMaterialsProvider, cryptoConfig); } - @Override - public PutObjectResult putObjectSecurely(PutObjectRequest putObjectRequest) - throws AmazonClientException, AmazonServiceException { - appendUserAgent(putObjectRequest, USER_AGENT); - - if (this.cryptoConfig.getStorageMode() == CryptoStorageMode.InstructionFile) { - return putObjectUsingInstructionFile(putObjectRequest); - } else { - return putObjectUsingMetadata(putObjectRequest); - } + /** + * Used for testing purposes only. + */ + S3CryptoModuleEO(AWSKMSClient kms, S3Direct s3, + EncryptionMaterialsProvider encryptionMaterialsProvider, + CryptoConfiguration cryptoConfig) { + this(kms, s3, new DefaultAWSCredentialsProviderChain(), + encryptionMaterialsProvider, cryptoConfig); } @Override - public S3Object getObjectSecurely(GetObjectRequest getObjectRequest) - throws AmazonClientException, AmazonServiceException { + public S3Object getObjectSecurely(GetObjectRequest getObjectRequest) { // Should never get here, as S3 object encrypted in either EO or AE // format should all be handled by the AE module. throw new IllegalStateException(); } @Override - public ObjectMetadata getObjectSecurely(GetObjectRequest getObjectRequest, File destinationFile) - throws AmazonClientException, AmazonServiceException { + public ObjectMetadata getObjectSecurely(GetObjectRequest getObjectRequest, + File destinationFile) { // Should never get here, as S3 object encrypted in either EO or AE // format should all be handled by the AE module. throw new IllegalStateException(); } @Override - public CompleteMultipartUploadResult completeMultipartUploadSecurely( - CompleteMultipartUploadRequest completeMultipartUploadRequest) - throws AmazonClientException, AmazonServiceException { - appendUserAgent(completeMultipartUploadRequest, USER_AGENT); - - String uploadId = completeMultipartUploadRequest.getUploadId(); - EncryptedUploadContext encryptedUploadContext = multipartUploadContexts.get(uploadId); - - if (encryptedUploadContext.hasFinalPartBeenSeen() == false) { - throw new AmazonClientException( - "Unable to complete an encrypted multipart upload without being told which part was the last. " - + - "Without knowing which part was the last, the encrypted data in Amazon S3 is incomplete and corrupt."); - } - - CompleteMultipartUploadResult result = s3 - .completeMultipartUpload(completeMultipartUploadRequest); - - // In InstructionFile mode, we want to write the instruction file only - // after the whole upload has completed correctly. - if (cryptoConfig.getStorageMode() == CryptoStorageMode.InstructionFile) { - Cipher symmetricCipher = createSymmetricCipher( - encryptedUploadContext.getEnvelopeEncryptionKey(), - Cipher.ENCRYPT_MODE, cryptoConfig.getCryptoProvider(), - encryptedUploadContext.getFirstInitializationVector()); - - EncryptionMaterials encryptionMaterials; - if (encryptedUploadContext.getMaterialsDescription() != null) { - encryptionMaterials = kekMaterialsProvider - .getEncryptionMaterials(encryptedUploadContext.getMaterialsDescription()); - } else { - encryptionMaterials = kekMaterialsProvider.getEncryptionMaterials(); - } - - // Encrypt the envelope symmetric key - byte[] encryptedEnvelopeSymmetricKey = getEncryptedSymmetricKey( - encryptedUploadContext.getEnvelopeEncryptionKey(), encryptionMaterials, - cryptoConfig.getCryptoProvider()); - EncryptionInstruction instruction = new EncryptionInstruction( - encryptionMaterials.getMaterialsDescription(), encryptedEnvelopeSymmetricKey, - encryptedUploadContext.getEnvelopeEncryptionKey(), symmetricCipher); + final MultipartUploadCbcContext newUploadContext( + InitiateMultipartUploadRequest req, + ContentCryptoMaterial cekMaterial) { + MultipartUploadCbcContext encryptedUploadContext = new MultipartUploadCbcContext( + req.getBucketName(), req.getKey(), cekMaterial); + byte[] iv = cekMaterial.getCipherLite().getIV(); + encryptedUploadContext.setNextInitializationVector(iv); + return encryptedUploadContext; + } - // Put the instruction file into S3 - s3.putObject(EncryptionUtils.createInstructionPutRequest( - encryptedUploadContext.getBucketName(), encryptedUploadContext.getKey(), - instruction)); - } - multipartUploadContexts.remove(uploadId); - return result; + @Override + final void updateUploadContext(MultipartUploadCbcContext uploadContext, + SdkFilterInputStream is) { + ByteRangeCapturingInputStream bis = (ByteRangeCapturingInputStream)is; + uploadContext.setNextInitializationVector(bis.getBlock()); + return; } @Override - public InitiateMultipartUploadResult initiateMultipartUploadSecurely( - InitiateMultipartUploadRequest initiateMultipartUploadRequest) - throws AmazonClientException, AmazonServiceException { - appendUserAgent(initiateMultipartUploadRequest, USER_AGENT); - - // Generate a one-time use symmetric key and initialize a cipher to - // encrypt object data - SecretKey envelopeSymmetricKey = generateOneTimeUseSymmetricKey(); - Cipher symmetricCipher = createSymmetricCipher(envelopeSymmetricKey, Cipher.ENCRYPT_MODE, - cryptoConfig.getCryptoProvider(), null); - - if (cryptoConfig.getStorageMode() == CryptoStorageMode.ObjectMetadata) { - EncryptionMaterials encryptionMaterials = null; - if (initiateMultipartUploadRequest instanceof EncryptedInitiateMultipartUploadRequest) { - encryptionMaterials = kekMaterialsProvider - .getEncryptionMaterials(((EncryptedInitiateMultipartUploadRequest) initiateMultipartUploadRequest) - .getMaterialsDescription()); - } else { - encryptionMaterials = kekMaterialsProvider.getEncryptionMaterials(); - } - // Encrypt the envelope symmetric key - byte[] encryptedEnvelopeSymmetricKey = getEncryptedSymmetricKey(envelopeSymmetricKey, - encryptionMaterials, cryptoConfig.getCryptoProvider()); - - // Store encryption info in metadata - ObjectMetadata metadata = EncryptionUtils.updateMetadataWithEncryptionInfo( - initiateMultipartUploadRequest, encryptedEnvelopeSymmetricKey, symmetricCipher, - encryptionMaterials.getMaterialsDescription()); - - // Update the request's metadata to the updated metadata - initiateMultipartUploadRequest.setObjectMetadata(metadata); - } - - InitiateMultipartUploadResult result = s3 - .initiateMultipartUpload(initiateMultipartUploadRequest); - EncryptedUploadContext encryptedUploadContext = new EncryptedUploadContext( - initiateMultipartUploadRequest.getBucketName(), - initiateMultipartUploadRequest.getKey(), envelopeSymmetricKey); - encryptedUploadContext.setNextInitializationVector(symmetricCipher.getIV()); - encryptedUploadContext.setFirstInitializationVector(symmetricCipher.getIV()); - if (initiateMultipartUploadRequest instanceof EncryptedInitiateMultipartUploadRequest) { - encryptedUploadContext - .setMaterialsDescription(((EncryptedInitiateMultipartUploadRequest) initiateMultipartUploadRequest) - .getMaterialsDescription()); - } - multipartUploadContexts.put(result.getUploadId(), encryptedUploadContext); - - return result; + final ByteRangeCapturingInputStream wrapForMultipart( + CipherLiteInputStream is, long partSize) { + int blockSize = contentCryptoScheme.getBlockSizeInBytes(); + return new ByteRangeCapturingInputStream(is, + partSize - blockSize, + partSize); } - - /** - * {@inheritDoc} - *

- * NOTE: Because the encryption process requires context from block - * N-1 in order to encrypt block N, parts uploaded with the - * AmazonS3EncryptionClient (as opposed to the normal AmazonS3Client) must - * be uploaded serially, and in order. Otherwise, the previous encryption - * context isn't available to use when encrypting the current part. - */ + @Override - public UploadPartResult uploadPartSecurely(UploadPartRequest uploadPartRequest) - throws AmazonClientException, AmazonServiceException { - - appendUserAgent(uploadPartRequest, USER_AGENT); - - boolean isLastPart = uploadPartRequest.isLastPart(); - String uploadId = uploadPartRequest.getUploadId(); - - boolean partSizeMultipleOfCipherBlockSize = uploadPartRequest.getPartSize() - % JceEncryptionConstants.SYMMETRIC_CIPHER_BLOCK_SIZE == 0; - if (!isLastPart && !partSizeMultipleOfCipherBlockSize) { - throw new AmazonClientException( - "Invalid part size: part sizes for encrypted multipart uploads must be multiples " - + - "of the cipher block size (" - + JceEncryptionConstants.SYMMETRIC_CIPHER_BLOCK_SIZE - + ") with the exception of the last part. " - + - "Otherwise encryption adds extra padding that will corrupt the final object."); - } - - // Generate the envelope symmetric key and initialize a cipher to - // encrypt the object's data - EncryptedUploadContext encryptedUploadContext = multipartUploadContexts.get(uploadId); - if (encryptedUploadContext == null) - throw new AmazonClientException("No client-side information available on upload ID " - + uploadId); - - SecretKey envelopeSymmetricKey = encryptedUploadContext.getEnvelopeEncryptionKey(); - byte[] iv = encryptedUploadContext.getNextInitializationVector(); - CipherFactory cipherFactory = new CipherFactory(envelopeSymmetricKey, Cipher.ENCRYPT_MODE, - iv, this.cryptoConfig.getCryptoProvider()); - - // Create encrypted input stream - ByteRangeCapturingInputStream encryptedInputStream = EncryptionUtils - .getEncryptedInputStream(uploadPartRequest, cipherFactory); - uploadPartRequest.setInputStream(encryptedInputStream); - - // The last part of the multipart upload will contain extra padding from - // the encryption process - if (uploadPartRequest.isLastPart()) { - // We only change the size of the last part - long cryptoContentLength = EncryptionUtils.calculateCryptoContentLength( - cipherFactory.createCipher(), uploadPartRequest); - if (cryptoContentLength > 0) - uploadPartRequest.setPartSize(cryptoContentLength); - - if (encryptedUploadContext.hasFinalPartBeenSeen()) { - throw new AmazonClientException( - "This part was specified as the last part in a multipart upload, but a previous part was already marked as the last part. " - + - "Only the last part of the upload should be marked as the last part, otherwise it will cause the encrypted data to be corrupted."); - } - - encryptedUploadContext.setHasFinalPartBeenSeen(true); + final long computeLastPartSize(UploadPartRequest request) { + long plaintextLength; + if (request.getFile() != null) { + if (request.getPartSize() > 0) + plaintextLength = request.getPartSize(); + else + plaintextLength = request.getFile().length(); + } else if (request.getInputStream() != null) { + plaintextLength = request.getPartSize(); + } else { + return -1; } - - // Treat all encryption requests as input stream upload requests, not as - // file upload requests. - uploadPartRequest.setFile(null); - uploadPartRequest.setFileOffset(0); - - UploadPartResult result = s3.uploadPart(uploadPartRequest); - encryptedUploadContext.setNextInitializationVector(encryptedInputStream.getBlock()); - return result; + long cipherBlockSize = contentCryptoScheme.getBlockSizeInBytes(); + long offset = cipherBlockSize - (plaintextLength % cipherBlockSize); + return plaintextLength + offset; } @Override - public CopyPartResult copyPartSecurely(CopyPartRequest copyPartRequest) { - String uploadId = copyPartRequest.getUploadId(); - EncryptedUploadContext encryptedUploadContext = multipartUploadContexts.get(uploadId); - - if (!encryptedUploadContext.hasFinalPartBeenSeen()) { - encryptedUploadContext.setHasFinalPartBeenSeen(true); - } - - return s3.copyPart(copyPartRequest); + final CipherLite cipherLiteForNextPart( + MultipartUploadCbcContext uploadContext) { + CipherLite cipherLite = uploadContext.getCipherLite(); + byte[] nextIV = uploadContext.getNextInitializationVector(); + return cipherLite.createUsingIV(nextIV); } /* * Private helper methods */ - /** - * Puts an encrypted object into S3 and stores encryption info in the object - * metadata. - * - * @param putObjectRequest The request object containing all the parameters - * to upload a new object to Amazon S3. - * @return A {@link PutObjectResult} object containing the information - * returned by Amazon S3 for the new, created object. - * @throws AmazonClientException If any errors are encountered on the client - * while making the request or handling the response. - * @throws AmazonServiceException If any errors occurred in Amazon S3 while - * processing the request. - */ - private PutObjectResult putObjectUsingMetadata(PutObjectRequest putObjectRequest) - throws AmazonClientException, AmazonServiceException { - // Create instruction - EncryptionInstruction instruction = encryptionInstructionOf(putObjectRequest); - - // Encrypt the object data with the instruction - PutObjectRequest encryptedObjectRequest = encryptRequestUsingInstruction(putObjectRequest, - instruction); - - // Update the metadata - EncryptionUtils.updateMetadataWithEncryptionInstruction(putObjectRequest, instruction); - - // Put the encrypted object into S3 - return s3.putObject(encryptedObjectRequest); - } - - /** - * Puts an encrypted object into S3, and puts an instruction file into S3. - * Encryption info is stored in the instruction file. - * - * @param putObjectRequest The request object containing all the parameters - * to upload a new object to Amazon S3. - * @return A {@link PutObjectResult} object containing the information - * returned by Amazon S3 for the new, created object. - * @throws AmazonClientException If any errors are encountered on the client - * while making the request or handling the response. - * @throws AmazonServiceException If any errors occurred in Amazon S3 while - * processing the request. - */ - private PutObjectResult putObjectUsingInstructionFile(PutObjectRequest putObjectRequest) - throws AmazonClientException, AmazonServiceException { - // Create instruction - EncryptionInstruction instruction = encryptionInstructionOf(putObjectRequest); - - // Encrypt the object data with the instruction - PutObjectRequest encryptedObjectRequest = encryptRequestUsingInstruction(putObjectRequest, - instruction); - - // Put the encrypted object into S3 - PutObjectResult encryptedObjectResult = s3.putObject(encryptedObjectRequest); - - // Put the instruction file into S3 - PutObjectRequest instructionRequest = EncryptionUtils.createInstructionPutRequest( - putObjectRequest, instruction); - s3.putObject(instructionRequest); - - // Return the result of the encrypted object PUT. - return encryptedObjectResult; - } - - private EncryptionInstruction encryptionInstructionOf( - AmazonWebServiceRequest req) { - EncryptionInstruction instruction; - if (req instanceof MaterialsDescriptionProvider) { - MaterialsDescriptionProvider p = (MaterialsDescriptionProvider) req; - instruction = generateInstruction(this.kekMaterialsProvider, - p.getMaterialsDescription(), - this.cryptoConfig.getCryptoProvider()); - } else { - instruction = generateInstruction(this.kekMaterialsProvider, - this.cryptoConfig.getCryptoProvider()); - } - return instruction; - } - @Override protected final long ciphertextLength(long plaintextLength) { long cipherBlockSize = contentCryptoScheme.getBlockSizeInBytes(); diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3KeyWrapScheme.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3KeyWrapScheme.java index 658b6fea47..5ce908cb86 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3KeyWrapScheme.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3KeyWrapScheme.java @@ -18,6 +18,10 @@ import java.security.Key; class S3KeyWrapScheme { + /** + * Used for backward compatibility where the encryption only mode has no + * explicit key wrapping scheme. + */ static final S3KeyWrapScheme NONE = new S3KeyWrapScheme() { @Override String getKeyWrapAlgorithm(Key key) { @@ -32,6 +36,11 @@ public String toString() { public static final String AESWrap = "AESWrap"; public static final String RSA_ECB_OAEPWithSHA256AndMGF1Padding = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; + /** + * @param key + * the key encrypting key, which is either an AES key or a public + * key + */ String getKeyWrapAlgorithm(Key key) { String algorithm = key.getAlgorithm(); if (S3CryptoScheme.AES.equals(algorithm)) { @@ -43,4 +52,6 @@ String getKeyWrapAlgorithm(Key key) { } return null; } + + @Override public String toString() { return "S3KeyWrapScheme"; } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3ObjectWrapper.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3ObjectWrapper.java index d32998fd87..760d4fbb6d 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3ObjectWrapper.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/S3ObjectWrapper.java @@ -19,6 +19,7 @@ import com.amazonaws.services.s3.Headers; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectId; import com.amazonaws.services.s3.model.S3ObjectInputStream; import com.amazonaws.util.StringUtils; @@ -34,11 +35,18 @@ */ class S3ObjectWrapper implements Closeable { private final S3Object s3obj; + private final S3ObjectId id; - S3ObjectWrapper(S3Object s3obj) { - if (s3obj == null) + S3ObjectWrapper(S3Object s3obj, S3ObjectId id) { + if (s3obj == null) { throw new IllegalArgumentException(); + } this.s3obj = s3obj; + this.id = id; + } + + public S3ObjectId getS3ObjectId() { + return id; } ObjectMetadata getObjectMetadata() { @@ -94,8 +102,8 @@ public String toString() { * Returns true if this S3 object is an instruction file; false otherwise. */ final boolean isInstructionFile() { - ObjectMetadata metadata = s3obj.getObjectMetadata(); - Map userMeta = metadata.getUserMetadata(); + final ObjectMetadata metadata = s3obj.getObjectMetadata(); + final Map userMeta = metadata.getUserMetadata(); return userMeta != null && userMeta.containsKey(Headers.CRYPTO_INSTRUCTION_FILE); } @@ -105,8 +113,8 @@ final boolean isInstructionFile() { * user meta data; false otherwise. */ final boolean hasEncryptionInfo() { - ObjectMetadata metadata = s3obj.getObjectMetadata(); - Map userMeta = metadata.getUserMetadata(); + final ObjectMetadata metadata = s3obj.getObjectMetadata(); + final Map userMeta = metadata.getUserMetadata(); return userMeta != null && userMeta.containsKey(Headers.CRYPTO_IV) && (userMeta.containsKey(Headers.CRYPTO_KEY_V2) @@ -121,17 +129,18 @@ final boolean hasEncryptionInfo() { String toJsonString() { try { return from(s3obj.getObjectContent()); - } catch (Exception e) { + } catch (final Exception e) { throw new AmazonClientException("Error parsing JSON: " + e.getMessage()); } } private static String from(InputStream is) throws IOException { - if (is == null) + if (is == null) { return ""; - StringBuilder stringBuilder = new StringBuilder(); + } + final StringBuilder stringBuilder = new StringBuilder(); try { - BufferedReader reader = new BufferedReader( + final BufferedReader reader = new BufferedReader( new InputStreamReader(is, StringUtils.UTF8)); String line; while ((line = reader.readLine()) != null) { @@ -162,12 +171,12 @@ S3Object getS3Object() { */ ContentCryptoScheme encryptionSchemeOf(Map instructionFile) { if (instructionFile != null) { - String cekAlgo = instructionFile.get(Headers.CRYPTO_CEK_ALGORITHM); + final String cekAlgo = instructionFile.get(Headers.CRYPTO_CEK_ALGORITHM); return ContentCryptoScheme.fromCEKAlgo(cekAlgo); } - ObjectMetadata meta = s3obj.getObjectMetadata(); - Map userMeta = meta.getUserMetadata(); - String cekAlgo = userMeta.get(Headers.CRYPTO_CEK_ALGORITHM); + final ObjectMetadata meta = s3obj.getObjectMetadata(); + final Map userMeta = meta.getUserMetadata(); + final String cekAlgo = userMeta.get(Headers.CRYPTO_CEK_ALGORITHM); return ContentCryptoScheme.fromCEKAlgo(cekAlgo); } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/SecuredCEK.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/SecuredCEK.java new file mode 100644 index 0000000000..81a73bb82a --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/internal/crypto/SecuredCEK.java @@ -0,0 +1,63 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Portions copyright 2006-2009 James Murty. Please see LICENSE.txt + * for applicable license terms and NOTICE.txt for applicable notices. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.internal.crypto; + +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +/** + * Internal class used to carry both the secured CEK and the key wrapping + * algorithm, if any. Byte array cloning is intentionally skipped for + * performance reasons. + */ +class SecuredCEK { + /** + * The encrypted CEK either via key wrapping or simple encryption. + */ + private final byte[] encrypted; + /** + * The key wrapping algorithm used, or null if the CEK is not secured via + * key wrapping. + */ + private final String keyWrapAlgorithm; + + /** Unmodifiable material description. */ + private final Map matdesc; + + SecuredCEK(byte[] encryptedKey, String keyWrapAlgorithm, Map matdesc) { + this.encrypted = encryptedKey; + this.keyWrapAlgorithm = keyWrapAlgorithm; + this.matdesc = Collections.unmodifiableMap(new TreeMap(matdesc)); + } + + byte[] getEncrypted() { + return encrypted; + } + + String getKeyWrapAlgorithm() { + return keyWrapAlgorithm; + } + + /** + * Returns an unmodifable material description of this secured CEK. + */ + Map getMaterialDescription() { + return matdesc; + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/metrics/S3ServiceMetric.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/metrics/S3ServiceMetric.java index badc235da2..7481bc007c 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/metrics/S3ServiceMetric.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/metrics/S3ServiceMetric.java @@ -75,7 +75,7 @@ public String name() { @Override public String getServiceName() { - return Constants.S3_SERVICE_NAME; + return Constants.S3_SERVICE_DISPLAY_NAME; } private static abstract class S3ThroughputMetric extends S3ServiceMetric @@ -90,7 +90,7 @@ public static S3ServiceMetric[] values() { } public static S3ServiceMetric valueOf(String name) { - for (S3ServiceMetric e : values()) { + for (final S3ServiceMetric e : values()) { if (e.name().equals(name)) { return e; } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbortIncompleteMultipartUpload.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbortIncompleteMultipartUpload.java new file mode 100644 index 0000000000..e2af2bc54f --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbortIncompleteMultipartUpload.java @@ -0,0 +1,74 @@ +/* + * Copyright 2016-2017 Amazon Technologies, 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +/** + * Specifies the days since the initiation of an Incomplete Multipart Upload + * that Lifecycle will wait before permanently removing all parts of the upload. + */ +public class AbortIncompleteMultipartUpload implements Serializable { + + /** + * Indicates the number of days that must pass since initiation for + * Lifecycle to abort an Incomplete Multipart Upload. + */ + private int daysAfterInitiation; + + public int getDaysAfterInitiation() { + return daysAfterInitiation; + } + + public void setDaysAfterInitiation(int daysAfterInitiation) { + this.daysAfterInitiation = daysAfterInitiation; + } + + public AbortIncompleteMultipartUpload withDaysAfterInitiation(int daysAfterInitiation) { + setDaysAfterInitiation(daysAfterInitiation); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final AbortIncompleteMultipartUpload that = (AbortIncompleteMultipartUpload) o; + + return daysAfterInitiation == that.daysAfterInitiation; + } + + @Override + public int hashCode() { + return daysAfterInitiation; + } + + @Override + protected AbortIncompleteMultipartUpload clone() throws CloneNotSupportedException { + try { + return (AbortIncompleteMultipartUpload) super.clone(); + } catch (final CloneNotSupportedException e) { + throw new IllegalStateException( + "Got a CloneNotSupportedException from Object.clone() " + + "even though we're Cloneable!", + e); + } + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbortMultipartUploadRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbortMultipartUploadRequest.java index fbe6a802b8..68e0667886 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbortMultipartUploadRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbortMultipartUploadRequest.java @@ -18,6 +18,8 @@ import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** * The AbortMultipartUploadRequest contains the parameters used for the * AbortMultipartUpload method. @@ -26,7 +28,7 @@ * * @see AmazonS3#abortMultipartUpload(AbortMultipartUploadRequest) */ -public class AbortMultipartUploadRequest extends AmazonWebServiceRequest { +public class AbortMultipartUploadRequest extends AmazonWebServiceRequest implements Serializable { /** The name of the bucket containing the multipart upload to abort */ private String bucketName; @@ -37,6 +39,12 @@ public class AbortMultipartUploadRequest extends AmazonWebServiceRequest { /** The ID of the multipart upload to abort */ private String uploadId; + /** + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; + /** * Constructs a new request to abort a multipart upload. * @@ -141,4 +149,69 @@ public AbortMultipartUploadRequest withUploadId(String uploadId) { return this; } + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated AbortMultipartUploadRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated AbortMultipartUploadRequest object. + */ + public AbortMultipartUploadRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } + } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbstractPutObjectRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbstractPutObjectRequest.java new file mode 100644 index 0000000000..61cab1f781 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AbstractPutObjectRequest.java @@ -0,0 +1,825 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.event.ProgressListener; + +import java.io.File; +import java.io.InputStream; +import java.io.Serializable; + +/** + * Abstract base class for a put object or put object like request. + */ +public abstract class AbstractPutObjectRequest extends AmazonWebServiceRequest implements + SSECustomerKeyProvider, SSEAwsKeyManagementParamsProvider, S3DataSource, + Serializable { + /** + * The name of an existing bucket, to which this request will upload a new + * object. You must have {@link Permission#Write} permission granted to you + * in order to upload new objects to a bucket. + */ + private String bucketName; + + /** + * The key under which to store the new object. + */ + private String key; + + /** + * The file containing the data to be uploaded to Amazon S3. You must either + * specify a file or an InputStream containing the data to be uploaded to + * Amazon S3. + */ + private File file; + + /** + * The InputStream containing the data to be uploaded to Amazon S3. You must + * either specify a file or an InputStream containing the data to be + * uploaded to Amazon S3. + */ + private transient InputStream inputStream; + + /** + * Optional metadata instructing Amazon S3 how to handle the uploaded data + * (e.g. custom user metadata, hooks for specifying content type, etc.). If + * you are uploading from an InputStream, you should always + * specify metadata with the content size set, otherwise the contents of the + * InputStream will have to be buffered in memory before they can be sent to + * Amazon S3, which can have very negative performance impacts. + */ + private ObjectMetadata metadata; + + /** + * An optional pre-configured access control policy to use for the new + * object. Ignored in favor of accessControlList, if present. + */ + private CannedAccessControlList cannedAcl; + + /** + * An optional access control list to apply to the new object. If specified, + * cannedAcl will be ignored. + */ + private AccessControlList accessControlList; + + /** + * The optional Amazon S3 storage class to use when storing the new object. + * If not specified, the default, standard storage class will be used. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + */ + private String storageClass; + + /** The optional redirect location about an object */ + private String redirectLocation; + + /** + * The optional customer-provided server-side encryption key to use to + * encrypt the uploaded object. + */ + private SSECustomerKey sseCustomerKey; + + /** + * The optional AWS Key Management system parameters to be used to encrypt + * the the object on the server side. + */ + private SSEAwsKeyManagementParams sseAwsKeyManagementParams; + + private ObjectTagging tagging; + + /** + * Constructs a new {@link AbstractPutObjectRequest} object to upload a file + * to the specified bucket and key. After constructing the request, users + * may optionally specify object metadata or a canned ACL as well. + * + * @param bucketName The name of an existing bucket to which the new object + * will be uploaded. + * @param key The key under which to store the new object. + * @param file The path of the file to upload to Amazon S3. + */ + public AbstractPutObjectRequest(String bucketName, String key, File file) { + this.bucketName = bucketName; + this.key = key; + this.file = file; + } + + /** + * Constructs a new {@link AbstractPutObjectRequest} object with redirect + * location. After constructing the request, users may optionally specify + * object metadata or a canned ACL as well. + * + * @param bucketName The name of an existing bucket to which the new object + * will be uploaded. + * @param key The key under which to store the new object. + * @param redirectLocation The redirect location of this new object. + */ + public AbstractPutObjectRequest(String bucketName, String key, + String redirectLocation) { + this.bucketName = bucketName; + this.key = key; + this.redirectLocation = redirectLocation; + } + + /** + * Constructs a new {@link AbstractPutObjectRequest} object to upload a + * stream of data to the specified bucket and key. After constructing the + * request, users may optionally specify object metadata or a canned ACL as + * well. + *

+ * Content length for the data stream must be specified in the object + * metadata parameter; Amazon S3 requires it be passed in before the data is + * uploaded. Failure to specify a content length will cause the entire + * contents of the input stream to be buffered locally in memory so that the + * content length can be calculated, which can result in negative + * performance problems. + *

+ * + * @param bucketName The name of an existing bucket to which the new object + * will be uploaded. + * @param key The key under which to store the new object. + * @param input The stream of data to upload to Amazon S3. + * @param metadata The object metadata. At minimum this specifies the + * content length for the stream of data being uploaded. + */ + protected AbstractPutObjectRequest(String bucketName, String key, + InputStream input, ObjectMetadata metadata) { + this.bucketName = bucketName; + this.key = key; + this.inputStream = input; + this.metadata = metadata; + } + + /** + * Gets the name of the existing bucket where this request will upload a new + * object to. In order to upload the object, users must have + * {@link Permission#Write} permission granted. + * + * @return The name of an existing bucket where this request will upload a + * new object to. + * @see AbstractPutObjectRequest#setBucketName(String) + * @see AbstractPutObjectRequest#withBucketName(String) + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of an existing bucket where this request will upload a new + * object to. In order to upload the object, users must have + * {@link Permission#Write} permission granted. + * + * @param bucketName The name of an existing bucket where this request will + * upload a new object to. In order to upload the object, users + * must have {@link Permission#Write} permission granted. + * @see AbstractPutObjectRequest#getBucketName() + * @see AbstractPutObjectRequest#withBucketName(String) + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket where this request will upload a new object + * to. Returns this object, enabling additional method calls to be chained + * together. + *

+ * In order to upload the object, users must have {@link Permission#Write} + * permission granted. + * + * @param bucketName The name of an existing bucket where this request will + * upload a new object to. In order to upload the object, users + * must have {@link Permission#Write} permission granted. + * @return This {@link AbstractPutObjectRequest}, enabling additional method + * calls to be chained together. + * @see AbstractPutObjectRequest#getBucketName() + * @see AbstractPutObjectRequest#setBucketName(String) + */ + public T withBucketName( + String bucketName) { + setBucketName(bucketName); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Gets the key under which to store the new object. + * + * @return The key under which to store the new object. + * @see AbstractPutObjectRequest#setKey(String) + * @see AbstractPutObjectRequest#withKey(String) + */ + public String getKey() { + return key; + } + + /** + * Sets the key under which to store the new object. + * + * @param key The key under which to store the new object. + * @see AbstractPutObjectRequest#getKey() + * @see AbstractPutObjectRequest#withKey(String) + */ + public void setKey(String key) { + this.key = key; + } + + /** + * Sets the key under which to store the new object. Returns this object, + * enabling additional method calls to be chained together. + * + * @param key The key under which to store the new object. + * @return This {@link AbstractPutObjectRequest}, enabling additional method + * calls to be chained together. + * @see AbstractPutObjectRequest#getKey() + * @see AbstractPutObjectRequest#setKey(String) + */ + public T withKey(String key) { + setKey(key); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Gets the optional Amazon S3 storage class to use when storing the new + * object. If not specified, the default standard storage class is used when + * storing the object. + *

+ * For more information on available Amazon S3 storage classes, see the + * {@link StorageClass} enumeration. + *

+ * + * @return The Amazon S3 storage class to use when storing the newly copied + * object. + * @see AbstractPutObjectRequest#setStorageClass(String) + * @see AbstractPutObjectRequest#setStorageClass(StorageClass) + * @see AbstractPutObjectRequest#withStorageClass(StorageClass) + * @see AbstractPutObjectRequest#withStorageClass(String) + */ + public String getStorageClass() { + return storageClass; + } + + /** + * Sets the optional Amazon S3 storage class to use when storing the new + * object. If not specified, the default standard storage class will be used + * when storing the new object. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + *

+ * + * @param storageClass The storage class to use when storing the new object. + * @see #getStorageClass() + * @see #setStorageClass(String) + * @see #withStorageClass(StorageClass) + * @see #withStorageClass(String) + */ + public void setStorageClass(String storageClass) { + this.storageClass = storageClass; + } + + /** + * Sets the optional Amazon S3 storage class to use when storing the new + * object. Returns this {@link AbstractPutObjectRequest}, enabling + * additional method calls to be chained together. If not specified, the + * default standard storage class will be used when storing the object. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + *

+ * + * @param storageClass The storage class to use when storing the new object. + * @return This {@link AbstractPutObjectRequest}, enabling additional method + * calls to be chained together. + * @see AbstractPutObjectRequest#getStorageClass() + * @see AbstractPutObjectRequest#setStorageClass(StorageClass) + * @see AbstractPutObjectRequest#setStorageClass(String) + * @see AbstractPutObjectRequest#withStorageClass(StorageClass) + */ + public T withStorageClass( + String storageClass) { + setStorageClass(storageClass); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Sets the optional Amazon S3 storage class to use when storing the new + * object. If not specified, the default standard storage class will be used + * when storing the object. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + *

+ * + * @param storageClass The storage class to use when storing the new object. + * @see #getStorageClass() + * @see #setStorageClass(String) + */ + public void setStorageClass(StorageClass storageClass) { + this.storageClass = storageClass.toString(); + } + + /** + * Sets the optional Amazon S3 storage class to use when storing the new + * object. Returns this {@link AbstractPutObjectRequest}, enabling + * additional method calls to be chained together. If not specified, the + * default standard storage class will be used when storing the object. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + *

+ * + * @param storageClass The storage class to use when storing the new object. + * @return This {@link AbstractPutObjectRequest}, enabling additional method + * calls to be chained together. + * @see AbstractPutObjectRequest#getStorageClass() + * @see AbstractPutObjectRequest#setStorageClass(StorageClass) + * @see AbstractPutObjectRequest#setStorageClass(String) + * @see AbstractPutObjectRequest#withStorageClass(String) + */ + public T withStorageClass( + StorageClass storageClass) { + setStorageClass(storageClass); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Gets the path and name of the file containing the data to be uploaded to + * Amazon S3. Either specify a file or an input stream containing the data + * to be uploaded to Amazon S3; both cannot be specified. + * + * @return The path and name of the file containing the data to be uploaded + * to Amazon S3. + * @see AbstractPutObjectRequest#setFile(File) + * @see AbstractPutObjectRequest#withFile(File) + * @see AbstractPutObjectRequest#setInputStream(InputStream) + * @see AbstractPutObjectRequest#withInputStream(InputStream) + */ + @Override + public File getFile() { + return file; + } + + /** + * Sets the path and name of the file containing the data to be uploaded to + * Amazon S3. Either specify a file or an input stream containing the data + * to be uploaded to Amazon S3; both cannot be specified. + * + * @param file The path and name of the file containing the data to be + * uploaded to Amazon S3. + * @see AbstractPutObjectRequest#getFile() + * @see AbstractPutObjectRequest#withFile(File) + * @see AbstractPutObjectRequest#getInputStream() + * @see AbstractPutObjectRequest#withInputStream(InputStream) + */ + @Override + public void setFile(File file) { + this.file = file; + } + + /** + * Sets the file containing the data to be uploaded to Amazon S3. Returns + * this {@link AbstractPutObjectRequest}, enabling additional method calls + * to be chained together. + *

+ * Either specify a file or an input stream containing the data to be + * uploaded to Amazon S3; both cannot be specified. + * + * @param file The file containing the data to be uploaded to Amazon S3. + * @return This {@link AbstractPutObjectRequest}, enabling additional method + * calls to be chained together. + * @see AbstractPutObjectRequest#getFile() + * @see AbstractPutObjectRequest#setFile(File) + * @see AbstractPutObjectRequest#getInputStream() + * @see AbstractPutObjectRequest#setInputStream(InputStream) + */ + public T withFile(File file) { + setFile(file); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Gets the optional metadata instructing Amazon S3 how to handle the + * uploaded data (e.g. custom user metadata, hooks for specifying content + * type, etc.). + *

+ * If uploading from an input stream, always specify metadata with + * the content size set. Otherwise the contents of the input stream have to + * be buffered in memory before being sent to Amazon S3. This can cause very + * negative performance impacts. + *

+ * + * @return The optional metadata instructing Amazon S3 how to handle the + * uploaded data (e.g. custom user metadata, hooks for specifying + * content type, etc.). + * @see AbstractPutObjectRequest#setMetadata(ObjectMetadata) + * @see AbstractPutObjectRequest#withMetadata(ObjectMetadata) + */ + public ObjectMetadata getMetadata() { + return metadata; + } + + /** + * Sets the optional metadata instructing Amazon S3 how to handle the + * uploaded data (e.g. custom user metadata, hooks for specifying content + * type, etc.). + *

+ * If uploading from an input stream, always specify metadata with + * the content size set. Otherwise the contents of the input stream have to + * be buffered in memory before being sent to Amazon S3. This can cause very + * negative performance impacts. + *

+ * + * @param metadata The optional metadata instructing Amazon S3 how to handle + * the uploaded data (e.g. custom user metadata, hooks for + * specifying content type, etc.). + * @see AbstractPutObjectRequest#getMetadata() + * @see AbstractPutObjectRequest#withMetadata(ObjectMetadata) + */ + public void setMetadata(ObjectMetadata metadata) { + this.metadata = metadata; + } + + /** + * Sets the optional metadata instructing Amazon S3 how to handle the + * uploaded data (e.g. custom user metadata, hooks for specifying content + * type, etc.). Returns this {@link AbstractPutObjectRequest}, enabling + * additional method calls to be chained together. + *

+ * If uploading from an input stream, always specify metadata with + * the content size set. Otherwise the contents of the input stream have to + * be buffered in memory before being sent to Amazon S3. This can cause very + * negative performance impacts. + *

+ * + * @param metadata The optional metadata instructing Amazon S3 how to handle + * the uploaded data (e.g. custom user metadata, hooks for + * specifying content type, etc.). + * @return This {@link AbstractPutObjectRequest}, enabling additional method + * calls to be chained together. + * @see AbstractPutObjectRequest#getMetadata() + * @see AbstractPutObjectRequest#setMetadata(ObjectMetadata) + */ + public T withMetadata( + ObjectMetadata metadata) { + setMetadata(metadata); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Gets the optional pre-configured access control policy to use for the new + * object. + * + * @return The optional pre-configured access control policy to use for the + * new object. + * @see AbstractPutObjectRequest#setCannedAcl(CannedAccessControlList) + * @see AbstractPutObjectRequest#withCannedAcl(CannedAccessControlList) + */ + public CannedAccessControlList getCannedAcl() { + return cannedAcl; + } + + /** + * Sets the optional pre-configured access control policy to use for the new + * object. + * + * @param cannedAcl The optional pre-configured access control policy to use + * for the new object. + * @see AbstractPutObjectRequest#getCannedAcl() + * @see AbstractPutObjectRequest#withCannedAcl(CannedAccessControlList) + */ + public void setCannedAcl(CannedAccessControlList cannedAcl) { + this.cannedAcl = cannedAcl; + } + + /** + * Sets the optional pre-configured access control policy to use for the new + * object. Returns this {@link AbstractPutObjectRequest}, enabling + * additional method calls to be chained together. + * + * @param cannedAcl The optional pre-configured access control policy to use + * for the new object. + * @return This {@link AbstractPutObjectRequest}, enabling additional method + * calls to be chained together. + * @see AbstractPutObjectRequest#getCannedAcl() + * @see AbstractPutObjectRequest#setCannedAcl(CannedAccessControlList) + */ + public T withCannedAcl( + CannedAccessControlList cannedAcl) { + setCannedAcl(cannedAcl); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Returns the optional access control list for the new object. If + * specified, cannedAcl will be ignored. + */ + public AccessControlList getAccessControlList() { + return accessControlList; + } + + /** + * Sets the optional access control list for the new object. If specified, + * cannedAcl will be ignored. + * + * @param accessControlList The access control list for the new object. + */ + public void setAccessControlList(AccessControlList accessControlList) { + this.accessControlList = accessControlList; + } + + /** + * Sets the optional access control list for the new object. If specified, + * cannedAcl will be ignored. Returns this {@link AbstractPutObjectRequest}, + * enabling additional method calls to be chained together. + * + * @param accessControlList The access control list for the new object. + */ + public T withAccessControlList( + AccessControlList accessControlList) { + setAccessControlList(accessControlList); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Gets the input stream containing the data to be uploaded to Amazon S3. + * The user of this request must either specify a file or an input stream + * containing the data to be uploaded to Amazon S3; both cannot be + * specified. + * + * @return The input stream containing the data to be uploaded to Amazon S3. + * Either specify a file or an input stream containing the data to + * be uploaded to Amazon S3, not both. + * @see AbstractPutObjectRequest#setInputStream(InputStream) + * @see AbstractPutObjectRequest#withInputStream(InputStream) + * @see AbstractPutObjectRequest#setFile(File) + * @see AbstractPutObjectRequest#withFile(File) + */ + @Override + public InputStream getInputStream() { + return inputStream; + } + + /** + * Sets the input stream containing the data to be uploaded to Amazon S3. + * Either specify a file or an input stream containing the data to be + * uploaded to Amazon S3; both cannot be specified. + * + * @param inputStream The input stream containing the data to be uploaded to + * Amazon S3. Either specify a file or an input stream containing + * the data to be uploaded to Amazon S3, not both. + * @see AbstractPutObjectRequest#getInputStream() + * @see AbstractPutObjectRequest#withInputStream(InputStream) + * @see AbstractPutObjectRequest#getFile() + * @see AbstractPutObjectRequest#withFile(File) + */ + @Override + public void setInputStream(InputStream inputStream) { + this.inputStream = inputStream; + } + + /** + * Sets the input stream containing the data to be uploaded to Amazon S3. + * Returns this {@link AbstractPutObjectRequest}, enabling additional method + * calls to be chained together. + *

+ * Either specify a file or an input stream containing the data to be + * uploaded to Amazon S3; both cannot be specified. + *

+ * + * @param inputStream The InputStream containing the data to be uploaded to + * Amazon S3. + * @return This PutObjectRequest, so that additional method calls can be + * chained together. + * @see AbstractPutObjectRequest#getInputStream() + * @see AbstractPutObjectRequest#setInputStream(InputStream) + * @see AbstractPutObjectRequest#getFile() + * @see AbstractPutObjectRequest#setFile(File) + */ + public T withInputStream( + InputStream inputStream) { + setInputStream(inputStream); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Sets the optional redirect location for the new object. + * + * @param redirectLocation The redirect location for the new object. + */ + public void setRedirectLocation(String redirectLocation) { + this.redirectLocation = redirectLocation; + } + + /** + * Gets the optional redirect location for the new object. + */ + public String getRedirectLocation() { + return this.redirectLocation; + } + + /** + * Sets the optional redirect location for the new object.Returns this + * {@link AbstractPutObjectRequest}, enabling additional method calls to be + * chained together. + * + * @param redirectLocation The redirect location for the new object. + */ + public T withRedirectLocation( + String redirectLocation) { + this.redirectLocation = redirectLocation; + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + @Override + public SSECustomerKey getSSECustomerKey() { + return sseCustomerKey; + } + + /** + * Sets the optional customer-provided server-side encryption key to use to + * encrypt the uploaded object. + * + * @param sseKey The optional customer-provided server-side encryption key + * to use to encrypt the uploaded object. + */ + public void setSSECustomerKey(SSECustomerKey sseKey) { + if (sseKey != null && this.sseAwsKeyManagementParams != null) { + throw new IllegalArgumentException( + "Either SSECustomerKey or SSEAwsKeyManagementParams must not be set at the same time."); + } + this.sseCustomerKey = sseKey; + } + + /** + * Sets the optional customer-provided server-side encryption key to use to + * encrypt the uploaded object, and returns the updated request object so + * that additional method calls can be chained together. + * + * @param sseKey The optional customer-provided server-side encryption key + * to use to encrypt the uploaded object. + * @return This updated request object so that additional method calls can + * be chained together. + */ + public T withSSECustomerKey( + SSECustomerKey sseKey) { + setSSECustomerKey(sseKey); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + public ObjectTagging getTagging() { + return tagging; + } + + public void setTagging(ObjectTagging tagging) { + this.tagging = tagging; + } + + public T withTagging(ObjectTagging tagSet) { + setTagging(tagSet); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Sets the optional progress listener for receiving updates for object + * upload status. + * + * @param progressListener The legacy progress listener that is used + * exclusively for Amazon S3 client. + * @deprecated use {@link #setGeneralProgressListener(ProgressListener)} + * instead. + */ + @Deprecated + public void setProgressListener( + com.amazonaws.services.s3.model.ProgressListener progressListener) { + setGeneralProgressListener(new LegacyS3ProgressListener(progressListener)); + } + + /** + * Returns the optional progress listener for receiving updates about object + * upload status. + * + * @return the optional progress listener for receiving updates about object + * upload status. + * @deprecated use {@link #getGeneralProgressListener()} instead. + */ + @Deprecated + public com.amazonaws.services.s3.model.ProgressListener getProgressListener() { + final ProgressListener generalProgressListener = getGeneralProgressListener(); + if (generalProgressListener instanceof LegacyS3ProgressListener) { + return ((LegacyS3ProgressListener) generalProgressListener).unwrap(); + } else { + return null; + } + } + + /** + * Sets the optional progress listener for receiving updates about object + * upload status, and returns this updated object so that additional method + * calls can be chained together. + * + * @param progressListener The legacy progress listener that is used + * exclusively for Amazon S3 client. + * @return This updated PutObjectRequest object. + * @deprecated use {@link #withGeneralProgressListener(ProgressListener)} + * instead. + */ + @Deprecated + public T withProgressListener( + com.amazonaws.services.s3.model.ProgressListener progressListener) { + setProgressListener(progressListener); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + /** + * Returns the AWS Key Management System parameters used to encrypt the + * object on server side. + */ + @Override + public SSEAwsKeyManagementParams getSSEAwsKeyManagementParams() { + return sseAwsKeyManagementParams; + } + + /** + * Sets the AWS Key Management System parameters used to encrypt the object + * on server side. + */ + public void setSSEAwsKeyManagementParams(SSEAwsKeyManagementParams params) { + if (params != null && this.sseCustomerKey != null) { + throw new IllegalArgumentException( + "Either SSECustomerKey or SSEAwsKeyManagementParams must not be set at the same time."); + } + this.sseAwsKeyManagementParams = params; + } + + /** + * Sets the AWS Key Management System parameters used to encrypt the object + * on server side. + * + * @return returns the update PutObjectRequest + */ + public T withSSEAwsKeyManagementParams( + SSEAwsKeyManagementParams sseAwsKeyManagementParams) { + setSSEAwsKeyManagementParams(sseAwsKeyManagementParams); + @SuppressWarnings("unchecked") + final T t = (T) this; + return t; + } + + @Override + public AbstractPutObjectRequest clone() { + return (AbstractPutObjectRequest) super.clone(); + } + + protected final T copyPutObjectBaseTo( + T target) { + copyBaseTo(target); + final ObjectMetadata metadata = getMetadata(); + return target.withAccessControlList(getAccessControlList()) + .withCannedAcl(getCannedAcl()) + .withInputStream(getInputStream()) + .withMetadata(metadata == null ? null : metadata.clone()) + .withRedirectLocation(getRedirectLocation()) + .withStorageClass(getStorageClass()) + .withSSEAwsKeyManagementParams(getSSEAwsKeyManagementParams()) + .withSSECustomerKey(getSSECustomerKey()); + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AccessControlList.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AccessControlList.java index d34082b580..8cce0e83b1 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AccessControlList.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AccessControlList.java @@ -18,9 +18,13 @@ package com.amazonaws.services.s3.model; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; + import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; /** @@ -55,17 +59,26 @@ *

* Important: Do not grant the anonymous group write access to buckets, as you * will have no control over the objects others can store and their associated - * charges. For more information, see {@link Grantee} and {@link Permissions}. + * charges. *

* * @see CannedAccessControlList */ -public class AccessControlList implements Serializable { +public class AccessControlList implements Serializable, S3RequesterChargedResult { private static final long serialVersionUID = 8095040648034788376L; - private HashSet grants = new HashSet(); + // grant set is maintained for backwards compatibility. Both grantSet and + // grantList cannot be non null at the same time. + private Set grantSet; + private List grantList; private Owner owner = null; + /** + * Indicate if the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterCharged; + /** * Gets the owner of the {@link AccessControlList}. *

@@ -89,28 +102,40 @@ public Owner getOwner() { } /** - * For internal use only. Sets the owner on this access control list (ACL). - * This method is only intended for internal use by the library. The owner - * of a bucket or object cannot be changed. However the object can be - * overwritten by the new desired owner (deleted and rewritten). + * Sets the owner of the {@link AccessControlList}. Note that an owner of a resource can't + * change once created. + * + *

+ * Every bucket and object in Amazon S3 has an owner, the user that created + * the bucket or object. The owner of a bucket or object cannot be changed. + * However, if the object is overwritten by another user (deleted and + * rewritten), the new object will have a new owner. + *

+ *

+ * Note: Even the owner is subject to the access control list (ACL). For example, if an owner does + * not have {@link Permission#Read} access to an object, the owner cannot + * read that object. However, the owner of an object always has write access + * to the access control policy ({@link Permission#WriteAcp}) and can change + * the ACL to read the object. + *

* - * @param owner The owner for this ACL. + * @param owner Owner of the bucket. */ public void setOwner(Owner owner) { this.owner = owner; } /** - * Adds a grantee to the access control list (ACL) with the given - * permission. If this access control list already contains the grantee - * (i.e. the same grantee object) the permission for the grantee will be - * updated. + * Adds a grantee to the access control list (ACL) with the given permission. + * If this access control list already + * contains the grantee (i.e. the same grantee object) the permission for the + * grantee will be updated. * * @param grantee The grantee to whom the permission will apply. * @param permission The permission to apply to the grantee. */ public void grantPermission(Grantee grantee, Permission permission) { - grants.add(new Grant(grantee, permission)); + getGrantsAsList().add(new Grant(grantee, permission)); } /** @@ -120,7 +145,7 @@ public void grantPermission(Grantee grantee, Permission permission) { * @param grants A collection of {@link Grant} objects */ public void grantAllPermissions(Grant... grantsVarArg) { - for (Grant gap : grantsVarArg) { + for (final Grant gap : grantsVarArg) { grantPermission(gap.getGrantee(), gap.getPermission()); } } @@ -132,27 +157,138 @@ public void grantAllPermissions(Grant... grantsVarArg) { * @param grantee The grantee to remove from this ACL. */ public void revokeAllPermissions(Grantee grantee) { - ArrayList grantsToRemove = new ArrayList(); - for (Grant gap : grants) { + final ArrayList grantsToRemove = new ArrayList(); + final List existingGrants = getGrantsAsList(); + for (final Grant gap : existingGrants) { if (gap.getGrantee().equals(grantee)) { grantsToRemove.add(gap); } } - grants.removeAll(grantsToRemove); + grantList.removeAll(grantsToRemove); } /** * Gets the set of {@link Grant} objects in this access control list (ACL). * * @return The set of {@link Grant} objects in this ACL. + * + * @deprecated This will remove the duplicate grants if received from Amazon + * S3. Use {@link AccessControlList#getGrantsAsList} instead. */ + @Deprecated public Set getGrants() { - return grants; + checkState(); + if (grantSet == null) { + if (grantList == null) { + grantSet = new HashSet(); + } else { + grantSet = new HashSet(grantList); + grantList = null; + } + } + return grantSet; + } + + /** + * Both grant set and grant list cannot be null at the same time. + */ + private void checkState() { + if (grantSet != null && grantList != null) { + throw new IllegalStateException( + "Both grant set and grant list cannot be null"); + } + } + + + /** + * Gets the list of {@link Grant} objects in this access control list (ACL). + * + * @return The list of {@link Grant} objects in this ACL. + */ + public List getGrantsAsList() { + checkState(); + if (grantList == null) { + if (grantSet == null) { + grantList = new LinkedList(); + } else { + grantList = new LinkedList(grantSet); + grantSet = null; + } + } + + return grantList; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((owner == null) ? 0 : owner.hashCode()); + result = prime * result + ((grantSet == null) ? 0 : grantSet.hashCode()); + result = prime * result + ((grantList == null) ? 0 : grantList.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (getClass() != obj.getClass()) { + return false; + } + final AccessControlList other = (AccessControlList) obj; + + if (owner == null) { + if (other.owner != null) { + return false; + } + } else if (!owner.equals(other.owner)) { + return false; + } + + if (grantSet == null) { + if (other.grantSet != null) { + return false; + } + } else if (!grantSet.equals(other.grantSet)) { + return false; + } + + if (grantList == null) { + if (other.grantList != null) { + return false; + } + } else if (!grantList.equals(other.grantList)) { + return false; + } + + return true; } + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ @Override public String toString() { - return "AccessControlList [owner=" + owner + ", grants=" + getGrants() + "]"; + return "AccessControlList [owner=" + owner + ", grants=" + getGrantsAsList() + "]"; + } + + @Override + public boolean isRequesterCharged() { + return isRequesterCharged; + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + this.isRequesterCharged = isRequesterCharged; } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AmazonS3Exception.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AmazonS3Exception.java index 75d45cec9b..e474578a72 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AmazonS3Exception.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/AmazonS3Exception.java @@ -17,6 +17,9 @@ import com.amazonaws.AmazonServiceException; +import java.io.Serializable; +import java.util.Map; + /** * Provides an extension of the AmazonServiceException for errors reported by * Amazon S3 while processing a request. In particular, this class provides @@ -24,7 +27,7 @@ * information in the case the user needs to contact Amazon about an issue where * Amazon S3 is incorrectly handling a request. */ -public class AmazonS3Exception extends AmazonServiceException { +public class AmazonS3Exception extends AmazonServiceException implements Serializable { private static final long serialVersionUID = 7573680383273658477L; /** @@ -37,6 +40,17 @@ public class AmazonS3Exception extends AmazonServiceException { */ private String cloudFrontId; + /** + * Additional information on the exception. + */ + private Map additionalDetails; + + /** + * Returns the error XML received in the HTTP Response or null if the + * exception is constructed from the headers. + */ + private final String errorResponseXml; + /** * Constructs a new {@link AmazonS3Exception} with the specified message. * @@ -46,6 +60,7 @@ public class AmazonS3Exception extends AmazonServiceException { */ public AmazonS3Exception(String message) { super(message); + this.errorResponseXml = null; } /** @@ -59,6 +74,25 @@ public AmazonS3Exception(String message) { */ public AmazonS3Exception(String message, Exception cause) { super(message, cause); + this.errorResponseXml = null; + } + + /** + * Constructs a new {@link AmazonS3Exception} with the specified message and + * error response xml from Amazon S3. + * + * @param message The error message describing why this exception was + * thrown. + * @param errorResponseXml The original error response XML received from + * Amazon S3 + * @see AmazonS3Exception#AmazonS3Exception(String) + */ + public AmazonS3Exception(String message, String errorResponseXml) { + super(message); + if (errorResponseXml == null) { + throw new IllegalArgumentException("Error Response XML cannot be null"); + } + this.errorResponseXml = errorResponseXml; } /** @@ -105,6 +139,20 @@ public void setCloudFrontId(String cloudFrontId) { this.cloudFrontId = cloudFrontId; } + /** + * Returns any additional information retrieved in the error response. + */ + public Map getAdditionalDetails() { + return additionalDetails; + } + + /** + * Sets additional information about the error response. + */ + public void setAdditionalDetails(Map additionalDetails) { + this.additionalDetails = additionalDetails; + } + /** * Extends the implementation from AmazonServiceException to include * additional information on S3's extended request ID. @@ -115,4 +163,11 @@ public String toString() { + "S3 Extended Request ID: " + getExtendedRequestId(); } -} + /** + * Returns the error XML received in the HTTP Response or null if the + * exception is constructed from the headers. + */ + public String getErrorResponseXml() { + return errorResponseXml; + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Bucket.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Bucket.java index d8ab476b1a..a5fb63bc77 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Bucket.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Bucket.java @@ -18,6 +18,7 @@ package com.amazonaws.services.s3.model; +import java.io.Serializable; import java.util.Date; /** @@ -55,7 +56,7 @@ * objects within a single bucket or organize them across several buckets. *

*/ -public class Bucket { +public class Bucket implements Serializable { private static final long serialVersionUID = -8646831898339939580L; /** The name of this S3 bucket */ diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketCrossOriginConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketCrossOriginConfiguration.java index 23626a8b26..1243d7bd5d 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketCrossOriginConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketCrossOriginConfiguration.java @@ -15,6 +15,8 @@ package com.amazonaws.services.s3.model; +import java.io.Serializable; + /** * Container for bucket cross origin configuration operations. */ @@ -22,7 +24,8 @@ import java.util.Arrays; import java.util.List; -public class BucketCrossOriginConfiguration { +public class BucketCrossOriginConfiguration implements Serializable { + private List rules; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketLifecycleConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketLifecycleConfiguration.java index 121008e55f..4b50b0f1b5 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketLifecycleConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketLifecycleConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2011-2017 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,10 @@ package com.amazonaws.services.s3.model; +import com.amazonaws.services.s3.model.lifecycle.LifecycleFilter; + +import java.io.Serializable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -22,7 +26,7 @@ /** * Container for bucket lifecycle configuration operations. */ -public class BucketLifecycleConfiguration { +public class BucketLifecycleConfiguration implements Serializable { /** * Constant for an enabled rule. @@ -86,11 +90,12 @@ public BucketLifecycleConfiguration() { super(); } - public static class Rule { + public static class Rule implements Serializable { private String id; private String prefix; private String status; + private LifecycleFilter filter; /** * The time, in days, between when the object is uploaded to the bucket @@ -99,6 +104,8 @@ public static class Rule { */ private int expirationInDays = -1; + private boolean expiredObjectDeleteMarker = false; + /** * The time, in days, between when a new version of the object is * uploaded to the bucket and when older versions of the object expire. @@ -111,8 +118,22 @@ public static class Rule { */ private Date expirationDate; - private Transition transition; - private NoncurrentVersionTransition noncurrentVersionTransition; + /** + * Transition rules for the objects in the bucket. + */ + private List transitions; + + /** + * Transition rules for the non current objects in the bucket. + */ + private List noncurrentVersionTransitions; + + /** + * Specifies the days since the initiation of an Incomplete Multipart + * Upload that Lifecycle will wait before permanently removing all parts + * of the upload. + */ + private AbortIncompleteMultipartUpload abortIncompleteMultipartUpload; /** * Sets the ID of this rule. Rules must be less than 255 alphanumeric @@ -125,7 +146,10 @@ public void setId(String id) { /** * Sets the key prefix for which this rule will apply. + * + * @deprecated Use {@link LifecycleFilter} instead. */ + @Deprecated public void setPrefix(String prefix) { this.prefix = prefix; } @@ -165,8 +189,15 @@ public Rule withId(String id) { } /** - * Returns the key prefix for which this rule will apply. + * Returns the key prefix for which this rule will apply. This method + * should be used only if prefix was set using + * {@link #setPrefix(String)}. + * + * @deprecated The method returns prefix only if was was set using + * {@link #setPrefix(String)}. Otherwise, Use + * {@link LifecycleFilter}. */ + @Deprecated public String getPrefix() { return prefix; } @@ -176,7 +207,9 @@ public String getPrefix() { * object for method chaining. * * @see Rule#setPrefix(String) + * @deprecated Use {@link LifecycleFilter} instead. */ + @Deprecated public Rule withPrefix(String prefix) { this.prefix = prefix; return this; @@ -278,56 +311,268 @@ public Rule withExpirationDate(Date expirationDate) { /** * Sets the transition describing how this object will move between - * different storage classes in Amazon S3. + * different storage classes in Amazon S3. Bucket Life cycle + * configuration can now accept multiple transitions in a rule. Note : + * This method overwrites all the existing transitions with given + * transition. @Deprecated in favor of {@link #setTransitions(List)} */ + @Deprecated public void setTransition(Transition transition) { - this.transition = transition; + setTransitions(Arrays.asList(transition)); } /** - * Returns the transition attribute of the rule. + * Returns the transition associated with the rule. If there are more + * than one transition associated with a given rule, this method returns + * the last transition rule. + * + * @Deprecated in favor of {@link #getTransitions()} */ + @Deprecated public Transition getTransition() { - return this.transition; + final List transitions = getTransitions(); + return (transitions != null && !transitions.isEmpty()) + ? transitions.get(transitions.size() - 1) + : null; } /** * Sets the transition describing how this object will move between - * different storage classes in Amazon S3 and returns a reference to - * this object(Rule) for method chaining. + * different storage classes in Amazon S3. Bucket Life cycle + * configuration can now accept multiple transitions in a rule. + * + * @Deprecated in favor of {@link #withTransitions(List)} Returns an + * updated reference of this object. */ + @Deprecated public Rule withTransition(Transition transition) { - this.transition = transition; + setTransitions(Arrays.asList(transition)); return this; } /** * Sets the transition describing how non-current versions of objects - * will move between different storage classes in Amazon S3. + * will move between different storage classes in Amazon S3. Bucket Life + * cycle configuration can now accept multiple non current transitions + * in a rule. Note: This method overwrites all the existing transitions + * with given transition. @Deprecated in favor of + * {@link #setNoncurrentVersionTransitions(List)} */ + @Deprecated public void setNoncurrentVersionTransition( - NoncurrentVersionTransition value) { + NoncurrentVersionTransition nonCurrentVersionTransition) { - noncurrentVersionTransition = value; + setNoncurrentVersionTransitions(Arrays + .asList(nonCurrentVersionTransition)); } /** - * Returns the transition describing how non-current versions of objects - * will move between different storage classes in Amazon S3. + * Returns the non-current transition associated with the life cycle + * configuration rule. If there are more than one transitions associated + * with a rule, this method returns the last transition in the + * rule. @Deprecated in favor of + * {@link #getNoncurrentVersionTransitions()} */ + @Deprecated public NoncurrentVersionTransition getNoncurrentVersionTransition() { - return noncurrentVersionTransition; + final List transitions = getNoncurrentVersionTransitions(); + return (transitions != null && !transitions.isEmpty()) + ? transitions.get(transitions.size() - 1) + : null; } /** * Sets the transition describing how non-current versions of objects - * will move between different storage classes in Amazon S3, and returns - * a reference to this object for method chaining. + * will move between different storage classes in Amazon S3. Bucket Life + * cycle configuration can now accept multiple non current transitions + * in a rule. @Deprecated in favor of + * {@link #withNoncurrentVersionTransitions(List)} Returns a updated + * reference of this object. */ + @Deprecated public Rule withNoncurrentVersionTransition( - NoncurrentVersionTransition value) { + NoncurrentVersionTransition nonCurrentVersionTransition) { - setNoncurrentVersionTransition(value); + setNoncurrentVersionTransitions(Arrays + .asList(nonCurrentVersionTransition)); + return this; + } + + /** + * Returns the Amazon S3 object transition rules associated with the + * given rule. + */ + public List getTransitions() { + return transitions; + } + + /** + * Sets the Amazon S3 object transition rules for the given bucket. + */ + public void setTransitions(List transitions) { + if (transitions != null) { + this.transitions = new ArrayList(transitions); + } + } + + /** + * Sets the Amazon S3 object transition rules for the given bucket. + * Returns an updated version of this object. + */ + public Rule withTransitions(List transitions) { + setTransitions(transitions); + return this; + } + + /** + * Adds a new transition to the rule. + */ + public Rule addTransition(Transition transition) { + if (transition == null) { + throw new IllegalArgumentException("Transition cannot be null."); + } + + if (transitions == null) { + transitions = new ArrayList(); + } + transitions.add(transition); + return this; + } + + /** + * Returns the Amazon S3 non current object transition rules associated + * with the given rule. + */ + public List getNoncurrentVersionTransitions() { + return noncurrentVersionTransitions; + } + + /** + * Sets the Amazon S3 non current object transition rules for the given + * bucket. + */ + public void setNoncurrentVersionTransitions( + List noncurrentVersionTransitions) { + this.noncurrentVersionTransitions = new ArrayList( + noncurrentVersionTransitions); + } + + /** + * Sets the Amazon S3 non current object transition rules for the given + * bucket. Returns an updated version of this object. + */ + public Rule withNoncurrentVersionTransitions( + List noncurrentVersionTransitions) { + setNoncurrentVersionTransitions(noncurrentVersionTransitions); + return this; + } + + /** + * Adds a new Non current transition to the rule. + */ + public Rule addNoncurrentVersionTransition( + NoncurrentVersionTransition noncurrentVersionTransition) { + if (noncurrentVersionTransition == null) { + throw new IllegalArgumentException( + "NoncurrentVersionTransition cannot be null."); + } + + if (noncurrentVersionTransitions == null) { + noncurrentVersionTransitions = new ArrayList(); + } + noncurrentVersionTransitions.add(noncurrentVersionTransition); + return this; + } + + public AbortIncompleteMultipartUpload getAbortIncompleteMultipartUpload() { + return abortIncompleteMultipartUpload; + } + + public void setAbortIncompleteMultipartUpload( + AbortIncompleteMultipartUpload abortIncompleteMultipartUpload) { + this.abortIncompleteMultipartUpload = abortIncompleteMultipartUpload; + } + + public Rule withAbortIncompleteMultipartUpload( + AbortIncompleteMultipartUpload abortIncompleteMultipartUpload) { + setAbortIncompleteMultipartUpload(abortIncompleteMultipartUpload); + return this; + } + + /** + * Returns whether the current expiration policy for the object is set + * to remove objects when only a delete marker is left + *

+ * If set to true the lifecycle policy will delete the current version + * of an object if and only if the current version is a expired object + * delete marker. This option only makes sense to use for versioned + * buckets and cannot be used in conjunction with expirationInDays or + * expirationDate. Note that the current version can only be removed if + * all non-current versions have been removed (either through a + * non-current version expiration policy or being explicitly deleted) + *

+ * + * @return True if this lifecycle's configuration is configured to + * delete the current version of an object if it's the only + * version left and it's a delete marker. False otherwise + */ + public boolean isExpiredObjectDeleteMarker() { + return expiredObjectDeleteMarker; + } + + /** + * Sets the value of the ExpiredObjectDeleteMarkers attribute. + * + * @param expiredObjectDeleteMarker True to allow the current expiration + * policy to remove the current version of the object if it's + * the only version left and it's a delete marker. False has + * no effect on the current expiration policy + */ + public void setExpiredObjectDeleteMarker(boolean expiredObjectDeleteMarker) { + this.expiredObjectDeleteMarker = expiredObjectDeleteMarker; + } + + /** + * Fluent method for setting the value of the ExpiredObjectDeleteMarkers + * attributes. See {@link #setExpiredObjectDeleteMarker(boolean)} + * + * @param expiredObjectDeleteMarker + * @return This object for method chaining + */ + public Rule withExpiredObjectDeleteMarker(boolean expiredObjectDeleteMarker) { + this.expiredObjectDeleteMarker = expiredObjectDeleteMarker; + return this; + } + + /** + * Returns a {@link LifecycleFilter} that is used to identify objects + * that a Lifecycle Rule applies to. + */ + public LifecycleFilter getFilter() { + return filter; + } + + /** + * Sets the {@link LifecycleFilter} that is used to identify objects + * that a Lifecycle Rule applies to. A rule cannot have both + * {@link LifecycleFilter} and the deprecated {@link #prefix}. + * + * @param filter {@link LifecycleFilter} + */ + public void setFilter(LifecycleFilter filter) { + this.filter = filter; + } + + /** + * Fluent method to set the {@link LifecycleFilter} that is used to + * identify objects that a Lifecycle Rule applies to. A rule cannot have + * both {@link LifecycleFilter} and the deprecated {@link #prefix}. + * + * @param filter {@link LifecycleFilter} + * @return This object for method chaining. + */ + public Rule withFilter(LifecycleFilter filter) { + setFilter(filter); return this; } } @@ -336,7 +581,7 @@ public Rule withNoncurrentVersionTransition( * The transition attribute of the rule describing how this object will move * between different storage classes in Amazon S3. */ - public static class Transition { + public static class Transition implements Serializable { /** * The time, in days, between when the object is uploaded to the bucket @@ -351,7 +596,7 @@ public static class Transition { */ private Date date; - private StorageClass storageClass; + private String storageClass; /** * Sets the time, in days, between when an object is uploaded to the @@ -384,22 +629,57 @@ public Transition withDays(int expirationInDays) { * Sets the storage class of this object. */ public void setStorageClass(StorageClass storageClass) { + if (storageClass == null) { + setStorageClass((String) null); + } else { + setStorageClass(storageClass.toString()); + } + } + + /** + * Sets the storage class of this object. + */ + public void setStorageClass(String storageClass) { this.storageClass = storageClass; } /** * Returns the storage class of this object. + * + * @deprecated This method should not be used. Use + * {@link #getStorageClassAsString()} instead. */ + @Deprecated public StorageClass getStorageClass() { + try { + return StorageClass.fromValue(this.storageClass); + } catch (final IllegalArgumentException ignored) { + return null; + } + } + + /** + * Returns the storage class of this object. + */ + public String getStorageClassAsString() { return this.storageClass; } /** * Sets the storage class of this object and returns a reference to this - * object(Transition) for method chaining. + * object for method chaining. */ public Transition withStorageClass(StorageClass storageClass) { - this.storageClass = storageClass; + setStorageClass(storageClass); + return this; + } + + /** + * Sets the storage class of this object and returns a reference to this + * object for method chaining. + */ + public Transition withStorageClass(String storageClass) { + setStorageClass(storageClass); return this; } @@ -433,7 +713,7 @@ public Transition withDate(Date expirationDate) { * non-current versions of objects will move between different storage * classes in Amazon S3. */ - public static class NoncurrentVersionTransition { + public static class NoncurrentVersionTransition implements Serializable { /** * The time, in days, between when a new version of the object is @@ -441,7 +721,7 @@ public static class NoncurrentVersionTransition { */ private int days = -1; - private StorageClass storageClass; + private String storageClass; /** * Sets the time, in days, between when a new version of the object is @@ -473,13 +753,39 @@ public NoncurrentVersionTransition withDays(int expirationInDays) { * Sets the storage class of this object. */ public void setStorageClass(StorageClass storageClass) { + if (storageClass == null) { + setStorageClass((String) null); + } else { + setStorageClass(storageClass.toString()); + } + } + + /** + * Sets the storage class of this object. + */ + public void setStorageClass(String storageClass) { this.storageClass = storageClass; } /** * Returns the storage class of this object. + * + * @deprecated This method should not be used. Use + * {@link #getStorageClassAsString()} instead. */ + @Deprecated public StorageClass getStorageClass() { + try { + return StorageClass.fromValue(this.storageClass); + } catch (final IllegalArgumentException ignored) { + return null; + } + } + + /** + * Returns the storage class of this object. + */ + public String getStorageClassAsString() { return this.storageClass; } @@ -487,12 +793,19 @@ public StorageClass getStorageClass() { * Sets the storage class of this object and returns a reference to this * object for method chaining. */ - public NoncurrentVersionTransition withStorageClass( - StorageClass storageClass) { + public NoncurrentVersionTransition withStorageClass(StorageClass storageClass) { + setStorageClass(storageClass); + return this; + } - this.storageClass = storageClass; + /** + * Sets the storage class of this object and returns a reference to this + * object for method chaining. + */ + public NoncurrentVersionTransition withStorageClass(String storageClass) { + setStorageClass(storageClass); return this; } } -} +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketLoggingConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketLoggingConfiguration.java index d1258929ac..d3a96ddee0 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketLoggingConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketLoggingConfiguration.java @@ -21,6 +21,8 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; +import java.io.Serializable; + /** *

* Represents bucket logging configuration used to control bucket-based server @@ -45,7 +47,7 @@ * @see AmazonS3#getBucketLoggingConfiguration(String) * @see AmazonS3#setBucketLoggingConfiguration(SetBucketLoggingConfigurationRequest) */ -public class BucketLoggingConfiguration { +public class BucketLoggingConfiguration implements Serializable { private String destinationBucketName = null; private String logFilePrefix = null; @@ -103,8 +105,9 @@ public String getLogFilePrefix() { */ public void setLogFilePrefix(String logFilePrefix) { // Default log file prefix to the empty string if none is specified - if (logFilePrefix == null) + if (logFilePrefix == null) { logFilePrefix = ""; + } this.logFilePrefix = logFilePrefix; } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketNotificationConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketNotificationConfiguration.java index 1ac96ab103..1a4def3908 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketNotificationConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketNotificationConfiguration.java @@ -19,10 +19,17 @@ package com.amazonaws.services.s3.model; import com.amazonaws.services.s3.AmazonS3; +import com.google.gson.Gson; +import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; /** *

@@ -53,9 +60,9 @@ * @see AmazonS3#setBucketNotificationConfiguration(String, * BucketNotificationConfiguration) */ -public class BucketNotificationConfiguration { +public class BucketNotificationConfiguration implements Serializable { - private List topicConfigurations = null; + private Map configurations = null; /** *

@@ -71,7 +78,98 @@ public class BucketNotificationConfiguration { * @see BucketNotificationConfiguration#BucketNotificationConfiguration(Collection) */ public BucketNotificationConfiguration() { - this.topicConfigurations = new ArrayList(1); + this.configurations = new HashMap(); + } + + /** + *

+ * Creates a new bucket notification configuration with the given + * configuration. + *

+ * + * @param name the name for the configuration + * @param notificationConfiguration the notification configuration for the + * Amazon S3 bucket. + */ + public BucketNotificationConfiguration(String name, + NotificationConfiguration notificationConfiguration) { + this.configurations = new HashMap(); + addConfiguration(name, notificationConfiguration); + } + + /** + * Sets the given notification configurations and returns this object. + * + * @param notificationConfiguration the notification configurations to set + * @return The updated {@link BucketNotificationConfiguration} object. + */ + public BucketNotificationConfiguration withNotificationConfiguration( + Map notificationConfiguration) { + configurations.clear(); + configurations.putAll(notificationConfiguration); + return this; + } + + /** + * Adds the given notification configuration to the + * {@link BucketNotificationConfiguration} object + * + * @param name the name of the configuration + * @param notificationConfiguration the notification configuration for the + * Amazon S3 bucket. + * @return The updated {@link BucketNotificationConfiguration} object. + */ + public BucketNotificationConfiguration addConfiguration(String name, + NotificationConfiguration notificationConfiguration) { + configurations.put(name, notificationConfiguration); + return this; + } + + /** + * Returns all the notification configurations associated with the Amazon S3 + * bucket. + */ + public Map getConfigurations() { + return configurations; + } + + /** + * Sets the given notification configurations in this + * {@link BucketNotificationConfiguration} object. + * + * @param configurations the notification configurations to set + */ + public void setConfigurations( + Map configurations) { + this.configurations = configurations; + } + + /** + * Returns the notification configuration for the given name. + * + * @param name the name of the notification configuration + * @return {@link NotificationConfiguration} associated with the given name. + */ + public NotificationConfiguration getConfigurationByName(String name) { + return configurations.get(name); + } + + /** + *

+ * Removes the notification configuration for the given name in the + * {@link BucketNotificationConfiguration} object. + *

+ *

+ * Pass the updated {@link BucketNotificationConfiguration} to + * {@link AmazonS3#setBucketNotificationConfiguration(String,BucketNotificationConfiguration)} + * to update the configuration in Amazon S3 for the bucket. + *

+ * + * @param name the name of the notification configuration + * @return {@link NotificationConfiguration} associated with the given name. + */ + public NotificationConfiguration removeConfiguration(String name) { + return configurations.remove(name); } /** @@ -86,11 +184,18 @@ public BucketNotificationConfiguration() { * existing configuration. *

* - * @see BucketNotificationConfiguration#BucketNotificationConfiguration() + * @deprecated + * @see BucketNotificationConfiguration#BucketNotificationConfiguration(String, + * NotificationConfiguration) */ + @Deprecated public BucketNotificationConfiguration(Collection topicConfigurations) { - this.topicConfigurations = new ArrayList(1); - this.topicConfigurations.addAll(topicConfigurations); + this.configurations = new HashMap(); + if (topicConfigurations != null) { + for (final TopicConfiguration config : topicConfigurations) { + addConfiguration(UUID.randomUUID().toString(), config); + } + } } /** @@ -107,16 +212,13 @@ public BucketNotificationConfiguration(Collection topicConfi * @param topicConfigurations A set of topic configurations. * @return The updated {@link BucketNotificationConfiguration} object, * enabling additional method calls to be chained together. - * @see BucketNotificationConfiguration#setTopicConfigurations(Collection) + * @deprecated + * @see BucketNotificationConfiguration#withNotificationConfiguration(Map) */ + @Deprecated public BucketNotificationConfiguration withTopicConfigurations( TopicConfiguration... topicConfigurations) { - this.topicConfigurations.clear(); - - for (int index = 0; index < topicConfigurations.length; index++) { - this.topicConfigurations.add(topicConfigurations[index]); - } - + setTopicConfigurations(Arrays.asList(topicConfigurations)); return this; } @@ -130,11 +232,19 @@ public BucketNotificationConfiguration withTopicConfigurations( *

* * @param topicConfigurations A collection of topic configurations. - * @see BucketNotificationConfiguration#withTopicConfigurations(TopicConfiguration) + * @deprecated + * @see BucketNotificationConfiguration#setConfigurations(Map) */ + @Deprecated public void setTopicConfigurations(Collection topicConfigurations) { - this.topicConfigurations.clear(); - this.topicConfigurations.addAll(topicConfigurations); + this.configurations.clear(); + + if (topicConfigurations != null) { + for (final TopicConfiguration topicConfiguration : topicConfigurations) { + addConfiguration(UUID.randomUUID().toString(), + topicConfiguration); + } + } } /** @@ -144,21 +254,33 @@ public void setTopicConfigurations(Collection topicConfigura * contained in this object. This method may return an empty list if no * TopicConfiguration objects are present. *

+ *

+ * This method is deprecated and will not return all the notification + * configuration associated with the Amazon S3 bucket. To retrieve all the + * configuration use @see + * BucketNotificationConfiguration#getConfigurations() + *

* + * @deprecated + * @see BucketNotificationConfiguration#getConfigurations() * @return The list of TopicConfiguration objects contained in * this object. May return an empty list. */ + @Deprecated public List getTopicConfigurations() { - return this.topicConfigurations; + final List topicConfigs = new ArrayList(); + for (final Map.Entry entry : configurations + .entrySet()) { + if (entry.getValue() instanceof TopicConfiguration) { + topicConfigs.add((TopicConfiguration) entry.getValue()); + } + } + return topicConfigs; } @Override public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("{"); - sb.append("TopicConfigurations: " + this.getTopicConfigurations()); - sb.append("}"); - return sb.toString(); + return new Gson().toJson(this.getConfigurations()); } /** @@ -166,10 +288,14 @@ public String toString() { * Represents the SNS topic to publish event notification to. Notifications * are published to the topic only if the specified event is triggered. *

+ * + * @deprecated Use + * {@link com.amazonaws.services.s3.model.TopicConfiguration} + * instead */ - public static class TopicConfiguration { - private final String topic; - private final String event; + @Deprecated + public static class TopicConfiguration + extends com.amazonaws.services.s3.model.TopicConfiguration { /** *

@@ -183,8 +309,7 @@ public static class TopicConfiguration { * publication. */ public TopicConfiguration(final String topic, final String event) { - this.topic = topic; - this.event = event; + super(topic, event); } /** @@ -196,29 +321,28 @@ public TopicConfiguration(final String topic, final String event) { * @return The topic ARN for the topic to publish events to. */ public String getTopic() { - return this.topic; + return getTopicARN(); } /** *

- * Gets the event that must occur for the notification to be published. + * Gets the first event that is configured in the list of events. *

* - * @return The event that must occur for the notification to be - * published. + * @deprecated use + * {@link com.amazonaws.services.s3.model.TopicConfiguration#getEvents()} + * instead. */ + @Deprecated public String getEvent() { - return this.event; + final Set events = getEvents(); + final String[] eventArray = events.toArray(new String[events.size()]); + return eventArray[0]; } @Override public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("{"); - sb.append("Topic: " + this.getTopic() + ", "); - sb.append("Event: " + this.getEvent() + ", "); - sb.append("}"); - return sb.toString(); + return new Gson().toJson(this); } } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketPolicy.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketPolicy.java index e2ac95a2f1..fc9c19ca7e 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketPolicy.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketPolicy.java @@ -18,6 +18,8 @@ package com.amazonaws.services.s3.model; +import java.io.Serializable; + /** *

* Represents a Amazon S3 bucket policy. Bucket policies provide access control @@ -34,7 +36,7 @@ * Amazon S3 developer guide for more information on forming bucket polices. *

*/ -public class BucketPolicy { +public class BucketPolicy implements Serializable { /** The raw, policy JSON text, as returned by Amazon S3 */ private String policyText; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketReplicationConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketReplicationConfiguration.java index e30750049c..1c2071bea4 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketReplicationConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketReplicationConfiguration.java @@ -14,13 +14,14 @@ */ package com.amazonaws.services.s3.model; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Replication configuration for an Amazon S3 bucket. */ -public class BucketReplicationConfiguration { +public class BucketReplicationConfiguration implements Serializable { /** The ARN of the IAM role that Amazon S3 assumes while replication. */ private String roleARN; @@ -139,4 +140,4 @@ public BucketReplicationConfiguration removeRule(String id) { rules.remove(id); return this; } -} \ No newline at end of file +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketTaggingConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketTaggingConfiguration.java index 1bd5bd1797..6ff76f5d33 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketTaggingConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketTaggingConfiguration.java @@ -21,11 +21,12 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.BucketNotificationConfiguration.TopicConfiguration; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; -public class BucketTaggingConfiguration { +public class BucketTaggingConfiguration implements Serializable { private List tagSets = null; @@ -149,7 +150,7 @@ public TagSet getTagSetAtIndex(int index) { @Override public String toString() { - StringBuffer sb = new StringBuffer(); + final StringBuffer sb = new StringBuffer(); sb.append("{"); sb.append("TagSets: " + this.getAllTagSets()); sb.append("}"); diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketVersioningConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketVersioningConfiguration.java index bd929479c2..82ba24da11 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketVersioningConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketVersioningConfiguration.java @@ -17,15 +17,17 @@ import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** * Represents the versioning configuration for a bucket. *

* A bucket's versioning configuration can be in one of three possible states: - *

    - *
  • {@link BucketVersioningConfiguration#OFF} - *
  • {@link BucketVersioningConfiguration#ENABLED} - *
  • {@link BucketVersioningConfiguration#SUSPENDED} - *
+ *
    + *
  • {@link BucketVersioningConfiguration#OFF}
  • + *
  • {@link BucketVersioningConfiguration#ENABLED}
  • + *
  • {@link BucketVersioningConfiguration#SUSPENDED}
  • + *
*

*

* By default, new buckets are in the {@link BucketVersioningConfiguration#OFF @@ -61,7 +63,7 @@ * @see AmazonS3#getBucketVersioningConfiguration(String) * @see AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest) */ -public class BucketVersioningConfiguration { +public class BucketVersioningConfiguration implements Serializable { /** * S3 bucket versioning status indicating that versioning is off for a diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketWebsiteConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketWebsiteConfiguration.java index 0177a468f0..cf305d56ad 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketWebsiteConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/BucketWebsiteConfiguration.java @@ -17,6 +17,7 @@ import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; import java.util.LinkedList; import java.util.List; @@ -63,7 +64,7 @@ * @see AmazonS3#getBucketWebsiteConfiguration(String) * @see AmazonS3#deleteBucketWebsiteConfiguration(String) */ -public class BucketWebsiteConfiguration { +public class BucketWebsiteConfiguration implements Serializable { /** * The document to serve when a directory is specified (ex: index.html). diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CannedAccessControlList.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CannedAccessControlList.java index a6443c7602..8e99f5a617 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CannedAccessControlList.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CannedAccessControlList.java @@ -98,7 +98,14 @@ public enum CannedAccessControlList { * not give full access to all users. *

*/ - BucketOwnerFullControl("bucket-owner-full-control"); + BucketOwnerFullControl("bucket-owner-full-control"), + + /** + * Specifies the owner is granted {@link Permission#FullControl}. Amazon EC2 + * is granted {@link Permission#Read} access to GET an Amazon Machine Image + * (AMI) bundle from Amazon S3. + */ + AwsExecRead("aws-exec-read"); /** The Amazon S3 x-amz-acl header value representing the canned acl */ private final String cannedAclHeader; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CanonicalGrantee.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CanonicalGrantee.java index 041bc954f1..c15ea219e8 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CanonicalGrantee.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CanonicalGrantee.java @@ -18,6 +18,8 @@ package com.amazonaws.services.s3.model; +import java.io.Serializable; + /** * Represents a grantee identified by their canonical Amazon ID. The canonical * Amazon ID can be thought of as an Amazon-internal ID specific to a user. For @@ -31,7 +33,7 @@ * * @see CanonicalGrantee#CanonicalGrantee(String) */ -public class CanonicalGrantee implements Grantee { +public class CanonicalGrantee implements Grantee,Serializable { private String id = null; private String displayName = null; @@ -105,7 +107,7 @@ public String getDisplayName() { @Override public boolean equals(Object obj) { if (obj instanceof CanonicalGrantee) { - CanonicalGrantee canonicalGrantee = (CanonicalGrantee) obj; + final CanonicalGrantee canonicalGrantee = (CanonicalGrantee) obj; return id.equals(canonicalGrantee.id); } return false; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CloudFunctionConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CloudFunctionConfiguration.java new file mode 100644 index 0000000000..94a64a19d1 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CloudFunctionConfiguration.java @@ -0,0 +1,87 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import java.util.EnumSet; + +/** + * Represents the lambda configuration for an Amazon S3 bucket notification. + * @deprecated use {@link LambdaConfiguration} instead + */ +@Deprecated +public class CloudFunctionConfiguration extends NotificationConfiguration implements Serializable { + + /** + * The ARN for the IAM role to be used by Amazon S3. + */ + private final String invocationRoleARN; + + /** + * The ARN for the lambda function to be invoked. + */ + private final String cloudFunctionARN; + + /** + * Creates a new lambda configuration with the given invocation role , + * function and set of events. + * + * @param invocationRole + * the IAM role to be used by Amazon S3 for authentication. + * @param function + * the ARN of the lambda function to be invoked + * @param events + * the events for which the notifications are to be sent + */ + public CloudFunctionConfiguration(String invocationRole, String function, + EnumSet events) { + super(events); + this.invocationRoleARN = invocationRole; + this.cloudFunctionARN = function; + } + + /** + * Creates a new lambda configuration with the given invocation role , + * function and set of events. + * + * @param invocationRole + * the IAM role to be used by Amazon S3 for authentication. + * @param function + * the ARN of the lambda function to be invoked + * @param events + * the events for which the notifications are to be sent + */ + public CloudFunctionConfiguration(String invocationRole, String function, + String... events) { + super(events); + this.invocationRoleARN = invocationRole; + this.cloudFunctionARN = function; + } + + /** + * Returns the invocation role associated with this configuration. + */ + public String getInvocationRoleARN() { + return invocationRoleARN; + } + + /** + * Returns the ARN of the cloud function to be invoked. + */ + public String getCloudFunctionARN() { + return cloudFunctionARN; + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CompleteMultipartUploadRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CompleteMultipartUploadRequest.java index 694156095a..deae79ba5e 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CompleteMultipartUploadRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CompleteMultipartUploadRequest.java @@ -18,6 +18,7 @@ import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -25,11 +26,20 @@ /** * Container for the parameters of the CompleteMultipartUpload operation. *

+ * If you are performing a complete multipart upload for KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify + * -signature-version + *

+ *

* Required Parameters: BucketName, Key, UploadId, PartETags * * @see AmazonS3#completeMultipartUpload(CompleteMultipartUploadRequest) */ -public class CompleteMultipartUploadRequest extends AmazonWebServiceRequest { +public class CompleteMultipartUploadRequest extends AmazonWebServiceRequest implements Serializable { /** The name of the bucket containing the multipart upload to complete */ private String bucketName; @@ -40,12 +50,16 @@ public class CompleteMultipartUploadRequest extends AmazonWebServiceRequest { /** The ID of the multipart upload to complete */ private String uploadId; + /** The list of part numbers and ETags to use when completing the multipart upload */ + private List partETags = new ArrayList(); + /** - * The list of part numbers and ETags to use when completing the multipart - * upload + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. */ - private List partETags = new ArrayList(); + private boolean isRequesterPays; + public CompleteMultipartUploadRequest() {} /** * Constructs a new request to complete a multipart upload. * @@ -210,7 +224,7 @@ public CompleteMultipartUploadRequest withPartETags(List partETags) { * @return This updated CompleteMultipartUploadRequest object. */ public CompleteMultipartUploadRequest withPartETags(UploadPartResult... uploadPartResults) { - for (UploadPartResult result : uploadPartResults) { + for (final UploadPartResult result : uploadPartResults) { this.partETags.add(new PartETag(result.getPartNumber(), result.getETag())); } return this; @@ -227,10 +241,75 @@ public CompleteMultipartUploadRequest withPartETags(UploadPartResult... uploadPa */ public CompleteMultipartUploadRequest withPartETags( Collection uploadPartResultsCollection) { - for (UploadPartResult result : uploadPartResultsCollection) { + for (final UploadPartResult result : uploadPartResultsCollection) { this.partETags.add(new PartETag(result.getPartNumber(), result.getETag())); } return this; } + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated CompleteMultipartUploadRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated CompleteMultipartUploadRequest object. + */ + public CompleteMultipartUploadRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } + } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CompleteMultipartUploadResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CompleteMultipartUploadResult.java index a655f6f25d..043b53bbaf 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CompleteMultipartUploadResult.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CompleteMultipartUploadResult.java @@ -16,15 +16,18 @@ package com.amazonaws.services.s3.model; import com.amazonaws.services.s3.internal.ObjectExpirationResult; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; import com.amazonaws.services.s3.internal.SSEResultBase; +import java.io.Serializable; import java.util.Date; /** * The CompleteMultipartUploadResult contains all the information about the * CompleteMultipartUpload method. */ -public class CompleteMultipartUploadResult extends SSEResultBase implements ObjectExpirationResult { +public class CompleteMultipartUploadResult extends SSEResultBase + implements ObjectExpirationResult, S3RequesterChargedResult, Serializable { /** The name of the bucket containing the completed multipart upload. */ private String bucketName; @@ -53,6 +56,12 @@ public class CompleteMultipartUploadResult extends SSEResultBase implements Obje /** The expiration rule for this object */ private String expirationTimeRuleId; + /** + * Indicate if the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterCharged; + /** * Returns the URL identifying the new multipart object. * @@ -185,4 +194,14 @@ public String getExpirationTimeRuleId() { public void setExpirationTimeRuleId(String expirationTimeRuleId) { this.expirationTimeRuleId = expirationTimeRuleId; } + + @Override + public boolean isRequesterCharged() { + return isRequesterCharged; + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + this.isRequesterCharged = isRequesterCharged; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyObjectRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyObjectRequest.java index e12d6bd56e..534565ddbe 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyObjectRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyObjectRequest.java @@ -20,6 +20,7 @@ import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.internal.Constants; +import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -43,7 +44,10 @@ * @see AmazonS3Client#copyObject(com.amazonaws.services.s3.model.CopyObjectRequest) * @see CopyObjectResult */ -public class CopyObjectRequest extends AmazonWebServiceRequest implements S3AccelerateUnsupported { +public class CopyObjectRequest extends AmazonWebServiceRequest implements + SSEAwsKeyManagementParamsProvider, + Serializable, + S3AccelerateUnsupported { /** The name of the bucket containing the object to be copied */ private String sourceBucketName; @@ -136,6 +140,20 @@ public class CopyObjectRequest extends AmazonWebServiceRequest implements S3Acce */ private SSECustomerKey destinationSSECustomerKey; + /** + * The optional AWS Key Management system parameters to be used to encrypt + * the the object on the server side. + */ + private SSEAwsKeyManagementParams sseAwsKeyManagementParams; + + /** + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; + + private ObjectTagging newObjectTagging; + /** *

* Constructs a new @@ -1015,4 +1033,129 @@ public CopyObjectRequest withDestinationSSECustomerKey(SSECustomerKey sseKey) { setDestinationSSECustomerKey(sseKey); return this; } + + /** + * Returns the AWS Key Management System parameters used to encrypt the + * object on server side. + */ + @Override + public SSEAwsKeyManagementParams getSSEAwsKeyManagementParams() { + return sseAwsKeyManagementParams; + } + + /** + * Sets the AWS Key Management System parameters used to encrypt the object + * on server side. + */ + public void setSSEAwsKeyManagementParams(SSEAwsKeyManagementParams params) { + if (params != null && this.destinationSSECustomerKey != null) { + throw new IllegalArgumentException( + "Either SSECustomerKey or SSEAwsKeyManagementParams must not be set at the same time."); + } + this.sseAwsKeyManagementParams = params; + } + + /** + * Sets the AWS Key Management System parameters used to encrypt the object + * on server side. + * + * @return returns the update CopyObjectRequest + */ + public CopyObjectRequest withSSEAwsKeyManagementParams( + SSEAwsKeyManagementParams sseAwsKeyManagementParams) { + setSSEAwsKeyManagementParams(sseAwsKeyManagementParams); + return this; + } + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated CopyObjectRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated CopyObjectRequest object. + */ + public CopyObjectRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } + + /** + * @return the tagging for the new object. + */ + public ObjectTagging getNewObjectTagging() { + return newObjectTagging; + } + + /** + * set the tagging for the new object. + * + * @param newObjectTagging the tagging for the new object. + */ + public void setNewObjectTagging(ObjectTagging newObjectTagging) { + this.newObjectTagging = newObjectTagging; + } + + /** + * set the tagging for the new object. + * + * @param newObjectTagging the tagging for the new object. + * + * @return This object for chaining. + */ + public CopyObjectRequest withNewObjectTagging(ObjectTagging newObjectTagging) { + setNewObjectTagging(newObjectTagging); + return this; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyObjectResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyObjectResult.java index 21041b2c8f..cb62ca524a 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyObjectResult.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyObjectResult.java @@ -18,8 +18,11 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.internal.ObjectExpirationResult; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; +import com.amazonaws.services.s3.internal.S3VersionResult; import com.amazonaws.services.s3.internal.SSEResultBase; +import java.io.Serializable; import java.util.Date; /** @@ -33,7 +36,8 @@ * @see AmazonS3Client#copyObject(String, String, String, String) * @see AmazonS3Client#copyObject(com.amazonaws.services.s3.model.CopyObjectRequest) */ -public class CopyObjectResult extends SSEResultBase implements ObjectExpirationResult { +public class CopyObjectResult extends SSEResultBase + implements ObjectExpirationResult, S3RequesterChargedResult, S3VersionResult, Serializable { /** The ETag value of the new object */ private String etag; @@ -54,6 +58,12 @@ public class CopyObjectResult extends SSEResultBase implements ObjectExpirationR /** The expiration rule for this object */ private String expirationTimeRuleId; + /** + * Indicate if the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterCharged; + /** * Gets the ETag value for the new object that was created in the associated * {@link CopyObjectRequest}. @@ -105,6 +115,7 @@ public void setLastModifiedDate(Date lastModifiedDate) { * @return The version ID of the newly copied object. * @see CopyObjectResult#setVersionId(String) */ + @Override public String getVersionId() { return versionId; } @@ -115,6 +126,7 @@ public String getVersionId() { * @param versionId The version ID of the newly copied object. * @see CopyObjectResult#getVersionId() */ + @Override public void setVersionId(String versionId) { this.versionId = versionId; } @@ -157,4 +169,14 @@ public String getExpirationTimeRuleId() { public void setExpirationTimeRuleId(String expirationTimeRuleId) { this.expirationTimeRuleId = expirationTimeRuleId; } + + @Override + public boolean isRequesterCharged() { + return isRequesterCharged; + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + this.isRequesterCharged = isRequesterCharged; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyPartRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyPartRequest.java index 1edd13aedc..20eb4266e1 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyPartRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyPartRequest.java @@ -20,6 +20,7 @@ import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.internal.Constants; +import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -39,7 +40,8 @@ * @see AmazonS3Client#uploadPart(UploadPartRequest) * @see CopyPartResult */ -public class CopyPartRequest extends AmazonWebServiceRequest implements S3AccelerateUnsupported { +public class CopyPartRequest extends AmazonWebServiceRequest implements Serializable, + S3AccelerateUnsupported { /** * The upload id of the multipart upload into which to copy this part. @@ -551,6 +553,14 @@ public void setMatchingETagConstraints(List eTagList) { this.matchingETagConstraints.addAll(eTagList); } + /** + * Fluent API for {@link #setMatchingETagConstraints(List)}. + */ + public CopyPartRequest withMatchingETagConstraints(List eTagList) { + setMatchingETagConstraints(eTagList); + return this; + } + /** *

* Adds a single ETag constraint to this request and returns this object, @@ -617,6 +627,14 @@ public void setNonmatchingETagConstraints(List eTagList) { this.nonmatchingEtagConstraints.addAll(eTagList); } + /** + * Fluent API for {@link #setNonmatchingETagConstraints(List)}. + */ + public CopyPartRequest withNonmatchingETagConstraints(List eTagList) { + setNonmatchingETagConstraints(eTagList); + return this; + } + /** *

* Adds a single ETag constraint to this request and returns this object, diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyPartResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyPartResult.java index 361b9845d2..51d703464e 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyPartResult.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CopyPartResult.java @@ -17,12 +17,13 @@ import com.amazonaws.services.s3.internal.SSEResultBase; +import java.io.Serializable; import java.util.Date; /** * Result of the copy part operation. */ -public class CopyPartResult extends SSEResultBase { +public class CopyPartResult extends SSEResultBase implements Serializable { /** The ETag value of the new part */ private String etag; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CryptoConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CryptoConfiguration.java index abf081e018..a2f25ae1fa 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CryptoConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/CryptoConfiguration.java @@ -15,8 +15,10 @@ package com.amazonaws.services.s3.model; +import com.amazonaws.regions.Regions; import com.amazonaws.services.s3.internal.crypto.CryptoRuntime; +import java.io.Serializable; import java.security.Provider; /** @@ -26,11 +28,26 @@ * encryption information. You can also specify your own crypto provider to be * used during encryption and decryption. */ -public class CryptoConfiguration { +public class CryptoConfiguration implements Cloneable,Serializable { + + private static final long serialVersionUID = -8646831898339939580L; private CryptoMode cryptoMode; private CryptoStorageMode storageMode; private Provider cryptoProvider; + /** + * True to ignore instruction file that cannot be found during a GET + * operation; false otherwise. Default is true. This property is ignored if + * the crypto mode is {@link CryptoMode#StrictAuthenticatedEncryption} where + * missing instruction file would always cause security exception. + */ + private boolean ignoreMissingInstructionFile = true; + /** + * Used to specify the KMS region for the AWS KMS client when such client + * is internally instantiated instead of externally passed in by users; or + * null if no explicit KMS region is specified. + */ + private transient com.amazonaws.regions.Region awskmsRegion; /** * Creates a new CryptoConfiguration object with default storage mode and @@ -140,22 +157,60 @@ public CryptoMode getCryptoMode() { * cannot be found or the necessary cryptographic operations are * not supported for the specified crypto mode. */ - public void setCryptoMode(CryptoMode cryptoMode) { - check(cryptoMode); + public void setCryptoMode(CryptoMode cryptoMode) + throws UnsupportedOperationException { this.cryptoMode = cryptoMode; + check(cryptoMode); } /** * Fluent API to set the crypto mode; applicable only to the S3 encryption * client. * - * @throws UnsupportedOperationException if the necessary security provider - * cannot be found or the necessary cryptographic operations are - * not supported for the specified crypto mode. + * @throws UnsupportedOperationException + * if the necessary security provider cannot be found or the + * necessary cryptographic operations are not supported for the + * specified crypto mode.Note the crypto mode can and will still + * (intentionally) be set in such case, and it's up to the + * caller to decide what to do about it. */ - public CryptoConfiguration withCryptoMode(CryptoMode cryptoMode) { - check(cryptoMode); + public CryptoConfiguration withCryptoMode(CryptoMode cryptoMode) + throws UnsupportedOperationException { this.cryptoMode = cryptoMode; + check(cryptoMode); + return this; + } + + /** + * Returns true to ignore instruction file that cannot be found during a GET + * operation; false otherwise. Default is true. This property is ignored if + * the crypto mode is {@link CryptoMode#StrictAuthenticatedEncryption} where + * missing instruction file would always cause security exception. + */ + public boolean isIgnoreMissingInstructionFile() { + return ignoreMissingInstructionFile; + } + + /** + * @param ignoreMissingInstructionFile + * true to ignore instruction file that cannot be found during a + * GET operation; false otherwise. Default is true. This property + * is ignored if the crypto mode is + * {@link CryptoMode#StrictAuthenticatedEncryption} where missing + * instruction file would always cause security exception. + */ + public void setIgnoreMissingInstructionFile( + boolean ignoreMissingInstructionFile) { + this.ignoreMissingInstructionFile = ignoreMissingInstructionFile; + } + + /** + * Fluent API to set the property to ignore instruction file that cannot be + * found during a GET operation. + */ + public CryptoConfiguration withIgnoreMissingInstructionFile( + boolean ignoreMissingInstructionFile) { + this.ignoreMissingInstructionFile = ignoreMissingInstructionFile; return this; } @@ -176,9 +231,172 @@ private void check(CryptoMode cryptoMode) { "The Bouncy castle library jar is required on the classpath to enable authenticated encryption"); } } - if (!CryptoRuntime.isAesGcmAvailable()) + if (!CryptoRuntime.isAesGcmAvailable()) { throw new UnsupportedOperationException( "More recent version of the Bouncy castle library is required to enable authenticated encryption"); + } + } + } + + public boolean isReadOnly() { return false; } + + /** + * Used to provide a read-only copy of the configuration. + */ + private static final class ReadOnly extends CryptoConfiguration { + private ReadOnly() {} + @Override public boolean isReadOnly() { return true; } + @Override public void setStorageMode(CryptoStorageMode storageMode) { + throw new UnsupportedOperationException(); + } + @Override public CryptoConfiguration withStorageMode(CryptoStorageMode storageMode) { + throw new UnsupportedOperationException(); + } + @Override public void setCryptoProvider(Provider cryptoProvider) { + throw new UnsupportedOperationException(); + } + @Override public CryptoConfiguration withCryptoProvider(Provider cryptoProvider) { + throw new UnsupportedOperationException(); + } + @Override public void setCryptoMode(CryptoMode cryptoMode) { + throw new UnsupportedOperationException(); + } + @Override public CryptoConfiguration withCryptoMode(CryptoMode cryptoMode) { + throw new UnsupportedOperationException(); + } + @Override public void setIgnoreMissingInstructionFile( + boolean ignoreMissingInstructionFile) { + throw new UnsupportedOperationException(); + } + @Override public CryptoConfiguration withIgnoreMissingInstructionFile( + boolean ignoreMissingInstructionFile) { + throw new UnsupportedOperationException(); + } + @Override public void setKmsRegion(Regions kmsRegion) { + throw new UnsupportedOperationException(); + } + @Override public CryptoConfiguration withKmsRegion(Regions kmsRegion) { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns a read-only copy of this configuration. + */ + public CryptoConfiguration readOnly() { + if (isReadOnly()) { + return this; + } + return copyTo(new CryptoConfiguration.ReadOnly()); + } + + @Override + public CryptoConfiguration clone() { + return copyTo(new CryptoConfiguration()); + } + + private CryptoConfiguration copyTo(CryptoConfiguration that) { + that.cryptoMode = this.cryptoMode; + that.storageMode = this.storageMode; + that.cryptoProvider = this.cryptoProvider; + that.ignoreMissingInstructionFile = this.ignoreMissingInstructionFile; + that.awskmsRegion = this.awskmsRegion; + return that; + } + + /** + * Returns the the KMS region explicitly specified for the AWS KMS client + * when such client is internally instantiated; or null if no explicit KMS + * region is specified. This KMS region parameter is ignored when the AWS + * KMS client of the S3 encryption client is explicitly passed in by the + * users, instead of being implicitly created. + * + * @Deprecated This method is not forward compatible. Throws + * IllegalArguementException when a new region is encountered. + * + * @use {@link #getAwsKmsRegion()} instead + */ + @Deprecated + public Regions getKmsRegion() { + if (awskmsRegion == null) { + return null; + } + return Regions.fromName(awskmsRegion.getName()); + } + + /** + * Sets the KMS region for the AWS KMS client when such client is internally + * instantiated instead of externally passed in by users; or null if no + * explicit KMS region is explicitly configured.This KMS region parameter is + * ignored when the AWS KMS client of the S3 encryption client is explicitly + * passed in by the users, instead of being implicitly created. + * + * @Deprecated This method is not forward compatible. Doesn't handle new + * regions. + * + * @use {@link #setAwsKmsRegion(com.amazonaws.regions.Region)} instead + */ + @Deprecated + public void setKmsRegion(Regions kmsRegion) { + if (kmsRegion != null) { + setAwsKmsRegion(com.amazonaws.regions.Region.getRegion(kmsRegion)); + } else { + setAwsKmsRegion(null); } } + + /** + * Fluent API for setting the KMS region for the AWS KMS client when such + * client is internally instantiated instead of externally passed in by + * users; or null if no explicit KMS region is explicitly configured.This + * KMS region parameter is ignored when the AWS KMS client of the S3 + * encryption client is explicitly passed in by the users, instead of being + * implicitly created. + * + * @Deprecated This method is not forward compatible. Doesn't handle new + * regions. + * + * @use {@link #withAwsKmsRegion(com.amazonaws.regions.Region)} AwsKmsRegion} + * instead + */ + @Deprecated + public CryptoConfiguration withKmsRegion(Regions kmsRegion) { + setKmsRegion(kmsRegion); + return this; + } + + /** + * Returns the the KMS region explicitly specified for the AWS KMS client + * when such client is internally instantiated; or null if no explicit KMS + * region is specified. This KMS region parameter is ignored when the AWS + * KMS client of the S3 encryption client is explicitly passed in by the + * users, instead of being implicitly created. + */ + public com.amazonaws.regions.Region getAwsKmsRegion() { + return awskmsRegion; + } + + /** + * Sets the KMS region for the AWS KMS client when such client is internally + * instantiated instead of externally passed in by users; or null if no + * explicit KMS region is explicitly configured.This KMS region parameter is + * ignored when the AWS KMS client of the S3 encryption client is explicitly + * passed in by the users, instead of being implicitly created. + */ + public void setAwsKmsRegion(com.amazonaws.regions.Region awsKmsRegion) { + this.awskmsRegion = awsKmsRegion; + } + + /** + * Fluent API for setting the KMS region for the AWS KMS client when such + * client is internally instantiated instead of externally passed in by + * users; or null if no explicit KMS region is explicitly configured.This + * KMS region parameter is ignored when the AWS KMS client of the S3 + * encryption client is explicitly passed in by the users, instead of being + * implicitly created. + */ + public CryptoConfiguration withAwsKmsRegion(com.amazonaws.regions.Region awsKmsRegion) { + this.awskmsRegion = awsKmsRegion; + return this; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketAnalyticsConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketAnalyticsConfigurationRequest.java new file mode 100644 index 0000000000..54ce5cd82c --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketAnalyticsConfigurationRequest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object to delete an analytics configuration. + */ +public class DeleteBucketAnalyticsConfigurationRequest extends AmazonWebServiceRequest implements Serializable { + + private String bucketName; + private String id; + + public DeleteBucketAnalyticsConfigurationRequest() { } + + public DeleteBucketAnalyticsConfigurationRequest(String bucketName, String id) { + this.bucketName = bucketName; + this.id = id; + } + + /** + * Returns the name of the bucket from which an analytics configuration is deleted. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket from which an analytics configuration is deleted. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket from which an analytics configuration is deleted + * and returns this object for method chaining. + */ + public DeleteBucketAnalyticsConfigurationRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Returns the identifier used to represent an analytics configuration. + */ + public String getId() { + return id; + } + + /** + * Sets the identifier used to represent an analytics configuration. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the identifier used to represent an analytics configuration + * and returns this object for method chaining. + */ + public DeleteBucketAnalyticsConfigurationRequest withId(String id) { + setId(id); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketAnalyticsConfigurationResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketAnalyticsConfigurationResult.java new file mode 100644 index 0000000000..4a50f4140b --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketAnalyticsConfigurationResult.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#deleteBucketAnalyticsConfiguration(DeleteBucketAnalyticsConfigurationRequest)} + * operation. + */ +public class DeleteBucketAnalyticsConfigurationResult implements Serializable { +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketInventoryConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketInventoryConfigurationRequest.java new file mode 100644 index 0000000000..d493ff8949 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketInventoryConfigurationRequest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object to delete an inventory configuration. + */ +public class DeleteBucketInventoryConfigurationRequest extends AmazonWebServiceRequest implements Serializable { + + private String bucketName; + + private String id; + + public DeleteBucketInventoryConfigurationRequest() { + } + + public DeleteBucketInventoryConfigurationRequest(String bucketName, String id) { + this.bucketName = bucketName; + this.id = id; + } + + /** + * Returns the name of the bucket containing the inventory configuration to delete. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket containing the inventory configuration to delete. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket containing the inventory configuration to delete + * and return {@link DeleteBucketInventoryConfigurationRequest} object for + * method chaining. + */ + public DeleteBucketInventoryConfigurationRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Returns the ID used to identify the inventory configuration. + */ + public String getId() { + return id; + } + + /** + * Sets the ID used to identify the inventory configuration. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the ID used to identify the inventory configuration and + * returns {@link DeleteBucketInventoryConfigurationRequest} object + * for method chaining. + */ + public DeleteBucketInventoryConfigurationRequest withId(String id) { + setId(id); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketInventoryConfigurationResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketInventoryConfigurationResult.java new file mode 100644 index 0000000000..0ff85735ab --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketInventoryConfigurationResult.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#deleteBucketInventoryConfiguration(DeleteBucketInventoryConfigurationRequest)} + * operation. + */ +public class DeleteBucketInventoryConfigurationResult implements Serializable { +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketMetricsConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketMetricsConfigurationRequest.java new file mode 100644 index 0000000000..6292a3735f --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketMetricsConfigurationRequest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object to delete a metrics configuration from a bucket. + */ +public class DeleteBucketMetricsConfigurationRequest extends AmazonWebServiceRequest implements Serializable { + private String bucketName; + private String id; + + public DeleteBucketMetricsConfigurationRequest() { + } + + public DeleteBucketMetricsConfigurationRequest(String bucketName, String id) { + this.bucketName = bucketName; + this.id = id; + } + + /** + * Returns the name of the bucket from which the metrics configuration is deleted. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket from which the metrics configuration is deleted. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket from which the metrics configuration is deleted + * and returns this object for method chaining. + */ + public DeleteBucketMetricsConfigurationRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Returns the ID used to identify the metrics configuration. + */ + public String getId() { + return id; + } + + /** + * Sets the ID used to identify the metrics configuration. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the ID used to identify the metrics configuration + * and returns {@link DeleteBucketMetricsConfigurationRequest} object for method chaining. + */ + public DeleteBucketMetricsConfigurationRequest withId(String id) { + setId(id); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketMetricsConfigurationResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketMetricsConfigurationResult.java new file mode 100644 index 0000000000..e22f980566 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketMetricsConfigurationResult.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#deleteBucketMetricsConfiguration(DeleteBucketMetricsConfigurationRequest)} + * operation. + */ +public class DeleteBucketMetricsConfigurationResult implements Serializable { +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketPolicyRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketPolicyRequest.java index 4db745342a..abc11e58e6 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketPolicyRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketPolicyRequest.java @@ -18,6 +18,8 @@ import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** * Deletes the policy associated with the specified bucket. Only the owner of * the bucket can delete the bucket policy.

@@ -33,7 +35,7 @@ * * @see AmazonS3#deleteBucketPolicy(DeleteBucketPolicyRequest) */ -public class DeleteBucketPolicyRequest extends AmazonWebServiceRequest { +public class DeleteBucketPolicyRequest extends AmazonWebServiceRequest implements Serializable { /** The name of the Amazon S3 bucket whose policy is being deleted. */ private String bucketName; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketRequest.java index 280ce49e0a..7a176d8208 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketRequest.java @@ -17,6 +17,8 @@ import com.amazonaws.AmazonWebServiceRequest; +import java.io.Serializable; + /** *

* Provides options for deleting a specified bucket. Amazon S3 buckets can only @@ -27,8 +29,8 @@ * returns a success message, not an error message. *

*/ -public class DeleteBucketRequest extends AmazonWebServiceRequest - implements S3AccelerateUnsupported { +public class DeleteBucketRequest extends AmazonWebServiceRequest implements + Serializable, S3AccelerateUnsupported { /** * The name of the Amazon S3 bucket to delete. diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketTaggingConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketTaggingConfigurationRequest.java index e50900551b..6b537698cd 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketTaggingConfigurationRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketTaggingConfigurationRequest.java @@ -17,12 +17,14 @@ import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** * Request object for the parameters to delete a bucket's tagging configuration. * * @see AmazonS3#deleteBucketTaggingConfiguration(DeleteBucketTaggingConfigurationRequest) */ -public class DeleteBucketTaggingConfigurationRequest extends GenericBucketRequest { +public class DeleteBucketTaggingConfigurationRequest extends GenericBucketRequest implements Serializable { /** * Creates a new request object, ready to be executed to delete the tagging diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketWebsiteConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketWebsiteConfigurationRequest.java index 173ba796bc..af6d258afd 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketWebsiteConfigurationRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteBucketWebsiteConfigurationRequest.java @@ -17,6 +17,8 @@ import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** * Request object for the parameters to delete a bucket's website configuration. *

@@ -43,7 +45,7 @@ * * @see AmazonS3#deleteBucketWebsiteConfiguration(DeleteBucketWebsiteConfigurationRequest) */ -public class DeleteBucketWebsiteConfigurationRequest extends GenericBucketRequest { +public class DeleteBucketWebsiteConfigurationRequest extends GenericBucketRequest implements Serializable { /** * Creates a new request object, ready to be executed to delete the website diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectRequest.java index 31a34aa2c6..6e5412dcd3 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectRequest.java @@ -16,22 +16,25 @@ package com.amazonaws.services.s3.model; import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.services.s3.AmazonS3Client; + +import java.io.Serializable; /** *

- * Provides options for deleting a specified object in a specified bucket. Once - * deleted, the object can only be restored if versioning was enabled when the - * object was deleted. + * Provides options for deleting a specified object in a specified bucket. + * Once deleted, the object + * can only be restored if versioning was enabled when the object was deleted. *

*

- * Note: If deleting an object that does not exist, Amazon S3 returns a success - * message, not an error message. + * Note: If deleting an object that does not exist, Amazon S3 returns + * a success message, not an error message. *

* - * @see deleteObject(String bucketName, String key) - * @see deleteObject(DeleteObjectRequest deleteObjectRequest) + * @see AmazonS3Client#deleteObject(String, String) + * @see AmazonS3Client#deleteObject(DeleteObjectRequest) */ -public class DeleteObjectRequest extends AmazonWebServiceRequest { +public class DeleteObjectRequest extends AmazonWebServiceRequest implements Serializable { /** * The name of the Amazon S3 bucket containing the object to delete. @@ -44,22 +47,36 @@ public class DeleteObjectRequest extends AmazonWebServiceRequest { private String key; /** - * Constructs a new {@link DeleteObjectRequest}, specifying the object's - * bucket name and key. + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; + + + /** + * Constructs a new + * {@link DeleteObjectRequest}, + * specifying the object's bucket name and key. * - * @param bucketName The name of the Amazon S3 bucket containing the object - * to delete. - * @param key The key of the object to delete. + * @param bucketName + * The name of the Amazon S3 bucket containing the object to + * delete. + * @param key + * The key of the object to delete. */ public DeleteObjectRequest(String bucketName, String key) { setBucketName(bucketName); setKey(key); } + /** - * Gets the name of the Amazon S3 bucket containing the object to delete. + * Gets the name of the Amazon S3 bucket containing the object to + * delete. + * + * @return The name of the Amazon S3 bucket containing the object to + * delete. * - * @return The name of the Amazon S3 bucket containing the object to delete. * @see DeleteObjectRequest#setBucketName(String) */ public String getBucketName() { @@ -69,8 +86,9 @@ public String getBucketName() { /** * Sets the name of the Amazon S3 bucket containing the object to delete. * - * @param bucketName The name of the Amazon S3 bucket containing the object - * to delete. + * @param bucketName + * The name of the Amazon S3 bucket containing the object to + * delete. * @see DeleteObjectRequest#getBucketName() */ public void setBucketName(String bucketName) { @@ -78,14 +96,17 @@ public void setBucketName(String bucketName) { } /** - * Sets the name of the Amazon S3 bucket containing the object to delete and - * returns this object, enabling additional method calls to be chained + * Sets the name of the Amazon S3 bucket containing the object to delete + * and returns this object, enabling additional method calls to be chained * together. * - * @param bucketName The name of the Amazon S3 bucket containing the object - * to delete. - * @return The updated {@link DeleteObjectRequest} object, enabling - * additional method calls to be chained together. + * @param bucketName + * The name of the Amazon S3 bucket containing the object to + * delete. + * + * @return The updated {@link DeleteObjectRequest} + * object, enabling additional method + * calls to be chained together. */ public DeleteObjectRequest withBucketName(String bucketName) { setBucketName(bucketName); @@ -96,6 +117,7 @@ public DeleteObjectRequest withBucketName(String bucketName) { * Gets the key of the object to delete. * * @return The key of the object to delete. + * * @see DeleteObjectRequest#setKey(String) */ public String getKey() { @@ -105,7 +127,9 @@ public String getKey() { /** * Sets the key of the object to delete. * - * @param key The key of the object to delete. + * @param key + * The key of the object to delete. + * * @see DeleteObjectRequest#getKey() */ public void setKey(String key) { @@ -116,13 +140,80 @@ public void setKey(String key) { * Sets the key of the object to delete and returns this object, enabling * additional method calls to be chained together. * - * @param key The key of the object to delete. - * @return The updated {@link DeleteObjectRequest} object, enabling - * additional method calls to chained together. + * @param key + * The key of the object to delete. + * + * @return The updated {@link DeleteObjectRequest} object, enabling additional method + * calls to chained together. */ public DeleteObjectRequest withKey(String key) { setKey(key); return this; } + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated DeleteObjectRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated DeleteObjectRequest object. + */ + public DeleteObjectRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } + } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectTaggingRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectTaggingRequest.java new file mode 100644 index 0000000000..33061e912b --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectTaggingRequest.java @@ -0,0 +1,130 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object for the parameters to delete the tags for an object. + */ +public class DeleteObjectTaggingRequest extends AmazonWebServiceRequest implements Serializable { + private String bucketName; + private String key; + private String versionId; + + /** + * Constructs an instance of this object. + * + * @param bucketName + * The bucket name. + * @param key + * The object key. + */ + public DeleteObjectTaggingRequest(String bucketName, String key) { + this.bucketName = bucketName; + this.key = key; + } + + /** + * @return The bucket name. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Set the bucket name. + * + * @param bucketName + * The bucket name. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Set the bucket name. + * + * @param bucketName + * The bucket name. + * + * @return This object for chaining. + */ + public DeleteObjectTaggingRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * @return The object key. + */ + public String getKey() { + return key; + } + + /** + * Set the object key. + * + * @param key + * The object key. + */ + public void setKey(String key) { + this.key = key; + } + + /** + * Set the object key. + * + * @param key + * The object key. + * + * @return This object for chaining. + */ + public DeleteObjectTaggingRequest withKey(String key) { + setKey(key); + return this; + } + + /** + * @return The version of the object whose tags are to be deleted. + */ + public String getVersionId() { + return versionId; + } + + /** + * Set version of the object whose tages are to be deleted. + * + * @param versionId The object version. + */ + public void setVersionId(String versionId) { + this.versionId = versionId; + } + + /** + * Set version of the object whose tages are to be deleted. + * + * @param versionId + * The object version. + * + * @return This object for chaining. + */ + public DeleteObjectTaggingRequest withVersionId(String versionId) { + setVersionId(versionId); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectTaggingResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectTaggingResult.java new file mode 100644 index 0000000000..5e0dfbd70d --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectTaggingResult.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +/** + * Contains all the information returned from performing a {@link + * DeleteObjectTaggingRequest}. + */ +public class DeleteObjectTaggingResult { + private String versionId; + + /** + * @return The version of the object whose tags were deleted. + */ + public String getVersionId() { + return versionId; + } + + /** + * Set the version of the object whose tags were deleted. + * + * @param versionId + * The object version. + */ + public void setVersionId(String versionId) { + this.versionId = versionId; + } + + /** + * Set the version of the object whose tags were deleted. + * + * @param versionId + * The object version. + * + * @return This object for chaining. + */ + public DeleteObjectTaggingResult withVersionId(String versionId) { + setVersionId(versionId); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectsRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectsRequest.java index ad78526df1..489b522b29 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectsRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectsRequest.java @@ -18,6 +18,7 @@ import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -58,6 +59,12 @@ public class DeleteObjectsRequest extends AmazonWebServiceRequest { */ private final List keys = new ArrayList(); + /** + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; + /** * Constructs a new {@link DeleteObjectsRequest}, specifying the objects' * bucket name. @@ -238,18 +245,83 @@ public List getKeys() { * @see DeleteObjectsRequest#withKeys(List) */ public DeleteObjectsRequest withKeys(String... keys) { - List keyVersions = new ArrayList(keys.length); - for (String key : keys) { + final List keyVersions = new ArrayList(keys.length); + for (final String key : keys) { keyVersions.add(new KeyVersion(key)); } setKeys(keyVersions); return this; } + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated DeleteObjectsRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated DeleteObjectsRequest object. + */ + public DeleteObjectsRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } + /** * A key to delete, with an optional version attribute. */ - public static class KeyVersion { + public static class KeyVersion implements Serializable { private final String key; private final String version; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectsResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectsResult.java index ae1a663eb8..9f7187ee3a 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectsResult.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteObjectsResult.java @@ -16,7 +16,9 @@ package com.amazonaws.services.s3.model; import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -27,12 +29,23 @@ * * @see AmazonS3#deleteObjects(DeleteObjectsRequest) */ -public class DeleteObjectsResult { +public class DeleteObjectsResult implements Serializable, S3RequesterChargedResult { private final List deletedObjects = new ArrayList(); + /** + * Indicate if the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterCharged; + public DeleteObjectsResult(List deletedObjects) { + this(deletedObjects, false); + } + + public DeleteObjectsResult(List deletedObjects, boolean isRequesterCharged) { this.deletedObjects.addAll(deletedObjects); + this.setRequesterCharged(isRequesterCharged); } /** @@ -44,10 +57,20 @@ public List getDeletedObjects() { return deletedObjects; } + @Override + public boolean isRequesterCharged() { + return isRequesterCharged; + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + this.isRequesterCharged = isRequesterCharged; + } + /** * A successfully deleted object. */ - static public class DeletedObject { + static public class DeletedObject implements Serializable { private String key; private String versionId; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteVersionRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteVersionRequest.java index af5b68a344..69dbd74ff6 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteVersionRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/DeleteVersionRequest.java @@ -18,6 +18,8 @@ import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** *

* Provides options for deleting a specific version of an object in the @@ -42,7 +44,7 @@ * returns a success message, not an error message. *

*/ -public class DeleteVersionRequest extends AmazonWebServiceRequest { +public class DeleteVersionRequest extends AmazonWebServiceRequest implements Serializable { /** * The name of the Amazon S3 bucket containing the version to delete. diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedGetObjectRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedGetObjectRequest.java new file mode 100644 index 0000000000..7051e8b7e7 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedGetObjectRequest.java @@ -0,0 +1,207 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; +import static com.amazonaws.services.s3.model.ExtraMaterialsDescription.NONE; + +import java.util.Map; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3EncryptionClient; +import com.amazonaws.services.s3.KeyWrapException; + +/** + *

+ * An extension of {@link GetObjectRequest} to allow additional encryption + * material description to be specified on a per-request basis. In particular, + * {@link EncryptedGetObjectRequest} is only recognized by + * {@link AmazonS3EncryptionClient}. + *

+ *

+ * If {@link EncryptedGetObjectRequest} is used against the non-encrypting + * {@link AmazonS3Client}, the additional attributes will be ignored. + *

+ *

+ * The additional material description must not conflict with the existing one + * saved in S3 or else will cause the get request to fail fast later on. + */ +public class EncryptedGetObjectRequest extends GetObjectRequest implements Serializable { + /** + * Request specific additional material description; never null. + */ + private ExtraMaterialsDescription supplemental = NONE; + /** + * Used to retrieve the S3 encrypted object via instruction file with + * an explicit suffix. Applicable only if specified (which means non-null + * and non-blank.) + */ + private String instructionFileSuffix; + /** + * True if the retrieval of the encrypted object expects the CEK to have + * been key-wrapped; Default is false. + *

+ * Note, however, that if {@link CryptoMode#StrictAuthenticatedEncryption} + * is in use, key wrapping is always expected for the CEK regardless. + */ + private boolean keyWrapExpected; + + public EncryptedGetObjectRequest(String bucketName, String key) { + this(bucketName, key, null); + } + + public EncryptedGetObjectRequest(String bucketName, String key, + String versionId) { + super(bucketName, key, versionId); + setKey(key); + setVersionId(versionId); + } + + public EncryptedGetObjectRequest(S3ObjectId s3ObjectId) { + super(s3ObjectId); + } + + public EncryptedGetObjectRequest(String bucketName, String key, + boolean isRequesterPays) { + super(bucketName, key, isRequesterPays); + } + + /** + * Returns the supplemental material description to be used for retrieving + * the encryption materials. + * + * @return the supplemental material description; never null. + */ + public ExtraMaterialsDescription getExtraMaterialDescription() { + return supplemental; + } + + /** + * Sets the supplemental materials description for the encryption materials + * to be used with the current request. + * + * @param supplemental + * the materialsDescription to set; must not conflict with the + * existing one saved in S3 or else will cause the get request to + * fail fast later on + */ + public void setExtraMaterialDescription( + ExtraMaterialsDescription supplemental) { + this.supplemental = supplemental == null + ? NONE : supplemental; + } + + /** + * Sets the supplemental materials description for the encryption materials + * to be used with the current request. + * + * @param supplemental + * the materialsDescription to set; must not conflict with the + * existing one saved in S3 or else will cause the get request to + * fail fast later on + */ + public EncryptedGetObjectRequest withExtraMaterialsDescription( + ExtraMaterialsDescription supplemental) { + setExtraMaterialDescription(supplemental); + return this; + } + + /** + * Fluent API to set the supplemental materials description for the + * encryption materials to be used with the current request. + * + * @param supplemental + * the materialsDescription to set; must not conflict with the + * existing one saved in S3 or else will cause the get request to + * fail fast later on + */ + public EncryptedGetObjectRequest withExtraMaterialsDescription( + Map supplemental) { + setExtraMaterialDescription(supplemental == null ? null + : new ExtraMaterialsDescription(supplemental)); + return this; + } + + public String getInstructionFileSuffix() { + return instructionFileSuffix; + } + + /** + * Explicitly sets the suffix of an instruction file to be used to retrieve + * the S3 encrypted object. Typically this is for more advanced use cases + * where multiple crypto instruction files have been created for the same S3 + * object. Each instruction file contains the same CEK encrypted under a + * different KEK, the IV, and other meta information (aka material + * description). + * + * @param instructionFileSuffix + * suffix of the instruction file to be used. + * + * @see AmazonS3EncryptionClient#putInstructionFile(PutInstructionFileRequest) + */ + public void setInstructionFileSuffix(String instructionFileSuffix) { + this.instructionFileSuffix = instructionFileSuffix; + } + + /** + * Fluent API to explicitly sets the suffix of an instruction file to be + * used to retrieve the S3 encrypted object. Typically this is for more + * advanced use cases where multiple crypto instruction files have been + * created for the same S3 object. Each instruction file contains the same + * CEK encrypted under a different KEK, the IV, and other meta information + * (aka material description). + * + * @param instructionFileSuffix + * suffix of the instruction file to be used. + * + * @see AmazonS3EncryptionClient#putInstructionFile(PutInstructionFileRequest) + */ + public EncryptedGetObjectRequest withInstructionFileSuffix( + String instructionFileSuffix) { + this.instructionFileSuffix = instructionFileSuffix; + return this; + } + + /** + * Returns true if key wrapping is expected; false otherwise. Note, however, + * that if {@link CryptoMode#StrictAuthenticatedEncryption} or KMS is in + * use, key wrapping is always expected for the CEK regardless. + */ + public boolean isKeyWrapExpected() { + return keyWrapExpected; + } + + /** + * @param keyWrapExpected + * true if key wrapping is expected for the CEK; false otherwse. + * Note, however, that if + * {@link CryptoMode#StrictAuthenticatedEncryption} or KMS is in + * use, key wrapping is always expected for the CEK regardless. + *

+ * If keyWrapExpected is set to true but the CEK is found to be + * not key-wrapped, it would cause a {@link KeyWrapException} to + * be thrown. + */ + public void setKeyWrapExpected(boolean keyWrapExpected) { + this.keyWrapExpected = keyWrapExpected; + } + + /** + * Fluent API for {@link #setKeyWrapExpected(boolean)}. + */ + public EncryptedGetObjectRequest withKeyWrapExpected(boolean keyWrapExpected) { + this.keyWrapExpected = keyWrapExpected; + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedInitiateMultipartUploadRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedInitiateMultipartUploadRequest.java index 866b8bbcac..56f2a0410d 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedInitiateMultipartUploadRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedInitiateMultipartUploadRequest.java @@ -15,31 +15,43 @@ package com.amazonaws.services.s3.model; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3EncryptionClient; + +import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** - *

* This class is an extension of {@link InitiateMultipartUploadRequest} to allow - * additional encryption material description to be specified on a per-request - * basis. In particular, {@link EncryptedInitiateMultipartUploadRequest} is only - * recognized by {@link AmazonS3EncryptionJavaClient}. + * additional crypto related attributes to be specified. + *

+ * In particular, this includes the options to + *

    + *
  • specify encryption material description on a per-request basis;
  • + *
  • specify whether a new set of encryption material is to be created for the + * upload or not;
  • + *
+ * In particular, {@link EncryptedInitiateMultipartUploadRequest} is only + * recognized by {@link AmazonS3EncryptionClient}. *

*

* If {@link EncryptedInitiateMultipartUploadRequest} is used against the - * non-encrypting {@link AmazonS3JavaClient}, these additional attributes will - * be ignored. - *

+ * non-encrypting {@link AmazonS3Client}, these additional attributes will be + * ignored. */ - -public class EncryptedInitiateMultipartUploadRequest extends InitiateMultipartUploadRequest - implements MaterialsDescriptionProvider { - +public class EncryptedInitiateMultipartUploadRequest extends + InitiateMultipartUploadRequest implements MaterialsDescriptionProvider, Serializable { /** * description of encryption materials to be used with this request. */ private Map materialsDescription; + /** + * True if a new set of encryption material is to be created; false + * otherwise. Default is true. + */ + private boolean createEncryptionMaterial = true; public EncryptedInitiateMultipartUploadRequest(String bucketName, String key) { super(bucketName, key); @@ -78,4 +90,32 @@ public EncryptedInitiateMultipartUploadRequest withMaterialsDescription( setMaterialsDescription(materialsDescription); return this; } + + /** + * Returns true if a new set of encryption material is to be created; false + * otherwise. Default is true. + */ + public boolean isCreateEncryptionMaterial() { + return createEncryptionMaterial; + } + + /** + * @param createEncryptionMaterial + * true if a new set of encryption material is to be created; + * false otherwise. + */ + public void setCreateEncryptionMaterial(boolean createEncryptionMaterial) { + this.createEncryptionMaterial = createEncryptionMaterial; + } + + /** + * @param createEncryptionMaterial + * true if a new set of encryption material is to be created; + * false otherwise. + */ + public EncryptedInitiateMultipartUploadRequest withCreateEncryptionMaterial( + boolean createEncryptionMaterial) { + this.createEncryptionMaterial = createEncryptionMaterial; + return this; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedPutObjectRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedPutObjectRequest.java index 39e67cb861..70f714aa57 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedPutObjectRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptedPutObjectRequest.java @@ -17,6 +17,7 @@ import java.io.File; import java.io.InputStream; +import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -34,8 +35,7 @@ *

*/ public class EncryptedPutObjectRequest extends PutObjectRequest implements - MaterialsDescriptionProvider { - + MaterialsDescriptionProvider, Serializable { /** * description of encryption materials to be used with this request. */ @@ -83,4 +83,20 @@ public EncryptedPutObjectRequest withMaterialsDescription( return this; } + /** + * Returns a clone (as deep as possible) of this request object. + */ + @Override + public EncryptedPutObjectRequest clone() { + final EncryptedPutObjectRequest cloned = + new EncryptedPutObjectRequest( + getBucketName(), getKey(), getFile()); + super.copyPutObjectBaseTo(cloned); + final Map materialsDescription = getMaterialsDescription(); + cloned.withMaterialsDescription(materialsDescription == null + ? null + : new HashMap(materialsDescription)); + return cloned; + } + } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptionMaterials.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptionMaterials.java index 66eb5b9dc1..269974436f 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptionMaterials.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptionMaterials.java @@ -15,6 +15,7 @@ package com.amazonaws.services.s3.model; +import java.io.Serializable; import java.security.KeyPair; import java.util.HashMap; import java.util.Map; @@ -26,16 +27,16 @@ * materials may be either an asymmetric key pair or a symmetric key but not * both. */ -public class EncryptionMaterials { +public class EncryptionMaterials implements Serializable { private final KeyPair keyPair; private final SecretKey symmetricKey; + private final Map desc = new HashMap(); /** - * Constructs a new EncryptionMaterials object, storing an asymmetric key - * pair. + * Constructs a new EncryptionMaterials object, storing an asymmetric key pair. * - * @param keyPair The asymmetric key pair to be stored in this - * EncryptionMaterials object. + * @param keyPair + * The asymmetric key pair to be stored in this EncryptionMaterials object. */ public EncryptionMaterials(KeyPair keyPair) { this(keyPair, null); @@ -44,17 +45,17 @@ public EncryptionMaterials(KeyPair keyPair) { /** * Constructs a new EncryptionMaterials object, storing a symmetric key. * - * @param symmetricKey The symmetric key to be stored in this - * EncryptionMaterials object. + * @param symmetricKey + * The symmetric key to be stored in this EncryptionMaterials object. */ public EncryptionMaterials(SecretKey symmetricKey) { this(null, symmetricKey); } /** - * Base constructor for the EncryptionMaterials object. This is not publicly - * visible since it should not be possible to create an EncryptionMaterials - * object that contains both an asymmetric key pair and a symmetric key. + * Base constructor for the EncryptionMaterials object. This is not publicly visible since + * it should not be possible to create an EncryptionMaterials object that contains both an + * asymmetric key pair and a symmetric key. */ protected EncryptionMaterials(KeyPair keyPair, SecretKey symmetricKey) { this.keyPair = keyPair; @@ -65,6 +66,7 @@ protected EncryptionMaterials(KeyPair keyPair, SecretKey symmetricKey) { * Returns the key pair stored in this EncryptionMaterials object. * * @return the key pair stored in this EncryptionMaterials object. + * */ public KeyPair getKeyPair() { return this.keyPair; @@ -80,22 +82,55 @@ public SecretKey getSymmetricKey() { } /** - * Returns an empty map since the EncryptionMaterials base class does not - * have extra materials information. Subclasses may override this method. - * - * @return an empty map + * Returns a snapshot of the current material description; never null. */ public Map getMaterialsDescription() { - return new HashMap(); + return new HashMap(desc); } /** - * Returns null since the EncryptionMaterials base class does not have a - * materials accessor. Subclasses may override this method. + * Returns null since the EncryptionMaterials base class does not have a materials accessor. + * Subclasses may override this method. * * @return null */ public EncryptionMaterialsAccessor getAccessor() { return null; } + + /** + * Fluent API to add material description. + */ + public EncryptionMaterials addDescription(String name, String value) { + desc.put(name, value); + return this; + } + + /** + * Fluent API to add all the given material descriptions. + */ + public EncryptionMaterials addDescriptions(Map descriptions) { + desc.putAll(descriptions); + return this; + } + + /** + * Returns true if this is a KMS material description; false otherwise. + * + * @return false by default + */ + public boolean isKMSEnabled() { + return false; + } + + /** + * @throws UnsupportedOperationException by default + */ + public String getCustomerMasterKeyId() { + throw new UnsupportedOperationException(); + } + + protected String getDescription(String name) { + return desc.get(name); + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptionMaterialsFactory.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptionMaterialsFactory.java new file mode 100644 index 0000000000..2ae848f46f --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/EncryptionMaterialsFactory.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +/** + * Factory for providing the latest encryption materials. + */ +public interface EncryptionMaterialsFactory { + /** + * Returns EncryptionMaterials which the caller can use for encryption. + * Each implementation of EncryptionMaterialsProvider can choose its own + * strategy for loading encryption material. For example, an + * implementation might load encryption material from an existing key + * management system, or load new encryption material when keys are + * rotated. + * + * @return EncryptionMaterials which the caller can use to encrypt or + * decrypt data. + */ + public EncryptionMaterials getEncryptionMaterials(); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ExtraMaterialsDescription.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ExtraMaterialsDescription.java new file mode 100644 index 0000000000..b41f5bebdb --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ExtraMaterialsDescription.java @@ -0,0 +1,129 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Used to provide additional/supplemental material description (typically on a + * per-request basis and for more advanced use-case) to supplement the one + * stored in S3 for crypto material retrieval. + */ +public class ExtraMaterialsDescription implements Serializable { + @SuppressWarnings("unchecked") + public static final ExtraMaterialsDescription NONE = + new ExtraMaterialsDescription(Collections.EMPTY_MAP); + + /** + * Used to resolve conflicts when merging the additional material + * description to the core material description. + */ + public static enum ConflictResolution { + /** Fail fast upon conflicting keys. */ + FAIL_FAST, + /** Take precedence upon conflicting keys. */ + OVERRIDE, + /** To be overridden upon conflicting keys. */ + OVERRIDDEN, + ; + } + /** + * Supplemental material descriptions which are unmodifiable. + */ + private final Map extra; + /** + * Resolution behavior when there are overlapping entries. Defaults to + * {@link ConflictResolution#FAIL_FAST}. + */ + private final ConflictResolution resolve; + + public ExtraMaterialsDescription(Map matdesc) { + this(matdesc, ConflictResolution.FAIL_FAST); + } + + public ExtraMaterialsDescription(Map matdesc, + ConflictResolution resolve) { + if (matdesc == null || resolve == null) + throw new IllegalArgumentException(); + this.extra = Collections.unmodifiableMap( + new HashMap(matdesc)); + this.resolve = resolve; + } + + /** + * Returns the extra material description; never null. + */ + public Map getMaterialDescription() { + return extra; + } + + /** + * Returns the conflict resolution strategy; neve null. + */ + public ConflictResolution getConflictResolution() { + return resolve; + } + + /** + * Combine this supplemental material descriptions with those specified in + * the "core" parameter. This method has no side effect. + * + * @param core + * the core material descriptions to be supplemented; + * assumed to be unmodifiable. + * @return the merged material descriptions; never null. + * The returned map is always unmodifiable, assuming the passed in core + * material descriptions are unmodifiable. + * + * @throws IllegalArgumentException + * if this supplemental material descriptions contains + * conflicting entries + * @throws UnsupportedOperationException + * if the conflict resolution strategy is not supported + */ + public Map mergeInto(Map core) { + if (extra.size() == 0) + return core; // no supplemental descriptions + if (core == null || core.size() == 0) + return extra; // only core descriptions + switch(resolve) { + case FAIL_FAST: { + final int total = core.size() + extra.size(); + Map merged = new HashMap(core); + merged.putAll(extra); + if (total != merged.size()) { + throw new IllegalArgumentException( + "The supplemental material descriptions contains conflicting entries"); + } + return Collections.unmodifiableMap(merged); + } + case OVERRIDDEN: { + Map merged = new HashMap(extra); + merged.putAll(core); + return Collections.unmodifiableMap(merged); + } + case OVERRIDE: { + Map merged = new HashMap(core); + merged.putAll(extra); + return Collections.unmodifiableMap(merged); + } + default: + throw new UnsupportedOperationException(); + } + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Filter.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Filter.java new file mode 100644 index 0000000000..8e916f67d9 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Filter.java @@ -0,0 +1,57 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +/** + * Defines a set of filter criteria that limits the objects that can trigger event notifications + */ +public class Filter implements Serializable { + + private S3KeyFilter s3KeyFilter; + + /** + * Filter criteria that limits the objects that can trigger event notifications based on their + * S3 Key name + * + * @return The {@code S3KeyFilter} object associated with this {@code Filter} + */ + public S3KeyFilter getS3KeyFilter() { + return s3KeyFilter; + } + + /** + * Sets the {@code S3KeyFilter} for this {@code Filter} + * + * @param s3KeyFilter + * New {@code S3KeyFilter} + */ + public void setS3KeyFilter(S3KeyFilter s3KeyFilter) { + this.s3KeyFilter = s3KeyFilter; + } + + /** + * Sets the {@code S3KeyFilter} for this {@code Filter} and returns this object for method + * chaining + * + * @param s3KeyFilter + * New {@code S3KeyFilter} + * @return This object for method chaining + */ + public Filter withS3KeyFilter(S3KeyFilter s3KeyFilter) { + setS3KeyFilter(s3KeyFilter); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/FilterRule.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/FilterRule.java new file mode 100644 index 0000000000..aa1cbb8179 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/FilterRule.java @@ -0,0 +1,90 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +/** + * Model class representing a Filter Rule for a {@link NotificationConfiguration}. + */ +public class FilterRule implements Serializable { + + private String name; + private String value; + + /** + * Returns the name for this {@link FilterRule}. + * + * @return Name of this {@link FilterRule} + */ + public String getName() { + return name; + } + + /** + * Sets the name for this {@link FilterRule}. + * + * @param name + * New name for this {@link FilterRule}. + */ + public void setName(String name) { + if (name == null) { + throw new IllegalArgumentException("FilterRule Name is a required argument"); + } + this.name = name; + } + + /** + * Sets the name for this {@link FilterRule} and returns this object for method chaining. + * + * @param name + * New name for this {@link FilterRule}. + * @return This object for method chaining + */ + public FilterRule withName(String name) { + setName(name); + return this; + } + + /** + * Returns the value for this {@link FilterRule} + * + * @return Value for this {@link FilterRule} + */ + public String getValue() { + return value; + } + + /** + * Sets the value for this {@link FilterRule} + * + * @param value + * New value for this {@link FilterRule} + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Sets the value for this {@link FilterRule} and returns this object for method chaining + * + * @param value + * New value for this {@link FilterRule} + * @return This object for method chaining + */ + public FilterRule withValue(String value) { + setValue(value); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GeneratePresignedUrlRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GeneratePresignedUrlRequest.java index 1b236477a7..40e6ea1654 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GeneratePresignedUrlRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GeneratePresignedUrlRequest.java @@ -19,6 +19,7 @@ import com.amazonaws.HttpMethod; import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -29,19 +30,17 @@ *

*

* Pre-signed URLs allow clients to form a URL for an Amazon S3 resource and - * sign it with the current AWS security credentials. A pre-signed URL may be - * passed around for other users to access the resource without providing them + * sign it with the current AWS security credentials. + * A pre-signed URL may be passed around for other users to access + * the resource without providing them * access to an account's AWS security credentials. *

* * @see AmazonS3#generatePresignedUrl(GeneratePresignedUrlRequest) */ -public class GeneratePresignedUrlRequest extends AmazonWebServiceRequest { - - /** - * The HTTP method (GET, PUT, DELETE, HEAD) to be used in this request and - * when the pre-signed URL is used - */ +public class GeneratePresignedUrlRequest extends AmazonWebServiceRequest + implements SSECustomerKeyProvider, Serializable { + /** The HTTP method (GET, PUT, DELETE, HEAD) to be used in this request and when the pre-signed URL is used */ private HttpMethod method; /** The name of the bucket involved in this request */ @@ -50,16 +49,10 @@ public class GeneratePresignedUrlRequest extends AmazonWebServiceRequest { /** The key of the object involved in this request */ private String key; - /** - * The optional Content-Type header that will be sent when the presigned URL - * is accessed - */ + /** The optional Content-Type header that will be sent when the presigned URL is accessed */ private String contentType; - /** - * The optional Content-MD5 header that will be sent when the presigned URL - * is accessed - */ + /** The optional Content-MD5 header that will be sent when the presigned URL is accessed */ private String contentMd5; /** @@ -69,13 +62,20 @@ public class GeneratePresignedUrlRequest extends AmazonWebServiceRequest { */ private Date expiration; + /** + * True if the request content is set to zero byte instead of null. This is + * necessary to make pre-signed URL generation work for multi-part upload + * initiation using SigV4. Ref: TT0050059365 + */ + private boolean zeroByteContent; + /** * An optional map of additional parameters to include in the pre-signed * URL. Adding additional request parameters enables more advanced * pre-signed URLs, such as accessing Amazon S3's torrent resource for an * object, or for specifying a version ID when accessing an object. */ - private Map requestParameters = new HashMap(); + private final Map requestParameters = new HashMap(); /** * Optional field that overrides headers on the response. @@ -88,14 +88,101 @@ public class GeneratePresignedUrlRequest extends AmazonWebServiceRequest { */ private SSECustomerKey sseCustomerKey; + /** + * Used to specify the server side encryption algorithm. Null means + * no server side encryption is in use. + */ + private String sseAlgorithm; + + /** + * Used to specify the KMS CMS Key ID when KMS server side encryption is in + * use. + */ + private String kmsCmkId; + + /** + * Returns the KMS customer key id used for server side encryption; or null + * if there is none. + */ + public String getKmsCmkId() { + return kmsCmkId; + } + + /** + * Sets the KMS customer key id used for server side encryption. + *

+ * Note S3 does not require HTTP header + * “x-amz-server-side-encryption-aws-kms-key-id” to be always present (a + * default key ID will be used if this header is not present). + *

+ * It is also possible to set the header to “alias/aws/s3” to refer to the + * default KMS CMK ID. + */ + public void setKmsCmkId(String kmsCmkId) { + this.kmsCmkId = kmsCmkId; + } + + /** + * Fluent API for {@link #setKmsCmkId(String)} + */ + public GeneratePresignedUrlRequest withKmsCmkId(String kmsCmkId) { + setKmsCmkId(kmsCmkId); + return this; + } + + /** + * Returns the SSE algorithm used for SSE (with server side key); or null if + * SSE (with server side key) is not in use. + */ + public String getSSEAlgorithm() { + return sseAlgorithm; + } + + /** + * Sets the SSE algorithm for server side encryption. + * + * @param currently supported values: "AES256" or "aws:kms". + */ + public void setSSEAlgorithm(String sseAlgorithm) { + this.sseAlgorithm = sseAlgorithm; + } + + /** + * Fluent API for {@link #setSSEAlgorithm(String)} + */ + public GeneratePresignedUrlRequest withSSEAlgorithm(String sseAlgorithm) { + setSSEAlgorithm(sseAlgorithm); + return this; + + } + + /** + * Sets the SSE algorithm for server side encryption. + * + * @param currently supported values: "AES256" or "aws:kms". + */ + public void setSSEAlgorithm(SSEAlgorithm sseAlgorithm) { + this.sseAlgorithm = sseAlgorithm.getAlgorithm(); + } + + /** + * Fluent API for {@link #setSSEAlgorithm(SSEAlgorithm)} + */ + public GeneratePresignedUrlRequest withSSEAlgorithm(SSEAlgorithm sseAlgorithm) { + setSSEAlgorithm(sseAlgorithm); + return this; + } + /** * Creates a new request for generating a pre-signed URL that can be used as * part of an HTTP GET request to access the Amazon S3 object stored under * the specified key in the specified bucket. * - * @param bucketName The name of the bucket containing the desired Amazon S3 + * @param bucketName + * The name of the bucket containing the desired Amazon S3 * object. - * @param key The key under which the desired Amazon S3 object is stored. + * @param key + * The key under which the desired Amazon S3 object is stored. */ public GeneratePresignedUrlRequest(String bucketName, String key) { this(bucketName, key, HttpMethod.GET); @@ -112,10 +199,12 @@ public GeneratePresignedUrlRequest(String bucketName, String key) { * URL. *

* - * @param bucketName The name of the Amazon S3 bucket involved in the - * operation. - * @param key The key of the Amazon S3 object involved in the operation. - * @param method The HTTP method (GET, PUT, DELETE, HEAD) to be used in the + * @param bucketName + * The name of the Amazon S3 bucket involved in the operation. + * @param key + * The key of the Amazon S3 object involved in the operation. + * @param method + * The HTTP method (GET, PUT, DELETE, HEAD) to be used in the * request when the pre-signed URL is used. */ public GeneratePresignedUrlRequest(String bucketName, String key, HttpMethod method) { @@ -141,7 +230,8 @@ public HttpMethod getMethod() { * The same HTTP method must be used in the request when the * pre-signed URL is used. * - * @param method The HTTP method (GET, PUT, DELETE, HEAD) to be used in this + * @param method + * The HTTP method (GET, PUT, DELETE, HEAD) to be used in this * request. */ public void setMethod(HttpMethod method) { @@ -156,10 +246,12 @@ public void setMethod(HttpMethod method) { * The same HTTP method must be used in the request when the * pre-signed URL is used. * - * @param method The HTTP method (GET, PUT, DELETE, HEAD) to be used in this + * @param method + * The HTTP method (GET, PUT, DELETE, HEAD) to be used in this * request. - * @return The updated request object, so that additional method calls can be - * chained together. + * + * @return The updated request object, so that additional method calls can + * be chained together. */ public GeneratePresignedUrlRequest withMethod(HttpMethod method) { setMethod(method); @@ -178,7 +270,8 @@ public String getBucketName() { /** * Sets the name of the bucket involved in this request. * - * @param bucketName the name of the bucket involved in this request. + * @param bucketName + * the name of the bucket involved in this request. */ public void setBucketName(String bucketName) { this.bucketName = bucketName; @@ -188,9 +281,11 @@ public void setBucketName(String bucketName) { * Sets the name of the bucket involved in this request, and returns this * request object to enable additional method calls to be chained together. * - * @param bucketName the name of the bucket involved in this request. - * @return The updated request object, so that additional method calls can be - * chained together. + * @param bucketName + * the name of the bucket involved in this request. + * + * @return The updated request object, so that additional method calls can + * be chained together. */ public GeneratePresignedUrlRequest withBucketName(String bucketName) { setBucketName(bucketName); @@ -209,7 +304,8 @@ public String getKey() { /** * Sets the key of the object involved in this request. * - * @param key the key of the object involved in this request. + * @param key + * the key of the object involved in this request. */ public void setKey(String key) { this.key = key; @@ -219,9 +315,11 @@ public void setKey(String key) { * Sets the key of the object involved in this request, and returns this * request object to enable additional method calls to be chained together. * - * @param key the key of the object involved in this request. - * @return The updated request object, so that additional method calls can be - * chained together. + * @param key + * the key of the object involved in this request. + * + * @return The updated request object, so that additional method calls can + * be chained together. */ public GeneratePresignedUrlRequest withKey(String key) { setKey(key); @@ -245,8 +343,9 @@ public Date getExpiration() { * longer be accepted by Amazon S3. If not specified, a default value will * be supplied. * - * @param expiration The expiration date at which point the new pre-signed - * URL will no longer be accepted by Amazon S3. + * @param expiration + * The expiration date at which point the new pre-signed URL will + * no longer be accepted by Amazon S3. */ public void setExpiration(Date expiration) { this.expiration = expiration; @@ -259,10 +358,12 @@ public void setExpiration(Date expiration) { *

* If not specified, a default value will be supplied. * - * @param expiration The expiration date at which point the new pre-signed - * URL will no longer be accepted by Amazon S3. - * @return The updated request object, so that additional method calls can be - * chained together. + * @param expiration + * The expiration date at which point the new pre-signed URL will + * no longer be accepted by Amazon S3. + * + * @return The updated request object, so that additional method calls can + * be chained together. */ public GeneratePresignedUrlRequest withExpiration(Date expiration) { setExpiration(expiration); @@ -275,9 +376,11 @@ public GeneratePresignedUrlRequest withExpiration(Date expiration) { * pre-signed URLs, such as accessing Amazon S3's torrent resource for an * object, or for specifying a version ID when accessing an object. * - * @param key The name of the request parameter, as it appears in the URL's + * @param key + * The name of the request parameter, as it appears in the URL's * query string (e.g. versionId). - * @param value The (optional) value of the request parameter being added. + * @param value + * The (optional) value of the request parameter being added. */ public void addRequestParameter(String key, String value) { requestParameters.put(key, value); @@ -306,8 +409,8 @@ public ResponseHeaderOverrides getResponseHeaders() { /** * Sets the headers to be overridden in the service response. * - * @param responseHeaders The headers to be overridden in the service - * response. + * @param responseHeaders + * The headers to be overridden in the service response. */ public void setResponseHeaders(ResponseHeaderOverrides responseHeaders) { this.responseHeaders = responseHeaders; @@ -317,8 +420,10 @@ public void setResponseHeaders(ResponseHeaderOverrides responseHeaders) { * Sets the headers to be overridden in the service response and returns * this object, for method chaining. * - * @param responseHeaders The headers to be overridden in the service - * response. + * @param responseHeaders + * The headers to be overridden in the service response. + * + * * @return This {@link GeneratePresignedUrlRequest} for method chaining. */ public GeneratePresignedUrlRequest withResponseHeaders(ResponseHeaderOverrides responseHeaders) { @@ -327,8 +432,8 @@ public GeneratePresignedUrlRequest withResponseHeaders(ResponseHeaderOverrides r } /** - * Gets the expected content-type of the request. The content-type is - * included in the signature. + * Gets the expected content-type of the request. The content-type is included in + * the signature. * * @return The expected content-type */ @@ -337,20 +442,23 @@ public String getContentType() { } /** - * Sets the expected content-type of the request. The content-type is - * included in the signature. - * - * @param contentType The expected content-type + * Sets the expected content-type of the request. The content-type is included in + * the signature. + * @param contentType + * The expected content-type */ public void setContentType(String contentType) { this.contentType = contentType; } /** - * Sets the expected content-type of the request and returns this object, - * for method chaining. + * Sets the expected content-type of the request and returns + * this object, for method chaining. + * + * @param contentType + * The expected content-type + * * - * @param contentType The expected content-type * @return This {@link GeneratePresignedUrlRequest} for method chaining. */ public GeneratePresignedUrlRequest withContentType(String contentType) { @@ -373,8 +481,9 @@ public String getContentMd5() { * Sets the expected content-md5 header of the request. This header value * will be included when calculating the signature, and future requests must * include the same content-md5 header value to access the presigned URL. - * - * @param contentMd5 The expected content-md5 header value. + + * @param contentMd5 + * The expected content-md5 header value. */ public void setContentMd5(String contentMd5) { this.contentMd5 = contentMd5; @@ -384,7 +493,9 @@ public void setContentMd5(String contentMd5) { * Sets the expected content-md5 header of the request and returns this * object, for method chaining. * - * @param contentMd5 The expected content-md5 header value. + * @param contentMd5 + * The expected content-md5 header value. + * * @return This {@link GeneratePresignedUrlRequest} for method chaining. */ public GeneratePresignedUrlRequest withContentMd5(String contentMd5) { @@ -392,13 +503,7 @@ public GeneratePresignedUrlRequest withContentMd5(String contentMd5) { return this; } - /** - * Returns the customer-provided server-side encryption key to use as part - * of the generated pre-signed URL. - * - * @return The customer-provided server-side encryption key to use as part - * of the generated pre-signed URL. - */ + @Override public SSECustomerKey getSSECustomerKey() { return sseCustomerKey; } @@ -407,11 +512,12 @@ public SSECustomerKey getSSECustomerKey() { * Sets the customer-provided server-side encryption key to use as part of * the generated pre-signed URL. * - * @param sseKey The customer-provided server-side encryption key to use as + * @param sseCustomerKey + * The customer-provided server-side encryption key to use as * part of the generated pre-signed URL. */ - public void setSSECustomerKey(SSECustomerKey sseKey) { - this.sseCustomerKey = sseKey; + public void setSSECustomerKey(SSECustomerKey sseCustomerKey) { + this.sseCustomerKey = sseCustomerKey; } /** @@ -419,13 +525,107 @@ public void setSSECustomerKey(SSECustomerKey sseKey) { * the generated pre-signed URL, and returns the updated request object so * that additional method calls can be chained together. * - * @param sseKey The customer-provided server-side encryption key to use as + * @param sseKey + * The customer-provided server-side encryption key to use as * part of the generated pre-signed URL. - * @return This updated request object so that additional method calls can be - * chained together. + * + * @return This updated request object so that additional method calls can + * be chained together. */ public GeneratePresignedUrlRequest withSSECustomerKey(SSECustomerKey sseKey) { setSSECustomerKey(sseKey); return this; } + + /** + * Sets the use of SSE-C (Server Side Encryption with Customer Key) using + * the given encryption algorithm. + * + * @param sseAlgorithm + * The server-side encryption algorithm to use with this + * customer-provided server-side encryption key; or null if SSE-C + * is disabled. "AES256" is currently the only + * supported SSE-C encryption algorithm. + */ + public void setSSECustomerKeyAlgorithm(SSEAlgorithm sseAlgorithm) { + if (sseAlgorithm == null) + this.sseCustomerKey = null; + else if (sseAlgorithm.getAlgorithm().equals(SSEAlgorithm.AES256.getAlgorithm())) { + this.sseCustomerKey = + SSECustomerKey.generateSSECustomerKeyForPresignUrl(sseAlgorithm.getAlgorithm()); + } else { + throw new IllegalArgumentException( + "Currently the only supported Server Side Encryption algorithm is " + + SSEAlgorithm.AES256); + } + } + + /** + * Fluent method for {@link #setSSECustomerKeyAlgorithm(SSEAlgorithm)}. + */ + public GeneratePresignedUrlRequest withSSECustomerKeyAlgorithm(SSEAlgorithm algorithm) { + setSSECustomerKeyAlgorithm(algorithm); + return this; + } + + /** + * Returns true if zero byte content is to be used for generating pre-signed + * URL; false otherwise. + */ + public boolean isZeroByteContent() { + return zeroByteContent; + } + + /** + * Sets if zero byte content is to be used for generating pre-signed URL. + */ + public void setZeroByteContent(boolean zeroByteContent) { + this.zeroByteContent = zeroByteContent; + } + + /** + * Fluent method for {@link #setZeroByteContent(boolean)}. + */ + public GeneratePresignedUrlRequest withZeroByteContent(boolean zeroByteContent) { + setZeroByteContent(zeroByteContent); + return this; + } + + /** + * Rejects any illegal input (as attributes of this request) by the user. + * + * @throws IllegalArgumentException if there is illegal input from the user. + */ + public void rejectIllegalArguments() { + if (bucketName == null) { + throw new IllegalArgumentException( + "The bucket name parameter must be specified when generating a pre-signed URL"); + } + if (this.method == null) { + throw new IllegalArgumentException( + "The HTTP method request parameter must be specified when generating a pre-signed URL"); + } + if (this.sseCustomerKey != null) { + if (this.sseAlgorithm != null) { + throw new IllegalArgumentException("Either SSE or SSE-C can be specified but not both"); + } + if (this.kmsCmkId != null) { + throw new IllegalArgumentException("KMS CMK is not applicable for SSE-C"); + } + } else if (this.kmsCmkId != null) { + if (!SSEAlgorithm.KMS.getAlgorithm().equals(sseAlgorithm)) { + throw new IllegalArgumentException( + "For KMS server side encryption, the SSE algorithm must be set to " + + SSEAlgorithm.KMS); + } + } + /* + * S3 does not require HTTP header + * “x-amz-server-side-encryption-aws-kms-key-id” to be always present (a + * default key ID will be used if this header is not present). + * + * It is also possible to set the header to “alias/aws/s3” to refer + * to the default KMS CMK ID. + */ + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GenericBucketRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GenericBucketRequest.java index 04d4801c8e..6199f54ffb 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GenericBucketRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GenericBucketRequest.java @@ -17,10 +17,12 @@ import com.amazonaws.AmazonWebServiceRequest; +import java.io.Serializable; + /** * Generic request container for web service requests on buckets. */ -public class GenericBucketRequest extends AmazonWebServiceRequest { +public class GenericBucketRequest extends AmazonWebServiceRequest implements Serializable { private String bucketName; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketAnalyticsConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketAnalyticsConfigurationRequest.java new file mode 100644 index 0000000000..c59efe8b96 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketAnalyticsConfigurationRequest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object to get an analytics configuration. + */ +public class GetBucketAnalyticsConfigurationRequest extends AmazonWebServiceRequest implements Serializable { + + private String bucketName; + private String id; + + public GetBucketAnalyticsConfigurationRequest() { } + + public GetBucketAnalyticsConfigurationRequest(String bucketName, String id) { + this.bucketName = bucketName; + this.id = id; + } + + /** + * Returns the name of the bucket from which an analytics configuration is to be retrieved. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket from which an analytics configuration is to be retrieved. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket from which an analytics configuration is to be retrieved + * and returns this object for method chaining. + */ + public GetBucketAnalyticsConfigurationRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Returns the identifier used to represent an analytics configuration. + */ + public String getId() { + return id; + } + + /** + * Sets the identifier used to represent an analytics configuration. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the identifier used to represent an analytics configuration + * and returns this object for method chaining. + */ + public GetBucketAnalyticsConfigurationRequest withId(String id) { + setId(id); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketAnalyticsConfigurationResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketAnalyticsConfigurationResult.java new file mode 100644 index 0000000000..992ba1f876 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketAnalyticsConfigurationResult.java @@ -0,0 +1,52 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration; + +import java.io.Serializable; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#getBucketAnalyticsConfiguration(GetBucketAnalyticsConfigurationRequest)} + * operation. + */ +public class GetBucketAnalyticsConfigurationResult implements Serializable { + + private AnalyticsConfiguration analyticsConfiguration; + + /** + * Returns the requested analytics configuration. + */ + public AnalyticsConfiguration getAnalyticsConfiguration() { + return analyticsConfiguration; + } + + /** + * Sets the requested analytics configuration. + */ + public void setAnalyticsConfiguration(AnalyticsConfiguration analyticsConfiguration) { + this.analyticsConfiguration = analyticsConfiguration; + } + + /** + * Sets the requested analytics configuration and returns the + * {@link GetBucketAnalyticsConfigurationResult} object for method chaining. + */ + public GetBucketAnalyticsConfigurationResult withAnalyticsConfiguration(AnalyticsConfiguration analyticsConfiguration) { + setAnalyticsConfiguration(analyticsConfiguration); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketCrossOriginConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketCrossOriginConfigurationRequest.java new file mode 100644 index 0000000000..51b45e7d00 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketCrossOriginConfigurationRequest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015-2017 Amazon Technologies, 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import com.amazonaws.services.s3.AmazonS3; + +/** + * Request object for the parameters to get a bucket's cross origin configuration. + * + * @see AmazonS3#getBucketCrossOriginConfiguration(GetBucketCrossOriginConfigurationRequest) + */ +public class GetBucketCrossOriginConfigurationRequest extends GenericBucketRequest implements Serializable { + + /** + * Creates a request object, ready to be executed to fetch the cross origin + * configuration of the specified bucket. + * + * @param bucketName + * The name of the bucket whose cross origin configuration is + * being fetched. + */ + public GetBucketCrossOriginConfigurationRequest(String bucketName) { + super(bucketName); + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketInventoryConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketInventoryConfigurationRequest.java new file mode 100644 index 0000000000..e708772c2e --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketInventoryConfigurationRequest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object to retrieve an inventory configuration. + */ +public class GetBucketInventoryConfigurationRequest extends AmazonWebServiceRequest implements Serializable { + + private String bucketName; + + private String id; + + public GetBucketInventoryConfigurationRequest() { + } + + public GetBucketInventoryConfigurationRequest(String bucketName, String id) { + this.bucketName = bucketName; + this.id = id; + } + + /** + * Returns the name of the bucket containing the inventory configuration to retrieve. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket containing the inventory configuration to retrieve. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket containing the inventory configuration to retrieve + * and returns {@link GetBucketInventoryConfigurationRequest} object for + * method chaining. + */ + public GetBucketInventoryConfigurationRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Returns the ID used to identify the inventory configuration. + */ + public String getId() { + return id; + } + + /** + * Sets the ID used to identify the inventory configuration. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the ID used to identify the inventory configuration and + * returns {@link GetBucketInventoryConfigurationRequest} object + * for method chaining. + */ + public GetBucketInventoryConfigurationRequest withId(String id) { + setId(id); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketInventoryConfigurationResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketInventoryConfigurationResult.java new file mode 100644 index 0000000000..48c3bc9189 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketInventoryConfigurationResult.java @@ -0,0 +1,51 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.services.s3.model.inventory.InventoryConfiguration; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#getBucketInventoryConfiguration(GetBucketInventoryConfigurationRequest)} + * operation. + */ +public class GetBucketInventoryConfigurationResult { + + private InventoryConfiguration inventoryConfiguration; + + /** + * Returns the requested inventory configuration. + */ + public InventoryConfiguration getInventoryConfiguration() { + return inventoryConfiguration; + } + + /** + * Sets the inventory configuration. + */ + public void setInventoryConfiguration(InventoryConfiguration inventoryConfiguration) { + this.inventoryConfiguration = inventoryConfiguration; + } + + /** + * Sets the inventory configuration and returns the + * {@link GetBucketInventoryConfigurationResult} object + * for method chaining. + */ + public GetBucketInventoryConfigurationResult withInventoryConfiguration(InventoryConfiguration inventoryConfiguration) { + setInventoryConfiguration(inventoryConfiguration); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLifecycleConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLifecycleConfigurationRequest.java new file mode 100644 index 0000000000..c92eccc97b --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLifecycleConfigurationRequest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015-2017 Amazon Technologies, 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import com.amazonaws.services.s3.AmazonS3; + +/** + * Request object for the parameters to get a bucket's lifecycle configuration. + * + * @see AmazonS3#getBucketLifecycleConfiguration(GetBucketLifecycleConfigurationRequest) + */ +public class GetBucketLifecycleConfigurationRequest extends GenericBucketRequest implements Serializable { + + /** + * Creates a request object, ready to be executed to fetch the lifecycle + * configuration for the specified bucket. + * + * @param bucketName + * The name of the bucket whose lifecycle configuration is being + * fetched. + */ + public GetBucketLifecycleConfigurationRequest(String bucketName) { + super(bucketName); + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLocationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLocationRequest.java index fd3f54717e..6dfa2e5209 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLocationRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLocationRequest.java @@ -17,6 +17,8 @@ import com.amazonaws.AmazonWebServiceRequest; +import java.io.Serializable; + /** * Provides options for requesting an Amazon S3 bucket's location. You can * choose a bucket's location when creating it to ensure that the data stored in @@ -25,7 +27,7 @@ * * @see CreateBucketRequest */ -public class GetBucketLocationRequest extends AmazonWebServiceRequest { +public class GetBucketLocationRequest extends AmazonWebServiceRequest implements Serializable { /** The name of the bucket whose location is being requested. */ private String bucketName; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLoggingConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLoggingConfigurationRequest.java new file mode 100644 index 0000000000..b32f03bbd0 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketLoggingConfigurationRequest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015-2017 Amazon Technologies, 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import com.amazonaws.services.s3.AmazonS3; + +/** + * Request object for the parameters to get a bucket's logging configuration. + * + * @see AmazonS3#getBucketLoggingConfiguration(GetBucketLoggingConfigurationRequest) + */ +public class GetBucketLoggingConfigurationRequest extends GenericBucketRequest implements Serializable { + + /** + * Creates request object, ready to be executed to fetch the logging + * configuration for the specified bucket. + * + * @param bucketName + * The name of the bucket whose logging configuration is being + * fetched. + */ + public GetBucketLoggingConfigurationRequest(String bucketName) { + super(bucketName); + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketMetricsConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketMetricsConfigurationRequest.java new file mode 100644 index 0000000000..4f2fb700f3 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketMetricsConfigurationRequest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object to retrieve metrics configuration from a bucket. + */ +public class GetBucketMetricsConfigurationRequest extends AmazonWebServiceRequest implements Serializable { + private String bucketName; + private String id; + + public GetBucketMetricsConfigurationRequest() { + } + + public GetBucketMetricsConfigurationRequest(String bucketName, String id) { + this.bucketName = bucketName; + this.id = id; + } + + /** + * Returns the name of the bucket containing the metrics configuration to retrieve. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket containing the metrics configuration to retrieve. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket containing the metrics configuration to retrieve + * and returns this object for method chaining. + */ + public GetBucketMetricsConfigurationRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Returns the ID used to identify the metrics configuration. + */ + public String getId() { + return id; + } + + /** + * Sets the ID used to identify the metrics configuration. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the ID used to identify the metrics configuration and + * returns {@link GetBucketMetricsConfigurationRequest} object for method chaining. + */ + public GetBucketMetricsConfigurationRequest withId(String id) { + setId(id); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketMetricsConfigurationResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketMetricsConfigurationResult.java new file mode 100644 index 0000000000..390ee23b5d --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketMetricsConfigurationResult.java @@ -0,0 +1,52 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.services.s3.model.metrics.MetricsConfiguration; + +import java.io.Serializable; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#getBucketMetricsConfiguration(GetBucketMetricsConfigurationRequest)} + * operation. + */ +public class GetBucketMetricsConfigurationResult implements Serializable { + + private MetricsConfiguration metricsConfiguration; + + /** + * Returns the requested metrics configuration. + */ + public MetricsConfiguration getMetricsConfiguration() { + return metricsConfiguration; + } + + /** + * Sets the requested metrics configuration. + */ + public void setMetricsConfiguration(MetricsConfiguration metricsConfiguration) { + this.metricsConfiguration = metricsConfiguration; + } + + /** + * Sets the requested metrics configuration and returns + * {@link GetBucketMetricsConfigurationResult} object for method chaining. + */ + public GetBucketMetricsConfigurationResult withMetricsConfiguration(MetricsConfiguration metricsConfiguration) { + setMetricsConfiguration(metricsConfiguration); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketNotificationConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketNotificationConfigurationRequest.java new file mode 100644 index 0000000000..a563856994 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketNotificationConfigurationRequest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +import com.amazonaws.services.s3.AmazonS3; + +/** + * Request object for the parameters to get a bucket's notification + * configuration. + * + * @see AmazonS3#getBucketNotificationConfiguration(GetBucketNotificationConfigurationRequest) + */ +public class GetBucketNotificationConfigurationRequest extends + GenericBucketRequest implements Serializable { + + /** + * Creates a new request object, ready to be executed to fetch the + * notification configuration for the specified bucket. + * + * @param bucketName + * The name of the bucket whose notification configuration is + * being fetched. + */ + public GetBucketNotificationConfigurationRequest(String bucketName) { + super(bucketName); + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketPolicyRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketPolicyRequest.java index f025873547..d7570727a5 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketPolicyRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketPolicyRequest.java @@ -18,6 +18,8 @@ import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** *

* Gets the policy for the specified bucket. Only the owner of the bucket can @@ -36,7 +38,7 @@ * * @see AmazonS3#getBucketPolicy(GetBucketPolicyRequest) */ -public class GetBucketPolicyRequest extends AmazonWebServiceRequest { +public class GetBucketPolicyRequest extends AmazonWebServiceRequest implements Serializable { /** The name of the Amazon S3 bucket whose policy is being retrieved. */ private String bucketName; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketReplicationConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketReplicationConfigurationRequest.java index b87c19aaf4..eeccc74b16 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketReplicationConfigurationRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketReplicationConfigurationRequest.java @@ -17,13 +17,16 @@ import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** * Request object for the parameters to get a bucket's replication * configuration. * * @see AmazonS3#getBucketReplicationConfiguration(GetBucketReplicationConfiguration) */ -public class GetBucketReplicationConfigurationRequest extends GenericBucketRequest { +public class GetBucketReplicationConfigurationRequest extends + GenericBucketRequest implements Serializable { /** * Creates a new request object, ready to be executed to fetch the diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketTaggingConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketTaggingConfigurationRequest.java new file mode 100644 index 0000000000..c18b05a8d1 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketTaggingConfigurationRequest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015-2017 Amazon Technologies, 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import com.amazonaws.services.s3.AmazonS3; + +/** + * Request object for the parameters to get a bucket's tagging configuration. + * + * @see AmazonS3#getBucketTaggingConfiguration(GetBucketTaggingConfigurationRequest) + */ +public class GetBucketTaggingConfigurationRequest extends GenericBucketRequest implements Serializable { + + /** + * Creates request object, ready to be executed to fetch the tagging + * configuration for the specified bucket. + * + * @param bucketName + * The name of the bucket whose tagging configuration is being + * fetched. + */ + public GetBucketTaggingConfigurationRequest(String bucketName) { + super(bucketName); + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketVersioningConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketVersioningConfigurationRequest.java new file mode 100644 index 0000000000..44b7979eba --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetBucketVersioningConfigurationRequest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015-2017 Amazon Technologies, 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import com.amazonaws.services.s3.AmazonS3; + +/** + * Request object for the parameters to get a bucket's versioning configuration. + * + * @see AmazonS3#getBucketVersioningConfiguration(GetBucketVersioningConfigurationRequest) + */ +public class GetBucketVersioningConfigurationRequest extends GenericBucketRequest implements Serializable { + + /** + * Creates a request object, ready to be executed to fetch the versioning + * configuration for the specified bucket. + * + * @param bucketName + * The name of the bucket whose versioning configuration is being + * fetched. + */ + public GetBucketVersioningConfigurationRequest(String bucketName) { + super(bucketName); + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectAclRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectAclRequest.java new file mode 100644 index 0000000000..70b8efdcff --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectAclRequest.java @@ -0,0 +1,299 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.internal.Constants; +import com.amazonaws.AmazonWebServiceRequest; + +/** + *

+ * Provide options to get an object ACL. + *

+ *

+ * Each bucket and object in Amazon S3 has an ACL that defines its access + * control policy. When a request is made, Amazon S3 authenticates the request + * using its standard authentication procedure and then checks the ACL to verify + * the sender was granted access to the bucket or object. If the sender is + * approved, the request proceeds. Otherwise, Amazon S3 returns an error. + *

+ * + * @see AmazonS3#getObjectAcl(String, String) + * @see AmazonS3#getObjectAcl(String, String, String) + * @see AmazonS3#getObjectAcl(GetObjectAclRequest) + */ +public class GetObjectAclRequest extends AmazonWebServiceRequest implements Serializable{ + + /** + * Builder of an S3 object identifier. This member field is never null. + */ + private S3ObjectIdBuilder s3ObjectIdBuilder = new S3ObjectIdBuilder(); + + /** + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; + + public GetObjectAclRequest(String bucketName, String key) { + this(bucketName, key, null); + } + + public GetObjectAclRequest(String bucketName, String key, String versionId) { + setBucketName(bucketName); + setKey(key); + setVersionId(versionId); + } + + /** + * Gets the name of the bucket containing the object whose ACL is to be retrieved. + * + * @return The name of the bucket containing the object whose ACL is to be retrieved. + * + * @see GetObjectAclRequest#setBucketName(String) + * @see GetObjectAclRequest#withBucket(String) + */ + public String getBucketName() { + return s3ObjectIdBuilder.getBucket(); + } + + /** + * Sets the name of the bucket containing the object whose ACL is to be retrieved. + * + * @param bucketName + * The name of the bucket containing the object whose ACL is to be retrieved. + * @see GetObjectAclRequest#getBucketName() + * @see GetObjectAclRequest#withBucket(String) + */ + public void setBucketName(String bucketName) { + s3ObjectIdBuilder.setBucket(bucketName); + } + /** + * Sets the name of the bucket containing the object whose ACL is to be retrieved. + * Returns this {@link GetObjectAclRequest}, enabling additional method + * calls to be chained together. + * + * @param bucketName + * The name of the bucket containing the object whose ACL is to be retrieved. + * @return This {@link GetObjectAclRequest}, enabling additional method + * calls to be chained together. + * + * @see GetObjectAclRequest#getBucketName() + * @see GetObjectAclRequest#setBucketName(String) + */ + public GetObjectAclRequest withBucket(String bucketName) { + setBucketName(bucketName); + return this; + } + /** + * Gets the key under which the object whose ACL to be retrieved is stored. + * + * @return The key under which the object whose ACL to be retrieved is stored. + * + * @see GetObjectAclRequest#setKey(String) + * @see GetObjectAclRequest#withKey(String) + */ + public String getKey() { + return s3ObjectIdBuilder.getKey(); + } + + /** + * Sets the key under which the object whose ACL to be retrieved is stored. + * + * @param key + * The key under which the object whose ACL to be retrieved is stored. + * + * @see GetObjectAclRequest#getKey() + * @see GetObjectAclRequest#withKey(String) + */ + public void setKey(String key) { + s3ObjectIdBuilder.setKey(key); + } + + /** + * Sets the key under which the object whose ACL to be retrieved is stored. + * Returns this {@link GetObjectAclRequest}, enabling additional method + * calls to be chained together. + * + * @param key + * The key under which the object whose ACL to be retrieved is stored. + * + * @return This {@link GetObjectAclRequest}, enabling additional method + * calls to be chained together. + * + * @see GetObjectAclRequest#getKey() + * @see GetObjectAclRequest#setKey(String) + */ + public GetObjectAclRequest withKey(String key) { + setKey(key); + return this; + } + + /** + *

+ * Gets the optional version ID specifying which version of the object whose ACL to + * be retrieved. If not specified, the most recent version's ACL will be retrieved. + *

+ *

+ * Objects created before versioning was enabled or when versioning is + * suspended are given the default null version ID (see + * {@link Constants#NULL_VERSION_ID}). Note that the + * null version ID is a valid version ID and is not the + * same as not having a version ID. + *

+ *

+ * For more information about enabling versioning for a bucket, see + * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)}. + *

+ * + * @return The optional version ID specifying which version of the object whose ACL + * to be retrieved. If not specified, the most recent version will be + * retrieved. + * + * @see GetObjectAclRequest#setVersionId(String) + * @see GetObjectAclRequest#withVersionId(String) + */ + public String getVersionId() { + return s3ObjectIdBuilder.getVersionId(); + } + + /** + * Sets the optional version ID specifying which version of the object whose ACL to + * be retrieved. If not specified, the most recent version's ACL will be retrieved. + *

+ * Objects created before versioning was enabled or when versioning is + * suspended will be given the default null version ID (see + * {@link Constants#NULL_VERSION_ID}). Note that the + * null version ID is a valid version ID and is not the + * same as not having a version ID. + *

+ *

+ * For more information about enabling versioning for a bucket, see + * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)}. + *

+ * + * @param versionId + * The optional version ID specifying which version of the object whose ACL + * to be retrieved. + * + * @see GetObjectAclRequest#getVersionId() + * @see GetObjectAclRequest#withVersionId(String) + */ + public void setVersionId(String versionId) { + s3ObjectIdBuilder.setVersionId(versionId); + } + + /** + *

+ * Sets the optional version ID specifying which version of the object whose ACL to be + * retrieved and returns this {@link GetObjectAclRequest}, enabling additional method calls to be + * chained together. If not specified, the most recent version's ACL will be + * retrieved. + *

+ *

+ * Objects created before versioning was enabled or when versioning is + * suspended will be given the default or null version ID (see + * {@link Constants#NULL_VERSION_ID}). Note that the + * null version ID is a valid version ID and is not the + * same as not having a version ID. + *

+ *

+ * For more information about enabling versioning for a bucket, see + * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)}. + *

+ * + * @param versionId + * The optional version ID specifying which version of the object whose ACL is + * to be retrieved. + * + * @return The updated request object, enabling additional method calls to be + * chained together. + * + * @see GetObjectAclRequest#getVersionId() + * @see GetObjectAclRequest#setVersionId(String) + */ + public GetObjectAclRequest withVersionId(String versionId) { + setVersionId(versionId); + return this; + } + + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated GetObjectAclRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated GetObjectAclRequest object. + */ + public GetObjectAclRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectMetadataRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectMetadataRequest.java index fe115ed4c7..70240a6b39 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectMetadataRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectMetadataRequest.java @@ -18,6 +18,8 @@ import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** *

* Provides options for obtaining the metadata for the specified Amazon S3 @@ -41,8 +43,8 @@ * String) * @see GetObjectRequest */ -public class GetObjectMetadataRequest extends AmazonWebServiceRequest { - +public class GetObjectMetadataRequest extends AmazonWebServiceRequest implements + SSECustomerKeyProvider, Serializable { /** * The name of the bucket containing the object's whose metadata is being * retrieved. @@ -60,6 +62,12 @@ public class GetObjectMetadataRequest extends AmazonWebServiceRequest { */ private String versionId; + /** + * If enabled, the requester is charged for downloading the metadata from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; + /** * The optional customer-provided server-side encryption key to use when * retrieving the metadata of a server-side encrypted object. @@ -67,14 +75,24 @@ public class GetObjectMetadataRequest extends AmazonWebServiceRequest { private SSECustomerKey sseCustomerKey; /** - * Constructs a new {@link GetObjectMetadataRequest} used to retrieve a - * specified object's metadata. - * - * @param bucketName The name of the bucket containing the object whose - * metadata is being retrieved. - * @param key The key of the object whose metadata is being retrieved. - * @see GetObjectMetadataRequest#GetObjectMetadataRequest(String bucketName, - * String key, String versionId) + * The optional part number to find the number of parts of an object. + */ + private Integer partNumber; + + + /** + * Constructs a new + * {@link GetObjectMetadataRequest} + * used to retrieve a specified + * object's metadata. + * + * @param bucketName + * The name of the bucket containing the object whose metadata + * is being retrieved. + * @param key + * The key of the object whose metadata is being retrieved. + * + * @see GetObjectMetadataRequest#GetObjectMetadataRequest(String bucketName, String key, String versionId) */ public GetObjectMetadataRequest(String bucketName, String key) { setBucketName(bucketName); @@ -82,15 +100,20 @@ public GetObjectMetadataRequest(String bucketName, String key) { } /** - * Constructs a new {@link GetObjectMetadataRequest} with basic options. - * - * @param bucketName The name of the bucket containing the object whose - * metadata is being retrieved. - * @param key The key of the object whose metadata is being retrieved. - * @param versionId The version ID of the object version whose metadata is - * being retrieved. - * @see GetObjectMetadataRequest#GetObjectMetadataRequest(String bucketName, - * String key) + * Constructs a new + * {@link GetObjectMetadataRequest} + * with basic options. + * + * @param bucketName + * The name of the bucket containing the object whose metadata + * is being retrieved. + * @param key + * The key of the object whose metadata is being retrieved. + * @param versionId + * The version ID of the object version whose metadata is being + * retrieved. + * + * @see GetObjectMetadataRequest#GetObjectMetadataRequest(String bucketName, String key) */ public GetObjectMetadataRequest(String bucketName, String key, String versionId) { this(bucketName, key); @@ -98,11 +121,12 @@ public GetObjectMetadataRequest(String bucketName, String key, String versionId) } /** - * Gets the name of the bucket containing the object whose metadata is being - * retrieved. + * Gets the name of the bucket containing the object whose metadata is + * being retrieved. * * @return The name of the bucket containing the object whose metadata is * being retrieved. + * * @see GetObjectMetadataRequest#setBucketName(String bucketName) * @see GetObjectMetadataRequest#withBucketName(String) */ @@ -111,11 +135,13 @@ public String getBucketName() { } /** - * Sets the name of the bucket containing the object whose metadata is being - * retrieved. + * Sets the name of the bucket containing the object whose metadata is + * being retrieved. + * + * @param bucketName + * The name of the bucket containing the object's whose metadata + * is being retrieved. * - * @param bucketName The name of the bucket containing the object's whose - * metadata is being retrieved. * @see GetObjectMetadataRequest#getBucketName() * @see GetObjectMetadataRequest#withBucketName(String) */ @@ -124,14 +150,18 @@ public void setBucketName(String bucketName) { } /** - * Sets the name of the bucket containing the object whose metadata is being - * retrieved. Returns this {@link GetObjectMetadataRequest}, enabling - * additional method calls to be chained together. + * Sets the name of the bucket containing the object whose metadata is + * being retrieved. + * Returns this {@link GetObjectMetadataRequest}, enabling additional method + * calls to be chained together. + * + * @param bucketName + * The name of the bucket containing the object's whose metadata + * is being retrieved. * - * @param bucketName The name of the bucket containing the object's whose - * metadata is being retrieved. * @return This {@link GetObjectMetadataRequest}, enabling additional method * calls to be chained together. + * * @see GetObjectMetadataRequest#getBucketName() * @see GetObjectMetadataRequest#setBucketName(String bucketName) */ @@ -144,6 +174,7 @@ public GetObjectMetadataRequest withBucketName(String bucketName) { * Gets the key of the object whose metadata is being retrieved. * * @return The key of the object whose metadata is being retrieved. + * * @see GetObjectMetadataRequest#setKey(String) * @see GetObjectMetadataRequest#withKey(String) */ @@ -154,7 +185,9 @@ public String getKey() { /** * Sets the key of the object whose metadata is being retrieved. * - * @param key The key of the object whose metadata is being retrieved. + * @param key + * The key of the object whose metadata is being retrieved. + * * @see GetObjectMetadataRequest#getKey() * @see GetObjectMetadataRequest#withKey(String) */ @@ -163,13 +196,16 @@ public void setKey(String key) { } /** - * Sets the key of the object whose metadata is being retrieved. Returns - * this {@link GetObjectMetadataRequest}, enabling additional method calls - * to be chained together. + * Sets the key of the object whose metadata is being retrieved. + * Returns this {@link GetObjectMetadataRequest}, enabling additional method + * calls to be chained together. + * + * @param key + * The key of the object whose metadata is being retrieved. * - * @param key The key of the object whose metadata is being retrieved. * @return This {@link GetObjectMetadataRequest}, enabling additional method * calls to be chained together. + * * @see GetObjectMetadataRequest#getKey() * @see GetObjectMetadataRequest#setKey(String) */ @@ -185,6 +221,7 @@ public GetObjectMetadataRequest withKey(String key) { * @return The optional version ID of the object version whose metadata is * being retrieved. If not specified, the latest version will be * used. + * * @see GetObjectMetadataRequest#setVersionId(String) * @see GetObjectMetadataRequest#withVersionId(String) */ @@ -196,9 +233,11 @@ public String getVersionId() { * Sets the optional version ID of the object version whose metadata is * being retrieved. If not specified, the latest version will be used. * - * @param versionId The optional version ID of the object version whose - * metadata is being retrieved. If not specified, the latest - * version will be used. + * @param versionId + * The optional version ID of the object version whose metadata + * is being retrieved. If not specified, the latest version will + * be used. + * * @see GetObjectMetadataRequest#getVersionId() * @see GetObjectMetadataRequest#withVersionId(String) */ @@ -208,14 +247,18 @@ public void setVersionId(String versionId) { /** * Sets the optional version ID of the object version whose metadata is - * being retrieved. Returns this {@link GetObjectMetadataRequest}, enabling - * additional method calls to be chained together. If not specified, the - * latest version will be used. + * being retrieved. + * Returns this {@link GetObjectMetadataRequest}, enabling additional method + * calls to be chained together. + * If not specified, the latest version will be used. + * + * @param versionId + * The optional version ID of the object version whose metadata + * is being retrieved. * - * @param versionId The optional version ID of the object version whose - * metadata is being retrieved. * @return This {@link GetObjectMetadataRequest}, enabling additional method * calls to be chained together. + * * @see GetObjectMetadataRequest#getVersionId() * @see GetObjectMetadataRequest#setVersionId(String) */ @@ -224,13 +267,73 @@ public GetObjectMetadataRequest withVersionId(String versionId) { return this; } + /** - * Returns the optional customer-provided server-side encryption key to use - * when retrieving the metadata of a server-side encrypted object. + * Returns true if the user has enabled Requester Pays option when + * downloading the object metadata from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to read an + * object from it without Requester Pays enabled will result in a 403 error + * and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * downloading the object metadata from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for downloading an Amazon S3 Object metadata from a Requester Pays Bucket. If + * set the requester is charged for downloading the data from the bucket. * - * @return The optional customer-provided server-side encryption key to use - * when retrieving the metadata of a server-side encrypted object. + *

+ * If a bucket is enabled for Requester Pays, then any attempt to read an + * object metadata from it without Requester Pays enabled will result in a 403 error + * and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated GetObjectMetadataRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated GetObjectMetadataRequest object. + */ + public GetObjectMetadataRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } + + @Override public SSECustomerKey getSSECustomerKey() { return sseCustomerKey; } @@ -239,8 +342,9 @@ public SSECustomerKey getSSECustomerKey() { * Sets the optional customer-provided server-side encryption key to use * when retrieving the metadata of a server-side encrypted object. * - * @param sseKey The optional customer-provided server-side encryption key - * to use when retrieving the metadata of a server-side encrypted + * @param sseKey + * The optional customer-provided server-side encryption key to + * use when retrieving the metadata of a server-side encrypted * object. */ public void setSSECustomerKey(SSECustomerKey sseKey) { @@ -253,14 +357,85 @@ public void setSSECustomerKey(SSECustomerKey sseKey) { * retuns the updated request object so that additional method calls can be * chained together. * - * @param sseKey The optional customer-provided server-side encryption key - * to use when retrieving the metadata of a server-side encrypted + * @param sseKey + * The optional customer-provided server-side encryption key to + * use when retrieving the metadata of a server-side encrypted * object. - * @return This updated request object so that additional method calls can be - * chained together. + * + * @return This updated request object so that additional method calls can + * be chained together. */ public GetObjectMetadataRequest withSSECustomerKey(SSECustomerKey sseKey) { setSSECustomerKey(sseKey); return this; } + + /** + *

+ * Returns the optional part number that indicates a part in multipart object. + *

+ * + * @return The part number representing a part in a multipart object. + * + * @see GetObjectMetadataRequest#setPartNumber(Integer) + * @see GetObjectMetadataRequest#withPartNumber(Integer) + */ + public Integer getPartNumber() { + return partNumber; + } + + /** + *

+ * Sets the optional part number to find the number of parts of an object. + *

+ *

+ * To find the number of parts of an object, set partNumber to 1 and observe the x-amz-mp-parts-count response. + * If the object exists and x-amz-mp-parts-count is missing it's implicitly 1. + * Otherwise number of parts is equal to the value returned by x-amz-mp-parts-count. + *

+ *

+ * The valid range for part number is 1 - 10000 inclusive. + * For partNumber < 1, an AmazonS3Exception is thrown with response code 400 bad request + * For partNumber larger than actual part count, an AmazonS3Exception is thrown with response code 416 Request Range Not Satisfiable + *

+ * + * @param partNumber + * The part number representing a part in a multipart object. + * + * @see GetObjectMetadataRequest#getPartNumber() + * @see GetObjectMetadataRequest#withPartNumber(Integer) + */ + public void setPartNumber(Integer partNumber) { + this.partNumber = partNumber; + } + + /** + *

+ * Sets the optional part number to find the number of parts of an object. + *

+ *

+ * To find the number of parts of an object, set partNumber to 1 and observe the x-amz-mp-parts-count response. + * If the object exists and x-amz-mp-parts-count is missing it's implicitly 1. + * Otherwise number of parts is equal to the value returned by x-amz-mp-parts-count. + *

+ *

+ * The valid range for part number is 1 - 10000 inclusive. + * For partNumber < 1, an AmazonS3Exception is thrown with response code 400 bad request + * For partNumber larger than actual part count, an AmazonS3Exception is thrown with response code 416 Request Range Not Satisfiable + *

+ * + * @param partNumber + * The part number representing a part in a multipart object. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * + * @see GetObjectMetadataRequest#getPartNumber() + * @see GetObjectMetadataRequest#setPartNumber(Integer) + */ + public GetObjectMetadataRequest withPartNumber(Integer partNumber) { + setPartNumber(partNumber); + return this; + } + } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectRequest.java index 445e91908b..ea54b53690 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectRequest.java @@ -20,6 +20,7 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.internal.Constants; +import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -29,34 +30,34 @@ * Provides options for downloading an Amazon S3 object. *

*

- * All GetObjectRequests must specify a bucket name and key. Beyond - * that, requests can also specify: - *

    - *
  • The range of bytes within the object to download, - *
  • Constraints controlling if the object will be downloaded or not. - *
+ * All GetObjectRequests must specify a bucket name and key. + * Beyond that, requests can also specify: + * + *
    + *
  • The range of bytes within the object to download, + *
  • Constraints controlling if the object will be downloaded or not. + *
+ *

+ *

+ * If you are uploading or accessing KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify + * -signature-version *

* * @see GetObjectRequest#GetObjectRequest(String, String) * @see GetObjectRequest#GetObjectRequest(String, String, String) * @see GetObjectMetadataRequest */ -public class GetObjectRequest extends AmazonWebServiceRequest { - - /** The name of the bucket containing the object to retrieve */ - private String bucketName; - - /** The key under which the desired object is stored */ - private String key; - +public class GetObjectRequest extends AmazonWebServiceRequest implements + SSECustomerKeyProvider, Serializable { /** - * Optional version ID specifying which version of the object to download. - * If not specified, the most recent version will be downloaded. - *

- * For more information about enabling versioning for a bucket, see - * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)}. + * Builder of an S3 object identifier. This member field is never null. */ - private String versionId; + private S3ObjectIdBuilder s3ObjectIdBuilder = new S3ObjectIdBuilder(); /** Optional member indicating the byte range of data to retrieve */ private long[] range; @@ -110,12 +111,19 @@ public class GetObjectRequest extends AmazonWebServiceRequest { private SSECustomerKey sseCustomerKey; /** - * Constructs a new {@link GetObjectRequest} with all the required - * parameters. + * The part number of the requested part in a multipart object. + */ + private Integer partNumber; + + /** + * Constructs a new {@link GetObjectRequest} with all the required parameters. * - * @param bucketName The name of the bucket containing the desired object. - * @param key The key in the specified bucket under which the object is + * @param bucketName + * The name of the bucket containing the desired object. + * @param key + * The key in the specified bucket under which the object is * stored. + * * @see GetObjectRequest#GetObjectRequest(String, String, String) * @see GetObjectRequest#GetObjectRequest(String, String, boolean) */ @@ -124,14 +132,17 @@ public GetObjectRequest(String bucketName, String key) { } /** - * Constructs a new {@link GetObjectRequest} with all the required - * parameters. + * Constructs a new {@link GetObjectRequest} with all the required parameters. * - * @param bucketName The name of the bucket containing the desired object. - * @param key The key in the specified bucket under which the object is + * @param bucketName + * The name of the bucket containing the desired object. + * @param key + * The key in the specified bucket under which the object is * stored. - * @param versionId The Amazon S3 version ID specifying a specific version - * of the object to download. + * @param versionId + * The Amazon S3 version ID specifying a specific version of the + * object to download. + * * @see GetObjectRequest#GetObjectRequest(String, String) * @see GetObjectRequest#GetObjectRequest(String, String, boolean) */ @@ -139,25 +150,34 @@ public GetObjectRequest(String bucketName, String key, String versionId) { setBucketName(bucketName); setKey(key); setVersionId(versionId); - setRequesterPays(false); + } + + public GetObjectRequest(S3ObjectId s3ObjectId) { + this.s3ObjectIdBuilder = new S3ObjectIdBuilder(s3ObjectId); } /** * Constructs a new {@link GetObjectRequest} with all the required * parameters. * - * @param bucketName The name of the bucket containing the desired object. - * @param key The key in the specified bucket under which the object is + * @param bucketName + * The name of the bucket containing the desired object. + * @param key + * The key in the specified bucket under which the object is * stored. - * @param isRequesterPays If enabled, the requester is charged for - * downloading the data from Requester Pays Buckets. + * @param isRequesterPays + * If enabled, the requester is charged for downloading the data + * from Requester Pays Buckets. + * * @see GetObjectRequest#GetObjectRequest(String, String) * @see GetObjectRequest#GetObjectRequest(String, String, String) */ public GetObjectRequest(String bucketName, String key, boolean isRequesterPays) { - this.bucketName = bucketName; - this.key = key; + this.s3ObjectIdBuilder + .withBucket(bucketName) + .withKey(key) + ; this.isRequesterPays = isRequesterPays; } @@ -165,34 +185,38 @@ public GetObjectRequest(String bucketName, String key, * Gets the name of the bucket containing the object to be downloaded. * * @return The name of the bucket containing the object to be downloaded. + * * @see GetObjectRequest#setBucketName(String) * @see GetObjectRequest#withBucketName(String) */ public String getBucketName() { - return bucketName; + return s3ObjectIdBuilder.getBucket(); } /** * Sets the name of the bucket containing the object to be downloaded. * - * @param bucketName The name of the bucket containing the object to be - * downloaded. + * @param bucketName + * The name of the bucket containing the object to be downloaded. + * * @see GetObjectRequest#getBucketName() * @see GetObjectRequest#withBucketName(String) */ public void setBucketName(String bucketName) { - this.bucketName = bucketName; + s3ObjectIdBuilder.setBucket(bucketName); } /** * Sets the name of the bucket containing the object to be downloaded. - * Returns this {@link GetObjectRequest}, enabling additional method calls - * to be chained together. + * Returns this {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * + * @param bucketName + * The name of the bucket containing the object to be downloaded. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. * - * @param bucketName The name of the bucket containing the object to be - * downloaded. - * @return This {@link GetObjectRequest}, enabling additional method calls to - * be chained together. * @see GetObjectRequest#getBucketName() * @see GetObjectRequest#setBucketName(String) */ @@ -205,32 +229,38 @@ public GetObjectRequest withBucketName(String bucketName) { * Gets the key under which the object to be downloaded is stored. * * @return The key under which the object to be downloaded is stored. + * * @see GetObjectRequest#setKey(String) * @see GetObjectRequest#withKey(String) */ public String getKey() { - return key; + return s3ObjectIdBuilder.getKey(); } /** * Sets the key under which the object to be downloaded is stored. * - * @param key The key under which the object to be downloaded is stored. + * @param key + * The key under which the object to be downloaded is stored. + * * @see GetObjectRequest#getKey() * @see GetObjectRequest#withKey(String) */ public void setKey(String key) { - this.key = key; + s3ObjectIdBuilder.setKey(key); } /** - * Sets the key under which the object to be downloaded is stored. Returns - * this {@link GetObjectRequest}, enabling additional method calls to be - * chained together. + * Sets the key under which the object to be downloaded is stored. + * Returns this {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * + * @param key + * The key under which the object to be downloaded is stored. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. * - * @param key The key under which the object to be downloaded is stored. - * @return This {@link GetObjectRequest}, enabling additional method calls to - * be chained together. * @see GetObjectRequest#getKey() * @see GetObjectRequest#setKey(String) */ @@ -247,24 +277,24 @@ public GetObjectRequest withKey(String key) { *

* Objects created before versioning was enabled or when versioning is * suspended are given the default null version ID (see - * {@link Constants#NULL_VERSION_ID}). Note that the null - * version ID is a valid version ID and is not the same as not having a - * version ID. + * {@link Constants#NULL_VERSION_ID}). Note that the + * null version ID is a valid version ID and is not the + * same as not having a version ID. *

*

* For more information about enabling versioning for a bucket, see - * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)} - * . + * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)}. *

* * @return The optional version ID specifying which version of the object to * download. If not specified, the most recent version will be * downloaded. + * * @see GetObjectRequest#setVersionId(String) * @see GetObjectRequest#withVersionId(String) */ public String getVersionId() { - return versionId; + return s3ObjectIdBuilder.getVersionId(); } /** @@ -273,23 +303,24 @@ public String getVersionId() { *

* Objects created before versioning was enabled or when versioning is * suspended will be given the default null version ID (see - * {@link Constants#NULL_VERSION_ID}). Note that the null - * version ID is a valid version ID and is not the same as not having a - * version ID. + * {@link Constants#NULL_VERSION_ID}). Note that the + * null version ID is a valid version ID and is not the + * same as not having a version ID. *

*

* For more information about enabling versioning for a bucket, see - * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)} - * . + * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)}. *

* - * @param versionId The optional version ID specifying which version of the - * object to download. + * @param versionId + * The optional version ID specifying which version of the object + * to download. + * * @see GetObjectRequest#getVersionId() * @see GetObjectRequest#withVersionId(String) */ public void setVersionId(String versionId) { - this.versionId = versionId; + s3ObjectIdBuilder.setVersionId(versionId); } /** @@ -302,20 +333,22 @@ public void setVersionId(String versionId) { *

* Objects created before versioning was enabled or when versioning is * suspended will be given the default or null version ID (see - * {@link Constants#NULL_VERSION_ID}). Note that the null - * version ID is a valid version ID and is not the same as not having a - * version ID. + * {@link Constants#NULL_VERSION_ID}). Note that the + * null version ID is a valid version ID and is not the + * same as not having a version ID. *

*

* For more information about enabling versioning for a bucket, see - * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)} - * . + * {@link AmazonS3#setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest)}. *

* - * @param versionId The optional version ID specifying which version of the - * object to download. + * @param versionId + * The optional version ID specifying which version of the object + * to download. + * * @return The updated request object, enabling additional method calls to be - * chained together. + * chained together. + * * @see GetObjectRequest#getVersionId() * @see GetObjectRequest#setVersionId(String) */ @@ -324,27 +357,30 @@ public GetObjectRequest withVersionId(String versionId) { return this; } + /* * Optional Request Parameters */ /** *

- * Gets the optional inclusive byte range within the desired object that - * will be downloaded by this request. + * Gets the optional inclusive byte range within the desired object + * that will be downloaded by this request. *

*

- * The range is returned as a two element array, containing the start and - * end index of the byte range. If no byte range has been specified, the - * entire object is downloaded and this method returns null. + * The range is returned as + * a two element array, containing the start and end index of the byte range. + * If no byte range has been specified, the entire object is downloaded and + * this method returns null. *

+ * @return A two element array indicating the inclusive start index and end index + * within the object being downloaded by this request. + * Returns null if no range has been specified, + * and the whole object is + * to be downloaded. * - * @return A two element array indicating the inclusive start index and end - * index within the object being downloaded by this request. Returns - * null if no range has been specified, and the whole - * object is to be downloaded. - * @see GetObjectMetadataRequest#setRange(long, long) - * @see GetObjectRequest#withRange(long, long) + * @see #setRange(long, long) + * @see #withRange(long, long) */ public long[] getRange() { return range == null ? null : range.clone(); @@ -356,48 +392,77 @@ public long[] getRange() { * will be downloaded by this request. *

*

- * The first byte in an object has position 0; as an example, the first ten - * bytes of an object can be downloaded by specifying a range of 0 to 9. + * The first byte in an object has + * position 0; as an example, the first ten bytes of an object can be + * downloaded by specifying a range of 0 to 9. + *

+ *

+ * If no byte range is specified, this request downloads the entire + * object from Amazon S3. + *

+ * + * @param start + * The start of the inclusive byte range to download. + * @param end + * The end of the inclusive byte range to download. + * + * @see #getRange() + * @see #withRange(long, long) + */ + public void setRange(long start, long end) { + range = new long[] {start, end}; + } + + /** + *

+ * Sets the optional inclusive start range within the desired object that the + * rest of which will be downloaded by this request. *

*

- * If end is negative, that's equivalent to downloading the object from - * start of range to the end of the object, i.e. Range:bytes=start- . + * The first byte in an object has + * position 0; as an example, the object is of 10 bytes in length, the last + * 4 bytes can be downloaded by specifying the start range as 6. *

*

- * If no byte range is specified, this request downloads the entire object - * from Amazon S3. + * If no byte range is specified, this request downloads the entire + * object from Amazon S3. *

* - * @param start The start of the inclusive byte range to download. - * @param end The end of the inclusive byte range to download. - * @see GetObjectMetadataRequest#getRange() - * @see GetObjectRequest#withRange(long, long) + * @param start + * The start of the inclusive byte range to download. + * + * @see #setRange(long, long) + * @see #withRange(long) */ - public void setRange(long start, long end) { - range = new long[] { - start, end - }; + public void setRange(long start) { + setRange(start, Long.MAX_VALUE - 1); } /** *

* Sets the optional inclusive byte range within the desired object that - * will be downloaded by this request. Returns this {@link GetObjectRequest} - * , enabling additional method calls to be chained together. + * will be downloaded by this request. + * Returns this {@link GetObjectRequest}, enabling additional method + * calls to be chained together. *

*

- * The first byte in an object has position 0; as an example, the first ten - * bytes of an object can be downloaded by specifying a range of 0 to 9. + * The first byte in an object has + * position 0; as an example, the first ten bytes of an object can be + * downloaded by specifying a range of 0 to 9. *

*

- * If no byte range is specified, this request downloads the entire object - * from Amazon S3. + * If no byte range is specified, this request downloads the entire + * object from Amazon S3. *

* - * @param start The start of the inclusive byte range to download. - * @param end The end of the inclusive byte range to download. - * @return This {@link GetObjectRequest}, enabling additional method calls to - * be chained together. + * @param start + * The start of the inclusive byte range to download. + * @param end + * The end of the inclusive byte range to download. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * * @see GetObjectRequest#getRange() * @see GetObjectRequest#setRange(long, long) */ @@ -407,14 +472,46 @@ public GetObjectRequest withRange(long start, long end) { } /** - * Gets the optional list of ETag constraints that, when present, - * must include a match for the object's current ETag in order for - * this request to be executed. Only one ETag in the list needs to match for - * this request to be executed by Amazon S3. + *

+ * Sets the optional inclusive start range within the desired object that the + * rest of which will be downloaded by this request. + *

+ * Returns this {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + *

+ * The first byte in an object has + * position 0; as an example, the object is of 10 bytes in length, the last + * 4 bytes can be downloaded by specifying the start range as 6. + *

+ *

+ * If no byte range is specified, this request downloads the entire + * object from Amazon S3. + *

+ * + * @param start + * The start of the inclusive byte range to download. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * + * @see #withRange(long, long) + * @see #setRange(long) + */ + public GetObjectRequest withRange(long start) { + setRange(start); + return this; + } + + /** + * Gets the optional list of ETag constraints that, when present, must + * include a match for the object's current ETag in order for this + * request to be executed. Only one ETag in the list needs to match for this + * request to be executed by Amazon S3. + * + * @return The optional list of ETag constraints that when present must + * include a match for the object's current ETag in order for this + * request to be executed. * - * @return The optional list of ETag constraints that when present - * must include a match for the object's current ETag in - * order for this request to be executed. * @see GetObjectRequest#setMatchingETagConstraints(List) * @see GetObjectRequest#withMatchingETagConstraint(String) */ @@ -424,14 +521,16 @@ public List getMatchingETagConstraints() { /** * Sets the optional list of ETag constraints that when present must - * include a match for the object's current ETag in order for this request - * to be executed. If none of the specified ETags match the object's current - * ETag, this request will not be executed. Only one ETag in the list needs - * to match for the request to be executed by Amazon S3. + * include a match for the object's current ETag in order for this + * request to be executed. If none of the specified ETags match the object's + * current ETag, this request will not be executed. Only one ETag in the + * list needs to match for the request to be executed by Amazon S3. + * + * @param eTagList + * The optional list of ETag constraints that must include a + * match for the object's current ETag in order for this request + * to be executed. * - * @param eTagList The optional list of ETag constraints that must - * include a match for the object's current ETag in order for - * this request to be executed. * @see GetObjectRequest#getMatchingETagConstraints() * @see GetObjectRequest#withMatchingETagConstraint(String) */ @@ -440,19 +539,22 @@ public void setMatchingETagConstraints(List eTagList) { } /** - * Sets a single ETag constraint to this request. Returns this - * {@link GetObjectRequest}, enabling additional method calls to be chained - * together. + * Sets a single ETag constraint to this request. + * Returns this {@link GetObjectRequest}, enabling additional method + * calls to be chained together. *

- * Multiple ETag constraints can be added to a request, but one must match - * the object's current ETag in order for this request to be executed. If - * none of the ETag constraints added to this request match the object's - * current ETag, this request will not be executed by Amazon S3. + * Multiple ETag constraints can be added to a request, but one must match the object's + * current ETag in order for this request to be executed. If none of the + * ETag constraints added to this request match the object's current ETag, + * this request will not be executed by Amazon S3. *

* - * @param eTag The matching ETag constraint to add to this request. - * @return This {@link GetObjectRequest}, enabling additional method calls to - * be chained together. + * @param eTag + * The matching ETag constraint to add to this request. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * * @see GetObjectRequest#getMatchingETagConstraints() * @see GetObjectRequest#setMatchingETagConstraints(List) */ @@ -468,9 +570,10 @@ public GetObjectRequest withMatchingETagConstraint(String eTag) { * list matches the object's current ETag, this request will not be * executed by Amazon S3. * - * @return The optional list of ETag constraints that when present, - * must not include a match for the object's current ETag in - * order for this request to be executed. + * @return The optional list of ETag constraints that when present, must + * not include a match for the object's current ETag in order + * for this request to be executed. + * * @see GetObjectRequest#setNonmatchingETagConstraints(List) * @see GetObjectRequest#withNonmatchingETagConstraint(String) */ @@ -485,9 +588,11 @@ public List getNonmatchingETagConstraints() { * list matches the object's current ETag, this request will not be * executed by Amazon S3. * - * @param eTagList The list of ETag constraints that, when present, must - * not include a match for the object's current ETag in order - * for this request to be executed. + * @param eTagList + * The list of ETag constraints that, when present, must not + * include a match for the object's current ETag in order for + * this request to be executed. + * * @see GetObjectRequest#getNonmatchingETagConstraints() * @see GetObjectRequest#withNonmatchingETagConstraint(String) */ @@ -496,20 +601,24 @@ public void setNonmatchingETagConstraints(List eTagList) { } /** - * Sets a single ETag constraint to this request. Returns this - * {@link GetObjectRequest}, enabling additional method calls to be chained - * together. + * Sets a single ETag constraint to this request. + * Returns this {@link GetObjectRequest}, enabling additional method + * calls to be chained together. *

- * Multiple ETag constraints can be added to a request, but all ETag - * constraints must not match the object's current ETag in order for - * this request to be executed. If any entry in the non-matching ETag - * constraint list matches the object's current ETag, this request will - * not be executed by Amazon S3. + * Multiple ETag + * constraints can be added to a request, but all ETag constraints must + * not match the object's current ETag in order for this request to be + * executed. If any entry in the non-matching ETag constraint list matches + * the object's current ETag, this request will not be executed by + * Amazon S3. *

* - * @param eTag The non-matching ETag constraint to add to this request. - * @return This {@link GetObjectRequest}, enabling additional method calls to - * be chained together. + * @param eTag + * The non-matching ETag constraint to add to this request. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * * @see GetObjectRequest#getNonmatchingETagConstraints() * @see GetObjectRequest#setNonmatchingETagConstraints(List) */ @@ -519,13 +628,14 @@ public GetObjectRequest withNonmatchingETagConstraint(String eTag) { } /** - * Gets the optional unmodified constraint that restricts this request to - * executing only if the object has not been modified after the - * specified date. + * Gets the optional unmodified constraint that restricts this + * request to executing only if the object has not been + * modified after the specified date. + * + * @return The optional unmodified constraint that restricts this + * request to executing only if the object has not + * been modified after the specified date. * - * @return The optional unmodified constraint that restricts this request to - * executing only if the object has not been modified after - * the specified date. * @see GetObjectRequest#setUnmodifiedSinceConstraint(Date) * @see GetObjectRequest#withUnmodifiedSinceConstraint(Date) */ @@ -534,15 +644,17 @@ public Date getUnmodifiedSinceConstraint() { } /** - * Sets the optional unmodified constraint that restricts this request to - * executing only if the object has not been modified after the - * specified date. + * Sets the optional unmodified constraint that restricts this request + * to executing only if the object has not been modified after + * the specified date. *

* Note that Amazon S3 will ignore any dates occurring in the future. * - * @param date The unmodified constraint that restricts this request to - * executing only if the object has not been modified - * after this date. + * @param date + * The unmodified constraint that restricts this request to + * executing only if the object has not been + * modified after this date. + * * @see GetObjectRequest#getUnmodifiedSinceConstraint() * @see GetObjectRequest#withUnmodifiedSinceConstraint(Date) */ @@ -551,18 +663,22 @@ public void setUnmodifiedSinceConstraint(Date date) { } /** - * Sets the optional unmodified constraint that restricts this request to - * executing only if the object has not been modified after the - * specified date. Returns this {@link GetObjectRequest}, enabling - * additional method calls to be chained together. + * Sets the optional unmodified constraint that restricts this request + * to executing only if the object has not been modified after + * the specified date. + * Returns this {@link GetObjectRequest}, enabling additional method + * calls to be chained together. *

* Note that Amazon S3 will ignore any dates occurring in the future. * - * @param date The unmodified constraint that restricts this request to - * executing only if the object has not been modified - * after this date. - * @return This {@link GetObjectRequest}, enabling additional method calls to - * be chained together. + * @param date + * The unmodified constraint that restricts this request to + * executing only if the object has not been + * modified after this date. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * * @see GetObjectRequest#getUnmodifiedSinceConstraint() * @see GetObjectRequest#setUnmodifiedSinceConstraint(Date) */ @@ -572,13 +688,14 @@ public GetObjectRequest withUnmodifiedSinceConstraint(Date date) { } /** - * Gets the optional modified constraint that restricts this request to - * executing only if the object has been modified after the specified - * date. + * Gets the optional modified constraint that restricts this + * request to executing only if the object has been + * modified after the specified date. + * + * @return The optional modified constraint that restricts this + * request to executing only if the object has + * been modified after the specified date. * - * @return The optional modified constraint that restricts this request to - * executing only if the object has been modified after the - * specified date. * @see GetObjectRequest#setModifiedSinceConstraint(Date) * @see GetObjectRequest#withModifiedSinceConstraint(Date) */ @@ -587,16 +704,18 @@ public Date getModifiedSinceConstraint() { } /** - * Sets the optional modified constraint that restricts this request to - * executing only if the object has been modified after the specified - * date. + * Sets the optional modified constraint that restricts this request + * to executing only if the object has been modified after the + * specified date. *

* Note that Amazon S3 will ignore any dates occurring in the future. *

* - * @param date The modified constraint that restricts this request to - * executing only if the object has been modified after - * the specified date. + * @param date + * The modified constraint that restricts this request to + * executing only if the object has been modified + * after the specified date. + * * @see GetObjectRequest#getModifiedSinceConstraint() * @see GetObjectRequest#withModifiedSinceConstraint(Date) */ @@ -605,18 +724,22 @@ public void setModifiedSinceConstraint(Date date) { } /** - * Sets the optional modified constraint that restricts this request to - * executing only if the object has been modified after the specified - * date. Returns this {@link GetObjectRequest}, enabling additional method + * Sets the optional modified constraint that restricts this request + * to executing only if the object has been modified after the + * specified date. + * Returns this {@link GetObjectRequest}, enabling additional method * calls to be chained together. *

* Note that Amazon S3 will ignore any dates occurring in the future. * - * @param date The modified constraint that restricts this request to - * executing only if the object has been modified after - * the specified date. - * @return This {@link GetObjectRequest}, enabling additional method calls to - * be chained together. + * @param date + * The modified constraint that restricts this request to + * executing only if the object has been modified + * after the specified date. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * * @see GetObjectRequest#getModifiedSinceConstraint() * @see GetObjectRequest#setModifiedSinceConstraint(Date) */ @@ -637,8 +760,8 @@ public ResponseHeaderOverrides getResponseHeaders() { /** * Sets the headers to be overridden in the service response. * - * @param responseHeaders The headers to be overridden in the service - * response. + * @param responseHeaders + * The headers to be overridden in the service response. */ public void setResponseHeaders(ResponseHeaderOverrides responseHeaders) { this.responseHeaders = responseHeaders; @@ -648,8 +771,9 @@ public void setResponseHeaders(ResponseHeaderOverrides responseHeaders) { * Sets the headers to be overridden in the service response and returns * this object, for method chaining. * - * @param responseHeaders The headers to be overridden in the service - * response. + * @param responseHeaders + * The headers to be overridden in the service response. + * * @return This {@link GetObjectRequest} for method chaining. */ public GetObjectRequest withResponseHeaders(ResponseHeaderOverrides responseHeaders) { @@ -661,15 +785,15 @@ public GetObjectRequest withResponseHeaders(ResponseHeaderOverrides responseHead * Sets the optional progress listener for receiving updates about object * download status. * - * @param progressListener The legacy progress listener that is used - * exclusively for Amazon S3 client. + * @param progressListener + * The legacy progress listener that is used exclusively for Amazon S3 client. + * * @deprecated use {@link #setGeneralProgressListener(ProgressListener)} * instead. */ @Deprecated - public void setProgressListener( - com.amazonaws.services.s3.model.ProgressListener progressListener) { - this.generalProgressListener = new LegacyS3ProgressListener(progressListener); + public void setProgressListener(com.amazonaws.services.s3.model.ProgressListener progressListener) { + setGeneralProgressListener(new LegacyS3ProgressListener(progressListener)); } /** @@ -677,7 +801,8 @@ public void setProgressListener( * download status. * * @return the optional progress listener for receiving updates about object - * download status. + * download status. + * * @deprecated use {@link #getGeneralProgressListener()} instead. */ @Deprecated @@ -691,12 +816,14 @@ public com.amazonaws.services.s3.model.ProgressListener getProgressListener() { /** * Sets the optional progress listener for receiving updates about object - * download status, and returns this updated object so that additional - * method calls can be chained together. + * download status, and returns this updated object so that additional method + * calls can be chained together. + * + * @param progressListener + * The legacy progress listener that is used exclusively for Amazon S3 client. * - * @param progressListener The legacy progress listener that is used - * exclusively for Amazon S3 client. * @return This updated GetObjectRequest object. + * * @deprecated use {@link #withGeneralProgressListener(ProgressListener)} * instead. */ @@ -744,10 +871,12 @@ public GetObjectRequest withGeneralProgressListener(ProgressListener progressLis /** * Returns true if the user has enabled Requester Pays option when * downloading an object from Requester Pays Bucket; else false. + * *

* If a bucket is enabled for Requester Pays, then any attempt to read an * object from it without Requester Pays enabled will result in a 403 error * and the bucket owner will be charged for the request. + * *

* Enabling Requester Pays disables the ability to have anonymous access to * this bucket @@ -762,27 +891,49 @@ public boolean isRequesterPays() { /** * Used for downloading an Amazon S3 Object from a Requester Pays Bucket. If * set the requester is charged for downloading the data from the bucket. + * *

* If a bucket is enabled for Requester Pays, then any attempt to read an * object from it without Requester Pays enabled will result in a 403 error * and the bucket owner will be charged for the request. + * *

* Enabling Requester Pays disables the ability to have anonymous access to * this bucket * - * @param isRequesterPays Enable Requester Pays option for the operation. + * @param isRequesterPays + * Enable Requester Pays option for the operation. */ public void setRequesterPays(boolean isRequesterPays) { this.isRequesterPays = isRequesterPays; } /** - * Returns the optional customer-provided server-side encryption key to use - * to decrypt this object. + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated GetObjectRequest object so that additional method calls can be + * chained together. * - * @return The optional customer-provided server-side encryption key to use - * to decrypt this object. + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated GetObjectRequest object. */ + public GetObjectRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } + + @Override public SSECustomerKey getSSECustomerKey() { return sseCustomerKey; } @@ -791,8 +942,9 @@ public SSECustomerKey getSSECustomerKey() { * Sets the optional customer-provided server-side encryption key to use to * decrypt this object. * - * @param sseKey The optional customer-provided server-side encryption key - * to use to decrypt this object. + * @param sseKey + * The optional customer-provided server-side encryption key to + * use to decrypt this object. */ public void setSSECustomerKey(SSECustomerKey sseKey) { this.sseCustomerKey = sseKey; @@ -803,8 +955,10 @@ public void setSSECustomerKey(SSECustomerKey sseKey) { * decrypt this object, and returns the updated GetObjectRequest so that * additional method calls may be chained together. * - * @param sseKey The optional customer-provided server-side encryption key - * to use to decrypt this object. + * @param sseKey + * The optional customer-provided server-side encryption key to + * use to decrypt this object. + * * @return The optional customer-provided server-side encryption key to use * to decrypt this object. */ @@ -812,4 +966,87 @@ public GetObjectRequest withSSECustomerKey(SSECustomerKey sseKey) { setSSECustomerKey(sseKey); return this; } + + /** + *

+ * Returns the optional part number that indicates the part to be downloaded in a multipart object. + *

+ * + * @return The part number representing a part in a multipart object. + * + * @see GetObjectRequest#setPartNumber(Integer) + * @see GetObjectRequest#withPartNumber(Integer) + */ + public Integer getPartNumber() { + return partNumber; + } + + /** + *

+ * Sets the optional part number that indicates the part to be downloaded in a multipart object. + *

+ *

+ * The valid range for part number is 1 - 10000 inclusive. + * Part numbers are 1 based. If an object has 1 part, partNumber=1 would be the correct not 0. + * For partNumber < 1, an AmazonS3Exception is thrown with response code 400 bad request. + * For partNumber larger than actual part count, an AmazonS3Exception is thrown with response code 416 Request Range Not Satisfiable. + *

+ * + * @param partNumber + * The part number representing a part in a multipart object. + * + * @see GetObjectRequest#getPartNumber() + * @see GetObjectRequest#withPartNumber(Integer) + */ + public void setPartNumber(Integer partNumber) { + this.partNumber = partNumber; + } + + /** + *

+ * Sets the optional part number that indicates the part to be downloaded in a multipart object. + *

+ *

+ * The valid range for part number is 1 - 10000 inclusive. + * Part numbers are 1 based. If an object has 1 part, partNumber=1 would be the correct not 0. + * For partNumber < 1, an AmazonS3Exception is thrown with response code 400 bad request. + * For partNumber larger than actual part count, an AmazonS3Exception is thrown with response code 416 Request Range Not Satisfiable. + *

+ * + * @param partNumber + * The part number representing a part in a multipart object. + * + * @return This {@link GetObjectRequest}, enabling additional method + * calls to be chained together. + * + * @see GetObjectRequest#getPartNumber() + * @see GetObjectRequest#setPartNumber(Integer) + */ + public GetObjectRequest withPartNumber(Integer partNumber) { + setPartNumber(partNumber); + return this; + } + + + /** + * Returns an immutable S3 object id. + */ + public S3ObjectId getS3ObjectId() { + return s3ObjectIdBuilder.build(); + } + + /** + * Sets the S3 object id for this request. + */ + public void setS3ObjectId(S3ObjectId s3ObjectId) { + this.s3ObjectIdBuilder = new S3ObjectIdBuilder(s3ObjectId); + } + + /** + * Fluent API to set the S3 object id for this request. + */ + public GetObjectRequest withS3ObjectId(S3ObjectId s3ObjectId) { + setS3ObjectId(s3ObjectId); + return this; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectTaggingRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectTaggingRequest.java new file mode 100644 index 0000000000..ebe4c1f271 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectTaggingRequest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object for the parameters to get the tags for an object. + */ +public class GetObjectTaggingRequest extends AmazonWebServiceRequest implements Serializable { + private String bucketName; + private String key; + private String versionId; + + /** + * Construct an instance of this object. + * + * @param bucketName The name of the bucket. + * @param key The object key. + * @param versionId The version of the object. + */ + public GetObjectTaggingRequest(String bucketName, String key, String versionId) { + this.bucketName = bucketName; + this.key = key; + this.versionId = versionId; + } + + /** + * Construct an instance of this object. + * + * @param bucketName The name of the bucket. + * @param key The object key. + */ + public GetObjectTaggingRequest(String bucketName, String key) { + this(bucketName, key, null); + } + + /** + * @return The bucket name. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Set the bucket name. + * + * @param bucketName The bucket name. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Set the bucket name. + * + * @param bucketName the bucket name. + * @return This object for chaining. + */ + public GetObjectTaggingRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * @return The object key. + */ + public String getKey() { + return key; + } + + /** + * Set the object key. + * + * @param key The object key. + */ + public void setKey(String key) { + this.key = key; + } + + /** + * Set the object key. + * + * @param key The object key. + * @return This object for chaining. + */ + public GetObjectTaggingRequest withKey(String key) { + setKey(key); + return this; + } + + /** + * @return The version of the object. + */ + public String getVersionId() { + return versionId; + } + + /** + * Set object version. + * + * @param versionId The object version. + */ + public void setVersionId(String versionId) { + this.versionId = versionId; + } + + /** + * Set object version. + * + * @param versionId The object version. + * @return This object for chaining. + */ + public GetObjectTaggingRequest withVersionId(String versionId) { + setVersionId(versionId); + return this; + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectTaggingResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectTaggingResult.java new file mode 100644 index 0000000000..e9dabe2b22 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetObjectTaggingResult.java @@ -0,0 +1,89 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import java.util.List; + +/** + * Contains all the information returned from performing a + * {@link GetObjectTaggingRequest}. + */ +public class GetObjectTaggingResult { + private String versionId; + private List tagSet; + + /** + * Constructs an instance of this object. + * + * @param tagSet The tags set on the object. + */ + public GetObjectTaggingResult(List tagSet) { + this.tagSet = tagSet; + } + + /** + * @return The version ID of the object the tags were retrieved from. + */ + public String getVersionId() { + return versionId; + } + + /** + * Set the version ID of the object the tags were retrieved from. + * + * @param versionId The version ID. + */ + public void setVersionId(String versionId) { + this.versionId = versionId; + } + + /** + * Set the version ID of the object the tags were retrieved from. + * + * @param versionId The version ID. + * @return This object for method chaining. + */ + public GetObjectTaggingResult withVersionId(String versionId) { + setVersionId(versionId); + return this; + } + + /** + * @return The tag set. + */ + public List getTagSet() { + return tagSet; + } + + /** + * Set the tag set. + * + * @param tagSet The tag set. + */ + public void setTagSet(List tagSet) { + this.tagSet = tagSet; + } + + /** + * Set the tag set. + * + * @param tagSet The tag set. + * @return This object for chaining. + */ + public GetObjectTaggingResult withTagSet(List tagSet) { + setTagSet(tagSet); + return this; + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetRequestPaymentConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetRequestPaymentConfigurationRequest.java index 0755edec10..bf385992c4 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetRequestPaymentConfigurationRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetRequestPaymentConfigurationRequest.java @@ -17,12 +17,14 @@ import com.amazonaws.AmazonWebServiceRequest; +import java.io.Serializable; + /** * Request object for fetching the request payment configuration associated with * an Amazon S3 bucket. */ public class GetRequestPaymentConfigurationRequest extends - AmazonWebServiceRequest { + AmazonWebServiceRequest implements Serializable { /** The name of the Amazon S3 bucket. */ private String bucketName; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetS3AccountOwnerRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetS3AccountOwnerRequest.java new file mode 100644 index 0000000000..156b72db26 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/GetS3AccountOwnerRequest.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import com.amazonaws.AmazonWebServiceRequest; + +/** + * Request object for retrieving S3 account owner. + */ +public class GetS3AccountOwnerRequest extends AmazonWebServiceRequest + implements Serializable, S3AccelerateUnsupported { + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InitiateMultipartUploadRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InitiateMultipartUploadRequest.java index 9c9df8ac73..5865cbf58d 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InitiateMultipartUploadRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InitiateMultipartUploadRequest.java @@ -18,16 +18,27 @@ import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; + /** * The InitiateMultipartUploadRequest contains the parameters used for the * InitiateMultipartUpload method. *

+ * If you are initiating multipart upload for KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify + * -signature-version + *

+ *

* Required Parameters: BucketName, Key * * @see AmazonS3#initiateMultipartUpload(InitiateMultipartUploadRequest) */ -public class InitiateMultipartUploadRequest extends AmazonWebServiceRequest { - +public class InitiateMultipartUploadRequest extends AmazonWebServiceRequest + implements SSECustomerKeyProvider, SSEAwsKeyManagementParamsProvider, Serializable { /** * The name of the bucket in which to create the new multipart upload, and * hence, the eventual object created from the multipart upload. @@ -75,6 +86,18 @@ public class InitiateMultipartUploadRequest extends AmazonWebServiceRequest { */ private SSECustomerKey sseCustomerKey; + /** + * The optional AWS Key Management system parameters to be used to encrypt + * the the object on the server side. + */ + private SSEAwsKeyManagementParams sseAwsKeyManagementParams; + + /** + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; + /** * Constructs a request to initiate a new multipart upload in the specified * bucket, stored by the specified key. @@ -310,6 +333,15 @@ public InitiateMultipartUploadRequest withStorageClass(StorageClass storageClass return this; } + public InitiateMultipartUploadRequest withStorageClass(String storageClass) { + if (storageClass != null) { + this.storageClass = StorageClass.fromValue(storageClass); + } else { + this.storageClass = null; + } + return this; + } + /** * Returns the additional information about the new object being created, * such as content type, content encoding, user metadata, etc. @@ -378,13 +410,7 @@ public InitiateMultipartUploadRequest withRedirectLocation(String redirectLocati return this; } - /** - * Returns the optional customer-provided server-side encryption key to use - * to encrypt the upload being started. - * - * @return The optional customer-provided server-side encryption key to use - * to encrypt the upload being started. - */ + @Override public SSECustomerKey getSSECustomerKey() { return sseCustomerKey; } @@ -397,6 +423,10 @@ public SSECustomerKey getSSECustomerKey() { * to use to encrypt the upload being started. */ public void setSSECustomerKey(SSECustomerKey sseKey) { + if (sseKey != null && this.sseAwsKeyManagementParams != null) { + throw new IllegalArgumentException( + "Either SSECustomerKey or SSEAwsKeyManagementParams must not be set at the same time."); + } this.sseCustomerKey = sseKey; } @@ -415,4 +445,102 @@ public InitiateMultipartUploadRequest withSSECustomerKey(SSECustomerKey sseKey) setSSECustomerKey(sseKey); return this; } + + /** + * Returns the AWS Key Management System parameters used to encrypt the + * object on server side. + */ + @Override + public SSEAwsKeyManagementParams getSSEAwsKeyManagementParams() { + return sseAwsKeyManagementParams; + } + + /** + * Sets the AWS Key Management System parameters used to encrypt the object + * on server side. + */ + public void setSSEAwsKeyManagementParams(SSEAwsKeyManagementParams params) { + if (params != null && this.sseCustomerKey != null) { + throw new IllegalArgumentException( + "Either SSECustomerKey or SSEAwsKeyManagementParams must not be set at the same time."); + } + this.sseAwsKeyManagementParams = params; + } + + /** + * Sets the AWS Key Management System parameters used to encrypt the object + * on server side. + * + * @return returns the update InitiateMultipartUploadRequest + */ + public InitiateMultipartUploadRequest withSSEAwsKeyManagementParams( + SSEAwsKeyManagementParams sseAwsKeyManagementParams) { + setSSEAwsKeyManagementParams(sseAwsKeyManagementParams); + return this; + } + + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated InitiateMultipartUploadRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated InitiateMultipartUploadRequest object. + */ + public InitiateMultipartUploadRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InitiateMultipartUploadResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InitiateMultipartUploadResult.java index 73924bba4e..0d7aa63778 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InitiateMultipartUploadResult.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InitiateMultipartUploadResult.java @@ -18,6 +18,8 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.internal.SSEResultBase; +import java.util.Date; + /** * Contains the results of initiating a multipart upload, particularly the * unique ID of the new multipart upload. @@ -35,6 +37,18 @@ public class InitiateMultipartUploadResult extends SSEResultBase { /** The unique ID of the new multipart upload */ private String uploadId; + /** Date when multipart upload will become eligible for abort operation by lifecycle. */ + private Date abortDate; + + /** Id of the lifecycle rule that makes a multipart upload eligible for abort operation. */ + private String abortRuleId; + + /** + * Indicate if the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterCharged; + /** * Returns the name of the bucket in which the new multipart upload was * initiated. @@ -92,4 +106,50 @@ public String getUploadId() { public void setUploadId(String uploadId) { this.uploadId = uploadId; } + + /** + * Date when multipart upload will become eligible for abort operation by lifecycle. + * + * @return The date when the upload will be eligible for abort. + */ + public Date getAbortDate() { + return abortDate; + } + + /** + * Date when multipart upload will become eligible for abort operation by lifecycle. + * + * @param abortDate + * The date when the upload will be eligible for abort. + */ + public void setAbortDate(Date abortDate) { + this.abortDate = abortDate; + } + + /** + * Id of the lifecycle rule that makes a multipart upload eligible for abort operation. + * + * @return Rule ID + */ + public String getAbortRuleId() { + return abortRuleId; + } + + /** + * Id of the lifecycle rule that makes a multipart upload eligible for abort operation. + * + * @param abortRuleId Rule ID + */ + public void setAbortRuleId(String abortRuleId) { + this.abortRuleId = abortRuleId; + } + + public boolean isRequesterCharged() { + return isRequesterCharged; + } + + public void setRequesterCharged(boolean isRequesterCharged) { + this.isRequesterCharged = isRequesterCharged; + } + } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InstructionFileId.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InstructionFileId.java new file mode 100644 index 0000000000..c596ae9ea7 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/InstructionFileId.java @@ -0,0 +1,61 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + + +/** + * The S3 object identifier for an instruction file. + */ +public final class InstructionFileId extends S3ObjectId { + public static final String DEFAULT_INSTRUCTION_FILE_SUFFIX = "instruction"; + @Deprecated + public static final String DEFAULT_INSTURCTION_FILE_SUFFIX = DEFAULT_INSTRUCTION_FILE_SUFFIX; + public static final String DOT = "."; + + /** + * Package private to enable the enforcement of naming convention for + * instruction file. + * + * @param key + * key of the instruction file. + * @param versionId + * the version id of an instruction file is expected to be the + * same as that of the corresponding (encrypted) S3 object + * + * @see S3ObjectId#instructionFileId() + * @see S3ObjectId#instructionFileId(String) + */ + InstructionFileId(String bucket, String key, String versionId) { + super(bucket, key, versionId); + } + + /** + * Always throws {@link UnsupportedOperationException} since an instruction + * file itself cannot further have an instruction file. + */ + @Override + public InstructionFileId instructionFileId() { + throw new UnsupportedOperationException(); + } + + /** + * Always throws {@link UnsupportedOperationException} since an instruction + * file itself cannot further have an instruction file. + */ + @Override + public InstructionFileId instructionFileId(String suffix) { + throw new UnsupportedOperationException(); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/KMSEncryptionMaterials.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/KMSEncryptionMaterials.java new file mode 100644 index 0000000000..88a69ba78b --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/KMSEncryptionMaterials.java @@ -0,0 +1,88 @@ +/* + * Copyright 2010-20174 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import java.security.KeyPair; + +import javax.crypto.SecretKey; + +/** + * KMS specific encryption materials. + *

+ * The KEK has no relevance on the client-side, as KMS only requires the CMK + * id to be used to uniquely identify the KEK on the server side. + */ +public class KMSEncryptionMaterials extends EncryptionMaterials implements Serializable { + /** + * Name of the material description to be persisted in S3 for the KMS's + * customer master key id. + */ + public static final String CUSTOMER_MASTER_KEY_ID = "kms_cmk_id"; + /** + * @param defaultCustomerMasterKeyId + * KMS's customer master key id; must not be null + */ + public KMSEncryptionMaterials(String defaultCustomerMasterKeyId) { + super(null, null); + if (defaultCustomerMasterKeyId == null + || defaultCustomerMasterKeyId.length() == 0) + throw new IllegalArgumentException( + "The default customer master key id must be specified"); + addDescription(CUSTOMER_MASTER_KEY_ID, defaultCustomerMasterKeyId); + } + + /** + * Always throws UnsupportedOperationException. + */ + @Override + public final KeyPair getKeyPair() { + throw new UnsupportedOperationException(); + } + + /** + * Always throws UnsupportedOperationException. + */ + @Override + public final SecretKey getSymmetricKey() { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + * Always returns true. + * + * @return true + */ + @Override + public final boolean isKMSEnabled() { + return true; + } + + /** + * Returns the default KMS's Customer Master Key ID; or null if there + * isn't one. + */ + @Override + public String getCustomerMasterKeyId() { + return getDescription(CUSTOMER_MASTER_KEY_ID); + } + + @Override + public String toString() { + return String.valueOf(getMaterialsDescription()); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/KMSEncryptionMaterialsProvider.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/KMSEncryptionMaterialsProvider.java new file mode 100644 index 0000000000..c8e9a3a249 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/KMSEncryptionMaterialsProvider.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +public class KMSEncryptionMaterialsProvider extends StaticEncryptionMaterialsProvider implements Serializable { + public KMSEncryptionMaterialsProvider(String defaultCustomerMasterKeyId) { + this(new KMSEncryptionMaterials(defaultCustomerMasterKeyId)); + } + + public KMSEncryptionMaterialsProvider(KMSEncryptionMaterials materials) { + super(materials); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/LambdaConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/LambdaConfiguration.java new file mode 100644 index 0000000000..aec2cab099 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/LambdaConfiguration.java @@ -0,0 +1,65 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import java.util.EnumSet; + +/** + * Represents the lambda configuration for an Amazon S3 bucket notification. + * + */ +public class LambdaConfiguration extends NotificationConfiguration implements Serializable { + + /** + * The ARN for the lambda function to be invoked. + */ + private final String functionARN; + + /** + * Creates a new lambda configuration with the given cloud function arn + * and set of events. + * + * @param functionARN + * the ARN of the lambda function to be invoked + * @param events + * the events for which the notifications are to be sent + */ + public LambdaConfiguration(String functionARN, EnumSet events) { + super(events); + this.functionARN = functionARN; + } + + /** + * Creates a new lambda configuration with the given cloud function arn + * and set of events. + * + * @param functionARN + * the ARN of the lambda function to be invoked + * @param events + * the events for which the notifications are to be sent + */ + public LambdaConfiguration(String functionARN, String... events) { + super(events); + this.functionARN = functionARN; + } + + /** + * Returns the ARN of the cloud function to be invoked. + */ + public String getFunctionARN() { + return functionARN; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketAnalyticsConfigurationsRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketAnalyticsConfigurationsRequest.java new file mode 100644 index 0000000000..5c36acc3fa --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketAnalyticsConfigurationsRequest.java @@ -0,0 +1,107 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object to list the analytics configurations of a bucket. + */ +public class ListBucketAnalyticsConfigurationsRequest extends AmazonWebServiceRequest implements Serializable { + + /** The name of the Amazon S3 bucket to list the analytics configurations. */ + private String bucketName; + + /** + * Optional parameter which allows list to be continued from a specific point. + * ContinuationToken is provided in truncated list results. + */ + private String continuationToken; + + /** + * Gets the name of the Amazon S3 bucket whose + * analytics configurations are to be listed. + * + * @return The name of the Amazon S3 bucket whose + * analytics configurations are to be listed. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the Amazon S3 bucket whose analytics configurations are to be listed. + * + * @param bucketName + * The name of the Amazon S3 bucket whose analytics configurations are to be listed. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the Amazon S3 bucket whose analytics configurations are to be listed. + * Returns this {@link ListBucketAnalyticsConfigurationsRequest}, enabling additional method + * calls to be chained together. + * + * @param bucketName + * The name of the Amazon S3 bucket whose analytics configurations are to be listed. + * + * @return This {@link ListBucketAnalyticsConfigurationsRequest}, enabling additional method + * calls to be chained together. + */ + public ListBucketAnalyticsConfigurationsRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Gets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. ContinuationToken is provided in truncated list results. + * + * @return The optional continuation token associated with this request. + */ + public String getContinuationToken() { + return continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. ContinuationToken is provided in truncated list results. + * + * @param continuationToken + * The optional continuation token to associate with this request. + */ + public void setContinuationToken(String continuationToken) { + this.continuationToken = continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. ContinuationToken is provided in truncated list results. + * + * @param continuationToken + * The optional continuation token to associate with this request. + * + * @return This {@link ListBucketAnalyticsConfigurationsRequest}, enabling additional method + * calls to be chained together. + */ + public ListBucketAnalyticsConfigurationsRequest withContinuationToken(String continuationToken) { + setContinuationToken(continuationToken); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketAnalyticsConfigurationsResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketAnalyticsConfigurationsResult.java new file mode 100644 index 0000000000..283e9c8a35 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketAnalyticsConfigurationsResult.java @@ -0,0 +1,199 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration; + +import java.io.Serializable; +import java.util.List; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#listBucketAnalyticsConfigurations(ListBucketAnalyticsConfigurationsRequest)} + * operation. + */ +public class ListBucketAnalyticsConfigurationsResult implements Serializable { + + private List analyticsConfigurationList; + + /** + * Optional parameter which allows list to be continued from a specific point. + * This is the continuationToken that was sent in the current + * {@link ListBucketAnalyticsConfigurationsRequest}. + */ + private String continuationToken; + + /** + * Indicates if this is a complete listing, or if the caller needs to make + * additional requests to Amazon S3 to see the full analytics configuration + * listing for an S3 bucket. + */ + private boolean isTruncated; + + /** + * NextContinuationToken is sent when isTruncated is true meaning there are + * more analytics configurations in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + */ + private String nextContinuationToken; + + /** + * Returns the list of analytics configurations for a bucket. + */ + public List getAnalyticsConfigurationList() { + return analyticsConfigurationList; + } + + /** + * Sets the list of analytics configurations for a bucket. + */ + public void setAnalyticsConfigurationList(List analyticsConfigurationList) { + this.analyticsConfigurationList = analyticsConfigurationList; + } + + /** + * Sets the list of analytics configurations for a bucket and returns + * {@link ListBucketAnalyticsConfigurationsResult} object for method chaining. + */ + public ListBucketAnalyticsConfigurationsResult withAnalyticsConfigurationList(List analyticsConfigurationList) { + setAnalyticsConfigurationList(analyticsConfigurationList); + return this; + } + + /** + * Gets whether or not this analytics configuration listing is complete. + * + * @return The value true if the analytics configuration listing is not complete. + * Returns the value false if otherwise. + * When returning true, + * additional calls to Amazon S3 may be needed in order to + * obtain more results. + */ + public boolean isTruncated() { + return isTruncated; + } + + /** + * For internal use only. Sets the truncated property for + * this analytics configuration listing, indicating if this is a complete listing or not and + * whether the caller needs to make additional calls to S3 to get more results. + * + * @param isTruncated + * The value true if the analytics configuration listing is not complete. + * The value false if otherwise. + */ + public void setTruncated(boolean isTruncated) { + this.isTruncated = isTruncated; + } + + /** + * For internal use only. Sets the truncated property for + * this analytics configuration listing, indicating if this is a complete listing or not and + * whether the caller needs to make additional calls to S3 to get more results. + * + * @param isTruncated + * The value true if the analytics configuration listing is not complete. + * The value false if otherwise. + * + * @return + * This object for method chaining. + */ + public ListBucketAnalyticsConfigurationsResult withTruncated(boolean isTruncated) { + setTruncated(isTruncated); + return this; + } + + /** + * Gets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. This is the continuationToken that was sent in the current + * {@link ListBucketAnalyticsConfigurationsRequest}. + * + * @return The optional continuation token associated with this request. + */ + public String getContinuationToken() { + return continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. This is the continuationToken that was sent in the current + * {@link ListBucketAnalyticsConfigurationsRequest}. + * + * @param continuationToken + * The optional continuation token to associate with this request. + */ + public void setContinuationToken(String continuationToken) { + this.continuationToken = continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. This is the continuationToken that was sent in the current + * {@link ListBucketAnalyticsConfigurationsRequest}. + * + * @param continuationToken + * The optional continuation token to associate with this request. + * + * @return + * This object for method chaining. + */ + public ListBucketAnalyticsConfigurationsResult withContinuationToken(String continuationToken) { + setContinuationToken(continuationToken); + return this; + } + + /** + * Gets the optional NextContinuationToken. + * NextContinuationToken is sent when isTruncated is true meaning there are + * more keys in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + * + * @return The optional NextContinuationToken parameter. + */ + public String getNextContinuationToken() { + return nextContinuationToken; + } + + /** + * Sets the optional NextContinuationToken. + * NextContinuationToken is sent when isTruncated is true meaning there are + * more keys in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + * + * @param nextContinuationToken + * The optional NextContinuationToken parameter to associate with this request. + */ + public void setNextContinuationToken(String nextContinuationToken) { + this.nextContinuationToken = nextContinuationToken; + } + + /** + * Sets the optional NextContinuationToken. + * NextContinuationToken is sent when isTruncated is true meaning there are + * more keys in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + * + * @param nextContinuationToken + * The optional NextContinuationToken parameter to associate with this request. + * + * @return + * This object for method chaining. + */ + public ListBucketAnalyticsConfigurationsResult withNextContinuationToken(String nextContinuationToken) { + setNextContinuationToken(nextContinuationToken); + return this; + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketInventoryConfigurationsRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketInventoryConfigurationsRequest.java new file mode 100644 index 0000000000..8f78d560e1 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketInventoryConfigurationsRequest.java @@ -0,0 +1,110 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object to list inventory configurations of a bucket. + */ +public class ListBucketInventoryConfigurationsRequest extends AmazonWebServiceRequest implements Serializable { + + /** The name of the Amazon S3 bucket to list the inventory configurations. */ + private String bucketName; + + /** + * Optional parameter which allows list to be continued from a specific point. + * ContinuationToken is provided in truncated list results. + */ + private String continuationToken; + + /** + * Gets the name of the Amazon S3 bucket whose + * inventory configurations are to be listed. + * + * @return The name of the Amazon S3 bucket whose + * inventory configurations are to be listed. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the Amazon S3 bucket whose inventory configurations are to be listed. + * + * @param bucketName + * The name of the Amazon S3 bucket whose inventory + * configurations are to be listed. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the Amazon S3 bucket whose inventory configurations are to be listed. + * Returns this {@link ListBucketInventoryConfigurationsRequest}, enabling additional method + * calls to be chained together. + * + * @param bucketName + * The name of the Amazon S3 bucket whose inventory + * configurations are to be listed. + * + * @return This {@link ListBucketInventoryConfigurationsRequest}, enabling additional method + * calls to be chained together. + */ + public ListBucketInventoryConfigurationsRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Gets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. ContinuationToken is provided in truncated list results. + * + * @return The optional continuation token associated with this request. + */ + public String getContinuationToken() { + return continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. ContinuationToken is provided in truncated list results. + * + * @param continuationToken + * The optional continuation token to associate with this request. + */ + public void setContinuationToken(String continuationToken) { + this.continuationToken = continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. ContinuationToken is provided in truncated list results. + * + * @param continuationToken + * The optional continuation token to associate with this request. + * + * @return This {@link ListBucketInventoryConfigurationsRequest}, enabling additional method + * calls to be chained together. + */ + public ListBucketInventoryConfigurationsRequest withContinuationToken(String continuationToken) { + setContinuationToken(continuationToken); + return this; + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketInventoryConfigurationsResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketInventoryConfigurationsResult.java new file mode 100644 index 0000000000..d3e5de223e --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketInventoryConfigurationsResult.java @@ -0,0 +1,201 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.services.s3.model.inventory.InventoryConfiguration; + +import java.io.Serializable; +import java.util.List; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#listBucketInventoryConfigurations(ListBucketInventoryConfigurationsRequest)} + * operation. + */ +public class ListBucketInventoryConfigurationsResult implements Serializable { + + /** The list of inventory configurations for a bucket. */ + private List inventoryConfigurationList; + + /** + * Optional parameter which allows list to be continued from a specific point. + * This is the continuationToken that was sent in the current + * {@link ListBucketInventoryConfigurationsResult}. + */ + private String continuationToken; + + /** + * Indicates if this is a complete listing, or if the caller needs to make + * additional requests to Amazon S3 to see the full inventory configuration + * listing for an S3 bucket. + */ + private boolean isTruncated; + + /** + * NextContinuationToken is sent when isTruncated is true meaning there are + * more inventory configurations in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + */ + private String nextContinuationToken; + + /** + * Returns the list of inventory configurations for a bucket. + */ + public List getInventoryConfigurationList() { + return inventoryConfigurationList; + } + + /** + * Sets the list of inventory configurations for a bucket. + */ + public void setInventoryConfigurationList(List inventoryConfigurationList) { + this.inventoryConfigurationList = inventoryConfigurationList; + } + + /** + * Returns the list of inventory configurations for a bucket and returns + * {@link ListBucketInventoryConfigurationsResult} object for method chaining. + */ + public ListBucketInventoryConfigurationsResult withInventoryConfigurationList(List inventoryConfigurationList) { + setInventoryConfigurationList(inventoryConfigurationList); + return this; + } + + /** + * Gets whether or not this inventory configuration listing is complete. + * + * @return The value true if the inventory configuration listing is not complete. + * Returns the value false if otherwise. + * When returning true, + * additional calls to Amazon S3 may be needed in order to + * obtain more results. + */ + public boolean isTruncated() { + return isTruncated; + } + + /** + * For internal use only. Sets the truncated property for + * this inventory configuration listing, indicating if this is a complete listing or not and + * whether the caller needs to make additional calls to S3 to get more results. + * + * @param isTruncated + * The value true if the inventory configuration listing is not complete. + * The value false if otherwise. + */ + public void setTruncated(boolean isTruncated) { + this.isTruncated = isTruncated; + } + + /** + * For internal use only. Sets the truncated property for + * this inventory configuration listing, indicating if this is a complete listing or not and + * whether the caller needs to make additional calls to S3 to get more results. + * + * @param isTruncated + * The value true if the inventory configuration listing is not complete. + * The value false if otherwise. + * + * @return + * This object for method chaining. + */ + public ListBucketInventoryConfigurationsResult withTruncated(boolean isTruncated) { + setTruncated(isTruncated); + return this; + } + + /** + * Gets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. This is the continuationToken that was sent in the current + * {@link ListBucketInventoryConfigurationsResult}. + * + * @return The optional continuation token associated with this request. + */ + public String getContinuationToken() { + return continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. This is the continuationToken that was sent in the current + * {@link ListBucketInventoryConfigurationsResult}. + * + * @param continuationToken + * The optional continuation token to associate with this request. + */ + public void setContinuationToken(String continuationToken) { + this.continuationToken = continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. This is the continuationToken that was sent in the current + * {@link ListBucketInventoryConfigurationsResult}. + * + * @param continuationToken + * The optional continuation token to associate with this request. + * + * @return + * This object for method chaining. + */ + public ListBucketInventoryConfigurationsResult withContinuationToken(String continuationToken) { + setContinuationToken(continuationToken); + return this; + } + + /** + * Gets the optional NextContinuationToken. + * NextContinuationToken is sent when isTruncated is true meaning there are + * more keys in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + * + * @return The optional NextContinuationToken parameter. + */ + public String getNextContinuationToken() { + return nextContinuationToken; + } + + /** + * Sets the optional NextContinuationToken. + * NextContinuationToken is sent when isTruncated is true meaning there are + * more keys in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + * + * @param nextContinuationToken + * The optional NextContinuationToken parameter to associate with this request. + */ + public void setNextContinuationToken(String nextContinuationToken) { + this.nextContinuationToken = nextContinuationToken; + } + + + /** + * Sets the optional NextContinuationToken. + * NextContinuationToken is sent when isTruncated is true meaning there are + * more keys in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + * + * @param nextContinuationToken + * The optional NextContinuationToken parameter to associate with this request. + * + * @return + * This object for method chaining. + */ + public ListBucketInventoryConfigurationsResult withNextContinuationToken(String nextContinuationToken) { + setNextContinuationToken(nextContinuationToken); + return this; + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketMetricsConfigurationsRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketMetricsConfigurationsRequest.java new file mode 100644 index 0000000000..45db2f6348 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketMetricsConfigurationsRequest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object to list the metrics configurations of a bucket. + */ +public class ListBucketMetricsConfigurationsRequest extends AmazonWebServiceRequest implements Serializable { + + /** The name of the Amazon S3 bucket to list the metrics configurations. */ + private String bucketName; + + /** + * Optional parameter which allows list to be continued from a specific point. + * ContinuationToken is provided in truncated list results. + */ + private String continuationToken; + + /** + * Gets the name of the bucket containing the metrics configurations to retrieve. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket containing the metrics configurations to retrieve. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket containing the metrics configurations to retrieve + * and returns {@link ListBucketMetricsConfigurationsRequest} object for + * method chaining. + */ + public ListBucketMetricsConfigurationsRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Gets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. ContinuationToken is provided in truncated list results. + */ + public String getContinuationToken() { + return continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. ContinuationToken is provided in truncated list results. + */ + public void setContinuationToken(String continuationToken) { + this.continuationToken = continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. ContinuationToken is provided in truncated list results. + * + * Returns the {@link ListBucketMetricsConfigurationsRequest} object enabling + * method chaining. + */ + public ListBucketMetricsConfigurationsRequest withContinuationToken(String continuationToken) { + setContinuationToken(continuationToken); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketMetricsConfigurationsResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketMetricsConfigurationsResult.java new file mode 100644 index 0000000000..e6866ff964 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListBucketMetricsConfigurationsResult.java @@ -0,0 +1,199 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.services.s3.model.metrics.MetricsConfiguration; + +import java.io.Serializable; +import java.util.List; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#listBucketMetricsConfigurations(ListBucketMetricsConfigurationsRequest)} + * operation. + */ +public class ListBucketMetricsConfigurationsResult implements Serializable { + + /** The list of metrics configurations for a bucket. */ + private List metricsConfigurationList; + + /** + * Optional parameter which allows list to be continued from a specific point. + * This is the continuationToken that was sent in the current + * {@link ListBucketMetricsConfigurationsRequest}. + */ + private String continuationToken; + + /** + * Indicates if this is a complete listing, or if the caller needs to make + * additional requests to Amazon S3 to see the full metrics configuration + * listing for an S3 bucket. + */ + private boolean isTruncated; + + /** + * NextContinuationToken is sent when isTruncated is true meaning there are + * more metrics configurations in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + */ + private String nextContinuationToken; + + /** + * Returns the list of metrics configurations for a bucket. + */ + public List getMetricsConfigurationList() { + return metricsConfigurationList; + } + + /** + * Sets the list of metrics configurations. + */ + public void setMetricsConfigurationList(List metricsConfigurationList) { + this.metricsConfigurationList = metricsConfigurationList; + } + + /** + * Sets the list of metrics configurations and returns the + * {@link ListBucketMetricsConfigurationsResult} object for method chaining. + */ + public ListBucketMetricsConfigurationsResult withMetricsConfigurationList(List metricsConfigurationList) { + setMetricsConfigurationList(metricsConfigurationList); + return this; + } + + /** + * Gets whether or not this metrics configuration listing is complete. + * + * @return The value true if the metrics configuration listing is not complete. + * Returns the value false if otherwise. + * When returning true, + * additional calls to Amazon S3 may be needed in order to + * obtain more results. + */ + public boolean isTruncated() { + return isTruncated; + } + + /** + * For internal use only. Sets the truncated property for + * this metrics configuration listing, indicating if this is a complete listing or not and + * whether the caller needs to make additional calls to S3 to get more results. + * + * @param isTruncated + * The value true if the metrics configuration listing is not complete. + * The value false if otherwise. + */ + public void setTruncated(boolean isTruncated) { + this.isTruncated = isTruncated; + } + + /** + * For internal use only. Sets the truncated property for + * this metrics configuration listing, indicating if this is a complete listing or not and + * whether the caller needs to make additional calls to S3 to get more results. + * + * @param isTruncated + * The value true if the metrics configuration listing is not complete. + * The value false if otherwise. + * + * @return + * This object for method chaining. + */ + public ListBucketMetricsConfigurationsResult withTruncated(boolean isTruncated) { + setTruncated(isTruncated); + return this; + } + + /** + * Gets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. This is the continuationToken that was sent in the current + * {@link ListBucketMetricsConfigurationsRequest}. + * + * @return The optional continuation token associated with this request. + */ + public String getContinuationToken() { + return continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. This is the continuationToken that was sent in the current + * {@link ListBucketMetricsConfigurationsRequest}. + * + * @param continuationToken + * The optional continuation token to associate with this request. + */ + public void setContinuationToken(String continuationToken) { + this.continuationToken = continuationToken; + } + + /** + * Sets the optional continuation token. Continuation token allows a list to be + * continued from a specific point. This is the continuationToken that was sent in the current + * {@link ListBucketMetricsConfigurationsRequest}. + * + * @param continuationToken + * The optional continuation token to associate with this request. + * + * @return + * This object for method chaining. + */ + public ListBucketMetricsConfigurationsResult withContinuationToken(String continuationToken) { + setContinuationToken(continuationToken); + return this; + } + + /** + * Gets the optional NextContinuationToken. + * NextContinuationToken is sent when isTruncated is true meaning there are + * more keys in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + * + * @return The optional NextContinuationToken parameter. + */ + public String getNextContinuationToken() { + return nextContinuationToken; + } + + /** + * Sets the optional NextContinuationToken. + * NextContinuationToken is sent when isTruncated is true meaning there are + * more keys in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + * + * @param nextContinuationToken + * The optional NextContinuationToken parameter to associate with this request. + */ + public void setNextContinuationToken(String nextContinuationToken) { + this.nextContinuationToken = nextContinuationToken; + } + + /** + * Sets the optional NextContinuationToken. + * NextContinuationToken is sent when isTruncated is true meaning there are + * more keys in the bucket that can be listed. The next list requests to Amazon + * S3 can be continued by providing this NextContinuationToken. + * + * @param nextContinuationToken + * The optional NextContinuationToken parameter to associate with this request. + * + * @return + * This object for method chaining. + */ + public ListBucketMetricsConfigurationsResult withNextContinuationToken(String nextContinuationToken) { + setNextContinuationToken(nextContinuationToken); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListNextBatchOfObjectsRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListNextBatchOfObjectsRequest.java new file mode 100644 index 0000000000..88ac38e4af --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListNextBatchOfObjectsRequest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2015-2017 Amazon Technologies, 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import com.amazonaws.AmazonWebServiceRequest; + +/** + * Request object for parameters of listing next batch of objects. + */ +public class ListNextBatchOfObjectsRequest extends AmazonWebServiceRequest implements Serializable { + + private ObjectListing previousObjectListing; + + /** + * Creates a request object for listing next batch of objects. + * + * @param previousObjectListing + * The previous object listing whose features are to be fetched. + */ + public ListNextBatchOfObjectsRequest(ObjectListing previousObjectListing) { + setPreviousObjectListing(previousObjectListing); + } + + /** + * Returns the previous object listing. + * @return The previous object listing. + */ + public ObjectListing getPreviousObjectListing() { + return previousObjectListing; + } + + /** + * Sets the previous object listing and all the features of the next object listing as well. + * @param previousObjectListing + * This parameter must be specified. + */ + public void setPreviousObjectListing(ObjectListing previousObjectListing) { + if(previousObjectListing == null) { + throw new IllegalArgumentException("The parameter previousObjectListing must be specified."); + } + this.previousObjectListing = previousObjectListing; + } + + /** + * Sets the previous object listing and returns the updated request object so that additional + * method calls can be chained together. + * + * @param previousObjectListing + * The previous object listing whose features are to be fetched. + * @return The updated request object so that additional method calls can be chained together. + */ + public ListNextBatchOfObjectsRequest withPreviousObjectListing(ObjectListing previousObjectListing) { + setPreviousObjectListing(previousObjectListing); + return this; + } + + /** + * Creates a new {@link ListObjectsRequest} object using the previous object listing. + * @return A new {@link ListObjectsRequest} object using the previous object listing. + */ + public ListObjectsRequest toListObjectsRequest() { + return new ListObjectsRequest(previousObjectListing.getBucketName(), + previousObjectListing.getPrefix(), + previousObjectListing.getNextMarker(), + previousObjectListing.getDelimiter(), + Integer.valueOf(previousObjectListing.getMaxKeys())) + .withEncodingType(previousObjectListing.getEncodingType()); + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListNextBatchOfVersionsRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListNextBatchOfVersionsRequest.java new file mode 100644 index 0000000000..ce80ad62fe --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListNextBatchOfVersionsRequest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015-2017 Amazon Technologies, 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import com.amazonaws.AmazonWebServiceRequest; + +/** + * Request object for the parameters to list next batch of versions. + */ +public class ListNextBatchOfVersionsRequest extends AmazonWebServiceRequest implements Serializable { + + private VersionListing previousVersionListing; + + /** + * Creates a request object for listing next batch of versions. + * + * @param previousVersionListing + * The previous version listing whose features are to be fetched. + */ + public ListNextBatchOfVersionsRequest(VersionListing previousVersionListing) { + setPreviousVersionListing(previousVersionListing); + } + + /** + * Returns the previous version listing. + * @return The previous version listing. + */ + public VersionListing getPreviousVersionListing() { + return previousVersionListing; + } + + /** + * Sets the previous version listing and all the features of the next version listing as well. + * @param previousVersionListing + * This parameter must be specified. + */ + public void setPreviousVersionListing(VersionListing previousVersionListing) { + if(previousVersionListing == null) { + throw new IllegalArgumentException("The parameter previousVersionListing must be specified."); + } + this.previousVersionListing = previousVersionListing; + } + + /** + * Sets the previous version listing and returns the updated request object so that additional + * method calls can be chained together. + * + * @param previousVersionListing + * The previous version listing object whose features are to be fetched. + * @return This updated request object so that additional method calls can be chained together. + */ + public ListNextBatchOfVersionsRequest withPreviousVersionListing(VersionListing previousVersionListing) { + setPreviousVersionListing(previousVersionListing); + return this; + } + + /** + * Creates a new {@link ListVersionsRequest} object using the previous version listing. + * @return A new {@link ListVersionsRequest} object using the previous version listing. + */ + public ListVersionsRequest toListVersionsRequest() { + return new ListVersionsRequest(previousVersionListing.getBucketName(), + previousVersionListing.getPrefix(), + previousVersionListing.getNextKeyMarker(), + previousVersionListing.getNextVersionIdMarker(), + previousVersionListing.getDelimiter(), + Integer.valueOf(previousVersionListing.getMaxKeys())) + .withEncodingType(previousVersionListing.getEncodingType()); + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListPartsRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListPartsRequest.java index 6575369c57..08520e3785 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListPartsRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ListPartsRequest.java @@ -58,6 +58,13 @@ public class ListPartsRequest extends AmazonWebServiceRequest { */ private String encodingType; + /** + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; + + /** * Constructs a new ListPartsRequest from the required parameters bucket * name, key and upload ID. @@ -291,4 +298,70 @@ public ListPartsRequest withEncodingType(String encodingType) { return this; } + + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated ListPartsRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated ListPartsRequest object. + */ + public ListPartsRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } + } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/MultiObjectDeleteException.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/MultiObjectDeleteException.java index ef63965e43..1d56455d1e 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/MultiObjectDeleteException.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/MultiObjectDeleteException.java @@ -40,6 +40,17 @@ public MultiObjectDeleteException(Collection errors, this.errors.addAll(errors); } + /** + * Always returns {@code null} since this exception represents a + * "successful" response from the service with no top-level error code. Use + * {@link #getErrors()} to retrieve a list of objects whose deletion failed, + * along with the error code and message for each individual failure. + */ + @Override + public String getErrorCode() { + return super.getErrorCode(); + } + /** * Returns the list of successfully deleted objects from this request. If * {@link DeleteObjectsRequest#getQuiet()} is true, only error responses diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/NotificationConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/NotificationConfiguration.java new file mode 100644 index 0000000000..1e73cb468b --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/NotificationConfiguration.java @@ -0,0 +1,198 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * An abstract class for all the notification configurations associated with an Amazon S3 bucket. + */ +public abstract class NotificationConfiguration { + + /** + * Set of events for a notification configuration. + */ + private Set events = new HashSet(); + + /** + * @deprecated This field is not used by S3. It will be removed in the next major version of the + * SDK + */ + @Deprecated + private List objectPrefixes = new ArrayList(); + + private Filter filter; + + /** + * Creates a NotificationConfiguration with empty events and prefixes. + */ + protected NotificationConfiguration() { + } + + /** + * Creates a notification configuration with the given set of events. + * + * @param events + * the list of events for the notification configuration. + */ + protected NotificationConfiguration(EnumSet events) { + if (events != null) { + for (S3Event s3Event : events) { + this.events.add(s3Event.toString()); + } + } + } + + /** + * Creates a notification configuration with the given set of events. + * + * @param events + * the list of events for the notification configuration. + */ + protected NotificationConfiguration(String... events) { + if (events != null) { + for (String event : events) { + this.events.add(event); + } + } + } + + /** + * Returns the set of events associated with this notification configuration. + */ + public Set getEvents() { + return events; + } + + /** + * Sets the given events in this {@link NotificationConfiguration} object. + * + * @param events + * the set of events for the notification configuration. + */ + public void setEvents(Set events) { + this.events = events; + } + + /** + * @deprecated This field is not used by S3. It will be removed in the next major version of the + * SDK. See {@link #getFilter()} for the correct way to filter notifications. + */ + @Deprecated + public List getObjectPrefixes() { + return objectPrefixes; + } + + /** + * @deprecated This field is not used by S3. It will be removed in the next major version of the + * SDK. See {@link #setFilter(Filter)} for the correct way to filter notifications. + */ + @Deprecated + public void setObjectPrefixes(List objectPrefixes) { + this.objectPrefixes = objectPrefixes; + } + + /** + * Sets the given events in this {@link NotificationConfiguration} object and returns this + * object. + * + * @param events + * the set of events for the notification configuration. + */ + public NotificationConfiguration withEvents(Set events) { + this.events.clear(); + this.events.addAll(events); + return this; + } + + /** + * @deprecated This field is not used by S3. It will be removed in the next major version of the + * SDK. See {@link #withFilter(Filter)} for the correct way to filter notifications. + */ + @Deprecated + public NotificationConfiguration withObjectPrefixes(String... objectPrefixes) { + this.objectPrefixes.clear(); + if (objectPrefixes != null && objectPrefixes.length > 0) + this.objectPrefixes.addAll(Arrays.asList(objectPrefixes)); + return this; + } + + /** + * Adds the given event to the set of events for this {@link NotificationConfiguration} object. + * + * @param event + * the event to add to this notification configuration + */ + public void addEvent(String event) { + this.events.add(event); + } + + /** + * Adds the given event to the set of events for this {@link NotificationConfiguration} object. + * + * @param event + * the event to add to this notification configuration + */ + public void addEvent(S3Event event) { + this.events.add(event.toString()); + } + + /** + * @deprecated This field is not used by S3. It will be removed in the next major version of the + * SDK + */ + @Deprecated + public void addObjectPrefix(String prefix) { + this.objectPrefixes.add(prefix); + } + + /** + * Filter criteria for determining which S3 objects trigger event notifications. + * + * @return {@link Filter} object associated with this {@link NotificationConfiguration} + */ + public Filter getFilter() { + return filter; + } + + /** + * Sets the filter criteria for this {@link NotificationConfiguration}. + * + * @param filter + * New {@link Filter} + */ + public void setFilter(Filter filter) { + this.filter = filter; + } + + /** + * Sets the filter criteria for this {@link NotificationConfiguration} and returns this object + * for method chaining. + * + * @param filter + * New {@link Filter} + * @return This object for method chaining + */ + public NotificationConfiguration withFilter(Filter filter) { + setFilter(filter); + return this; + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectListing.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectListing.java index 64804e5625..73c87009ef 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectListing.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectListing.java @@ -17,9 +17,9 @@ import com.amazonaws.services.s3.AmazonS3; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; - /** * Contains the results of listing the objects in an Amazon S3 bucket. This * includes a list of {@link S3ObjectSummary} objects describing the objects @@ -31,11 +31,9 @@ * @see AmazonS3#listObjects(ListObjectsRequest) * @see AmazonS3#listNextBatchOfObjects(ObjectListing) */ -public class ObjectListing { - - /** - * A list of summary information describing the objects stored in the bucket - */ +public class ObjectListing implements Serializable { + + /** A list of summary information describing the objects stored in the bucket */ private List objectSummaries = new ArrayList(); /** diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectMetadata.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectMetadata.java index 1ca8a6370c..36e6cf535e 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectMetadata.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectMetadata.java @@ -15,23 +15,29 @@ package com.amazonaws.services.s3.model; +import static com.amazonaws.util.DateUtils.cloneDate; + +import com.amazonaws.AmazonClientException; import com.amazonaws.services.s3.Headers; +import com.amazonaws.services.s3.internal.Constants; import com.amazonaws.services.s3.internal.ObjectExpirationResult; import com.amazonaws.services.s3.internal.ObjectRestoreResult; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; import com.amazonaws.services.s3.internal.ServerSideEncryptionResult; +import java.io.Serializable; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; /** * Represents the object metadata that is stored with Amazon S3. This includes * custom user-supplied metadata, as well as the standard HTTP headers that * Amazon S3 sends and receives (Content-Length, ETag, Content-MD5, etc.). */ -public class ObjectMetadata implements ServerSideEncryptionResult, - ObjectExpirationResult, ObjectRestoreResult, Cloneable +public class ObjectMetadata implements ServerSideEncryptionResult, S3RequesterChargedResult, + ObjectExpirationResult, ObjectRestoreResult, Cloneable, Serializable { /* * TODO: Might be nice to get as many of the internal use only methods out @@ -44,17 +50,18 @@ public class ObjectMetadata implements ServerSideEncryptionResult, * Custom user metadata, represented in responses with the x-amz-meta- * header prefix */ - private Map userMetadata; + private Map userMetadata = new TreeMap(String.CASE_INSENSITIVE_ORDER); /** * All other (non user custom) headers such as Content-Length, Content-Type, * etc. */ - private Map metadata; + private Map metadata = new TreeMap(String.CASE_INSENSITIVE_ORDER); - public static final String AES_256_SERVER_SIDE_ENCRYPTION = "AES256"; + public static final String AES_256_SERVER_SIDE_ENCRYPTION = + SSEAlgorithm.AES256.getAlgorithm(); - public static final String KMS_SERVER_SIDE_ENCRYPTION = "aws:kms"; + public static final String KMS_SERVER_SIDE_ENCRYPTION = SSEAlgorithm.KMS.getAlgorithm(); /** * The date when the object is no longer cacheable. @@ -87,6 +94,22 @@ public class ObjectMetadata implements ServerSideEncryptionResult, * accessed. Null if this object has not been restored from Glacier. */ private Date restoreExpirationTime; + public ObjectMetadata() {} + + private ObjectMetadata(ObjectMetadata from) { + this.userMetadata = from.userMetadata == null + ? null + : new TreeMap(from.userMetadata); + // shallow clone the metadata data + this.metadata = from.metadata == null + ? null + : new TreeMap(from.metadata); + this.expirationTime = cloneDate(from.expirationTime); + this.expirationTimeRuleId = from.expirationTimeRuleId; + this.httpExpiresDate = cloneDate(from.httpExpiresDate); + this.ongoingRestore = from.ongoingRestore; + this.restoreExpirationTime = cloneDate(from.restoreExpirationTime); + } /** *

@@ -113,6 +136,7 @@ public class ObjectMetadata implements ServerSideEncryptionResult, *

* * @return The custom user metadata for the associated object. + * * @see ObjectMetadata#setUserMetadata(Map) * @see ObjectMetadata#addUserMetadata(String, String) */ @@ -203,7 +227,9 @@ public void addUserMetadata(String key, String value) { * @return A map of the raw metadata/headers for the associated object. */ public Map getRawMetadata() { - return Collections.unmodifiableMap(new HashMap(metadata)); + final Map copy = new TreeMap(String.CASE_INSENSITIVE_ORDER); + copy.putAll(metadata); + return Collections.unmodifiableMap(copy); } /** @@ -223,7 +249,7 @@ public Object getRawMetadataValue(String key) { * Last-Modified header hasn't been set. */ public Date getLastModified() { - return (Date) metadata.get(Headers.LAST_MODIFIED); + return cloneDate((Date) metadata.get(Headers.LAST_MODIFIED)); } /** @@ -262,10 +288,11 @@ public void setLastModified(Date lastModified) { * @see ObjectMetadata#setContentLength(long) */ public long getContentLength() { - Long contentLength = (Long) metadata.get(Headers.CONTENT_LENGTH); + final Long contentLength = (Long) metadata.get(Headers.CONTENT_LENGTH); - if (contentLength == null) + if (contentLength == null) { return 0; + } return contentLength.longValue(); } @@ -276,11 +303,12 @@ public long getContentLength() { public long getInstanceLength() { // See Content-Range in // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - String contentRange = (String) metadata.get(Headers.CONTENT_RANGE); + final String contentRange = (String) metadata.get(Headers.CONTENT_RANGE); if (contentRange != null) { - int pos = contentRange.lastIndexOf("/"); - if (pos >= 0) + final int pos = contentRange.lastIndexOf("/"); + if (pos >= 0) { return Long.parseLong(contentRange.substring(pos + 1)); + } } return getContentLength(); } @@ -368,6 +396,48 @@ public void setContentType(String contentType) { metadata.put(Headers.CONTENT_TYPE, contentType); } + /** + *

+ * Gets the Content-Language HTTP header, which describes the natural language(s) of the + * intended audience for the enclosed entity. + *

+ *

+ * For more information on the Content-Type header, see + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17 + *

+ * + * @return The HTTP Content-Language header, which describes the natural language(s) of the + * intended audience for the enclosed entity. Returns null + * if it hasn't been set. + * + * @see ObjectMetadata#setContentLanguage(String) + */ + public String getContentLanguage() { + return (String)metadata.get(Headers.CONTENT_LANGUAGE); + } + + /** + *

+ * Sets the Content-Language HTTP header which describes the natural language(s) of the + * intended audience for the enclosed entity. + *

+ *

+ * For more information on the Content-Type header, see + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17 + *

+ * + * @param contentLanguage + * The HTTP Content-Language header which describes the natural language(s) of the + * intended audience for the enclosed entity. + * + * @see ObjectMetadata#getContentLanguage() + */ + public void setContentLanguage(String contentLanguage) { + metadata.put(Headers.CONTENT_LANGUAGE, contentLanguage); + } + /** *

* Gets the optional Content-Encoding HTTP header specifying what content @@ -596,14 +666,6 @@ public String getSSEAlgorithm() { return (String) metadata.get(Headers.SERVER_SIDE_ENCRYPTION); } - /** - * Returns the KMS Key Id used for server-side encryption if set, or null - * otherwise. - */ - public String getSSEKMSKeyId() { - return (String) metadata.get(Headers.SERVER_SIDE_ENCRYPTION_KMS_KEY_ID); - } - /** * @deprecated Replaced by {@link #getSSEAlgorithm()} */ @@ -624,16 +686,6 @@ public void setSSEAlgorithm(String algorithm) { metadata.put(Headers.SERVER_SIDE_ENCRYPTION, algorithm); } - /** - * Optionally set the Id of the KMS key to use when encrypting data server - * side using KMS. You must call also - * setSSEAlgorithm(ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION) when using - * this method - */ - public void setSSEKMSKeyId(String kmsKeyId) { - metadata.put(Headers.SERVER_SIDE_ENCRYPTION_KMS_KEY_ID, kmsKeyId); - } - /** * @deprecated Replaced by {@link #setSSEAlgorithm(String))} */ @@ -684,7 +736,7 @@ public void setSSECustomerKeyMd5(String md5Digest) { */ @Override public Date getExpirationTime() { - return expirationTime; + return cloneDate(expirationTime); } /** @@ -727,7 +779,7 @@ public void setExpirationTimeRuleId(String expirationTimeRuleId) { */ @Override public Date getRestoreExpirationTime() { - return restoreExpirationTime; + return cloneDate(restoreExpirationTime); } /** @@ -773,7 +825,19 @@ public void setHttpExpiresDate(Date httpExpiresDate) { * Returns the date when the object is no longer cacheable. */ public Date getHttpExpiresDate() { - return httpExpiresDate; + return cloneDate(httpExpiresDate); + } + + /** + * @return The storage class of the object. Returns null if the object is in STANDARD storage. + * See {@link StorageClass} for possible values + */ + public String getStorageClass() { + final Object storageClass = metadata.get(Headers.STORAGE_CLASS); + if (storageClass == null) { + return null; + } + return storageClass.toString(); } /** @@ -783,25 +847,89 @@ public String getUserMetaDataOf(String key) { return userMetadata == null ? null : userMetadata.get(key); } - public ObjectMetadata() { - userMetadata = new HashMap(); - metadata = new HashMap(); + /** + * Returns a clone of this ObjectMetadata. Note the clone of + * the internal {@link #metadata} is limited to a shallow copy due to the + * unlimited type of value in the map. Other fields can be regarded as deep + * clone. + */ + @Override + public ObjectMetadata clone() { + return new ObjectMetadata(this); } - private ObjectMetadata(ObjectMetadata from) { - // shallow clone the internal hash maps - userMetadata = from.userMetadata == null ? null : new HashMap( - from.userMetadata); - metadata = from.metadata == null ? null : new HashMap(from.metadata); - this.expirationTime = from.expirationTime; - this.expirationTimeRuleId = from.expirationTimeRuleId; - this.httpExpiresDate = from.httpExpiresDate; - this.ongoingRestore = from.ongoingRestore; - this.restoreExpirationTime = from.restoreExpirationTime; + /** + * Returns the AWS Key Management System key id used for Server Side + * Encryption of the Amazon S3 object. + */ + public String getSSEAwsKmsKeyId() { + return (String) metadata + .get(Headers.SERVER_SIDE_ENCRYPTION_KMS_KEY_ID); } @Override - public ObjectMetadata clone() { - return new ObjectMetadata(this); + public boolean isRequesterCharged() { + return metadata.get(Headers.REQUESTER_CHARGED_HEADER) != null; + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + if (isRequesterCharged) { + metadata.put(Headers.REQUESTER_CHARGED_HEADER, Constants.REQUESTER_PAYS); + } + } + + /** + *

+ * Returns the value of x-amz-mp-parts-count header. + *

+ *

+ * The x-amz-mp-parts-count header is returned in the response only when + * a valid partNumber is specified in the request and the object has more than 1 part. + *

+ *

+ * To find the part count of an object, set the partNumber to 1 in GetObjectRequest. + * If the object has more than 1 part then part count will be returned, + * otherwise null is returned. + *

+ */ + public Integer getPartCount() { + return (Integer) metadata.get(Headers.S3_PARTS_COUNT); + } + + /** + *

+ * Returns the content range of the object if response contains the Content-Range header. + *

+ *

+ * If the request specifies a range or part number, then response returns the Content-Range range header. + * Otherwise, the response does not return Content-Range header. + *

+ * @return + * Returns content range if the object is requested with specific range or part number, + * null otherwise. + */ + public Long[] getContentRange() { + final String contentRange = (String) metadata.get(Headers.CONTENT_RANGE); + Long[] range = null; + if (contentRange != null) { + final String[] tokens = contentRange.split("[ -/]+"); + try { + range = new Long[] { Long.parseLong(tokens[1]), Long.parseLong(tokens[2]) }; + } catch (final NumberFormatException nfe) { + throw new AmazonClientException( + "Unable to parse content range. Header 'Content-Range' has corrupted data" + nfe.getMessage(), + nfe); + } + } + return range; + } + + /** + * @return The replication status of the object if it is from a bucket that + * is the source or destination in a cross-region replication. + */ + public String getReplicationStatus() { + return (String) metadata.get(Headers.OBJECT_REPLICATION_STATUS); } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectTagging.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectTagging.java new file mode 100644 index 0000000000..b8c9fb0e7c --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/ObjectTagging.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; +import java.util.List; + +/** + * The tagging for an object. + */ +public class ObjectTagging implements Serializable { + private List tagSet; + + /** + * Constructs an instance of this object. + * + * @param tagSet The tag set. + */ + public ObjectTagging(List tagSet) { + this.tagSet = tagSet; + } + + + /** + * @return The tag set. + */ + public List getTagSet() { + return tagSet; + } + + /** + * Set the tag set for the object. + * + * @param tagSet + * The tag set. + */ + public void setTagSet(List tagSet) { + this.tagSet = tagSet; + } + + /** + * Set the tag set for the object. + * + * @param tagSet + * The tag set. + * @return + * This object for chaining. + */ + private ObjectTagging withTagSet(List tagSet) { + this.tagSet = tagSet; + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PartListing.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PartListing.java index b488e1a886..b8252a9896 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PartListing.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PartListing.java @@ -15,14 +15,17 @@ package com.amazonaws.services.s3.model; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; + import java.util.ArrayList; +import java.util.Date; import java.util.List; /** * The ListPartsResponse contains all the information about the ListParts * method. */ -public class PartListing { +public class PartListing implements S3RequesterChargedResult { /** * The name of the bucket containing the listed parts, as specified in the @@ -87,6 +90,18 @@ public class PartListing { /** The list of parts described in this part listing. */ private List parts; + /** Date when multipart upload will become eligible for abort operation by lifecycle. */ + private Date abortDate; + + /** Id of the lifecycle rule that makes a multipart upload eligible for abort operation. */ + private String abortRuleId; + + /** + * Indicate if the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterCharged; + /** * Returns the name of the bucket containing the listed parts, as specified * in the original request. @@ -334,8 +349,9 @@ public void setTruncated(boolean isTruncated) { * @return The list of parts described in this part listing. */ public List getParts() { - if (parts == null) + if (parts == null) { parts = new ArrayList(); + } return parts; } @@ -348,4 +364,51 @@ public void setParts(List parts) { this.parts = parts; } + /** + * Date when multipart upload will become eligible for abort operation by lifecycle. + * + * @return The date when the upload will be eligible for abort. + */ + public Date getAbortDate() { + return abortDate; + } + + /** + * Date when multipart upload will become eligible for abort operation by lifecycle. + * + * @param abortDate + * The date when the upload will be eligible for abort. + */ + public void setAbortDate(Date abortDate) { + this.abortDate = abortDate; + } + + /** + * Id of the lifecycle rule that makes a multipart upload eligible for abort operation. + * + * @return Rule ID + */ + public String getAbortRuleId() { + return abortRuleId; + } + + /** + * Id of the lifecycle rule that makes a multipart upload eligible for abort operation. + * + * @param abortRuleId Rule ID + */ + public void setAbortRuleId(String abortRuleId) { + this.abortRuleId = abortRuleId; + } + + @Override + public boolean isRequesterCharged() { + return isRequesterCharged; + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + this.isRequesterCharged = isRequesterCharged; + } + } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutInstructionFileRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutInstructionFileRequest.java new file mode 100644 index 0000000000..c9a0d53e30 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutInstructionFileRequest.java @@ -0,0 +1,399 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.amazonaws.AmazonWebServiceRequest; + +/** + * Used to create a new instruction file, typically used to share an encrypted + * S3 object with partners via re-encrypting the CEK of the specified S3 object + * with a partner-specific public key. + */ +public final class PutInstructionFileRequest extends AmazonWebServiceRequest + implements MaterialsDescriptionProvider, EncryptionMaterialsFactory { + /** + * The S3 object id of the respective S3 object to which this instruction + * file is used. + */ + private final S3ObjectId s3ObjectId; + /** + * Material to be used in the instruction file. + */ + private final EncryptionMaterials encryptionMaterials; + /** + * Material description to be stored in the instruction file; applicable + * only if {@link #encryptionMaterials} is not specified. + */ + private final Map matDesc; + /** + * Suffix to be used for the instruction file. If suffix is null, the + * default suffix will be used. + */ + private final String suffix; + /** + * An optional pre-configured access control policy to use for the new + * object. Ignored in favor of accessControlList, if present. + */ + private CannedAccessControlList cannedAcl; + /** + * An optional access control list to apply to the new object. If specified, + * cannedAcl will be ignored. + */ + private AccessControlList accessControlList; + /** The optional redirect location about an object */ + private String redirectLocation; + /** + * The optional Amazon S3 storage class to use when storing the new object. + * If not specified, the default, standard storage class will be used. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + */ + private String storageClass; + + /** + * @param s3ObjectId + * id of the corresponding s3 object. + * @param matDesc + * material description of the instruction file + * @param suffix + * suffix of the instruction file to be put + */ + public PutInstructionFileRequest(S3ObjectId s3ObjectId, + Map matDesc, String suffix) { + if (s3ObjectId == null || s3ObjectId instanceof InstructionFileId) + throw new IllegalArgumentException("Invalid s3 object id"); + if (suffix == null || suffix.trim().isEmpty()) + throw new IllegalArgumentException("suffix must be specified"); + this.s3ObjectId = s3ObjectId; + @SuppressWarnings("unchecked") + Map md = matDesc == null + ? Collections.EMPTY_MAP + : Collections.unmodifiableMap(new HashMap(matDesc)); + this.matDesc = md; + this.suffix = suffix; + this.encryptionMaterials = null; + } + + /** + * @param s3ObjectId + * id of the corresponding s3 object. + * @param encryptionMaterials + * encryption materials to be used for the instruction file + * @param suffix + * suffix of the instruction file to be put + */ + public PutInstructionFileRequest(S3ObjectId s3ObjectId, + EncryptionMaterials encryptionMaterials, String suffix) { + if (s3ObjectId == null || s3ObjectId instanceof InstructionFileId) + throw new IllegalArgumentException("Invalid s3 object id"); + if (suffix == null || suffix.trim().isEmpty()) + throw new IllegalArgumentException("suffix must be specified"); + if (encryptionMaterials == null) + throw new IllegalArgumentException("encryption materials must be specified"); + this.s3ObjectId = s3ObjectId; + this.suffix = suffix; + this.encryptionMaterials = encryptionMaterials; + this.matDesc = null; + } + + /** + * Returns the S3 object id for which the new instruction file will be + * created. This is not the instruction file id. + */ + public S3ObjectId getS3ObjectId() { + return s3ObjectId; + } + + /** + * Returns the material description for the new instruction file. + */ + @Override + public Map getMaterialsDescription() { + return matDesc == null + ? encryptionMaterials.getMaterialsDescription() + : matDesc + ; + } + + /** + * Returns the encryption materials if specified; or null if not. + */ + @Override + public EncryptionMaterials getEncryptionMaterials() { + return encryptionMaterials; + } + /** + * Returns the suffix for the new instruction file. + */ + public String getSuffix() { + return suffix; + } + + /** + * Gets the optional pre-configured access control policy to use for the new + * object. + * + * @return The optional pre-configured access control policy to use for the + * new object. + * + * @see PutObjectRequest#setCannedAcl(CannedAccessControlList) + * @see PutObjectRequest#withCannedAcl(CannedAccessControlList) + */ + public CannedAccessControlList getCannedAcl() { + return cannedAcl; + } + + /** + * Sets the optional pre-configured access control policy to use for the new + * object. + * + * @param cannedAcl + * The optional pre-configured access control policy to use for + * the new object. + * + * @see PutObjectRequest#getCannedAcl() + * @see PutObjectRequest#withCannedAcl(CannedAccessControlList) + */ + public void setCannedAcl(CannedAccessControlList cannedAcl) { + this.cannedAcl = cannedAcl; + } + + /** + * Sets the optional pre-configured access control policy to use for the new + * object. Returns this {@link PutObjectRequest}, enabling additional method + * calls to be chained together. + * + * @param cannedAcl + * The optional pre-configured access control policy to use for + * the new object. + * + * @return This {@link PutObjectRequest}, enabling additional method calls + * to be chained together. + * + * @see PutObjectRequest#getCannedAcl() + * @see PutObjectRequest#setCannedAcl(CannedAccessControlList) + */ + public PutInstructionFileRequest withCannedAcl( + CannedAccessControlList cannedAcl) { + setCannedAcl(cannedAcl); + return this; + } + + /** + * Returns the optional access control list for the new object. If + * specified, cannedAcl will be ignored. + */ + public AccessControlList getAccessControlList() { + return accessControlList; + } + + /** + * Sets the optional access control list for the new object. If specified, + * cannedAcl will be ignored. + * + * @param accessControlList + * The access control list for the new object. + */ + public void setAccessControlList(AccessControlList accessControlList) { + this.accessControlList = accessControlList; + } + + /** + * Sets the optional access control list for the new object. If specified, + * cannedAcl will be ignored. Returns this {@link PutObjectRequest}, + * enabling additional method calls to be chained together. + * + * @param accessControlList + * The access control list for the new object. + */ + public PutInstructionFileRequest withAccessControlList( + AccessControlList accessControlList) { + setAccessControlList(accessControlList); + return this; + } + + /** + * Gets the optional redirect location for the new object. + */ + public String getRedirectLocation() { + return this.redirectLocation; + } + + /** + * Sets the optional redirect location for the new object. + * + * @param redirectLocation + * The redirect location for the new object. + */ + public void setRedirectLocation(String redirectLocation) { + this.redirectLocation = redirectLocation; + } + + /** + * Sets the optional redirect location for the new object.Returns this + * {@link PutObjectRequest}, enabling additional method calls to be chained + * together. + * + * @param redirectLocation + * The redirect location for the new object. + */ + public PutInstructionFileRequest withRedirectLocation( + String redirectLocation) { + this.redirectLocation = redirectLocation; + return this; + } + + /** + * Gets the optional Amazon S3 storage class to use when storing the new + * object. If not specified, the default standard storage class is used when + * storing the object. + *

+ * For more information on available Amazon S3 storage classes, see the + * {@link StorageClass} enumeration. + *

+ * + * @return The Amazon S3 storage class to use when storing the newly copied + * object. + * + * @see #setStorageClass(String) + * @see #setStorageClass(StorageClass) + * @see #withStorageClass(StorageClass) + * @see #withStorageClass(String) + */ + public String getStorageClass() { + return storageClass; + } + + /** + * Sets the optional Amazon S3 storage class to use when storing the new + * object. If not specified, the default standard storage class will be used + * when storing the new object. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + *

+ * + * @param storageClass + * The storage class to use when storing the new object. + * + * @see #getStorageClass() + * @see #setStorageClass(String) + * @see #withStorageClass(StorageClass) + * @see #withStorageClass(String) + */ + public void setStorageClass(String storageClass) { + this.storageClass = storageClass; + } + + /** + * Sets the optional Amazon S3 storage class to use when storing the new + * object. Returns this {@link PutObjectRequest}, enabling additional method + * calls to be chained together. If not specified, the default standard + * storage class will be used when storing the object. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + *

+ * + * @param storageClass + * The storage class to use when storing the new object. + * + * @return This {@link PutObjectRequest}, enabling additional method calls + * to be chained together. + * + * @see #getStorageClass() + * @see #setStorageClass(StorageClass) + * @see #setStorageClass(String) + * @see #withStorageClass(StorageClass) + */ + public PutInstructionFileRequest withStorageClass(String storageClass) { + setStorageClass(storageClass); + return this; + } + + /** + * Sets the optional Amazon S3 storage class to use when storing the new + * object. If not specified, the default standard storage class will be used + * when storing the object. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + *

+ * + * @param storageClass + * The storage class to use when storing the new object. + * + * @see #getStorageClass() + * @see #setStorageClass(String) + */ + public void setStorageClass(StorageClass storageClass) { + this.storageClass = storageClass.toString(); + } + + /** + * Sets the optional Amazon S3 storage class to use when storing the new + * object. Returns this {@link PutObjectRequest}, enabling additional method + * calls to be chained together. If not specified, the default standard + * storage class will be used when storing the object. + *

+ * For more information on Amazon S3 storage classes and available values, + * see the {@link StorageClass} enumeration. + *

+ * + * @param storageClass + * The storage class to use when storing the new object. + * + * @return This {@link PutObjectRequest}, enabling additional method calls + * to be chained together. + * + * @see #getStorageClass() + * @see #setStorageClass(StorageClass) + * @see #setStorageClass(String) + * @see #withStorageClass(String) + */ + public PutInstructionFileRequest withStorageClass(StorageClass storageClass) { + setStorageClass(storageClass); + return this; + } + + /** + * Creates and returns a {@link PutObjectRequest} for the instruction file + * with the specified suffix. + */ + public PutObjectRequest createPutObjectRequest(S3Object s3Object) { + if (!s3Object.getBucketName().equals(s3ObjectId.getBucket()) + || !s3Object.getKey().equals(s3ObjectId.getKey())) { + throw new IllegalArgumentException("s3Object passed inconsistent with the instruction file being created"); + } + InstructionFileId ifid= s3ObjectId.instructionFileId(suffix); +// ObjectMetadata metadata = s3Object.getObjectMetadata(); + return new PutObjectRequest(ifid.getBucket(), ifid.getKey(), redirectLocation) + .withAccessControlList(accessControlList) + .withCannedAcl(cannedAcl) +// .withFile(file) +// .withInputStream(inputStream) +// don't want the metadata for the new instruction file +// .withMetadata(metadata == null ? null : metadata.clone()) + .withStorageClass(storageClass) + .withGeneralProgressListener(getGeneralProgressListener()) + .withRequestMetricCollector(getRequestMetricCollector()) + ; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutObjectRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutObjectRequest.java index eab55d0c45..38a49fb37a 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutObjectRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutObjectRequest.java @@ -15,11 +15,11 @@ package com.amazonaws.services.s3.model; -import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.event.ProgressListener; import java.io.File; import java.io.InputStream; +import java.io.Serializable; /** *

@@ -39,10 +39,12 @@ * When uploading a file: *

*
    - *
  • The client automatically computes a checksum of the file. Amazon S3 uses + *
  • + * The client automatically computes a checksum of the file. Amazon S3 uses * checksums to validate the data in each file.
  • - *
  • Using the file extension, Amazon S3 attempts to determine the correct - * content type and content disposition to use for the object.
  • + *
  • + * Using the file extension, Amazon S3 attempts to determine the correct content + * type and content disposition to use for the object.
  • *
*

* When uploading directly from an input stream, content length must be @@ -69,762 +71,268 @@ * The specified bucket must already exist and the caller must have * {@link Permission#Write} permission to the bucket to upload an object. *

+ *

+ * If you are uploading or accessing KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify + * -signature-version + *

* * @see PutObjectRequest#PutObjectRequest(String, String, File) * @see PutObjectRequest#PutObjectRequest(String, String, InputStream, * ObjectMetadata) */ -public class PutObjectRequest extends AmazonWebServiceRequest implements Cloneable { - - /** - * The name of an existing bucket, to which this request will upload a new - * object. You must have {@link Permission#Write} permission granted to you - * in order to upload new objects to a bucket. - */ - private String bucketName; - - /** - * The key under which to store the new object. - */ - private String key; - - /** - * The file containing the data to be uploaded to Amazon S3. You must either - * specify a file or an InputStream containing the data to be uploaded to - * Amazon S3. - */ - private File file; - - /** - * The InputStream containing the data to be uploaded to Amazon S3. You must - * either specify a file or an InputStream containing the data to be - * uploaded to Amazon S3. - */ - private InputStream inputStream; - - /** - * Optional metadata instructing Amazon S3 how to handle the uploaded data - * (e.g. custom user metadata, hooks for specifying content type, etc.). If - * you are uploading from an InputStream, you should always - * specify metadata with the content size set, otherwise the contents of the - * InputStream will have to be buffered in memory before they can be sent to - * Amazon S3, which can have very negative performance impacts. - */ - private ObjectMetadata metadata; - - /** - * An optional pre-configured access control policy to use for the new - * object. Ignored in favor of accessControlList, if present. - */ - private CannedAccessControlList cannedAcl; - - /** - * An optional access control list to apply to the new object. If specified, - * cannedAcl will be ignored. - */ - private AccessControlList accessControlList; - - /** - * The optional Amazon S3 storage class to use when storing the new object. - * If not specified, the default, standard storage class will be used. - *

- * For more information on Amazon S3 storage classes and available values, - * see the {@link StorageClass} enumeration. - */ - private String storageClass; - - /** - * The optional progress listener for receiving updates about object - * download status. - */ - private ProgressListener generalProgressListener; - - /** The optional redirect location about an object */ - private String redirectLocation; +public class PutObjectRequest extends AbstractPutObjectRequest implements Serializable { /** - * The optional customer-provided server-side encryption key to use to - * encrypt the uploaded object. + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. */ - private SSECustomerKey sseCustomerKey; + private boolean isRequesterPays; /** - * Constructs a new {@link PutObjectRequest} object to upload a file to the - * specified bucket and key. After constructing the request, users may - * optionally specify object metadata or a canned ACL as well. + * Constructs a new + * {@link PutObjectRequest} object to upload a file to the + * specified bucket and key. After constructing the request, + * users may optionally specify object metadata or a canned ACL as well. * - * @param bucketName The name of an existing bucket to which the new object - * will be uploaded. - * @param key The key under which to store the new object. - * @param file The path of the file to upload to Amazon S3. - * @see PutObjectRequest#PutObjectRequest(String, String, InputStream, - * ObjectMetadata) - * @See PutObjectRequest(String bucketName, String key, String - * redirectLocation) + * @param bucketName + * The name of an existing bucket to which the new object will be + * uploaded. + * @param key + * The key under which to store the new object. + * @param file + * The path of the file to upload to Amazon S3. */ public PutObjectRequest(String bucketName, String key, File file) { - this.bucketName = bucketName; - this.key = key; - this.file = file; + super(bucketName, key, file); } /** - * Constructs a new {@link PutObjectRequest} object with redirect location. - * After constructing the request, users may optionally specify object - * metadata or a canned ACL as well. + * Constructs a new + * {@link PutObjectRequest} object to perform a redirect for the + * specified bucket and key. After constructing the request, + * users may optionally specify object metadata or a canned ACL as well. + *

+ * The redirect is performed using the + * {@link com.amazonaws.services.s3.Headers#REDIRECT_LOCATION} header. + *

* - * @param bucketName The name of an existing bucket to which the new object - * will be uploaded. - * @param key The key under which to store the new object. - * @param redirectLocation The redirect location of this new object. - * @see PutObjectRequest#PutObjectRequest(String, String, InputStream, - * ObjectMetadata) - * @see PutObjectRequest#PutObjectRequest(String, String, File) + * @param bucketName + * The name of an existing bucket to which the new object will be + * uploaded. + * @param key + * The key under which to store the new object. + * @param redirectLocation + * Sets the {@link com.amazonaws.services.s3.Headers#REDIRECT_LOCATION} + * header for the new object. */ public PutObjectRequest(String bucketName, String key, String redirectLocation) { - this.bucketName = bucketName; - this.key = key; - this.redirectLocation = redirectLocation; + super(bucketName, key, redirectLocation); } /** - * Constructs a new {@link PutObjectRequest} object to upload a stream of - * data to the specified bucket and key. After constructing the request, + * Constructs a new + * {@link PutObjectRequest} object to upload a stream of data to + * the specified bucket and key. After constructing the request, * users may optionally specify object metadata or a canned ACL as well. *

- * Content length for the data stream must be specified in the object - * metadata parameter; Amazon S3 requires it be passed in before the data is - * uploaded. Failure to specify a content length will cause the entire - * contents of the input stream to be buffered locally in memory so that the - * content length can be calculated, which can result in negative - * performance problems. + * Content length for the data stream must be + * specified in the object metadata parameter; Amazon S3 requires it + * be passed in before the data is uploaded. Failure to specify a content + * length will cause the entire contents of the input stream to be buffered + * locally in memory so that the content length can be calculated, which can + * result in negative performance problems. *

* - * @param bucketName The name of an existing bucket to which the new object - * will be uploaded. - * @param key The key under which to store the new object. - * @param input The stream of data to upload to Amazon S3. - * @param metadata The object metadata. At minimum this specifies the + * @param bucketName + * The name of an existing bucket to which the new object will be + * uploaded. + * @param key + * The key under which to store the new object. + * @param input + * The stream of data to upload to Amazon S3. + * @param metadata + * The object metadata. At minimum this specifies the * content length for the stream of data being uploaded. - * @see PutObjectRequest#PutObjectRequest(String, String, File) - * @see PutObjectRequest(String bucketName, String key, String - * redirectLocation) */ public PutObjectRequest(String bucketName, String key, InputStream input, ObjectMetadata metadata) { - this.bucketName = bucketName; - this.key = key; - this.inputStream = input; - this.metadata = metadata; + super(bucketName, key, input, metadata); } /** - * Gets the name of the existing bucket where this request will upload a new - * object to. In order to upload the object, users must have - * {@link Permission#Write} permission granted. + * Returns a clone (as deep as possible) of this request object. * - * @return The name of an existing bucket where this request will upload a - * new object to. - * @see PutObjectRequest#setBucketName(String) - * @see PutObjectRequest#withBucketName(String) + * @throws CloneNotSupportedException */ - public String getBucketName() { - return bucketName; - } - - /** - * Sets the name of an existing bucket where this request will upload a new - * object to. In order to upload the object, users must have - * {@link Permission#Write} permission granted. - * - * @param bucketName The name of an existing bucket where this request will - * upload a new object to. In order to upload the object, users - * must have {@link Permission#Write} permission granted. - * @see PutObjectRequest#getBucketName() - * @see PutObjectRequest#withBucketName(String) - */ - public void setBucketName(String bucketName) { - this.bucketName = bucketName; + @Override + public PutObjectRequest clone() { + final PutObjectRequest request = (PutObjectRequest) super.clone(); + return this.copyPutObjectBaseTo(request); } - /** - * Sets the name of the bucket where this request will upload a new object - * to. Returns this object, enabling additional method calls to be chained - * together. - *

- * In order to upload the object, users must have {@link Permission#Write} - * permission granted. - * - * @param bucketName The name of an existing bucket where this request will - * upload a new object to. In order to upload the object, users - * must have {@link Permission#Write} permission granted. - * @return This {@link PutObjectRequest}, enabling additional method calls to - * be chained together. - * @see PutObjectRequest#getBucketName() - * @see PutObjectRequest#setBucketName(String) - */ + @Override + @SuppressWarnings("unchecked") public PutObjectRequest withBucketName(String bucketName) { - setBucketName(bucketName); - return this; - } - - /** - * Gets the key under which to store the new object. - * - * @return The key under which to store the new object. - * @see PutObjectRequest#setKey(String) - * @see PutObjectRequest#withKey(String) - */ - public String getKey() { - return key; + return super.withBucketName(bucketName); } - /** - * Sets the key under which to store the new object. - * - * @param key The key under which to store the new object. - * @see PutObjectRequest#getKey() - * @see PutObjectRequest#withKey(String) - */ - public void setKey(String key) { - this.key = key; - } - - /** - * Sets the key under which to store the new object. Returns this object, - * enabling additional method calls to be chained together. - * - * @param key The key under which to store the new object. - * @return This {@link PutObjectRequest}, enabling additional method calls to - * be chained together. - * @see PutObjectRequest#getKey() - * @see PutObjectRequest#setKey(String) - */ + @Override + @SuppressWarnings("unchecked") public PutObjectRequest withKey(String key) { - setKey(key); - return this; + return super.withKey(key); } - /** - * Gets the optional Amazon S3 storage class to use when storing the new - * object. If not specified, the default standard storage class is used when - * storing the object. - *

- * For more information on available Amazon S3 storage classes, see the - * {@link StorageClass} enumeration. - *

- * - * @return The Amazon S3 storage class to use when storing the newly copied - * object. - * @see PutObjectRequest#setStorageClass(String) - * @see PutObjectRequest#setStorageClass(StorageClass) - * @see PutObjectRequest#withStorageClass(StorageClass) - * @see PutObjectRequest#withStorageClass(String) - */ - public String getStorageClass() { - return storageClass; - } - - /** - * Sets the optional Amazon S3 storage class to use when storing the new - * object. If not specified, the default standard storage class will be used - * when storing the new object. - *

- * For more information on Amazon S3 storage classes and available values, - * see the {@link StorageClass} enumeration. - *

- * - * @param storageClass The storage class to use when storing the new object. - * @return The Amazon S3 storage class to use when storing the newly copied - * object. - * @see PutObjectRequest#getStorageClass() - * @see PutObjectRequest#setStorageClass(StorageClass - * @see PutObjectRequest#withStorageClass(StorageClass) - * @see PutObjectRequest#withStorageClass(String) - */ - public void setStorageClass(String storageClass) { - this.storageClass = storageClass; - } - /** - * Sets the optional Amazon S3 storage class to use when storing the new - * object. Returns this {@link PutObjectRequest}, enabling additional method - * calls to be chained together. If not specified, the default standard - * storage class will be used when storing the object. - *

- * For more information on Amazon S3 storage classes and available values, - * see the {@link StorageClass} enumeration. - *

- * - * @param storageClass The storage class to use when storing the new object. - * @return This {@link PutObjectRequest}, enabling additional method calls to - * be chained together. - * @see PutObjectRequest#getStorageClass() - * @see PutObjectRequest#setStorageClass(StorageClass) - * @see PutObjectRequest#setStorageClass(String) - * @see PutObjectRequest#withStorageClass(StorageClass) - */ + @Override + @SuppressWarnings("unchecked") public PutObjectRequest withStorageClass(String storageClass) { - setStorageClass(storageClass); - return this; - } - - /** - * Sets the optional Amazon S3 storage class to use when storing the new - * object. If not specified, the default standard storage class will be used - * when storing the object. - *

- * For more information on Amazon S3 storage classes and available values, - * see the {@link StorageClass} enumeration. - *

- * - * @param storageClass The storage class to use when storing the new object. - * @return The Amazon S3 storage class to use when storing the newly copied - * object. - * @see PutObjectRequest#getStorageClass() - * @see PutObjectRequest#setStorageClass(String) - */ - public void setStorageClass(StorageClass storageClass) { - this.storageClass = storageClass.toString(); + return super.withStorageClass(storageClass); } - /** - * Sets the optional Amazon S3 storage class to use when storing the new - * object. Returns this {@link PutObjectRequest}, enabling additional method - * calls to be chained together. If not specified, the default standard - * storage class will be used when storing the object. - *

- * For more information on Amazon S3 storage classes and available values, - * see the {@link StorageClass} enumeration. - *

- * - * @param storageClass The storage class to use when storing the new object. - * @return This {@link PutObjectRequest}, enabling additional method calls to - * be chained together. - * @see PutObjectRequest#getStorageClass() - * @see PutObjectRequest#setStorageClass(StorageClass) - * @see PutObjectRequest#setStorageClass(String) - * @see PutObjectRequest#withStorageClass(String) - */ + @Override + @SuppressWarnings("unchecked") public PutObjectRequest withStorageClass(StorageClass storageClass) { - setStorageClass(storageClass); - return this; - } - - /** - * Gets the path and name of the file containing the data to be uploaded to - * Amazon S3. Either specify a file or an input stream containing the data - * to be uploaded to Amazon S3; both cannot be specified. - * - * @return The path and name of the file containing the data to be uploaded - * to Amazon S3. - * @see PutObjectRequest#setFile(File) - * @see PutObjectRequest#withFile(File) - * @see PutObjectRequest#setInputStream(InputStream) - * @see PutObjectRequest#withInputStream(InputStream) - */ - public File getFile() { - return file; - } - - /** - * Sets the path and name of the file containing the data to be uploaded to - * Amazon S3. Either specify a file or an input stream containing the data - * to be uploaded to Amazon S3; both cannot be specified. - * - * @param file The path and name of the file containing the data to be - * uploaded to Amazon S3. - * @see PutObjectRequest#getFile() - * @see PutObjectRequest#withFile(File) - * @see PutObjectRequest#getInputStream() - * @see PutObjectRequest#withInputStream(InputStream) - */ - public void setFile(File file) { - this.file = file; - } - - /** - * Sets the file containing the data to be uploaded to Amazon S3. Returns - * this {@link PutObjectRequest}, enabling additional method calls to be - * chained together. - *

- * Either specify a file or an input stream containing the data to be - * uploaded to Amazon S3; both cannot be specified. - * - * @param file The file containing the data to be uploaded to Amazon S3. - * @return This {@link PutObjectRequest}, enabling additional method calls to - * be chained together. - * @see PutObjectRequest#getFile() - * @see PutObjectRequest#setFile(File) - * @see PutObjectRequest#getInputStream() - * @see PutObjectRequest#setInputStream(InputStream) - */ - public PutObjectRequest withFile(File file) { - setFile(file); - return this; - } - - /** - * Gets the optional metadata instructing Amazon S3 how to handle the - * uploaded data (e.g. custom user metadata, hooks for specifying content - * type, etc.). - *

- * If uploading from an input stream, always specify metadata with - * the content size set. Otherwise the contents of the input stream have to - * be buffered in memory before being sent to Amazon S3. This can cause very - * negative performance impacts. - *

- * - * @return The optional metadata instructing Amazon S3 how to handle the - * uploaded data (e.g. custom user metadata, hooks for specifying - * content type, etc.). - * @see PutObjectRequest#setMetadata(ObjectMetadata) - * @see PutObjectRequest#withMetadata(ObjectMetadata) - */ - public ObjectMetadata getMetadata() { - return metadata; + return super.withStorageClass(storageClass); } - /** - * Sets the optional metadata instructing Amazon S3 how to handle the - * uploaded data (e.g. custom user metadata, hooks for specifying content - * type, etc.). - *

- * If uploading from an input stream, always specify metadata with - * the content size set. Otherwise the contents of the input stream have to - * be buffered in memory before being sent to Amazon S3. This can cause very - * negative performance impacts. - *

- * - * @param metadata The optional metadata instructing Amazon S3 how to handle - * the uploaded data (e.g. custom user metadata, hooks for - * specifying content type, etc.). - * @see PutObjectRequest#getMetadata() - * @see PutObjectRequest#withMetadata(ObjectMetadata) - */ - public void setMetadata(ObjectMetadata metadata) { - this.metadata = metadata; + @Override + @SuppressWarnings("unchecked") + public PutObjectRequest withFile(File file) { + return super.withFile(file); } - /** - * Sets the optional metadata instructing Amazon S3 how to handle the - * uploaded data (e.g. custom user metadata, hooks for specifying content - * type, etc.). Returns this {@link PutObjectRequest}, enabling additional - * method calls to be chained together. - *

- * If uploading from an input stream, always specify metadata with - * the content size set. Otherwise the contents of the input stream have to - * be buffered in memory before being sent to Amazon S3. This can cause very - * negative performance impacts. - *

- * - * @param metadata The optional metadata instructing Amazon S3 how to handle - * the uploaded data (e.g. custom user metadata, hooks for - * specifying content type, etc.). - * @return This {@link PutObjectRequest}, enabling additional method calls to - * be chained together. - * @see PutObjectRequest#getMetadata() - * @see PutObjectRequest#setMetadata(ObjectMetadata) - */ + @Override + @SuppressWarnings("unchecked") public PutObjectRequest withMetadata(ObjectMetadata metadata) { - setMetadata(metadata); - return this; + return super.withMetadata(metadata); } - /** - * Gets the optional pre-configured access control policy to use for the new - * object. - * - * @return The optional pre-configured access control policy to use for the - * new object. - * @see PutObjectRequest#setCannedAcl(CannedAccessControlList) - * @see PutObjectRequest#withCannedAcl(CannedAccessControlList) - */ - public CannedAccessControlList getCannedAcl() { - return cannedAcl; + @Override + @SuppressWarnings("unchecked") + public PutObjectRequest withCannedAcl(CannedAccessControlList cannedAcl) { + return super.withCannedAcl(cannedAcl); } - /** - * Sets the optional pre-configured access control policy to use for the new - * object. - * - * @param cannedAcl The optional pre-configured access control policy to use - * for the new object. - * @see PutObjectRequest#getCannedAcl() - * @see PutObjectRequest#withCannedAcl(CannedAccessControlList) - */ - public void setCannedAcl(CannedAccessControlList cannedAcl) { - this.cannedAcl = cannedAcl; + @Override + @SuppressWarnings("unchecked") + public PutObjectRequest withAccessControlList( + AccessControlList accessControlList) { + return super.withAccessControlList(accessControlList); } - /** - * Sets the optional pre-configured access control policy to use for the new - * object. Returns this {@link PutObjectRequest}, enabling additional method - * calls to be chained together. - * - * @param cannedAcl The optional pre-configured access control policy to use - * for the new object. - * @return This {@link PutObjectRequest}, enabling additional method calls to - * be chained together. - * @see PutObjectRequest#getCannedAcl() - * @see PutObjectRequest#setCannedAcl(CannedAccessControlList) - */ - public PutObjectRequest withCannedAcl(CannedAccessControlList cannedAcl) { - setCannedAcl(cannedAcl); - return this; + @Override + @SuppressWarnings("unchecked") + public PutObjectRequest withInputStream(InputStream inputStream) { + return super.withInputStream(inputStream); } - /** - * Returns the optional access control list for the new object. If - * specified, cannedAcl will be ignored. - */ - public AccessControlList getAccessControlList() { - return accessControlList; + @Override + @SuppressWarnings("unchecked") + public PutObjectRequest withRedirectLocation(String redirectLocation) { + return super.withRedirectLocation(redirectLocation); } - /** - * Sets the optional access control list for the new object. If specified, - * cannedAcl will be ignored. - * - * @param accessControlList The access control list for the new object. - */ - public void setAccessControlList(AccessControlList accessControlList) { - this.accessControlList = accessControlList; + @Override + @SuppressWarnings("unchecked") + public PutObjectRequest withSSECustomerKey(SSECustomerKey sseKey) { + return super.withSSECustomerKey(sseKey); } - /** - * Sets the optional access control list for the new object. If specified, - * cannedAcl will be ignored. Returns this {@link PutObjectRequest}, - * enabling additional method calls to be chained together. - * - * @param accessControlList The access control list for the new object. - */ - public PutObjectRequest withAccessControlList(AccessControlList accessControlList) { - setAccessControlList(accessControlList); + @Override + public PutObjectRequest withTagging(ObjectTagging tagSet) { + super.setTagging(tagSet); return this; } - /** - * Gets the input stream containing the data to be uploaded to Amazon S3. - * The user of this request must either specify a file or an input stream - * containing the data to be uploaded to Amazon S3; both cannot be - * specified. - * - * @return The input stream containing the data to be uploaded to Amazon S3. - * Either specify a file or an input stream containing the data to - * be uploaded to Amazon S3, not both. - * @see PutObjectRequest#setInputStream(InputStream) - * @see PutObjectRequest#withInputStream(InputStream) - * @see PutObjectRequest#setFile(File) - * @see PutObjectRequest#withFile(File) - */ - public InputStream getInputStream() { - return inputStream; - } - - /** - * Sets the input stream containing the data to be uploaded to Amazon S3. - * Either specify a file or an input stream containing the data to be - * uploaded to Amazon S3; both cannot be specified. - * - * @param inputStream The input stream containing the data to be uploaded to - * Amazon S3. Either specify a file or an input stream containing - * the data to be uploaded to Amazon S3, not both. - * @see PutObjectRequest#getInputStream() - * @see PutObjectRequest#withInputStream(InputStream) - * @see PutObjectRequest#getFile() - * @see PutObjectRequest#withFile(File) - */ - public void setInputStream(InputStream inputStream) { - this.inputStream = inputStream; - } - - /** - * Sets the input stream containing the data to be uploaded to Amazon S3. - * Returns this {@link PutObjectRequest}, enabling additional method calls - * to be chained together. - *

- * Either specify a file or an input stream containing the data to be - * uploaded to Amazon S3; both cannot be specified. - *

- * - * @param inputStream The InputStream containing the data to be uploaded to - * Amazon S3. - * @return This PutObjectRequest, so that additional method calls can be - * chained together. - * @see PutObjectRequest#getInputStream() - * @see PutObjectRequest#setInputStream(InputStream) - * @see PutObjectRequest#getFile() - * @see PutObjectRequest#setFile(File) - */ - public PutObjectRequest withInputStream(InputStream inputStream) { - setInputStream(inputStream); - return this; + @Deprecated + @Override + @SuppressWarnings("unchecked") + public PutObjectRequest withProgressListener( + com.amazonaws.services.s3.model.ProgressListener progressListener) { + return super.withProgressListener(progressListener); } - /** - * Sets the optional redirect location for the new object. - * - * @param redirectLocation The redirect location for the new object. - */ - public void setRedirectLocation(String redirectLocation) { - this.redirectLocation = redirectLocation; + @Override + @SuppressWarnings("unchecked") + public PutObjectRequest withSSEAwsKeyManagementParams( + SSEAwsKeyManagementParams sseAwsKeyManagementParams) { + return super.withSSEAwsKeyManagementParams(sseAwsKeyManagementParams); } - /** - * Gets the optional redirect location for the new object. - */ - public String getRedirectLocation() { - return this.redirectLocation; + @SuppressWarnings("unchecked") + @Override + public PutObjectRequest withGeneralProgressListener( + ProgressListener generalProgressListener) { + return super.withGeneralProgressListener(generalProgressListener); } /** - * Sets the optional redirect location for the new object.Returns this - * {@link PutObjectRequest}, enabling additional method calls to be chained - * together. + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. * - * @param redirectLocation The redirect location for the new object. - */ - public PutObjectRequest withRedirectLocation(String redirectLocation) { - this.redirectLocation = redirectLocation; - return this; - } - - /** - * Returns the optional customer-provided server-side encryption key to use - * to encrypt the uploaded object. + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. * - * @return The optional customer-provided server-side encryption key to use - * to encrypt the uploaded object. - */ - public SSECustomerKey getSSECustomerKey() { - return sseCustomerKey; - } - - /** - * Sets the optional customer-provided server-side encryption key to use to - * encrypt the uploaded object. + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket * - * @param sseKey The optional customer-provided server-side encryption key - * to use to encrypt the uploaded object. + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. */ - public void setSSECustomerKey(SSECustomerKey sseKey) { - this.sseCustomerKey = sseKey; + public boolean isRequesterPays() { + return isRequesterPays; } /** - * Sets the optional customer-provided server-side encryption key to use to - * encrypt the uploaded object, and returns the updated request object so - * that additional method calls can be chained together. + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. * - * @param sseKey The optional customer-provided server-side encryption key - * to use to encrypt the uploaded object. - * @return This updated request object so that additional method calls can be - * chained together. - */ - public PutObjectRequest withSSECustomerKey(SSECustomerKey sseKey) { - setSSECustomerKey(sseKey); - return this; - } - - /** - * Sets the optional progress listener for receiving updates for object - * upload status. + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. * - * @param progressListener The legacy progress listener that is used - * exclusively for Amazon S3 client. - * @deprecated use {@link #setGeneralProgressListener(ProgressListener)} - * instead. - */ - @Deprecated - public void setProgressListener( - com.amazonaws.services.s3.model.ProgressListener progressListener) { - this.generalProgressListener = new LegacyS3ProgressListener(progressListener); - } - - /** - * Returns the optional progress listener for receiving updates about object - * upload status. + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. * - * @return the optional progress listener for receiving updates about object - * upload status. - * @deprecated use {@link #getGeneralProgressListener()} instead. + * @param isRequesterPays + * Enable Requester Pays option for the operation. */ - @Deprecated - public com.amazonaws.services.s3.model.ProgressListener getProgressListener() { - if (generalProgressListener instanceof LegacyS3ProgressListener) { - return ((LegacyS3ProgressListener) generalProgressListener).unwrap(); - } else { - return null; - } + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; } /** - * Sets the optional progress listener for receiving updates about object - * upload status, and returns this updated object so that additional method - * calls can be chained together. + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated PutObjectRequest object so that additional method calls can be + * chained together. * - * @param progressListener The legacy progress listener that is used - * exclusively for Amazon S3 client. - * @return This updated PutObjectRequest object. - * @deprecated use {@link #withGeneralProgressListener(ProgressListener)} - * instead. - */ - @Deprecated - public PutObjectRequest withProgressListener( - com.amazonaws.services.s3.model.ProgressListener progressListener) { - setProgressListener(progressListener); - return this; - } - - /** - * Sets the optional progress listener for receiving updates about object - * upload status. + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. * - * @param generalProgressListener The new progress listener. - */ - public void setGeneralProgressListener(ProgressListener generalProgressListener) { - this.generalProgressListener = generalProgressListener; - } - - /** - * Returns the optional progress listener for receiving updates about object - * upload status. + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. * - * @return the optional progress listener for receiving updates about object - * upload status. - */ - public ProgressListener getGeneralProgressListener() { - return generalProgressListener; - } - - /** - * Sets the optional progress listener for receiving updates about object - * upload status, and returns this updated object so that additional method - * calls can be chained together. + * @param isRequesterPays + * Enable Requester Pays option for the operation. * - * @param generalProgressListener The new progress listener. - * @return This updated PutObjectRequest object. + * @return The updated PutObjectRequest object. */ - public PutObjectRequest withGeneralProgressListener(ProgressListener generalProgressListener) { - setGeneralProgressListener(generalProgressListener); + public PutObjectRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); return this; } - - /** - * Returns a clone of this object so that the metadata can be further - * modified without affecting the original. - */ - @Override - public PutObjectRequest clone() { - return new PutObjectRequest(bucketName, key, redirectLocation). - withAccessControlList(accessControlList) - .withCannedAcl(cannedAcl) - .withFile(file) - .withGeneralProgressListener(generalProgressListener) - .withInputStream(inputStream) - .withMetadata(metadata == null ? null : metadata.clone()) - .withStorageClass(storageClass) - .withRequestMetricCollector(getRequestMetricCollector()); - } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutObjectResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutObjectResult.java index 790120b03c..ecf2945ec8 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutObjectResult.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/PutObjectResult.java @@ -17,6 +17,8 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.internal.ObjectExpirationResult; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; +import com.amazonaws.services.s3.internal.S3VersionResult; import com.amazonaws.services.s3.internal.SSEResultBase; import java.util.Date; @@ -32,7 +34,8 @@ * S3ObjectMetadata) * @see AmazonS3#putObject(PutObjectRequest) */ -public class PutObjectResult extends SSEResultBase implements ObjectExpirationResult { +public class PutObjectResult extends SSEResultBase + implements ObjectExpirationResult, S3RequesterChargedResult, S3VersionResult { /** * The version ID of the new, uploaded object. This field will only be @@ -53,6 +56,15 @@ public class PutObjectResult extends SSEResultBase implements ObjectExpirationRe /** The content MD5 */ private String contentMd5; + /** The metadata returned as a result of PutObject operation.*/ + private ObjectMetadata metadata; + + /** + * Indicate if the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterCharged; + /** * Gets the optional version ID of the newly uploaded object. This field * will be set only if object versioning is enabled for the bucket the @@ -61,6 +73,7 @@ public class PutObjectResult extends SSEResultBase implements ObjectExpirationRe * @return The optional version ID of the newly uploaded object. * @see PutObjectResult#setVersionId(String) */ + @Override public String getVersionId() { return versionId; } @@ -71,6 +84,7 @@ public String getVersionId() { * @param versionId The optional version ID of the newly uploaded object. * @see PutObjectResult#getVersionId() */ + @Override public void setVersionId(String versionId) { this.versionId = versionId; } @@ -136,7 +150,8 @@ public void setExpirationTimeRuleId(String expirationTimeRuleId) { } /** - * Sets the content MD5. + * Sets the Base64-encoded MD5 hash of the object content that was + * calculated on the client-side. * * @param contentMd5 The content MD5 */ @@ -145,9 +160,38 @@ public void setContentMd5(String contentMd5) { } /** - * Returns the content MD5. + * Returns the Base64-encoded MD5 hash of the object content that was + * calculated on the client-side. This method returns null if the MD5 + * validation is disabled and the caller didn't provide the MD5 hash in the + * ObjectMetadata when sending the PutObjectRequest. */ public String getContentMd5() { return contentMd5; } + + /** + * Returns the metadata retrieved as a response to + * {@link AmazonS3Client#putObject(PutObjectRequest)} operation. + */ + public ObjectMetadata getMetadata() { + return metadata; + } + + /** + * Sets the metadata retrieved as a response to + * {@link AmazonS3Client#putObject(PutObjectRequest)} operation. + */ + public void setMetadata(ObjectMetadata metadata) { + this.metadata = metadata; + } + + @Override + public boolean isRequesterCharged() { + return isRequesterCharged; + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + this.isRequesterCharged = isRequesterCharged; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/QueueConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/QueueConfiguration.java new file mode 100644 index 0000000000..918a611127 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/QueueConfiguration.java @@ -0,0 +1,88 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import java.util.EnumSet; + +/** + * Represents the queue configuration for an Amazon S3 bucket. + */ +public class QueueConfiguration extends NotificationConfiguration implements Serializable { + + /** + * The Amazon SQS queue ARN for this configuration. + */ + private String queueARN; + + public QueueConfiguration() { + super(); + } + + /** + * Creates a new queue configuration with the given queue arn and set of events. + * + * @param queueARN + * the Amazon SQS queue arn to which the notifications are to be sent. + * @param events + * the events for which the notifications are to be sent + */ + public QueueConfiguration(String queueARN, EnumSet events) { + super(events); + this.queueARN = queueARN; + } + + /** + * Creates a new queue configuration with the given queue arn and set of events. + * + * @param queueARN + * the Amazon SQS queue arn to which the notifications are to be sent. + * @param events + * the events for which the notifications are to be sent + */ + public QueueConfiguration(String queueARN, String... events) { + super(events); + this.queueARN = queueARN; + } + + /** + * Returns the queue arn for this notification configuration. + */ + public String getQueueARN() { + return queueARN; + } + + /** + * Sets the queue ARN for this configuration + * + * @param queueARN + * ARN for the SQS queue + */ + public void setQueueARN(String queueARN) { + this.queueARN = queueARN; + } + + /** + * Fluent method to set the queue ARN for this configuration + * + * @param queueARN + * ARN for the SQS queue + * @return This object for method chaining + */ + public QueueConfiguration withQueueARN(String queueARN) { + setQueueARN(queueARN); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Region.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Region.java index 79316437e6..58552d564d 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Region.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Region.java @@ -30,8 +30,13 @@ * to optimize latency, minimize costs, or address regulatory requirements. *

*

- * Objects stored in a Amazon S3 Region never leave that region unless - * explicitly transferred to another region. + * Objects stored in a Amazon S3 Region never leave that region unless explicitly + * transferred to another region. + *

+ *

+ * In Amazon S3, all the regions provides + * read-after-write consistency for PUTS of new objects in Amazon + * S3 buckets and eventual consistency for overwrite PUTS and DELETES. *

*/ public enum Region { @@ -285,7 +290,7 @@ private String getFirstRegionId0() { */ public static Region fromValue(final String s3RegionId) throws IllegalArgumentException { - if (s3RegionId == null || s3RegionId.equals("US")) { + if (s3RegionId == null || s3RegionId.equals("US") || s3RegionId.equals("us-east-1")) { return Region.US_Standard; } for (final Region region : Region.values()) { diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/RestoreObjectRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/RestoreObjectRequest.java index e0733da864..dde2354c41 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/RestoreObjectRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/RestoreObjectRequest.java @@ -63,6 +63,11 @@ public class RestoreObjectRequest extends AmazonWebServiceRequest { */ private String versionId; + /** + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; /** *

* Constructs a new RestoreObjectRequest. @@ -215,4 +220,68 @@ public RestoreObjectRequest withExpirationInDays(int expirationInDays) { return this; } + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated RestoreObjectRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated RestoreObjectRequest object. + */ + public RestoreObjectRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3DataSource.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3DataSource.java new file mode 100644 index 0000000000..d83b0d332f --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3DataSource.java @@ -0,0 +1,56 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import static com.amazonaws.util.IOUtils.release; + +import java.io.File; +import java.io.InputStream; + +import org.apache.commons.logging.Log; + +/** + * Used to represent an S3 data source that either has a file or an input + * stream. + */ +public interface S3DataSource { + public File getFile(); + public void setFile(File file); + public InputStream getInputStream(); + public void setInputStream(InputStream inputStream); + + /** + * {@link S3DataSource} specific utilities. + */ + public static enum Utils { + ; + /** + * Clean up any temporary streams created during the execution, + * and restore the original file and/or input stream. + */ + public static void cleanupDataSource(S3DataSource req, + final File fileOrig, final InputStream inputStreamOrig, + InputStream inputStreamCurr, Log log) { + if (fileOrig != null) { + // We opened a file underneath so would need to release it + release(inputStreamCurr, log); + } + // restore the original input stream so the caller could close + // it if necessary + req.setInputStream(inputStreamOrig); + req.setFile(fileOrig); + } + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3Event.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3Event.java new file mode 100644 index 0000000000..52c12bcc28 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3Event.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +/** + * A enum class for all Amazon S3 events. + */ +public enum S3Event { + ReducedRedundancyLostObject("s3:ReducedRedundancyLostObject"), + + ObjectCreated("s3:ObjectCreated:*"), + + ObjectCreatedByPut("s3:ObjectCreated:Put"), + + ObjectCreatedByPost("s3:ObjectCreated:Post"), + + ObjectCreatedByCopy("s3:ObjectCreated:Copy"), + + ObjectCreatedByCompleteMultipartUpload("s3:ObjectCreated:CompleteMultipartUpload"), + + ObjectRemoved("s3:ObjectRemoved:*"), + + ObjectRemovedDelete("s3:ObjectRemoved:Delete"), + + ObjectRemovedDeleteMarkerCreated("s3:ObjectRemoved:DeleteMarkerCreated"); + + private final String event; + + private S3Event(String event) { + this.event = event; + } + + @Override + public String toString() { + return this.event; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3KeyFilter.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3KeyFilter.java new file mode 100644 index 0000000000..f240de18dc --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3KeyFilter.java @@ -0,0 +1,124 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Filter criteria that allows for event notification filtering based on an S3 Object's key name + */ +public class S3KeyFilter implements Serializable { + + /** + * Allowable values for the name of a {@link FilterRule} for an {@link S3KeyFilter} + */ + public enum FilterRuleName { + Prefix, + Suffix, + ; + + /** + * Convenience factory method to create a new {@link FilterRule} with name initialized to + * this {@link FilterRuleName} + * + *

+         * FilterRule prefixRule = FilterRuleName.Prefix.newRule().withValue("prefix-value");
+         * 
+ * + * @return New {@link FilterRule} with name initialized to this {@link FilterRuleName} + */ + public FilterRule newRule() { + return new FilterRule().withName(this.toString()); + } + + /** + * Convenience factory method to create a new {@link FilterRule} with name initialized to + * this {@link FilterRuleName} and value initialized to {@code value} + * + *
+         * FilterRule prefixRule = FilterRuleName.Prefix.newRule("prefix-value");
+         * 
+ * + * @return New {@link FilterRule} with name initialized to this {@link FilterRuleName} + */ + public FilterRule newRule(String value) { + return newRule().withValue(value); + } + } + + private List filterRules; + + public S3KeyFilter() { + filterRules = new ArrayList(); + } + + /** + * @return The list of {@link FilterRule}s for this {@link S3KeyFilter} + */ + public List getFilterRules() { + return Collections.unmodifiableList(filterRules); + } + + /** + * Set the list of {@link FilterRule}s for this {@link S3KeyFilter} + * + * @param filterRules + * New list of {@link FilterRule}s + */ + public void setFilterRules(List filterRules) { + this.filterRules = new ArrayList(filterRules); + } + + /** + * Set the list of {@link FilterRule}s for this {@link S3KeyFilter} and returns this object for + * method chaining + * + * @param filterRules + * New List of {@link FilterRule}s + * @return This object for method chaining + */ + public S3KeyFilter withFilterRules(List filterRules) { + setFilterRules(filterRules); + return this; + } + + /** + * Convenience varargs method to set the list of {@link FilterRule}s for this + * {@link S3KeyFilter} and returns this object for method chaining + * + * @param filterRules + * New {@link FilterRule}s for this {@link S3KeyFilter} + * @return This object for method chaining + */ + public S3KeyFilter withFilterRules(FilterRule... filterRules) { + setFilterRules(Arrays.asList(filterRules)); + return this; + } + + /** + * Append a {@link FilterRule} to the list of {@link FilterRule}s for this {@link S3KeyFilter} + * + * @param filterRule + * New {@link FilterRule} to append + */ + public void addFilterRule(FilterRule filterRule) { + this.filterRules.add(filterRule); + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3Object.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3Object.java index 3c27c351a8..9f45d9c344 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3Object.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3Object.java @@ -19,10 +19,12 @@ package com.amazonaws.services.s3.model; import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.io.Serializable; /** * Represents an object stored in Amazon S3. This object contains the data @@ -31,7 +33,7 @@ * * @see ObjectMetadata */ -public class S3Object implements Closeable { +public class S3Object implements Closeable, Serializable, S3RequesterChargedResult { /** The key under which this object is stored */ private String key = null; @@ -42,11 +44,13 @@ public class S3Object implements Closeable { private ObjectMetadata metadata = new ObjectMetadata(); /** The stream containing the contents of this object from S3 */ - private S3ObjectInputStream objectContent; + private transient S3ObjectInputStream objectContent; /** The redirect location for this object */ private String redirectLocation; + private Integer taggingCount; + /** * Indicates if the requester is charged for downloading the data from * Requester Pays Buckets. @@ -82,11 +86,18 @@ public void setObjectMetadata(ObjectMetadata metadata) { } /** - * Gets an input stream containing the contents of this object. Callers - * should close this input stream as soon as possible, because the object - * contents aren't buffered in memory and stream directly from Amazon S3. + * Gets the input stream containing the contents of this object. + * + *

+ * Note: The method is a simple getter and does not actually create a + * stream. If you retrieve an S3Object, you should close this input stream + * as soon as possible, because the object contents aren't buffered in + * memory and stream directly from Amazon S3. Further, failure to close this + * stream can cause the request pool to become blocked. + *

* * @return An input stream containing the contents of this object. + * * @see S3Object#getObjectMetadata() * @see S3Object#setObjectContent(InputStream) */ @@ -97,7 +108,9 @@ public S3ObjectInputStream getObjectContent() { /** * Sets the input stream containing this object's contents. * - * @param objectContent The input stream containing this object's contents. + * @param objectContent + * The input stream containing this object's contents. + * * @see S3Object#getObjectContent() */ public void setObjectContent(S3ObjectInputStream objectContent) { @@ -107,8 +120,9 @@ public void setObjectContent(S3ObjectInputStream objectContent) { /** * Sets the input stream containing this object's contents. * - * @param objectContent The input stream containing this object's contents. - * Will get wrapped in an S3ObjectInputStream. + * @param objectContent + * The input stream containing this object's contents. Will get + * wrapped in an S3ObjectInputStream. * @see S3Object#getObjectContent() */ public void setObjectContent(InputStream objectContent) { @@ -120,6 +134,7 @@ public void setObjectContent(InputStream objectContent) { * Gets the name of the bucket in which this object is contained. * * @return The name of the bucket in which this object is contained. + * * @see S3Object#setBucketName(String) */ public String getBucketName() { @@ -129,7 +144,9 @@ public String getBucketName() { /** * Sets the name of the bucket in which this object is contained. * - * @param bucketName The name of the bucket containing this object. + * @param bucketName + * The name of the bucket containing this object. + * * @see S3Object#getBucketName() */ public void setBucketName(String bucketName) { @@ -140,6 +157,7 @@ public void setBucketName(String bucketName) { * Gets the key under which this object is stored. * * @return The key under which this object is stored. + * * @see S3Object#setKey(String) */ public String getKey() { @@ -149,7 +167,9 @@ public String getKey() { /** * Sets the key under which this object is stored. * - * @param key The key under which this object is stored. + * @param key + * The key under which this object is stored. + * * @see S3Object#getKey() */ public void setKey(String key) { @@ -158,6 +178,7 @@ public void setKey(String key) { /** * Gets the redirect location for this object. + * */ public String getRedirectLocation() { return this.redirectLocation; @@ -166,16 +187,24 @@ public String getRedirectLocation() { /** * Sets the redirect location for this object. * - * @param redirectLocation the redirect location for that object. + * @param redirectLocation + * the redirect location for that object. */ public void setRedirectLocation(String redirectLocation) { this.redirectLocation = redirectLocation; } + public Integer getTaggingCount() { + return taggingCount; + } + + public void setTaggingCount(Integer taggingCount) { + this.taggingCount = taggingCount; + } + /** * @see java.lang.Object#toString() */ - @Override public String toString() { return "S3Object [key=" + getKey() + ",bucket=" + (bucketName == null ? "" : bucketName) @@ -195,32 +224,12 @@ public void close() throws IOException { } } - /** - * Returns true if the user is charged for downloading the object from an - * Requester Pays Bucket; else false. - *

- * If a bucket is enabled for Requester Pays, then any attempt to read an - * object from it without Requester Pays enabled will result in a 403 error - * and the bucket owner will be charged for the request. - * - * @return true if the user has been charged for the download operation from - * Requester Pays Bucket. - */ + @Override public boolean isRequesterCharged() { return isRequesterCharged; } - /** - * Used for downloading an Amazon S3 Object from a Requester Pays Bucket. If - * set the requester is charged for downloading the data from the bucket. - *

- * If a bucket is enabled for Requester Pays, then any attempt to read an - * object from it without Requester Pays enabled will result in a 403 error - * and the bucket owner will be charged for the request. - * - * @param isRequesterCharged Indicates requester is charged for this - * operation. - */ + @Override public void setRequesterCharged(boolean isRequesterCharged) { this.isRequesterCharged = isRequesterCharged; } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectId.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectId.java new file mode 100644 index 0000000000..6c35a44f6d --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectId.java @@ -0,0 +1,107 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import static com.amazonaws.services.s3.model.InstructionFileId.DEFAULT_INSTRUCTION_FILE_SUFFIX; +import static com.amazonaws.services.s3.model.InstructionFileId.DOT; + +import java.io.Serializable; + +/** + * Used to uniquely identify an S3 object. Can be instantiated directly, or via + * the convenient builder {@link S3ObjectIdBuilder}. + */ +public class S3ObjectId implements Serializable { + private final String bucket; + private final String key; + /** + * Optional and applicable only for get operation. + */ + private final String versionId; + + public S3ObjectId(String bucket, String key) { + this(bucket, key, null); + } + + /** + * @param bucket + * the S3 bucket name which must not be null + * @param key + * the S3 key name which must not be null + * @param versionId + * optional version id + */ + public S3ObjectId(String bucket, String key, String versionId) { + if (bucket == null || key == null) { + throw new IllegalArgumentException( + "bucket and key must be specified"); + } + this.bucket = bucket; + this.key = key; + this.versionId = versionId; + } + + /** + * @param builder + * must not be null. + */ + public S3ObjectId(S3ObjectIdBuilder builder) { + this.bucket = builder.getBucket(); + this.key = builder.getKey(); + this.versionId = builder.getVersionId(); + } + + public String getBucket() { + return bucket; + } + + public String getKey() { + return key; + } + + /** + * Returns the version id which is optionally applicable for S3 get (but not + * put) operations. + */ + public String getVersionId() { + return versionId; + } + + /** + * Returns the instruction file id of the default instruction file. + */ + public InstructionFileId instructionFileId() { + return instructionFileId(null); + } + + /** + * Returns the instruction file id of an instruction file with the given + * suffix. + */ + public InstructionFileId instructionFileId(String suffix) { + String ifileKey = key + DOT; + ifileKey += (suffix == null || suffix.trim().length() == 0) + ? DEFAULT_INSTRUCTION_FILE_SUFFIX + : suffix + ; + return new InstructionFileId(bucket, ifileKey, versionId); + } + + @Override + public String toString() { + return "bucket: " + bucket + ", key: " + key + ", versionId: " + + versionId; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectIdBuilder.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectIdBuilder.java new file mode 100644 index 0000000000..91f4cd82bd --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectIdBuilder.java @@ -0,0 +1,79 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +/** + * Convenient builder for {@link S3ObjectId}. + */ +public final class S3ObjectIdBuilder implements Serializable { + private String bucket; + private String key; + private String versionId; + + public S3ObjectIdBuilder() {} + + /** + * @param id S3 object id, which must not be null. + */ + public S3ObjectIdBuilder(S3ObjectId id) { + this.bucket = id.getBucket(); + this.key = id.getKey(); + this.versionId = id.getVersionId(); + } + + public String getBucket() { + return bucket; + } + + public String getKey() { + return key; + } + + public String getVersionId() { + return versionId; + } + public void setBucket(String bucket) { + this.bucket = bucket; + } + + public void setKey(String key) { + this.key = key; + } + + public void setVersionId(String versionId) { + this.versionId = versionId; + } + + public S3ObjectIdBuilder withBucket(String bucket) { + this.bucket = bucket; + return this; + } + + public S3ObjectIdBuilder withKey(String key) { + this.key = key; + return this; + } + + public S3ObjectIdBuilder withVersionId(String versionId) { + this.versionId = versionId; + return this; + } + + public S3ObjectId build() { + return new S3ObjectId(bucket, key, versionId); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectInputStream.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectInputStream.java index 061e46aac9..8164e66313 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectInputStream.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectInputStream.java @@ -39,6 +39,8 @@ public class S3ObjectInputStream extends SdkFilterInputStream { private final HttpRequestBase httpRequest; + private boolean eof; + public S3ObjectInputStream(InputStream in) { this(in, null); } @@ -67,9 +69,11 @@ public S3ObjectInputStream( */ private static boolean wrapWithByteCounting(InputStream in) { if (!AwsSdkMetrics.isMetricsEnabled()) + { return false; // metrics is disabled + } if (in instanceof MetricAware) { - MetricAware aware = (MetricAware) in; + final MetricAware aware = (MetricAware) in; // wrap only if not already wrapped in one of it's inner chain of // input stream return !aware.isMetricActivated(); @@ -95,9 +99,17 @@ private static boolean wrapWithByteCounting(InputStream in) { */ @Override public void abort() { + doAbort(); + } + + /** + * To allow customers to override abort to just close. We can think about exposing this method + * as protected to allow customers to completely prevent the abort behavior if there is a need + */ + private void doAbort() { try { close(); - } catch (IOException e) { + } catch (final IOException e) { // expected from some implementations because the stream is closed LogFactory.getLog(getClass()).debug("FYI", e); } @@ -110,4 +122,61 @@ public void abort() { public HttpRequestBase getHttpRequest() { return httpRequest; } + + /** + * Returns the value of super.available() if the result is nonzero, or 1 + * otherwise. + *

+ * This is necessary to work around a known bug in + * GZIPInputStream.available(), which returns zero in some edge cases, + * causing file truncation. + *

+ * Ref: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7036144 + */ + @Override + public int available() throws IOException { + final int estimate = super.available(); + return estimate == 0 ? 1 : estimate; + } + + /** + * {@inheritDoc} + */ + @Override + public int read() throws IOException { + final int value = super.read(); + if (value == -1) { + eof = true; + } + return value; + } + + /** + * {@inheritDoc} + */ + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + /** + * {@inheritDoc} + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + final int value = super.read(b, off, len); + if (value == -1) { + eof = true; + } + return value; + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() throws IOException { + super.reset(); + eof = false; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectSummary.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectSummary.java index 08efbb6d99..4ce5f47cc0 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectSummary.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/S3ObjectSummary.java @@ -200,4 +200,16 @@ public void setStorageClass(String storageClass) { this.storageClass = storageClass; } + @Override + public String toString() { + return "S3ObjectSummary{" + + "bucketName='" + bucketName + '\'' + + ", key='" + key + '\'' + + ", eTag='" + eTag + '\'' + + ", size=" + size + + ", lastModified=" + lastModified + + ", storageClass='" + storageClass + '\'' + + ", owner=" + owner + + '}'; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAlgorithm.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAlgorithm.java new file mode 100644 index 0000000000..78d28ec625 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAlgorithm.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +/** + * Server-side Encryption Algorithm. + */ +public enum SSEAlgorithm { + AES256("AES256"), + KMS("aws:kms"), + ; + + private final String algorithm; + + public String getAlgorithm() { + return algorithm; + } + + private SSEAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + @Override + public String toString() { + return algorithm; + } + + /** + * Returns the SSEAlgorithm enum corresponding to the given string; + * or null if and only if the given algorithm is null. + * + * @throws IllegalArgumentException if the specified algorithm is not + * supported. + */ + public static SSEAlgorithm fromString(String algorithm) { + if (algorithm == null) + return null; + for (SSEAlgorithm e: values()) { + if (e.getAlgorithm().equals(algorithm)) + return e; + } + throw new IllegalArgumentException("Unsupported algorithm " + algorithm); + } + + /** + * Returns the default server side encryption algorithm, which is AES256. + */ + public static SSEAlgorithm getDefault() { + return AES256; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAwsKeyManagementParams.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAwsKeyManagementParams.java new file mode 100644 index 0000000000..673f91bf3b --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAwsKeyManagementParams.java @@ -0,0 +1,61 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +public class SSEAwsKeyManagementParams implements Serializable { + + /** + * The AWS Key Management Key id to be used for Server Side Encryption of + * the Amazon S3 object. + */ + private final String awsKmsKeyId; + + /** + * Constructs a new instance of SSEAwsKeyManagementParams. The default AWS + * KMS Key id is used for encryption. + */ + public SSEAwsKeyManagementParams() { + this.awsKmsKeyId = null; + } + + /** + * Constructs a new instance of SSEAwsKeyManagementParams with the user + * specified AWS Key Management System Key Id. + */ + public SSEAwsKeyManagementParams(String awsKmsKeyId) { + if (awsKmsKeyId == null || awsKmsKeyId.trim().isEmpty()) { + throw new IllegalArgumentException( + "AWS Key Management System Key id cannot be null"); + } + this.awsKmsKeyId = awsKmsKeyId; + } + + /** + * Returns the AWS Key Management System Key Id used for encryption. Returns + * null if default Key Id is used. + */ + public String getAwsKmsKeyId() { + return awsKmsKeyId; + } + + /** + * Returns the scheme used for encrypting the Amazon S3 object. Currently + * the encryption is always "aws:kms". + */ + public String getEncryption() { + return SSEAlgorithm.KMS.getAlgorithm(); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAwsKeyManagementParamsProvider.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAwsKeyManagementParamsProvider.java new file mode 100644 index 0000000000..5ba5875c92 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSEAwsKeyManagementParamsProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +/** + * Implemented by classes that support the option of using SSE with AWS Key + * Management System. + */ +public interface SSEAwsKeyManagementParamsProvider { + /** + * Returns the optional SSEAwsKeyManagementParams to use to encrypt the + * uploaded object. + * + * @return The optional SSEAwsKeyManagementParams to use to encrypt the + * uploaded object. + */ + public SSEAwsKeyManagementParams getSSEAwsKeyManagementParams(); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSECustomerKey.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSECustomerKey.java index 7c67037ce3..3dcd50071c 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSECustomerKey.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSECustomerKey.java @@ -21,19 +21,26 @@ /** * Represents a customer provided key for use with Amazon S3 server-side - * encryption. Server-side encryption is about data encryption at rest, that is, - * Amazon S3 encrypts your data as it writes it to disks in its data centers and - * decrypts it for you when you access it. Amazon S3 manages encryption and - * decryption for you. This class allows you to specify your own encryption key - * for Amazon S3 to use when encrypting your data on the server-side, instead of - * allowing Amazon to automatically generate an encryption key for you. For more - * information on Amazon S3 server-side encryption, see: + * encryption. + * + * Server-side encryption is about data encryption at rest, that is, Amazon S3 + * encrypts your data as it writes it to disks in its data centers and decrypts + * it for you when you access it. Amazon S3 manages encryption and decryption + * for you. + * + * This class allows you to specify your own encryption key for Amazon S3 to use + * when encrypting your data on the server-side, instead of allowing Amazon to + * automatically generate an encryption key for you. + * + * For more information on Amazon S3 server-side encryption, see: * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html - * This encryption is done entirely on the server-side. Your data is transmitted + * + * This encryption is done entirely on the server-side. Your data is transmitted * to Amazon S3 over a secure SSL connection and then encrypted when it reaches - * Amazon's servers. The SDK also offers client-side encryption, where the - * encryption keys and unencrypted data never leave your machines. For more - * information on client-side encryption for Amazon S3 data, see: + * Amazon's servers. The SDK also offers client-side encryption, where the encryption + * keys and unencrypted data never leave your machines. + * + * For more information on client-side encryption for Amazon S3 data, see: * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingClientSideEncryption.html */ public class SSECustomerKey { @@ -41,61 +48,78 @@ public class SSECustomerKey { private String base64EncodedMd5; private String algorithm; + /** - * Constructs a new customer provided server-side encryption key using the - * specified base64-encoded key material. By default, this is assumed to be - * an AES-256 key, but the key algorithm can be set through the - * {@link #setAlgorithm(String)} method. Currently, Amazon S3 only supports - * AES-256 encryption keys. + * Constructs a new customer provided server-side encryption key using the specified + * base64-encoded key material. + * + * By default, this is assumed to be an AES-256 key, but the key algorithm + * can be set through the {@link #setAlgorithm(String)} method. + * + * Currently, Amazon S3 only supports AES-256 encryption keys. * - * @param base64EncodedKey The base 64 encoded encryption key material. + * @param base64EncodedKey + * The base 64 encoded encryption key material. */ public SSECustomerKey(String base64EncodedKey) { if (base64EncodedKey == null || base64EncodedKey.length() == 0) throw new IllegalArgumentException("Encryption key must be specified"); // Default to AES-256 encryption - this.algorithm = ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION; + this.algorithm = SSEAlgorithm.AES256.getAlgorithm(); this.base64EncodedKey = base64EncodedKey; } /** * Constructs a new customer provided server-side encryption key using the - * specified raw key material. By default, this is assumed to be an AES-256 - * key, but the key algorithm can be set through the - * {@link #setAlgorithm(String)} method. Currently, Amazon S3 only supports - * AES-256 encryption keys. + * specified raw key material. * - * @param rawKeyMaterial The raw bytes of the customer provided encryption - * key. + * By default, this is assumed to be an AES-256 key, but the key algorithm + * can be set through the {@link #setAlgorithm(String)} method. + * + * Currently, Amazon S3 only supports AES-256 encryption keys. + * + * @param rawKeyMaterial + * The raw bytes of the customer provided encryption key. */ public SSECustomerKey(byte[] rawKeyMaterial) { if (rawKeyMaterial == null || rawKeyMaterial.length == 0) throw new IllegalArgumentException("Encryption key must be specified"); // Default to AES-256 encryption - this.algorithm = ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION; + this.algorithm = SSEAlgorithm.AES256.getAlgorithm(); this.base64EncodedKey = Base64.encodeAsString(rawKeyMaterial); } /** * Constructs a new customer provided server-side encryption key using the - * specified SecretKey. By default, this is assumed to be an AES-256 key, - * but the key algorithm can be set through the - * {@link #setAlgorithm(String)} method. Currently, Amazon S3 only supports - * AES-256 encryption keys. + * specified SecretKey. + * + * By default, this is assumed to be an AES-256 key, but the key algorithm + * can be set through the {@link #setAlgorithm(String)} method. * - * @param key The customer provided server-side encryption key. + * Currently, Amazon S3 only supports AES-256 encryption keys. + * + * @param key + * The customer provided server-side encryption key. */ public SSECustomerKey(SecretKey key) { if (key == null) throw new IllegalArgumentException("Encryption key must be specified"); // Default to AES-256 encryption - this.algorithm = ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION; + this.algorithm = SSEAlgorithm.AES256.getAlgorithm(); this.base64EncodedKey = Base64.encodeAsString(key.getEncoded()); } + /** + * Constructs a new SSECustomerKey instance with key as null. + */ + private SSECustomerKey() { + this.base64EncodedKey = null; + } + + /** * Returns the base64-encoded server-side encryption key that was provided * in this object's constructor. @@ -108,8 +132,9 @@ public String getKey() { /** * Returns the encryption algorithm to use with this customer-provided - * server-side encryption key. Currently, "AES256" is the only supported - * algorithm. + * server-side encryption key. + * + * Currently, "AES256" is the only supported algorithm. * * @return The encryption algorithm to use with this customer-provided * server-side encryption key. @@ -120,11 +145,14 @@ public String getAlgorithm() { /** * Sets the encryption algorithm to use with this customer-provided - * server-side encryption key. Currently, "AES256" is the only supported - * algorithm. + * server-side encryption key. + * + * Currently, "AES256" is the only supported algorithm. + * + * @see SSEAlgorithm#AES256 * - * @see ObjectMetadata#AES_256_SERVER_SIDE_ENCRYPTION - * @param algorithm The server-side encryption algorithm to use with this + * @param algorithm + * The server-side encryption algorithm to use with this * customer-provided server-side encryption key. */ public void setAlgorithm(String algorithm) { @@ -134,12 +162,16 @@ public void setAlgorithm(String algorithm) { /** * Sets the encryption algorithm to use with this customer-provided * server-side encryption key, and returns this object so that method calls - * can be chained together. Currently, "AES256" is the only supported - * algorithm. + * can be chained together. + * + * Currently, "AES256" is the only supported algorithm. + * + * @see SSEAlgorithm#AES256 * - * @see ObjectMetadata#AES_256_SERVER_SIDE_ENCRYPTION - * @param algorithm The server-side encryption algorithm to use with this + * @param algorithm + * The server-side encryption algorithm to use with this * customer-provided server-side encryption key. + * * @return The updated ServerSideEncryptionKey object, so that method calls * may be chained together. */ @@ -151,8 +183,10 @@ public SSECustomerKey withAlgorithm(String algorithm) { /** * Returns the optional base64-encoded MD5 digest of the encryption key to * use when validating the integrity of the transmitted server-side - * encryption key. If a MD5 digest is not explicitly specified, then it will - * be automatically calculated. + * encryption key. + * + * If a MD5 digest is not explicitly specified, then it will be + * automatically calculated. * * @return The base64-encoded MD5 digest of this customer-provided * server-side encryption key. @@ -162,14 +196,15 @@ public String getMd5() { } /** - * Sets the optional MD5 digest (base64-encoded) of the encryption key to - * use when encrypting the object. This will be used as a message integrity - * check that the key was transmitted without error. If not set, the SDK - * will fill in this value by calculating the MD5 digest of the secret key, - * before sending the request. + * Sets the optional MD5 digest (base64-encoded) of the encryption key to use when + * encrypting the object. This will be used as a message integrity check + * that the key was transmitted without error. If not set, the SDK will fill + * in this value by calculating the MD5 digest of the secret key, before + * sending the request. * - * @param md5Digest The MD5 digest (base64-encoded) of the encryption key to - * use when encrypting the object. + * @param md5Digest + * The MD5 digest (base64-encoded) of the encryption key to use + * when encrypting the object. */ public void setMd5(String md5Digest) { this.base64EncodedMd5 = md5Digest; @@ -183,13 +218,38 @@ public void setMd5(String md5Digest) { * not set, the SDK will fill in this value by calculating the MD5 digest of * the secret key, before sending the request. * - * @param md5Digest The MD5 digest (base64-encoded) of the encryption key to - * use when encrypting the object. - * @return The updated object, so that additional method calls can be chained - * together. + * @param md5Digest + * The MD5 digest (base64-encoded) of the encryption key to use + * when encrypting the object. + * + * @return The updated object, so that additional method calls can be + * chained together. */ public SSECustomerKey withMd5(String md5Digest) { setMd5(md5Digest); return this; } + + /** + * Constructs a new SSECustomerKey that can be used for generating the + * presigned URL's. + * + * Currently, "AES256" is the only supported algorithm. + * + * @see SSEAlgorithm#AES256 + * + * @param algorithm + * The server-side encryption algorithm to use with this + * customer-provided server-side encryption key; must not be + * null. + * + * @throws IllegalArgumentException + * if the input parameter is null. + */ + public static SSECustomerKey generateSSECustomerKeyForPresignUrl( + String algorithm) { + if (algorithm == null) + throw new IllegalArgumentException(); + return new SSECustomerKey().withAlgorithm(algorithm); + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSECustomerKeyProvider.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSECustomerKeyProvider.java new file mode 100644 index 0000000000..bbd95025d6 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SSECustomerKeyProvider.java @@ -0,0 +1,29 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +/** + * Implemented by classes that support the option of using SSE Customer key. + */ +public interface SSECustomerKeyProvider { + /** + * Returns the optional customer-provided server-side encryption key to use + * to encrypt the uploaded object. + * + * @return The optional customer-provided server-side encryption key to use + * to encrypt the uploaded object. + */ + public SSECustomerKey getSSECustomerKey(); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketAnalyticsConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketAnalyticsConfigurationRequest.java new file mode 100644 index 0000000000..6d626e3b67 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketAnalyticsConfigurationRequest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration; + +import java.io.Serializable; + +/** + * Request object to set analytics configuration to a bucket. + */ +public class SetBucketAnalyticsConfigurationRequest extends AmazonWebServiceRequest implements Serializable { + + private String bucketName; + private AnalyticsConfiguration analyticsConfiguration; + + public SetBucketAnalyticsConfigurationRequest() { } + + public SetBucketAnalyticsConfigurationRequest(String bucketName, AnalyticsConfiguration analyticsConfiguration) { + this.bucketName = bucketName; + this.analyticsConfiguration = analyticsConfiguration; + } + + /** + * Returns the name of the bucket to which an analytics configuration is stored. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket to which an analytics configuration is stored. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket to which an analytics configuration is stored + * and returns this object for method chaining. + */ + public SetBucketAnalyticsConfigurationRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Returns the {@link AnalyticsConfiguration} object. + */ + public AnalyticsConfiguration getAnalyticsConfiguration() { + return analyticsConfiguration; + } + + /** + * Sets the {@link AnalyticsConfiguration} object. + */ + public void setAnalyticsConfiguration(AnalyticsConfiguration analyticsConfiguration) { + this.analyticsConfiguration = analyticsConfiguration; + } + + /** + * Sets the {@link AnalyticsConfiguration} object and + * returns this object for method chaining. + */ + public SetBucketAnalyticsConfigurationRequest withAnalyticsConfiguration(AnalyticsConfiguration analyticsConfiguration) { + setAnalyticsConfiguration(analyticsConfiguration); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketAnalyticsConfigurationResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketAnalyticsConfigurationResult.java new file mode 100644 index 0000000000..e6ffff90f4 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketAnalyticsConfigurationResult.java @@ -0,0 +1,26 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#setBucketAnalyticsConfiguration(SetBucketAnalyticsConfigurationRequest)} + * operation. + */ +public class SetBucketAnalyticsConfigurationResult implements Serializable { + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketInventoryConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketInventoryConfigurationRequest.java new file mode 100644 index 0000000000..ea29f6850a --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketInventoryConfigurationRequest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.services.s3.model.inventory.InventoryConfiguration; + +import java.io.Serializable; + +/** + * Request object to set an inventory configuration to a bucket. + */ +public class SetBucketInventoryConfigurationRequest extends AmazonWebServiceRequest implements Serializable { + + private String bucketName; + + private InventoryConfiguration inventoryConfiguration; + + public SetBucketInventoryConfigurationRequest() { + } + + public SetBucketInventoryConfigurationRequest(String bucketName, InventoryConfiguration inventoryConfiguration) { + this.bucketName = bucketName; + this.inventoryConfiguration = inventoryConfiguration; + } + + /** + * Returns the name of the bucket where the inventory configuration will be stored. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket where the inventory configuration will be stored. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket where the inventory configuration will be stored + * and returns {@link SetBucketInventoryConfigurationRequest} object for + * method chaining. + */ + public SetBucketInventoryConfigurationRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Returns the inventory configuration. + */ + public InventoryConfiguration getInventoryConfiguration() { + return inventoryConfiguration; + } + + /** + * Sets the inventory configuration. + */ + public void setInventoryConfiguration(InventoryConfiguration inventoryConfiguration) { + this.inventoryConfiguration = inventoryConfiguration; + } + + /** + * Sets the inventory configuration and returns the + * {@link SetBucketInventoryConfigurationRequest} object + * for method chaining. + */ + public SetBucketInventoryConfigurationRequest withInventoryConfiguration(InventoryConfiguration inventoryConfiguration) { + setInventoryConfiguration(inventoryConfiguration); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketInventoryConfigurationResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketInventoryConfigurationResult.java new file mode 100644 index 0000000000..36b04e0ebe --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketInventoryConfigurationResult.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#setBucketInventoryConfiguration(SetBucketInventoryConfigurationRequest)} + * operation. + */ +public class SetBucketInventoryConfigurationResult implements Serializable { +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketMetricsConfigurationRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketMetricsConfigurationRequest.java new file mode 100644 index 0000000000..7d74437881 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketMetricsConfigurationRequest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.services.s3.model.metrics.MetricsConfiguration; + +import java.io.Serializable; + +/** + * Request object to set metrics configuration to a bucket. + */ +public class SetBucketMetricsConfigurationRequest extends AmazonWebServiceRequest implements Serializable { + + private String bucketName; + private MetricsConfiguration metricsConfiguration; + + public SetBucketMetricsConfigurationRequest() { + } + + public SetBucketMetricsConfigurationRequest(String bucketName, MetricsConfiguration metricsConfiguration) { + this.bucketName = bucketName; + this.metricsConfiguration = metricsConfiguration; + } + + /** + * Returns the name of the bucket for which the metrics configuration is set. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Sets the name of the bucket for which the metrics configuration is set. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Sets the name of the bucket for which the metrics configuration is set + * and returns {@link SetBucketMetricsConfigurationRequest} object for method chaining. + */ + public SetBucketMetricsConfigurationRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * Returns the metrics configuration that is set on the bucket. + */ + public MetricsConfiguration getMetricsConfiguration() { + return metricsConfiguration; + } + + /** + * Sets the metrics configuration. + */ + public void setMetricsConfiguration(MetricsConfiguration metricsConfiguration) { + this.metricsConfiguration = metricsConfiguration; + } + + /** + * Sets the metrics configuration and returns the + * {@link SetBucketMetricsConfigurationRequest} object for method chaining. + */ + public SetBucketMetricsConfigurationRequest withMetricsConfiguration(MetricsConfiguration metricsConfiguration) { + setMetricsConfiguration(metricsConfiguration); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketMetricsConfigurationResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketMetricsConfigurationResult.java new file mode 100644 index 0000000000..f6d012e4a6 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetBucketMetricsConfigurationResult.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +/** + * Result object to contain the response returned from + * {@link com.amazonaws.services.s3.AmazonS3Client#setBucketMetricsConfiguration(SetBucketMetricsConfigurationRequest)} + * operation. + */ +public class SetBucketMetricsConfigurationResult implements Serializable { +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectAclRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectAclRequest.java new file mode 100644 index 0000000000..9294ff687d --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectAclRequest.java @@ -0,0 +1,262 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import com.amazonaws.AmazonWebServiceRequest; + +/** + * Request object containing all the options for setting a object's Access + * Control List (ACL). + */ +public class SetObjectAclRequest extends AmazonWebServiceRequest implements Serializable { + + /** The name of the bucket containing the object whose ACL is being set. */ + private final String bucketName; + + /** The name of the object whose ACL is being set. */ + private final String key; + + /** The version ID of the object version whose ACL is being set. */ + private final String versionId; + + /** The custom ACL to apply to the specified object. */ + private final AccessControlList acl; + + /** The canned ACL to apply to the specified object. */ + private final CannedAccessControlList cannedAcl; + + /** + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; + + /** + * Constructs a new SetObjectAclRequest object, ready to set the specified + * ACL on the specified object when this request is executed. + * + * @param bucketName + * The name of the bucket containing the object whose ACL is + * being set. + * @param key + * The name of the object whose ACL is being set. + * @param acl + * The custom Access Control List containing the access rules to + * apply to the specified bucket when this request is executed. + */ + public SetObjectAclRequest(String bucketName, String key, + AccessControlList acl) { + this.bucketName = bucketName; + this.key = key; + this.versionId = null; + + this.acl = acl; + this.cannedAcl = null; + } + + /** + * Constructs a new SetObjectAclRequest object, ready to set the specified + * ACL on the specified object when this request is executed. + * + * @param bucketName + * The name of the bucket containing the object whose ACL is + * being set. + * @param key + * The name of the object whose ACL is being set. + * @param acl + * The Canned Access Control List to apply to the specified + * bucket when this request is executed. + */ + public SetObjectAclRequest(String bucketName, String key, + CannedAccessControlList acl) { + this.bucketName = bucketName; + this.key = key; + this.versionId = null; + + this.acl = null; + this.cannedAcl = acl; + } + + /** + * Constructs a new SetObjectAclRequest object, ready to set the specified + * ACL on the specified object when this request is executed. + * + * @param bucketName + * The name of the bucket containing the object whose ACL is + * being set. + * @param key + * The name of the object whose ACL is being set. + * @param versionId + * The version ID of the object version whose ACL is being set. + * @param acl + * The custom Access Control List containing the access rules to + * apply to the specified bucket when this request is executed. + */ + public SetObjectAclRequest(String bucketName, String key, String versionId, + AccessControlList acl) { + this.bucketName = bucketName; + this.key = key; + this.versionId = versionId; + + this.acl = acl; + this.cannedAcl = null; + } + + /** + * Constructs a new SetObjectAclRequest object, ready to set the specified + * ACL on the specified object when this request is executed. + * + * @param bucketName + * The name of the bucket containing the object whose ACL is + * being set. + * @param key + * The name of the object whose ACL is being set. + * @param versionId + * The version ID of the object version whose ACL is being set. + * @param acl + * The Canned Access Control List to apply to the specified + * bucket when this request is executed. + */ + public SetObjectAclRequest(String bucketName, String key, String versionId, + CannedAccessControlList acl) { + this.bucketName = bucketName; + this.key = key; + this.versionId = versionId; + + this.acl = null; + this.cannedAcl = acl; + } + + /** + * Returns the name of the bucket containing the object whose ACL is being + * set. + * + * @return The name of the bucket containing the object whose ACL is being + * set. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Returns the name of the object whose ACL is being set. + * + * @return The name of the object whose ACL is being set. + */ + public String getKey() { + return key; + } + + /** + * Returns the version ID of the object version whose ACL is being set. + * + * @return The version ID of the object version whose ACL is being set. + */ + public String getVersionId() { + return versionId; + } + + /** + * Returns the custom ACL to be applied to the specified object when this + * request is executed. A request can use either a custom ACL or a canned + * ACL, but not both. + * + * @return The custom ACL to be applied to the specified bucket when this + * request is executed, or null if the request is to be executed + * with a canned ACL. + */ + public AccessControlList getAcl() { + return acl; + } + + /** + * Returns the canned ACL to be applied to the specified object when this + * request is executed. A request can use either a custom ACL or a canned + * ACL, but not both. + * + * @return The canned ACL to be applied to the specified bucket when this + * request is executed, or null if the request is to be executed + * with a custom ACL. + */ + public CannedAccessControlList getCannedAcl() { + return cannedAcl; + } + + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket + * + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. + */ + public boolean isRequesterPays() { + return isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + */ + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; + } + + /** + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated SetObjectAclRequest object so that additional method calls can be + * chained together. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated SetObjectAclRequest object. + */ + public SetObjectAclRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectTaggingRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectTaggingRequest.java new file mode 100644 index 0000000000..6f51c9b213 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectTaggingRequest.java @@ -0,0 +1,174 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.AmazonWebServiceRequest; + +import java.io.Serializable; + +/** + * Request object for the parameters to set the tags for an object. + */ +public class SetObjectTaggingRequest extends AmazonWebServiceRequest implements Serializable { + private String bucketName; + private String key; + private String versionId; + private ObjectTagging tagging; + + /** + * Constructs an instance of this object. + * + * @param bucketName + * The bucket name. + * @param key + * The object key. + * @param tagging + * The set of tags to set for the specified object. + */ + public SetObjectTaggingRequest(String bucketName, String key, ObjectTagging tagging) { + this(bucketName, key, null, tagging); + } + + /** + * Constructs an instance of this object. + * + * @param bucketName + * The bucket name. + * @param key + * The object key. + * @param versionId + * The version of the object. + * @param tagging + * The set of tags to set for the specified object. + */ + public SetObjectTaggingRequest(String bucketName, String key, String versionId, ObjectTagging tagging) { + this.bucketName = bucketName; + this.key = key; + this.versionId = versionId; + this.tagging = tagging; + } + + /** + * @return The bucket name. + */ + public String getBucketName() { + return bucketName; + } + + /** + * Set the bucket name. + * + * @param bucketName The bucket name. + */ + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * Set the bucket name. + * + * @param bucketName the bucket name. + * + * @return This object for chaining. + */ + public SetObjectTaggingRequest withBucketName(String bucketName) { + setBucketName(bucketName); + return this; + } + + /** + * @return The object key. + */ + public String getKey() { + return key; + } + + /** + * Set the object key. + * + * @param key The object key. + */ + public void setKey(String key) { + this.key = key; + } + + /** + * Set the object key. + * + * @param key The object key. + * + * @return This object for chaining. + */ + public SetObjectTaggingRequest withKey(String key) { + setKey(key); + return this; + } + + /** + * @return The version of the object. + */ + public String getVersionId() { + return versionId; + } + + /** + * Set object version. + * + * @param versionId The object version. + */ + public void setVersionId(String versionId) { + this.versionId = versionId; + } + + /** + * Set object version. + * + * @param versionId The object version. + * + * @return This object for chaining. + */ + public SetObjectTaggingRequest withVersionId(String versionId) { + setVersionId(versionId); + return this; + } + + /** + * @return The set of tags to set for the specified object. + */ + public ObjectTagging getTagging() { + return tagging; + } + + /** + * Set the object tagging. + * + * @param tagging The object tagging. + */ + public void setTagging(ObjectTagging tagging) { + this.tagging = tagging; + } + + /** + * Set the object tagging. + * + * @param tagging The object tagging. + * + * @return This object for chaining. + */ + public SetObjectTaggingRequest withTagging(ObjectTagging tagging) { + setTagging(tagging); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectTaggingResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectTaggingResult.java new file mode 100644 index 0000000000..035eeddd1e --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/SetObjectTaggingResult.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +/** + * Contains all the information returned from performing a {@link + * SetObjectTaggingRequest}. + */ +public class SetObjectTaggingResult { + private String versionId; + + /** + * @return The version ID of the object whose tags were set. + */ + public String getVersionId() { + return versionId; + } + + /** + * Set the version ID of the object whose tags were set. + * + * @param versionId + * The version ID. + */ + public void setVersionId(String versionId) { + this.versionId = versionId; + } + + /** + * Set the version ID of the object whose tags were set. + * + * @param versionId + * The version ID. + * @return This object for chaining. + */ + public SetObjectTaggingResult withVersionId(String versionId) { + setVersionId(versionId); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/StorageClass.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/StorageClass.java index 058832d28c..71e399cdbf 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/StorageClass.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/StorageClass.java @@ -55,7 +55,14 @@ public enum StorageClass { * data is stored in Amazon Glacier, and Amazon S3 stores a reference to the * data in the Amazon S3 bucket. */ - Glacier("GLACIER"); + Glacier("GLACIER"), + + /** + * Standard Infrequent Access storage class + */ + StandardInfrequentAccess("STANDARD_IA"), + + ; /** * Returns the Amazon S3 {@link StorageClass} enumeration value representing @@ -71,9 +78,10 @@ public enum StorageClass { */ public static StorageClass fromValue(String s3StorageClassString) throws IllegalArgumentException { - for (StorageClass storageClass : StorageClass.values()) { - if (storageClass.toString().equals(s3StorageClassString)) + for (final StorageClass storageClass : StorageClass.values()) { + if (storageClass.toString().equals(s3StorageClassString)) { return storageClass; + } } throw new IllegalArgumentException( diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Tag.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Tag.java new file mode 100644 index 0000000000..f8c7ae6ab4 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/Tag.java @@ -0,0 +1,115 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import java.io.Serializable; + +/** + * Represents a tag on a resource. + */ +public class Tag implements Serializable { + private String key; + private String value; + + /** + * Constructs an instance of this object. + * + * @param key The tag key. + * @param value The tag value. + */ + public Tag(String key, String value) { + this.key = key; + this.value = value; + } + + /** + * @return The tag key. + */ + public String getKey() { + return key; + } + + /** + * Set the tag key. + * + * @param key The tag key. + */ + public void setKey(String key) { + this.key = key; + } + + /** + * Set the tag key. + * + * @param key The tag key. + * @return This object for chaining. + */ + public Tag withKey(String key) { + setKey(key); + return this; + } + + /** + * @return The tag value. + */ + public String getValue() { + return value; + } + + /** + * Set the tag value. + * + * @param value The tag value. + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Set the tag value. + * + * @param value The tag value. + * @return This object for chaining. + */ + public Tag withValue(String value) { + setValue(value); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final Tag tag = (Tag) o; + + if (key != null ? !key.equals(tag.key) : tag.key != null) { + return false; + } + return value != null ? value.equals(tag.value) : tag.value == null; + + } + + @Override + public int hashCode() { + int result = key != null ? key.hashCode() : 0; + result = 31 * result + (value != null ? value.hashCode() : 0); + return result; + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/TopicConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/TopicConfiguration.java new file mode 100644 index 0000000000..0146eb6321 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/TopicConfiguration.java @@ -0,0 +1,88 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; +import java.io.Serializable; + +import java.util.EnumSet; + +/** + * Represents the topic configuration for an Amazon S3 bucket. + */ +public class TopicConfiguration extends NotificationConfiguration implements Serializable { + + /** + * The Amazon SNS topic ARN for this configuration. + */ + private String topicARN; + + public TopicConfiguration() { + super(); + } + + /** + * Creates a new topic configuration with the given topic arn and set of events. + * + * @param topicARN + * the Amazon SNS topic arn to which the notifications are to be sent. + * @param events + * the events for which the notifications are to be sent + */ + public TopicConfiguration(String topicARN, EnumSet events) { + super(events); + this.topicARN = topicARN; + } + + /** + * Creates a new topic configuration with the given topic arn and set of events. + * + * @param topicARN + * the Amazon SNS topic arn to which the notifications are to be sent. + * @param events + * the events for which the notifications are to be sent + */ + public TopicConfiguration(String topicARN, String... events) { + super(events); + this.topicARN = topicARN; + } + + /** + * Returns the topic arn for this notification configuration. + */ + public String getTopicARN() { + return topicARN; + } + + /** + * Sets the topic ARN for this configuration + * + * @param topicARN + * ARN for the SNS topic + */ + public void setTopicARN(String topicARN) { + this.topicARN = topicARN; + } + + /** + * Fluent method to set the topic ARN for this configuration + * + * @param topicARN + * ARN for the SNS topic + * @return This object for method chaining + */ + public TopicConfiguration withTopicARN(String topicARN) { + setTopicARN(topicARN); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadObjectRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadObjectRequest.java new file mode 100644 index 0000000000..b0d721a573 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadObjectRequest.java @@ -0,0 +1,265 @@ +/* + * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; + +import com.amazonaws.services.s3.AmazonS3EncryptionClient; +import com.amazonaws.services.s3.UploadObjectObserver; +import com.amazonaws.services.s3.internal.MultiFileOutputStream; + +import java.io.File; +import java.io.InputStream; +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; + +/** + * Used to request the client-side encryption and upload of a large S3 object + * via pipelined parallel multi-part uploads. + * + * @see AmazonS3EncryptionClient#uploadObject(UploadObjectRequest) + */ +public class UploadObjectRequest extends AbstractPutObjectRequest implements + MaterialsDescriptionProvider, Serializable { + private static final long serialVersionUID = 1L; + + static final int MIN_PART_SIZE = 5 << 20; // 5 MB + + /** + * Optional metadata to be included in each upload part requests. + */ + private ObjectMetadata uploadPartMetadata; + /** + * description of encryption materials to be used with this request. + */ + private Map materialsDescription; + /** + * Part size (in bytes). Default is {@value #MIN_PART_SIZE}. This part size + * will be used as a reference for the multi-part uploads but the physical + * part size may vary. + */ + private long partSize = MIN_PART_SIZE; + /** + * Optional configuration of a custom thread pool used for concurrent uploads. + */ + private transient ExecutorService executorService; + /** + * Optional configuration of an object observer for advanced customization. + */ + private transient UploadObjectObserver uploadObjectObserver; + /** + * Optional configuration of a custom mutli-file output stream used for + * generating multiple parts for ciphertext. + */ + private transient MultiFileOutputStream multiFileOutputStream; + + /** + * Limitation (in bytes) on temporary disk space consumption for this + * request; must be at least twice the amount of the specified + * {@link #partSize}. The default is {@value Long#MAX_VALUE}. + */ + private long diskLimit = Long.MAX_VALUE; + + public UploadObjectRequest(String bucketName, String key, File file) { + super(bucketName, key, file); + } + + public UploadObjectRequest(String bucketName, String key, + InputStream input, ObjectMetadata metadata) { + super(bucketName, key, input, metadata); + } + + /** + * Returns the part-size used for muti-part upload for this request. This + * part size will be used as a reference for the multi-part uploads but the + * physical part size may vary. + */ + public long getPartSize() { + return partSize; + } + + /** + * Configured the part size for multi-part upload. Must be at least + * {@link #MIN_PART_SIZE}. This part size will be used as a reference for + * the multi-part uploads but the physical part size may vary. + * + * @return this object for method chaining purposes + */ + public UploadObjectRequest withPartSize(long partSize) { + if (partSize < MIN_PART_SIZE) { + throw new IllegalArgumentException("partSize must be at least " + + MIN_PART_SIZE); + } + this.partSize = partSize; + return this; + } + + /** + * Returns the maximum size (in bytes) of additional disk space that will be + * consumed for this request; or {@link Long#MAX_VALUE} if there is no + * limit. + */ + public long getDiskLimit() { + return diskLimit; + } + + /** + * Configured the maximum disk space (in bytes) that will be consumed for + * this request. The maximum disk space must be at least twice the size of + * {@link #partSize}. + * + * @return this object for method chaining purposes + */ + public UploadObjectRequest withDiskLimit(long diskLimit) { + this.diskLimit = diskLimit; + return this; + } + + /** + * Returns a custom executor service for concurrent uploads; or null there + * is no customization. + */ + public ExecutorService getExecutorService() { + return executorService; + } + + /** + * Configure a custom executor service for concurrent uploads. + * + * @return this object for method chaining purposes + */ + public UploadObjectRequest withExecutorService(ExecutorService executorService) { + this.executorService = executorService; + return this; + } + + /** + * Returns a custom multi-file output stream; or null if the default is to + * be used. + */ + public MultiFileOutputStream getMultiFileOutputStream() { + return multiFileOutputStream; + } + + /** + * Configure a custom multi-file output stream; or null if the default is to + * be used. + * + * @return this object for method chaining purposes + */ + public UploadObjectRequest withMultiFileOutputStream( + MultiFileOutputStream multiFileOutputStream) { + this.multiFileOutputStream = multiFileOutputStream; + return this; + } + + /** + * Returns a custom upload-object observer; or null there is no + * customization. + */ + public UploadObjectObserver getUploadObjectObserver() { + return uploadObjectObserver; + } + + /** + * Configure a custom upload-object observer; or null if the default is to + * be used. + * + * @return this object for method chaining purposes + */ + public UploadObjectRequest withUploadObjectObserver( + UploadObjectObserver uploadObjectObserver) { + this.uploadObjectObserver = uploadObjectObserver; + return this; + } + + @Override + public Map getMaterialsDescription() { + return materialsDescription; + } + + /** + * Sets the materials description for the encryption materials to be used + * with the current request. + * + * @param materialsDescription + * the materialsDescription to set + */ + public void setMaterialsDescription(Map materialsDescription) { + this.materialsDescription = materialsDescription == null + ? null + : Collections.unmodifiableMap( + new HashMap(materialsDescription)); + } + + /** + * Fluent API for {@link #setMaterialsDescription(Map)}. + */ + public UploadObjectRequest withMaterialsDescription( + Map materialsDescription) { + setMaterialsDescription(materialsDescription); + return this; + } + + /** + * Gets the optional metadata to be included in each UploadPart request. + */ + public ObjectMetadata getUploadPartMetadata() { + return uploadPartMetadata; + } + + /** + * Sets the optional metadata to be included in each UploadPart request. + */ + public void setUploadPartMetadata(ObjectMetadata partUploadMetadata) { + this.uploadPartMetadata = partUploadMetadata; + } + + /** + * Fluent API for {@link #setUploadPartMetadata(ObjectMetadata)}. + */ + public T withUploadPartMetadata( + ObjectMetadata partUploadMetadata) { + setUploadPartMetadata(partUploadMetadata); + @SuppressWarnings("unchecked") + final T t = (T)this; + return t; + } + + /** + * Returns a clone (as deep as possible) of this request object. + * + * @throws CloneNotSupportedException + */ + @Override + public UploadObjectRequest clone() { + final UploadObjectRequest cloned = (UploadObjectRequest) super.clone(); + super.copyPutObjectBaseTo(cloned); + final Map materialsDescription = getMaterialsDescription(); + final ObjectMetadata uploadPartMetadata = getUploadPartMetadata(); + return cloned + .withMaterialsDescription(materialsDescription == null + ? null + : new HashMap(materialsDescription)) + .withDiskLimit(getDiskLimit()) + .withExecutorService(getExecutorService()) + .withPartSize(getPartSize()) + .withUploadObjectObserver(getUploadObjectObserver()) + .withUploadPartMetadata(uploadPartMetadata == null + ? null : uploadPartMetadata.clone()) + ; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadPartRequest.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadPartRequest.java index 4a75da5bc9..cd2e84c95a 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadPartRequest.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadPartRequest.java @@ -20,13 +20,31 @@ import java.io.File; import java.io.InputStream; +import java.io.Serializable; /** * Contains the parameters used for the UploadPart operation on Amazon S3. *

+ * If you are uploading parts for KMS-encrypted objects, you need to + * specify the correct region of the bucket on your client and configure AWS + * Signature Version 4 for added security. For more information on how to do + * this, see + * http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify + * -signature-version + *

+ *

* Required Parameters: BucketName, Key, UploadId, PartNumber */ -public class UploadPartRequest extends AmazonWebServiceRequest { +public class UploadPartRequest extends AmazonWebServiceRequest implements + SSECustomerKeyProvider, S3DataSource, Serializable { + + private static final long serialVersionUID = 1L; + /** + * Additional information about the part being uploaded, such as + * referrer. + */ + private ObjectMetadata objectMetadata; /** * The transfer id of this upload part @@ -74,7 +92,7 @@ public class UploadPartRequest extends AmazonWebServiceRequest { * The stream containing the data to upload for the new part. Exactly one * File or InputStream must be specified as the input to this operation. */ - private InputStream inputStream; + private transient InputStream inputStream; /** * The file containing the data to upload. Exactly one File or InputStream @@ -89,12 +107,6 @@ public class UploadPartRequest extends AmazonWebServiceRequest { */ private long fileOffset; - /** - * The optional progress listener for receiving updates about object - * download status. - */ - private ProgressListener generalProgressListener; - /** * Allows the caller to indicate if this is the last part being uploaded in * a multipart upload. @@ -107,6 +119,11 @@ public class UploadPartRequest extends AmazonWebServiceRequest { */ private SSECustomerKey sseCustomerKey; + /** + * If enabled, the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterPays; /** * @param id the transfer id of the upload part */ @@ -159,6 +176,7 @@ public UploadPartRequest withMainUploadId(int id) { * @param inputStream the stream containing the data to upload for the new * part. */ + @Override public void setInputStream(InputStream inputStream) { this.inputStream = inputStream; } @@ -168,6 +186,7 @@ public void setInputStream(InputStream inputStream) { * * @return the stream containing the data to upload for the new part. */ + @Override public InputStream getInputStream() { return inputStream; } @@ -418,6 +437,7 @@ public UploadPartRequest withMD5Digest(String md5Digest) { * @return The file containing the data to upload. Exactly one File or * InputStream must be specified as the input to this operation. */ + @Override public File getFile() { return file; } @@ -429,6 +449,7 @@ public File getFile() { * @param file The file containing the data to upload. Exactly one File or * InputStream must be specified as the input to this operation. */ + @Override public void setFile(File file) { this.file = file; } @@ -503,9 +524,8 @@ public UploadPartRequest withFileOffset(long fileOffset) { * instead. */ @Deprecated - public void setProgressListener( - com.amazonaws.services.s3.model.ProgressListener progressListener) { - this.generalProgressListener = new LegacyS3ProgressListener(progressListener); + public void setProgressListener(com.amazonaws.services.s3.model.ProgressListener progressListener) { + setGeneralProgressListener(new LegacyS3ProgressListener(progressListener)); } /** @@ -518,6 +538,7 @@ public void setProgressListener( */ @Deprecated public com.amazonaws.services.s3.model.ProgressListener getProgressListener() { + final ProgressListener generalProgressListener = getGeneralProgressListener(); if (generalProgressListener instanceof LegacyS3ProgressListener) { return ((LegacyS3ProgressListener) generalProgressListener).unwrap(); } else { @@ -579,13 +600,7 @@ public UploadPartRequest withLastPart(boolean isLastPart) { return this; } - /** - * Returns the optional customer-provided server-side encryption key to use - * to encrypt the object part being uploaded. - * - * @return The optional customer-provided server-side encryption key to use - * to encrypt the object part being uploaded. - */ + @Override public SSECustomerKey getSSECustomerKey() { return sseCustomerKey; } @@ -615,38 +630,90 @@ public UploadPartRequest withSSECustomerKey(SSECustomerKey sseKey) { setSSECustomerKey(sseKey); return this; } + /** + * Returns the additional information about the part being uploaded. + */ + public ObjectMetadata getObjectMetadata() { + return objectMetadata; + } /** - * Sets the optional progress listener for receiving updates about object - * download status. + * Sets the additional information about the part being uploaded. + */ + public void setObjectMetadata(ObjectMetadata objectMetadata) { + this.objectMetadata = objectMetadata; + } + + /** + * Fluent API for {@link #setObjectMetadata(ObjectMetadata)}. + */ + public UploadPartRequest withObjectMetadata(ObjectMetadata objectMetadata) { + setObjectMetadata(objectMetadata); + return this; + } + + /** + * Returns true if the user has enabled Requester Pays option when + * conducting this operation from Requester Pays Bucket; else false. + * + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket * - * @param generalProgressListener The new progress listener. + * @return true if the user has enabled Requester Pays option for + * conducting this operation from Requester Pays Bucket. */ - public void setGeneralProgressListener(ProgressListener generalProgressListener) { - this.generalProgressListener = generalProgressListener; + public boolean isRequesterPays() { + return isRequesterPays; } /** - * Returns the optional progress listener for receiving updates about object - * download status. + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. * - * @return the optional progress listener for receiving updates about object - * download status. + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. */ - public ProgressListener getGeneralProgressListener() { - return generalProgressListener; + public void setRequesterPays(boolean isRequesterPays) { + this.isRequesterPays = isRequesterPays; } /** - * Sets the optional progress listener for receiving updates about object - * upload status, and returns this updated object so that additional method - * calls can be chained together. + * Used for conducting this operation from a Requester Pays Bucket. If + * set the requester is charged for requests from the bucket. It returns this + * updated UploadPartRequest object so that additional method calls can be + * chained together. * - * @param generalProgressListener The new progress listener. - * @return This updated UploadPartRequest object. + *

+ * If a bucket is enabled for Requester Pays, then any attempt to upload or + * download an object from it without Requester Pays enabled will result in + * a 403 error and the bucket owner will be charged for the request. + * + *

+ * Enabling Requester Pays disables the ability to have anonymous access to + * this bucket. + * + * @param isRequesterPays + * Enable Requester Pays option for the operation. + * + * @return The updated UploadPartRequest object. */ - public UploadPartRequest withGeneralProgressListener(ProgressListener progressListener) { - setGeneralProgressListener(progressListener); + public UploadPartRequest withRequesterPays(boolean isRequesterPays) { + setRequesterPays(isRequesterPays); return this; } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadPartResult.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadPartResult.java index 1d6cdf59e9..3dd3b4cdf5 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadPartResult.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/UploadPartResult.java @@ -15,13 +15,14 @@ package com.amazonaws.services.s3.model; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; import com.amazonaws.services.s3.internal.SSEResultBase; /** * Contains the details returned from Amazon S3 after calling the UploadPart * operation. */ -public class UploadPartResult extends SSEResultBase { +public class UploadPartResult extends SSEResultBase implements S3RequesterChargedResult { /** The part number of the newly uploaded part */ private int partNumber; @@ -29,6 +30,12 @@ public class UploadPartResult extends SSEResultBase { /** The entity tag generated from the content of the upload part */ private String eTag; + /** + * Indicate if the requester is charged for conducting this operation from + * Requester Pays Buckets. + */ + private boolean isRequesterCharged; + /** * Returns the part number of the newly uploaded part. * @@ -77,4 +84,14 @@ public void setETag(String eTag) { public PartETag getPartETag() { return new PartETag(partNumber, eTag); } + + @Override + public boolean isRequesterCharged() { + return isRequesterCharged; + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + this.isRequesterCharged = isRequesterCharged; + } } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/WebsiteConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/WebsiteConfiguration.java index 3dd30e7202..813879791a 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/WebsiteConfiguration.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/WebsiteConfiguration.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2016. Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model; diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsAndOperator.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsAndOperator.java new file mode 100644 index 0000000000..5c25dd9756 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsAndOperator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.util.List; + +/** + * A logical AND of two or more predicates, which are used in evaluating an analytics filter. + * + * The {@link AnalyticsAndOperator} can contain at most one {@link AnalyticsPrefixPredicate} and any number of {@link AnalyticsTagPredicate}s. + * The operator must have at least two predicates. + */ +public class AnalyticsAndOperator extends AnalyticsNAryOperator { + + public AnalyticsAndOperator(List operands) { + super(operands); + } + + @Override + public void accept(AnalyticsPredicateVisitor analyticsPredicateVisitor) { + analyticsPredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsConfiguration.java new file mode 100644 index 0000000000..0d862ead73 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsConfiguration.java @@ -0,0 +1,104 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.io.Serializable; + +/** + * The configuration and any analysis for the analytics filter. + */ +public class AnalyticsConfiguration implements Serializable { + private String id; + private AnalyticsFilter filter; + private StorageClassAnalysis storageClassAnalysis; + + /** + * Returns the identifier used to represent an analytics configuration. + */ + public String getId() { + return id; + } + + /** + * Sets the identifier used to represent an analytics configuration. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the identifier used to represent an analytics configuration + * and returns the {@link AnalyticsConfiguration} object + * for method chaining. + */ + public AnalyticsConfiguration withId(String id) { + setId(id); + return this; + } + + /** + * Returns the filter used to describe a set of objects for analysis. + */ + public AnalyticsFilter getFilter() { + return filter; + } + + /** + * Sets the filter used to describe a set of objects for analysis. + * If no filter is provided, all objects will be considered in any analysis. + */ + public void setFilter(AnalyticsFilter filter) { + this.filter = filter; + } + + /** + * Sets the filter used to describe a set of objects for analysis. + * If no filter is provided, all objects will be considered in any analysis. + * + * The {@link AnalyticsConfiguration} object is returned for method chaining. + */ + public AnalyticsConfiguration withFilter(AnalyticsFilter filter) { + setFilter(filter); + return this; + } + + /** + * Returns the {@link StorageClassAnalysis} object. + * If present, it indicates that data related to access patterns will be collected + * and made available to analyze the tradeoffs between different storage classes. + */ + public StorageClassAnalysis getStorageClassAnalysis() { + return storageClassAnalysis; + } + + /** + * Sets the StorageClassAnalysis object which indicates that data related to access patterns + * will be collected and made available to analyze the tradeoffs between different storage classes. + */ + public void setStorageClassAnalysis(StorageClassAnalysis storageClassAnalysis) { + this.storageClassAnalysis = storageClassAnalysis; + } + + /** + * Sets the StorageClassAnalysis object which indicates that data related to access patterns + * will be collected and made available to analyze the tradeoffs between different storage classes. + * + * The {@link AnalyticsConfiguration} object is returned for method chaining. + */ + public AnalyticsConfiguration withStorageClassAnalysis(StorageClassAnalysis storageClassAnalysis) { + setStorageClassAnalysis(storageClassAnalysis); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsExportDestination.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsExportDestination.java new file mode 100644 index 0000000000..0b8e1df488 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsExportDestination.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.io.Serializable; + +/** + * The place to store the data for an analysis. + */ +public class AnalyticsExportDestination implements Serializable { + + private AnalyticsS3BucketDestination s3BucketDestination; + + public AnalyticsS3BucketDestination getS3BucketDestination() { + return s3BucketDestination; + } + + public void setS3BucketDestination(AnalyticsS3BucketDestination s3BucketDestination) { + this.s3BucketDestination = s3BucketDestination; + } + + public AnalyticsExportDestination withS3BucketDestination(AnalyticsS3BucketDestination s3BucketDestination) { + setS3BucketDestination(s3BucketDestination); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsFilter.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsFilter.java new file mode 100644 index 0000000000..7596be3231 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsFilter.java @@ -0,0 +1,67 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.io.Serializable; + +/** + * The filter used to describe a set of objects for analysis. + * + * The analytics filter contains one of {@link AnalyticsPrefixPredicate}, {@link AnalyticsTagPredicate} + * or {@link AnalyticsAndOperator}. If no filter is provided, all objects will be considered + * for analysis. + */ +public class AnalyticsFilter implements Serializable { + + private AnalyticsFilterPredicate predicate; + + public AnalyticsFilter() { } + + public AnalyticsFilter(AnalyticsFilterPredicate predicate) { + this.predicate = predicate; + } + + /** + * Returns the {@link AnalyticsFilterPredicate} to be used when evaluating an analytics filter. + * + * The predicate is one of {@link AnalyticsPrefixPredicate}, {@link AnalyticsTagPredicate} + * or {@link AnalyticsAndOperator}. + */ + public AnalyticsFilterPredicate getPredicate() { + return predicate; + } + + /** + * Sets the {@link AnalyticsFilterPredicate} to be used when evaluating an analytics filter. + * + * The predicate should be one of {@link AnalyticsPrefixPredicate}, {@link AnalyticsTagPredicate} + * or {@link AnalyticsAndOperator}. + */ + public void setPredicate(AnalyticsFilterPredicate predicate) { + this.predicate = predicate; + } + + /** + * Sets the {@link AnalyticsFilterPredicate} to be used when evaluating an analytics filter + * and returns the {@link AnalyticsFilter} object for method chaining. + * + * The predicate should be one of {@link AnalyticsPrefixPredicate}, {@link AnalyticsTagPredicate} + * or {@link AnalyticsAndOperator}. + */ + public AnalyticsFilter withPredicate(AnalyticsFilterPredicate predicate) { + setPredicate(predicate); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsFilterPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsFilterPredicate.java new file mode 100644 index 0000000000..2b0a8c84de --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsFilterPredicate.java @@ -0,0 +1,28 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.io.Serializable; + +/** + * Base class to represent the root predicate in {@link AnalyticsFilter} class. + * + * @see AnalyticsPrefixPredicate + * @see AnalyticsTagPredicate + * @see AnalyticsAndOperator + */ +public abstract class AnalyticsFilterPredicate implements Serializable { + public abstract void accept(AnalyticsPredicateVisitor analyticsPredicateVisitor); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsNAryOperator.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsNAryOperator.java new file mode 100644 index 0000000000..4158092171 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsNAryOperator.java @@ -0,0 +1,33 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.util.List; + +/** + * Abstract class representing an operator that acts on N number of predicates. + */ +abstract class AnalyticsNAryOperator extends AnalyticsFilterPredicate { + + private final List operands; + + public AnalyticsNAryOperator(List operands) { + this.operands = operands; + } + + public List getOperands() { + return operands; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsPredicateVisitor.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsPredicateVisitor.java new file mode 100644 index 0000000000..c8a86f8615 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsPredicateVisitor.java @@ -0,0 +1,43 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +/** + * Interface to invoke specific behavior based on the type of {@link AnalyticsFilterPredicate} visited. + * + * When an implementation of this visitor is passed to an + * {@link AnalyticsFilterPredicate#accept(AnalyticsPredicateVisitor)} method, + * the visit method most applicable to that element is invoked. + */ +public interface AnalyticsPredicateVisitor { + + /** + * Implement this method to add behaviour performed when + * {@link AnalyticsPrefixPredicate} is visited. + */ + public void visit(AnalyticsPrefixPredicate analyticsPrefixPredicate); + + /** + * Implement this method to add behaviour performed when + * {@link AnalyticsTagPredicate} is visited. + */ + public void visit(AnalyticsTagPredicate analyticsTagPredicate); + + /** + * Implement this method to add behaviour performed when + * {@link AnalyticsAndOperator} is visited. + */ + public void visit(AnalyticsAndOperator analyticsAndOperator); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsPrefixPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsPrefixPredicate.java new file mode 100644 index 0000000000..693afa69ce --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsPrefixPredicate.java @@ -0,0 +1,40 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +/** + * A {@link AnalyticsFilterPredicate} class to represent the + * prefix to use when evaluating an analytics filter. + */ +public final class AnalyticsPrefixPredicate extends AnalyticsFilterPredicate { + + private final String prefix; + + public AnalyticsPrefixPredicate(String prefix) { + this.prefix = prefix; + } + + /** + * Returns the prefix to use when evaluating an analytics filter. + */ + public String getPrefix() { + return prefix; + } + + @Override + public void accept(AnalyticsPredicateVisitor analyticsPredicateVisitor) { + analyticsPredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsS3BucketDestination.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsS3BucketDestination.java new file mode 100644 index 0000000000..3be91e6fe0 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsS3BucketDestination.java @@ -0,0 +1,137 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.io.Serializable; + +public class AnalyticsS3BucketDestination implements Serializable { + private String format; + private String bucketAccountId; + private String bucketArn; + private String prefix; + + /** + * Sets the file format used when exporting data to Amazon S3. + */ + public void setFormat(AnalyticsS3ExportFileFormat format) { + if (format == null) { + setFormat((String) null); + } else { + setFormat(format.toString()); + } + } + + /** + * Sets the file format used when exporting data to Amazon S3. + * Returns this object for method chaining. + */ + public AnalyticsS3BucketDestination withFormat(AnalyticsS3ExportFileFormat format) { + setFormat(format); + return this; + } + + /** + * Returns the file format used when exporting data to Amazon S3. + */ + public String getFormat() { + return format; + } + + /** + * Sets the file format used when exporting data to Amazon S3. + */ + public void setFormat(String format) { + this.format = format; + } + + /** + * Sets the file format used when exporting data to Amazon S3. + * Returns this object for method chaining. + */ + public AnalyticsS3BucketDestination withFormat(String format) { + setFormat(format); + return this; + } + + /** + * Returns the account ID that owns the destination bucket. + */ + public String getBucketAccountId() { + return bucketAccountId; + } + + /** + * Sets the account ID that owns the destination bucket. + * If no account ID is provided, the owner will not be validated prior to exporting data. + */ + public void setBucketAccountId(String bucketAccountId) { + this.bucketAccountId = bucketAccountId; + } + + /** + * Sets the account ID that owns the destination bucket and returns this object for method chaining. + * If no account ID is provided, the owner will not be validated prior to exporting data. + */ + public AnalyticsS3BucketDestination withBucketAccountId(String bucketAccountId) { + setBucketAccountId(bucketAccountId); + return this; + } + + /** + * Returns the Amazon resource name (ARN) of the bucket to which data is exported. + */ + public String getBucketArn() { + return bucketArn; + } + + /** + * Sets the Amazon resource name (ARN) of the bucket to which data is exported. + */ + public void setBucketArn(String bucketArn) { + this.bucketArn = bucketArn; + } + + /** + * Sets the Amazon resource name (ARN) of the bucket to which data is exported + * and returns this object for method chaining. + */ + public AnalyticsS3BucketDestination withBucketArn(String bucketArn) { + setBucketArn(bucketArn); + return this; + } + + /** + * Returns the prefix to use when exporting data. + */ + public String getPrefix() { + return prefix; + } + + /** + * Sets the prefix to use when exporting data. The exported data begins with this prefix. + */ + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + /** + * Sets the prefix to use when exporting data. The exported data begins with this prefix. + * The object is returned for method chaining. + */ + public AnalyticsS3BucketDestination withPrefix(String prefix) { + setPrefix(prefix); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsS3ExportFileFormat.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsS3ExportFileFormat.java new file mode 100644 index 0000000000..6b308f2812 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsS3ExportFileFormat.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.io.Serializable; + +/** + * The file format used when exporting data to Amazon S3. + */ +public enum AnalyticsS3ExportFileFormat implements Serializable { + CSV("CSV"); + + private final String format; + + private AnalyticsS3ExportFileFormat(String format) { + this.format = format; + } + + @Override + public String toString() { + return format; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsTagPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsTagPredicate.java new file mode 100644 index 0000000000..73cd1b19cc --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/AnalyticsTagPredicate.java @@ -0,0 +1,39 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import com.amazonaws.services.s3.model.Tag; + +/** + * A {@link AnalyticsFilterPredicate} class to represent the {@link Tag} object + * to use when evaluating an analytics filter. + */ +public final class AnalyticsTagPredicate extends AnalyticsFilterPredicate { + + private final Tag tag; + + public AnalyticsTagPredicate(Tag tag) { + this.tag = tag; + } + + public Tag getTag() { + return tag; + } + + @Override + public void accept(AnalyticsPredicateVisitor analyticsPredicateVisitor) { + analyticsPredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysis.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysis.java new file mode 100644 index 0000000000..69633a7a5e --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysis.java @@ -0,0 +1,49 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.io.Serializable; + +public class StorageClassAnalysis implements Serializable { + + private StorageClassAnalysisDataExport dataExport; + + /** + * Returns the container used to describe how data related to the + * storage class analysis should be exported. + */ + public StorageClassAnalysisDataExport getDataExport() { + return dataExport; + } + + /** + * Sets the container used to describe how data related to the + * storage class analysis should be exported. + */ + public void setDataExport(StorageClassAnalysisDataExport dataExport) { + this.dataExport = dataExport; + } + + /** + * Sets the container used to describe how data related to the + * storage class analysis should be exported. + * + * Returns this object for method chaining. + */ + public StorageClassAnalysis withDataExport(StorageClassAnalysisDataExport dataExport) { + setDataExport(dataExport); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysisDataExport.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysisDataExport.java new file mode 100644 index 0000000000..cc67d11f32 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysisDataExport.java @@ -0,0 +1,88 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.io.Serializable; + +public class StorageClassAnalysisDataExport implements Serializable { + private String outputSchemaVersion; + private AnalyticsExportDestination destination; + + /** + * Sets the version of the output schema to use when exporting data. + */ + public void setOutputSchemaVersion(StorageClassAnalysisSchemaVersion outputSchemaVersion) { + if (outputSchemaVersion == null) { + setOutputSchemaVersion((String) null); + } else { + setOutputSchemaVersion(outputSchemaVersion.toString()); + } + } + + /** + * Sets the version of the output schema to use when exporting data + * and returns this object for method chaining. + */ + public StorageClassAnalysisDataExport withOutputSchemaVersion(StorageClassAnalysisSchemaVersion outputSchemaVersion) { + setOutputSchemaVersion(outputSchemaVersion); + return this; + } + + /** + * Returns the version of the output schema to use when exporting data. + */ + public String getOutputSchemaVersion() { + return outputSchemaVersion; + } + + /** + * Sets the version of the output schema to use when exporting data. + */ + public void setOutputSchemaVersion(String outputSchemaVersion) { + this.outputSchemaVersion = outputSchemaVersion; + } + + /** + * Sets the version of the output schema to use when exporting data + * and returns this object for method chaining. + */ + public StorageClassAnalysisDataExport withOutputSchemaVersion(String outputSchemaVersion) { + setOutputSchemaVersion(outputSchemaVersion); + return this; + } + + /** + * Returns the place to store the data for an analysis. + */ + public AnalyticsExportDestination getDestination() { + return destination; + } + + /** + * Sets the place to store the data for an analysis. + */ + public void setDestination(AnalyticsExportDestination destination) { + this.destination = destination; + } + + /** + * Sets the place to store the data for an analysis + * and return this object for method chaining. + */ + public StorageClassAnalysisDataExport withDestination(AnalyticsExportDestination destination) { + setDestination(destination); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysisSchemaVersion.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysisSchemaVersion.java new file mode 100644 index 0000000000..ad780406bb --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/analytics/StorageClassAnalysisSchemaVersion.java @@ -0,0 +1,36 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.analytics; + +import java.io.Serializable; + +/** + * The version of the output schema to use when exporting data. + */ +public enum StorageClassAnalysisSchemaVersion implements Serializable { + V_1("V_1"); + + private final String version; + + private StorageClassAnalysisSchemaVersion(String version) { + this.version = version; + } + + @Override + public String toString() { + return version; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryConfiguration.java new file mode 100644 index 0000000000..5a1fbb693c --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryConfiguration.java @@ -0,0 +1,257 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class InventoryConfiguration implements Serializable { + + /** The ID used to identify the inventory configuration. */ + private String id; + + /** Contains information about where to publish the inventory results. */ + private InventoryDestination destination; + + /** Specifies whether the inventory is enabled or disabled. */ + private Boolean isEnabled; + + /** Specifies an inventory inventoryFilter. */ + private InventoryFilter inventoryFilter; + + /** Specifies which object version(s) to included in the inventory results. */ + private String includedObjectVersions; + + /** List to store the optional fields that are included in the inventory results. */ + private List optionalFields; + + /** Specifies the schedule for generating inventory results. */ + private InventorySchedule schedule; + + + /** + * Returns the ID used to identify the inventory configuration. + */ + public String getId() { + return id; + } + + /** + * Sets the ID used to identify the inventory configuration. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the ID used to identify the inventory configuration + * and returns this object for method chaining. + */ + public InventoryConfiguration withId(String id) { + setId(id); + return this; + } + + /** + * Returns the {@link InventoryDestination} that contains information + * about where to publish the inventory results. + */ + public InventoryDestination getDestination() { + return destination; + } + + /** + * Sets the {@link InventoryDestination} that contains information + * about where to publish the inventory results. + */ + public void setDestination(InventoryDestination destination) { + this.destination = destination; + } + + /** + * Sets the {@link InventoryDestination} that contains information + * about where to publish the inventory results and returns + * this object for method chaining. + */ + public InventoryConfiguration withDestination(InventoryDestination destination) { + setDestination(destination); + return this; + } + + /** + * Returns true if the inventory is enabled or + * false if inventory is disabled. + */ + public Boolean isEnabled() { + return isEnabled; + } + + /** + * Sets the value whether the inventory is enabled or disabled. + * + * The value true indicates the inventory is enabled and + * false indicates the inventory is disabled. + */ + public void setEnabled(Boolean enabled) { + isEnabled = enabled; + } + + /** + * Sets the value whether the inventory is enabled or disabled + * and returns this object for method chaining. + * + * The value true indicates the inventory is enabled and + * false indicates the inventory is disabled. + */ + public InventoryConfiguration withEnabled(Boolean enabled) { + setEnabled(enabled); + return this; + } + + /** + * Returns the inventoryFilter used to describe a set of objects + * to include in inventory results. + */ + public InventoryFilter getInventoryFilter() { + return inventoryFilter; + } + + /** + * Sets the inventoryFilter used to describe a set of objects + * to include in inventory results. + */ + public void setInventoryFilter(InventoryFilter inventoryFilter) { + this.inventoryFilter = inventoryFilter; + } + + /** + * Sets the inventoryFilter used to describe a set of objects + * to include in inventory results. + * + * The {@link InventoryConfiguration} object is returned + * for method chaining. + */ + public InventoryConfiguration withFilter(InventoryFilter inventoryFilter) { + setInventoryFilter(inventoryFilter); + return this; + } + + /** + * Returns which object version(s) to included in the inventory results. + */ + public String getIncludedObjectVersions() { + return includedObjectVersions; + } + + /** + * Sets which object version(s) to included in the inventory results. + */ + public void setIncludedObjectVersions(String includedObjectVersions) { + this.includedObjectVersions = includedObjectVersions; + } + + /** + * Sets which object version(s) to included in the inventory results + * and returns this object for method chaining. + */ + public InventoryConfiguration withIncludedObjectVersions(String includedObjectVersions) { + setIncludedObjectVersions(includedObjectVersions); + return this; + } + + /** + * Sets which object version(s) to included in the inventory results. + */ + public void setIncludedObjectVersions(InventoryIncludedObjectVersions includedObjectVersions) { + setIncludedObjectVersions(includedObjectVersions == null ? (String) null : includedObjectVersions.toString()); + } + + /** + * Sets which object version(s) to included in the inventory results + * and returns this object for method chaining. + */ + public InventoryConfiguration withIncludedObjectVersions(InventoryIncludedObjectVersions includedObjectVersions) { + setIncludedObjectVersions(includedObjectVersions); + return this; + } + + /** + * Returns the optional fields that are included in the inventory results. + */ + public List getOptionalFields() { + return optionalFields; + } + + /** + * Sets the optional fields that are included in the inventory results. + */ + public void setOptionalFields(List optionalFields) { + this.optionalFields = optionalFields; + } + + /** + * Sets the optional fields that are included in the inventory results. + * The {@link InventoryConfiguration} object is returned for method chaining. + */ + public InventoryConfiguration withOptionalFields(List optionalFields) { + setOptionalFields(optionalFields); + return this; + } + + /** + * Add a field to the list of optional fields that are included in the inventory results. + */ + public void addOptionalField(String optionalField) { + if (optionalField == null) { + return; + } else if (this.optionalFields == null) { + this.optionalFields = new ArrayList(); + } + this.optionalFields.add(optionalField); + } + + /** + * Add a field to the list of optional fields that are included in the inventory results. + */ + public void addOptionalField(InventoryOptionalField optionalField) { + addOptionalField(optionalField == null ? (String) null : optionalField.toString()); + } + + /** + * Returns the schedule for generating inventory results. + */ + public InventorySchedule getSchedule() { + return schedule; + } + + /** + * Sets the schedule for generating inventory results. + */ + public void setSchedule(InventorySchedule schedule) { + this.schedule = schedule; + } + + /** + * Returns the schedule for generating inventory results + * and returns {@link InventoryConfiguration} object + * for method chaining. + */ + public InventoryConfiguration withSchedule(InventorySchedule schedule) { + setSchedule(schedule); + return this; + } + +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryDestination.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryDestination.java new file mode 100644 index 0000000000..de8d8a5635 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryDestination.java @@ -0,0 +1,54 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +import java.io.Serializable; + +/** + * Information about where to publish the inventory results. + */ +public class InventoryDestination implements Serializable { + + /** + * Contains the S3 destination information of where inventory results are published. + */ + private InventoryS3BucketDestination S3BucketDestination; + + /** + * Returns the {@link InventoryS3BucketDestination} which contains S3 bucket destination information + * of where inventory results are published. + */ + public InventoryS3BucketDestination getS3BucketDestination() { + return S3BucketDestination; + } + + /** + * Sets the {@link InventoryS3BucketDestination} which contains S3 bucket destination information + * of where inventory results are published. + */ + public void setS3BucketDestination(InventoryS3BucketDestination s3BucketDestination) { + S3BucketDestination = s3BucketDestination; + } + + /** + * Sets the {@link InventoryS3BucketDestination} which contains S3 bucket destination information + * of where inventory results are published. + * This {@link InventoryDestination} object is returned for method chaining. + */ + public InventoryDestination withS3BucketDestination(InventoryS3BucketDestination s3BucketDestination) { + setS3BucketDestination(s3BucketDestination); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFilter.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFilter.java new file mode 100644 index 0000000000..5050bac969 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFilter.java @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +import java.io.Serializable; + +/** + * Specifies an inventory filter. + * The inventory only includes objects that meet the filter's criteria. + */ +public class InventoryFilter implements Serializable { + + private InventoryFilterPredicate predicate; + + public InventoryFilter() { } + + public InventoryFilter(InventoryFilterPredicate predicate) { + this.predicate = predicate; + } + + /** + * Returns the {@link InventoryFilterPredicate} to be used when evaluating an inventory filter. + * + * The predicate should be of type {@link InventoryPrefixPredicate}. + */ + public InventoryFilterPredicate getPredicate() { + return predicate; + } + + /** + * Sets the {@link InventoryFilterPredicate} to be used when evaluating an inventory filter. + * + * The predicate should be of type {@link InventoryPrefixPredicate}. + */ + public void setPredicate(InventoryFilterPredicate predicate) { + this.predicate = predicate; + } + + /** + * Sets the {@link InventoryFilterPredicate} to be used when evaluating an inventory filter + * and returns the {@link InventoryFilter} object for method chaining. + * + * The predicate should be of type {@link InventoryPrefixPredicate}. + */ + public InventoryFilter withPredicate(InventoryFilterPredicate predicate) { + setPredicate(predicate); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFilterPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFilterPredicate.java new file mode 100644 index 0000000000..e695e9f932 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFilterPredicate.java @@ -0,0 +1,31 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +import java.io.Serializable; + +/** + * Base class to represent the root predicate in {@link InventoryFilter} class. + * + * @see InventoryPrefixPredicate + */ +public abstract class InventoryFilterPredicate implements Serializable { + + /** + * Helper method that accepts an implemenation of {@link InventoryPredicateVisitor} + * and invokes the most applicable visit method in the visitor. + */ + public abstract void accept(InventoryPredicateVisitor inventoryPredicateVisitor); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFormat.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFormat.java new file mode 100644 index 0000000000..b737fbb968 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFormat.java @@ -0,0 +1,39 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +/** + * The output format of the inventory results. + */ +public enum InventoryFormat { + + CSV("CSV"), + + ; + + private final String format; + + private InventoryFormat(String format) { + this.format = format; + } + + /* (non-Javadoc) + * @see java.lang.Enum#toString() + */ + @Override + public String toString() { + return format; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFrequency.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFrequency.java new file mode 100644 index 0000000000..19fdd3e367 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryFrequency.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +/** + * The frequency for producing inventory results. + */ +public enum InventoryFrequency { + + Daily("Daily"), + + Weekly("Weekly"), + + ; + + private final String frequency; + + private InventoryFrequency(String frequency) { + this.frequency = frequency; + } + + @Override + public String toString() { + return frequency; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryIncludedObjectVersions.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryIncludedObjectVersions.java new file mode 100644 index 0000000000..ae9ed6fa04 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryIncludedObjectVersions.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +/** + * The object version(s) that can be included in the inventory results. + */ +public enum InventoryIncludedObjectVersions { + + All("All"), + + Current("Current"), + + ; + + private final String name; + + private InventoryIncludedObjectVersions(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryOptionalField.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryOptionalField.java new file mode 100644 index 0000000000..a323d66d32 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryOptionalField.java @@ -0,0 +1,49 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +/** + * The optional fields that can be included in the inventory results. + */ +public enum InventoryOptionalField { + + Size("Size"), + + LastModifiedDate("LastModifiedDate"), + + StorageClass("StorageClass"), + + ETag("ETag"), + + IsMultipartUploaded("IsMultipartUploaded"), + + ReplicationStatus("ReplicationStatus"), + + ; + + private final String field; + + private InventoryOptionalField(String field) { + this.field = field; + } + + /* (non-Javadoc) + * @see java.lang.Enum#toString() + */ + @Override + public String toString() { + return field; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryPredicateVisitor.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryPredicateVisitor.java new file mode 100644 index 0000000000..87f743fd95 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryPredicateVisitor.java @@ -0,0 +1,31 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +/** + * Interface to invoke specific behavior based on the type of {@link InventoryFilterPredicate} visited. + * + * When an implementation of this visitor is passed to an + * {@link InventoryFilterPredicate#accept(InventoryPredicateVisitor)} method, + * the visit method most applicable to that element is invoked. + */ +public interface InventoryPredicateVisitor { + + /** + * Implement this method to add behaviour performed when + * {@link InventoryPrefixPredicate} is visited. + */ + public void visit(InventoryPrefixPredicate inventoryPrefixPredicate); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryPrefixPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryPrefixPredicate.java new file mode 100644 index 0000000000..cbcfd2b452 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryPrefixPredicate.java @@ -0,0 +1,40 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +/** + * A {@link InventoryFilterPredicate} class to represent the + * prefix to use when evaluating an inventory filter. + */ +public final class InventoryPrefixPredicate extends InventoryFilterPredicate { + + private final String prefix; + + public InventoryPrefixPredicate(String prefix) { + this.prefix = prefix; + } + + /** + * Returns the prefix to use when evaluating an inventory filter. + */ + public String getPrefix() { + return prefix; + } + + @Override + public void accept(InventoryPredicateVisitor inventoryPredicateVisitor) { + inventoryPredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryS3BucketDestination.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryS3BucketDestination.java new file mode 100644 index 0000000000..29e2a6348e --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventoryS3BucketDestination.java @@ -0,0 +1,145 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +import java.io.Serializable; + +/** + * Contains the bucket name, file format, bucket owner (optional), + * and prefix (optional) where inventory results are published. + */ +public class InventoryS3BucketDestination implements Serializable { + + private String accountId; + + private String bucketArn; + + private String format; + + private String prefix; + + /** + * Returns the account ID that owns the destination bucket. + */ + public String getAccountId() { + return accountId; + } + + /** + * Sets the account ID that owns the destination bucket. + */ + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + /** + * Returns the account ID that owns the destination bucket + * and returns this {@link InventoryS3BucketDestination} object + * for method chaining. + */ + public InventoryS3BucketDestination withAccountId(String accountId) { + setAccountId(accountId); + return this; + } + + /** + * Returns the Amazon resource name (ARN) of the bucket where inventory results will be published. + */ + public String getBucketArn() { + return bucketArn; + } + + /** + * Sets the Amazon resource name (ARN) of the bucket where inventory results will be published. + */ + public void setBucketArn(String bucketArn) { + this.bucketArn = bucketArn; + } + + /** + * Sets the Amazon resource name (ARN) of the bucket where inventory results will be published. + * + * The {@link InventoryS3BucketDestination} object is returned for method chaining. + */ + public InventoryS3BucketDestination withBucketArn(String bucketArn) { + setBucketArn(bucketArn); + return this; + } + + /** + * Returns the output format of the inventory results. + */ + public String getFormat() { + return format; + } + + /** + * Sets the output format of the inventory results. + */ + public void setFormat(String format) { + this.format = format; + } + + /** + * Sets the output format of the inventory results. + */ + public void setFormat(InventoryFormat format) { + setFormat(format == null ? (String) null : format.toString()); + } + + /** + * Sets the output format of the inventory results + * and returns this {@link InventoryS3BucketDestination} object + * for method chaining. + */ + public InventoryS3BucketDestination withFormat(String format) { + setFormat(format); + return this; + } + + /** + * Sets the output format of the inventory results + * and returns this {@link InventoryS3BucketDestination} object + * for method chaining. + */ + public InventoryS3BucketDestination withFormat(InventoryFormat format) { + setFormat(format); + return this; + } + + /** + * Returns the prefix that is prepended to all inventory results. + */ + public String getPrefix() { + return prefix; + } + + /** + * Sets the prefix that is prepended to all inventory results. + */ + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + /** + * Sets the prefix that is prepended to all inventory results + * and returns this {@link InventoryS3BucketDestination} object + * for method chaining. + */ + public InventoryS3BucketDestination withPrefix(String prefix) { + setPrefix(prefix); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventorySchedule.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventorySchedule.java new file mode 100644 index 0000000000..0587abfaf8 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/inventory/InventorySchedule.java @@ -0,0 +1,68 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.inventory; + +import java.io.Serializable; + +/** + * Schedule for generating inventory results. + */ +public class InventorySchedule implements Serializable { + + /** Specifies how frequently inventory results are produced. */ + private String frequency; + + /** + * Returns the frequency for producing inventory results + * in {@link String} format. + */ + public String getFrequency() { + return frequency; + } + + /** + * Sets the frequency for producing inventory results. + */ + public void setFrequency(String frequency) { + this.frequency = frequency; + } + + /** + * Sets the frequency for producing inventory results. + */ + public void setFrequency(InventoryFrequency frequency) { + setFrequency(frequency == null ? (String) null : frequency.toString()); + } + + /** + * Sets the frequency for producing inventory results + * and returns {@link InventorySchedule} object + * for method chaining. + */ + public InventorySchedule withFrequency(String frequency) { + setFrequency(frequency); + return this; + } + + /** + * Sets the frequency for producing inventory results + * and returns {@link InventorySchedule} object + * for method chaining. + */ + public InventorySchedule withFrequency(InventoryFrequency frequency) { + setFrequency(frequency); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleAndOperator.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleAndOperator.java new file mode 100644 index 0000000000..6a0d1f1522 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleAndOperator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.lifecycle; + +import java.util.List; + +/** + * A logical AND of two or more predicates of type {@link LifecycleFilterPredicate}. + * The Lifecycle Rule will apply to any object matching all of the predicates configured inside the And operator. + * + * The {@link LifecycleAndOperator} can contain at most one {@link LifecyclePrefixPredicate} and any number of {@link LifecycleTagPredicate}s. + */ +public final class LifecycleAndOperator extends LifecycleNAryOperator { + + public LifecycleAndOperator(List operands) { + super(operands); + } + + @Override + public void accept(LifecyclePredicateVisitor lifecyclePredicateVisitor) { + lifecyclePredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleFilter.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleFilter.java new file mode 100644 index 0000000000..3715eda081 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleFilter.java @@ -0,0 +1,77 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.lifecycle; + +import com.amazonaws.services.s3.model.BucketLifecycleConfiguration; + +import java.io.Serializable; + +/** + * The {@link LifecycleFilter} is used to identify objects that a Lifecycle Rule applies to. + * + * This predicate in {@link LifecycleFilter} should be one of + * {@link LifecyclePrefixPredicate}, {@link LifecycleTagPredicate}, or + * {@link LifecycleAndOperator}. + */ +public class LifecycleFilter implements Serializable{ + private LifecycleFilterPredicate predicate; + + public LifecycleFilter() {} + + public LifecycleFilter(LifecycleFilterPredicate predicate) { + this.predicate = predicate; + } + + /** + * Returns the {@link LifecycleFilterPredicate} to be applied to {@link BucketLifecycleConfiguration.Rule}. + * + * The predicate is one of {@link LifecyclePrefixPredicate}, + * {@link LifecycleTagPredicate} or + * {@link LifecycleAndOperator} type. + */ + public LifecycleFilterPredicate getPredicate() { + return predicate; + } + + /** + * Sets the {@link LifecycleFilterPredicate} to be applied to {@link BucketLifecycleConfiguration.Rule}. + * + * The predicate should be one of {@link LifecyclePrefixPredicate}, + * {@link LifecycleTagPredicate} or + * {@link LifecycleAndOperator} type. + * + * @param predicate An object of type {@link LifecycleFilterPredicate}. + */ + public void setPredicate(LifecycleFilterPredicate predicate) { + this.predicate = predicate; + } + + /** + * Sets the {@link LifecycleFilterPredicate} to be applied to {@link BucketLifecycleConfiguration.Rule} and returns the object + * for method chaining. + * + * The predicate should be one of {@link LifecyclePrefixPredicate}, + * {@link LifecycleTagPredicate} or + * {@link LifecycleAndOperator} type. + * + * @param predicate An object of type {@link LifecycleFilterPredicate}. + * + * @return This object for method chaining. + */ + public LifecycleFilter withPredicate(LifecycleFilterPredicate predicate) { + setPredicate(predicate); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleFilterPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleFilterPredicate.java new file mode 100644 index 0000000000..3609f1af86 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleFilterPredicate.java @@ -0,0 +1,34 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.lifecycle; + +import java.io.Serializable; + +/** + * Base class to represent the root predicate in + * {@link LifecycleFilter} class. + * + * @see LifecyclePrefixPredicate + * @see LifecycleTagPredicate + * @see LifecycleAndOperator + */ +public abstract class LifecycleFilterPredicate implements Serializable { + + /** + * Helper method that accepts an implemenation of {@link LifecyclePredicateVisitor} + * and invokes the most applicable visit method in the visitor. + */ + public abstract void accept(LifecyclePredicateVisitor lifecyclePredicateVisitor); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleNAryOperator.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleNAryOperator.java new file mode 100644 index 0000000000..311039c441 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleNAryOperator.java @@ -0,0 +1,33 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.lifecycle; + +import java.util.List; + +/** + * Abstract class representing an operator that acts on N number of predicates. + */ +abstract class LifecycleNAryOperator extends LifecycleFilterPredicate { + + private final List operands; + + public LifecycleNAryOperator(List operands) { + this.operands = operands; + } + + public List getOperands() { + return operands; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecyclePredicateVisitor.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecyclePredicateVisitor.java new file mode 100644 index 0000000000..1520705ea9 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecyclePredicateVisitor.java @@ -0,0 +1,44 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.lifecycle; + +/** + * Interface to invoke specific behavior based on the type of {@link LifecycleFilterPredicate} visited. + * This follows the visitor design pattern. + * + * When an implementation of this visitor is passed to an + * {@link LifecycleFilterPredicate#accept(LifecyclePredicateVisitor)} method, + * the visit method most applicable to that element is invoked. + */ +public interface LifecyclePredicateVisitor { + + /** + * Implement this method to add behaviour performed when + * {@link LifecyclePrefixPredicate} is visited. + */ + public void visit(LifecyclePrefixPredicate lifecyclePrefixPredicate); + + /** + * Implement this method to add behaviour performed when + * {@link LifecycleTagPredicate} is visited. + */ + public void visit(LifecycleTagPredicate lifecycleTagPredicate); + + /** + * Implement this method to add behaviour performed when + * {@link LifecycleAndOperator} is visited. + */ + public void visit(LifecycleAndOperator lifecycleAndOperator); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecyclePrefixPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecyclePrefixPredicate.java new file mode 100644 index 0000000000..db3b342b05 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecyclePrefixPredicate.java @@ -0,0 +1,42 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.lifecycle; + +/** + * A {@link LifecycleFilterPredicate} class to represent the + * prefix identifying one or more objects to which the + * {@link com.amazonaws.services.s3.model.BucketLifecycleConfiguration.Rule} applies. + */ +public final class LifecyclePrefixPredicate extends LifecycleFilterPredicate { + + private final String prefix; + + public LifecyclePrefixPredicate(String prefix) { + this.prefix = prefix; + } + + /** + * Returns the key prefix for which the + * {@link com.amazonaws.services.s3.model.BucketLifecycleConfiguration.Rule} will apply. + */ + public String getPrefix() { + return prefix; + } + + @Override + public void accept(LifecyclePredicateVisitor lifecyclePredicateVisitor) { + lifecyclePredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleTagPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleTagPredicate.java new file mode 100644 index 0000000000..3f99ee64ce --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/lifecycle/LifecycleTagPredicate.java @@ -0,0 +1,40 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.lifecycle; + +import com.amazonaws.services.s3.model.Tag; + +/** + * A {@link LifecycleFilterPredicate} class to represent the {@link Tag} object + * that must exist in the object's tag set in order for the + * {@link com.amazonaws.services.s3.model.BucketLifecycleConfiguration.Rule} to apply. + */ +public final class LifecycleTagPredicate extends LifecycleFilterPredicate { + + private final Tag tag; + + public LifecycleTagPredicate(Tag tag) { + this.tag = tag; + } + + public Tag getTag() { + return tag; + } + + @Override + public void accept(LifecyclePredicateVisitor lifecyclePredicateVisitor) { + lifecyclePredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsAndOperator.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsAndOperator.java new file mode 100644 index 0000000000..76123226c4 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsAndOperator.java @@ -0,0 +1,36 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.metrics; + +import java.util.List; + +/** + * A logical AND of predicates, which is used in evaluating a metrics filter. + * The operator must have at least two predicates. + * + * The {@link MetricsAndOperator} can contain at most one {@link MetricsPrefixPredicate} and + * any number of {@link MetricsTagPredicate}s. + */ +public final class MetricsAndOperator extends MetricsNAryOperator { + + public MetricsAndOperator(List operands) { + super(operands); + } + + @Override + public void accept(MetricsPredicateVisitor metricsPredicateVisitor) { + metricsPredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsConfiguration.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsConfiguration.java new file mode 100644 index 0000000000..f9f9859b3a --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsConfiguration.java @@ -0,0 +1,80 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.metrics; + +import java.io.Serializable; + +/** + * Class to specify the metrics configuration. + */ +public class MetricsConfiguration implements Serializable { + + private String id; + private MetricsFilter filter; + + /** + * Returns the identifier used to represent a metrics configuration. + */ + public String getId() { + return id; + } + + /** + * Sets the identifier used to represent a metrics configuration. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the identifier used to represent a metrics configuration + * and return this object for method chaining. + */ + public MetricsConfiguration withId(String id) { + setId(id); + return this; + } + + /** + * Returns the metrics configuration filter. The metrics configuration will only + * include objects that meet the filter's criteria. + * + * A null filter indicates that the metrics are computed for the entire bucket. + */ + public MetricsFilter getFilter() { + return filter; + } + + /** + * Sets a metrics configuration filter. The metrics configuration will only + * include objects that meet the filter's criteria. + * + * The filter may be omitted to get metrics for the entire bucket. + */ + public void setFilter(MetricsFilter filter) { + this.filter = filter; + } + + /** + * Sets a metrics configuration filter and returns {@link MetricsConfiguration} + * object for method chaining. The metrics configuration will only include objects that meet the filter's criteria. + * + * The filter may be omitted to get metrics for the entire bucket. + */ + public MetricsConfiguration withFilter(MetricsFilter filter) { + setFilter(filter); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsFilter.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsFilter.java new file mode 100644 index 0000000000..1cf705f4c7 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsFilter.java @@ -0,0 +1,67 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.metrics; + +import java.io.Serializable; + +/** + * The filter used to describe a set of objects to include for metrics. + * The metrics configuration will only include objects that meet the filter's criteria. + * + * A filter contains one of {@link MetricsPrefixPredicate}, {@link MetricsTagPredicate} + * or {@link MetricsAndOperator}. + */ +public class MetricsFilter implements Serializable { + + private MetricsFilterPredicate predicate; + + public MetricsFilter() { } + + public MetricsFilter(MetricsFilterPredicate predicate) { + this.predicate = predicate; + } + + /** + * Returns the {@link MetricsFilterPredicate} to be used when evaluating a metrics filter. + * + * The predicate is one of {@link MetricsPrefixPredicate}, {@link MetricsTagPredicate} + * or {@link MetricsAndOperator}. + */ + public MetricsFilterPredicate getPredicate() { + return predicate; + } + + /** + * Sets the {@link MetricsFilterPredicate} to be used when evaluating a metrics filter. + * + * The predicate should be one of {@link MetricsPrefixPredicate}, {@link MetricsTagPredicate} + * or {@link MetricsAndOperator}. + */ + public void setPredicate(MetricsFilterPredicate predicate) { + this.predicate = predicate; + } + + /** + * Sets the {@link MetricsFilterPredicate} to be used when evaluating a metrics filter + * and returns the {@link MetricsFilter} object for method chaining. + * + * The predicate should be one of {@link MetricsPrefixPredicate}, {@link MetricsTagPredicate} + * or {@link MetricsAndOperator}. + */ + public MetricsFilter withPredicate(MetricsFilterPredicate predicate) { + setPredicate(predicate); + return this; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsFilterPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsFilterPredicate.java new file mode 100644 index 0000000000..3e399d24d4 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsFilterPredicate.java @@ -0,0 +1,33 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.metrics; + +import java.io.Serializable; + +/** + * Base class to represent the root predicate in {@link MetricsFilter} class. + * + * @see MetricsPrefixPredicate + * @see MetricsTagPredicate + * @see MetricsAndOperator + */ +public abstract class MetricsFilterPredicate implements Serializable { + + /** + * Helper method that accepts an implemenation of {@link MetricsPredicateVisitor} + * and invokes the most applicable visit method in the visitor. + */ + public abstract void accept(MetricsPredicateVisitor metricsPredicateVisitor); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsNAryOperator.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsNAryOperator.java new file mode 100644 index 0000000000..16d0d155d3 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsNAryOperator.java @@ -0,0 +1,33 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.metrics; + +import java.util.List; + +/** + * Abstract class representing an operator that acts on N number of predicates. + */ +abstract class MetricsNAryOperator extends MetricsFilterPredicate { + + private final List operands; + + public MetricsNAryOperator(List operands) { + this.operands = operands; + } + + public List getOperands() { + return operands; + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsPredicateVisitor.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsPredicateVisitor.java new file mode 100644 index 0000000000..3be8b32765 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsPredicateVisitor.java @@ -0,0 +1,42 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.metrics; + +/** + * Interface to invoke specific behavior based on the type of {@link MetricsFilterPredicate} visited. + * + * When an implementation of this visitor is passed to an + * {@link MetricsFilterPredicate#accept(MetricsPredicateVisitor)} method, + * the visit method most applicable to that element is invoked. + */ +public interface MetricsPredicateVisitor { + /** + * Implement this method to add behaviour performed when + * {@link MetricsPrefixPredicate} is visited. + */ + public void visit(MetricsPrefixPredicate metricsPrefixPredicate); + + /** + * Implement this method to add behaviour performed when + * {@link MetricsTagPredicate} is visited. + */ + public void visit(MetricsTagPredicate metricsTagPredicate); + + /** + * Implement this method to add behaviour performed when + * {@link MetricsAndOperator} is visited. + */ + public void visit(MetricsAndOperator metricsAndOperator); +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsPrefixPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsPrefixPredicate.java new file mode 100644 index 0000000000..5086e86530 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsPrefixPredicate.java @@ -0,0 +1,40 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.metrics; + +/** + * A {@link MetricsFilterPredicate} class to represent the + * prefix to use when evaluating a metrics filter. + */ +public final class MetricsPrefixPredicate extends MetricsFilterPredicate { + + private final String prefix; + + public MetricsPrefixPredicate(String prefix) { + this.prefix = prefix; + } + + /** + * Returns the prefix to use when evaluating a metrics filter. + */ + public String getPrefix() { + return prefix; + } + + @Override + public void accept(MetricsPredicateVisitor metricsPredicateVisitor) { + metricsPredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsTagPredicate.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsTagPredicate.java new file mode 100644 index 0000000000..33fc93df01 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/metrics/MetricsTagPredicate.java @@ -0,0 +1,39 @@ +/* + * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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://aws.amazon.com/apache2.0 + * + * This file 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 com.amazonaws.services.s3.model.metrics; + +import com.amazonaws.services.s3.model.Tag; + +/** + * A {@link MetricsFilterPredicate} class to represent the {@link Tag} object + * to use when evaluating a metrics filter. + */ +public final class MetricsTagPredicate extends MetricsFilterPredicate { + + private final Tag tag; + + public MetricsTagPredicate(Tag tag) { + this.tag = tag; + } + + public Tag getTag() { + return tag; + } + + @Override + public void accept(MetricsPredicateVisitor metricsPredicateVisitor) { + metricsPredicateVisitor.visit(this); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/AbstractSSEHandler.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/AbstractSSEHandler.java index 6fbba22552..1f9c4414c1 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/AbstractSSEHandler.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/AbstractSSEHandler.java @@ -30,40 +30,27 @@ abstract class AbstractSSEHandler extends AbstractHandler implements ServerSideE @Override public final String getSSEAlgorithm() { - ServerSideEncryptionResult result = sseResult(); + final ServerSideEncryptionResult result = sseResult(); return result == null ? null : result.getSSEAlgorithm(); } @Override public final void setSSEAlgorithm(String serverSideEncryption) { - ServerSideEncryptionResult result = sseResult(); - if (result != null) + final ServerSideEncryptionResult result = sseResult(); + if (result != null) { result.setSSEAlgorithm(serverSideEncryption); - } - - @Override - public void setSSEKMSKeyId(String kmsKeyId) { - ServerSideEncryptionResult result = sseResult(); - if (result != null) - result.setSSEKMSKeyId(kmsKeyId); - - } - - @Override - public String getSSEKMSKeyId() { - ServerSideEncryptionResult result = sseResult(); - return result == null ? null : result.getSSEKMSKeyId(); + } } @Override public final String getSSECustomerAlgorithm() { - ServerSideEncryptionResult result = sseResult(); + final ServerSideEncryptionResult result = sseResult(); return result == null ? null : result.getSSECustomerAlgorithm(); } @Override public final void setSSECustomerAlgorithm(String algorithm) { - ServerSideEncryptionResult result = sseResult(); + final ServerSideEncryptionResult result = sseResult(); if (result != null) { result.setSSECustomerAlgorithm(algorithm); } @@ -71,13 +58,13 @@ public final void setSSECustomerAlgorithm(String algorithm) { @Override public final String getSSECustomerKeyMd5() { - ServerSideEncryptionResult result = sseResult(); + final ServerSideEncryptionResult result = sseResult(); return result == null ? null : result.getSSECustomerKeyMd5(); } @Override public final void setSSECustomerKeyMd5(String md5Digest) { - ServerSideEncryptionResult result = sseResult(); + final ServerSideEncryptionResult result = sseResult(); if (result != null) { result.setSSECustomerKeyMd5(md5Digest); } diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/BucketConfigurationXmlFactory.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/BucketConfigurationXmlFactory.java index fe51ed5d8c..b1bdd32cf7 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/BucketConfigurationXmlFactory.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/BucketConfigurationXmlFactory.java @@ -12,7 +12,6 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ - package com.amazonaws.services.s3.model.transform; import com.amazonaws.AmazonClientException; @@ -34,13 +33,53 @@ import com.amazonaws.services.s3.model.BucketWebsiteConfiguration; import com.amazonaws.services.s3.model.CORSRule; import com.amazonaws.services.s3.model.CORSRule.AllowedMethods; +import com.amazonaws.services.s3.model.CloudFunctionConfiguration; +import com.amazonaws.services.s3.model.Filter; +import com.amazonaws.services.s3.model.FilterRule; +import com.amazonaws.services.s3.model.LambdaConfiguration; +import com.amazonaws.services.s3.model.NotificationConfiguration; +import com.amazonaws.services.s3.model.QueueConfiguration; import com.amazonaws.services.s3.model.RedirectRule; import com.amazonaws.services.s3.model.ReplicationDestinationConfig; import com.amazonaws.services.s3.model.ReplicationRule; import com.amazonaws.services.s3.model.RoutingRule; import com.amazonaws.services.s3.model.RoutingRuleCondition; +import com.amazonaws.services.s3.model.S3KeyFilter; +import com.amazonaws.services.s3.model.Tag; import com.amazonaws.services.s3.model.TagSet; - +import com.amazonaws.services.s3.model.analytics.AnalyticsAndOperator; +import com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration; +import com.amazonaws.services.s3.model.analytics.AnalyticsExportDestination; +import com.amazonaws.services.s3.model.analytics.AnalyticsFilter; +import com.amazonaws.services.s3.model.analytics.AnalyticsFilterPredicate; +import com.amazonaws.services.s3.model.analytics.AnalyticsPredicateVisitor; +import com.amazonaws.services.s3.model.analytics.AnalyticsPrefixPredicate; +import com.amazonaws.services.s3.model.analytics.AnalyticsS3BucketDestination; +import com.amazonaws.services.s3.model.analytics.AnalyticsTagPredicate; +import com.amazonaws.services.s3.model.analytics.StorageClassAnalysis; +import com.amazonaws.services.s3.model.analytics.StorageClassAnalysisDataExport; +import com.amazonaws.services.s3.model.inventory.InventoryConfiguration; +import com.amazonaws.services.s3.model.inventory.InventoryDestination; +import com.amazonaws.services.s3.model.inventory.InventoryFilter; +import com.amazonaws.services.s3.model.inventory.InventoryFilterPredicate; +import com.amazonaws.services.s3.model.inventory.InventoryPrefixPredicate; +import com.amazonaws.services.s3.model.inventory.InventoryS3BucketDestination; +import com.amazonaws.services.s3.model.inventory.InventorySchedule; +import com.amazonaws.services.s3.model.lifecycle.LifecycleAndOperator; +import com.amazonaws.services.s3.model.lifecycle.LifecycleFilter; +import com.amazonaws.services.s3.model.lifecycle.LifecycleFilterPredicate; +import com.amazonaws.services.s3.model.lifecycle.LifecyclePredicateVisitor; +import com.amazonaws.services.s3.model.lifecycle.LifecyclePrefixPredicate; +import com.amazonaws.services.s3.model.lifecycle.LifecycleTagPredicate; +import com.amazonaws.services.s3.model.metrics.MetricsAndOperator; +import com.amazonaws.services.s3.model.metrics.MetricsConfiguration; +import com.amazonaws.services.s3.model.metrics.MetricsFilter; +import com.amazonaws.services.s3.model.metrics.MetricsFilterPredicate; +import com.amazonaws.services.s3.model.metrics.MetricsPredicateVisitor; +import com.amazonaws.services.s3.model.metrics.MetricsPrefixPredicate; +import com.amazonaws.services.s3.model.metrics.MetricsTagPredicate; + +import java.util.Collection; import java.util.List; import java.util.Map; @@ -56,11 +95,11 @@ public class BucketConfigurationXmlFactory { * @return The XML byte array representation. */ public byte[] convertToXmlByteArray(BucketVersioningConfiguration versioningConfiguration) { - XmlWriter xml = new XmlWriter(); + final XmlWriter xml = new XmlWriter(); xml.start("VersioningConfiguration", "xmlns", Constants.XML_NAMESPACE); xml.start("Status").value(versioningConfiguration.getStatus()).end(); - Boolean mfaDeleteEnabled = versioningConfiguration.isMfaDeleteEnabled(); + final Boolean mfaDeleteEnabled = versioningConfiguration.isMfaDeleteEnabled(); if (mfaDeleteEnabled != null) { if (mfaDeleteEnabled) { xml.start("MfaDelete").value("Enabled").end(); @@ -81,7 +120,7 @@ public byte[] convertToXmlByteArray(BucketVersioningConfiguration versioningConf * @return The XML byte array representation. */ public byte[] convertToXmlByteArray(BucketAccelerateConfiguration accelerateConfiguration) { - XmlWriter xml = new XmlWriter(); + final XmlWriter xml = new XmlWriter(); xml.start("AccelerateConfiguration", "xmlns", Constants.XML_NAMESPACE); xml.start("Status").value(accelerateConfiguration.getStatus()).end(); xml.end(); @@ -97,10 +136,11 @@ public byte[] convertToXmlByteArray(BucketAccelerateConfiguration accelerateConf public byte[] convertToXmlByteArray(BucketLoggingConfiguration loggingConfiguration) { // Default log file prefix to the empty string if none is specified String logFilePrefix = loggingConfiguration.getLogFilePrefix(); - if (logFilePrefix == null) + if (logFilePrefix == null) { logFilePrefix = ""; + } - XmlWriter xml = new XmlWriter(); + final XmlWriter xml = new XmlWriter(); xml.start("BucketLoggingStatus", "xmlns", Constants.XML_NAMESPACE); if (loggingConfiguration.isLoggingEnabled()) { xml.start("LoggingEnabled"); @@ -119,32 +159,108 @@ public byte[] convertToXmlByteArray(BucketLoggingConfiguration loggingConfigurat * @param notificationConfiguration The configuration to convert. * @return The XML byte array representation. */ - public byte[] convertToXmlByteArray(BucketNotificationConfiguration notificationConfiguration) { - XmlWriter xml = new XmlWriter(); + public byte[] convertToXmlByteArray( + BucketNotificationConfiguration notificationConfiguration) { + final XmlWriter xml = new XmlWriter(); xml.start("NotificationConfiguration", "xmlns", Constants.XML_NAMESPACE); + final Map configurations = notificationConfiguration + .getConfigurations(); + + for (final Map.Entry entry : configurations + .entrySet()) { + final String configName = entry.getKey(); + final NotificationConfiguration config = entry.getValue(); + if (config instanceof TopicConfiguration) { + xml.start("TopicConfiguration"); + xml.start("Id").value(configName).end(); + xml.start("Topic") + .value(((TopicConfiguration) config).getTopicARN()) + .end(); + addEventsAndFilterCriteria(xml, config); + xml.end(); + } else if (config instanceof QueueConfiguration) { + xml.start("QueueConfiguration"); + xml.start("Id").value(configName).end(); + xml.start("Queue") + .value(((QueueConfiguration) config).getQueueARN()) + .end(); + addEventsAndFilterCriteria(xml, config); + xml.end(); + } else if (config instanceof CloudFunctionConfiguration) { + xml.start("CloudFunctionConfiguration"); + xml.start("Id").value(configName).end(); + xml.start("InvocationRole") + .value(((CloudFunctionConfiguration) config) + .getInvocationRoleARN()) + .end(); + xml.start("CloudFunction") + .value(((CloudFunctionConfiguration) config).getCloudFunctionARN()) + .end(); + addEventsAndFilterCriteria(xml, config); + xml.end(); + } else if (config instanceof LambdaConfiguration) { + xml.start("CloudFunctionConfiguration"); + xml.start("Id").value(configName).end(); + xml.start("CloudFunction") + .value(((LambdaConfiguration) config).getFunctionARN()) + .end(); + addEventsAndFilterCriteria(xml, config); + xml.end(); + } + } + xml.end(); + return xml.getBytes(); + } + + private void addEventsAndFilterCriteria(XmlWriter xml, NotificationConfiguration config) { + for (final String event : config.getEvents()) { + xml.start("Event").value(event).end(); + } - List topicConfigurations = notificationConfiguration - .getTopicConfigurations(); - for (TopicConfiguration topicConfiguration : topicConfigurations) { - xml.start("TopicConfiguration"); - xml.start("Topic").value(topicConfiguration.getTopic()).end(); - xml.start("Event").value(topicConfiguration.getEvent()).end(); + final Filter filter = config.getFilter(); + if (filter != null) { + validateFilter(filter); + xml.start("Filter"); + if (filter.getS3KeyFilter() != null) { + validateS3KeyFilter(filter.getS3KeyFilter()); + xml.start("S3Key"); + for (final FilterRule filterRule : filter.getS3KeyFilter().getFilterRules()) { + xml.start("FilterRule"); + xml.start("Name").value(filterRule.getName()).end(); + xml.start("Value").value(filterRule.getValue()).end(); + xml.end(); + } + xml.end(); + } xml.end(); } + } - xml.end(); + private void validateFilter(Filter filter) { + if (filter.getS3KeyFilter() == null) { + throw new AmazonClientException("Cannot have a Filter without any criteria"); + } + } - return xml.getBytes(); + /** + * If S3Key filter is set make sure it has at least one rule + */ + private void validateS3KeyFilter(S3KeyFilter s3KeyFilter) { + if (isNullOrEmpty(s3KeyFilter.getFilterRules())) { + throw new AmazonClientException("Cannot have an S3KeyFilter without any filter rules"); + } } public byte[] convertToXmlByteArray(BucketReplicationConfiguration replicationConfiguration) { - XmlWriter xml = new XmlWriter(); + final XmlWriter xml = new XmlWriter(); xml.start("ReplicationConfiguration"); - Map rules = replicationConfiguration.getRules(); + final Map rules = replicationConfiguration + .getRules(); final String role = replicationConfiguration.getRoleARN(); xml.start("Role").value(role).end(); - for (Map.Entry entry : rules.entrySet()) { + for (final Map.Entry entry : rules + .entrySet()) { final String ruleId = entry.getKey(); final ReplicationRule rule = entry.getValue(); @@ -169,34 +285,35 @@ public byte[] convertToXmlByteArray(BucketReplicationConfiguration replicationCo /** * Converts the specified website configuration into an XML byte array to - * send to S3. Sample XML: - * index.html - * 404.html + * send to S3. Sample XML: + * + * index.html + * 404.html + * * * @param websiteConfiguration The configuration to convert. * @return The XML byte array representation. */ public byte[] convertToXmlByteArray(BucketWebsiteConfiguration websiteConfiguration) { - XmlWriter xml = new XmlWriter(); + final XmlWriter xml = new XmlWriter(); xml.start("WebsiteConfiguration", "xmlns", Constants.XML_NAMESPACE); if (websiteConfiguration.getIndexDocumentSuffix() != null) { - XmlWriter indexDocumentElement = xml.start("IndexDocument"); + final XmlWriter indexDocumentElement = xml.start("IndexDocument"); indexDocumentElement.start("Suffix") .value(websiteConfiguration.getIndexDocumentSuffix()).end(); indexDocumentElement.end(); } if (websiteConfiguration.getErrorDocument() != null) { - XmlWriter errorDocumentElement = xml.start("ErrorDocument"); + final XmlWriter errorDocumentElement = xml.start("ErrorDocument"); errorDocumentElement.start("Key").value(websiteConfiguration.getErrorDocument()).end(); errorDocumentElement.end(); } - RedirectRule redirectAllRequestsTo = websiteConfiguration.getRedirectAllRequestsTo(); + final RedirectRule redirectAllRequestsTo = websiteConfiguration.getRedirectAllRequestsTo(); if (redirectAllRequestsTo != null) { - XmlWriter redirectAllRequestsElement = xml.start("RedirectAllRequestsTo"); + final XmlWriter redirectAllRequestsElement = xml.start("RedirectAllRequestsTo"); if (redirectAllRequestsTo.getprotocol() != null) { xml.start("Protocol").value(redirectAllRequestsTo.getprotocol()).end(); } @@ -219,8 +336,8 @@ public byte[] convertToXmlByteArray(BucketWebsiteConfiguration websiteConfigurat if (websiteConfiguration.getRoutingRules() != null && websiteConfiguration.getRoutingRules().size() > 0) { - XmlWriter routingRules = xml.start("RoutingRules"); - for (RoutingRule rule : websiteConfiguration.getRoutingRules()) { + final XmlWriter routingRules = xml.start("RoutingRules"); + for (final RoutingRule rule : websiteConfiguration.getRoutingRules()) { writeRule(routingRules, rule); } @@ -238,27 +355,33 @@ public byte[] convertToXmlByteArray(BucketWebsiteConfiguration websiteConfigurat * @param config The {@link BucketLifecycleConfiguration} */ /* - * logs-rule logs/ + * logs-rule * Enabled 30 * GLACIER * 365 * 7 GLACIER * * 14 - * image-rule image/ + * + * logs/ key1 value1 + * logs/ key1 value1 + * key1 value1 + * image-rule image/ * Enabled * 2012-12-31T00:00:00.000Z * GLACIER - * 2020-12-31T00:00:00.000Z - * + * 2020-12-31T00:00:00.000Z + * + * 10 + * */ public byte[] convertToXmlByteArray(BucketLifecycleConfiguration config) throws AmazonClientException { - XmlWriter xml = new XmlWriter(); + final XmlWriter xml = new XmlWriter(); xml.start("LifecycleConfiguration"); - for (Rule rule : config.getRules()) { + for (final Rule rule : config.getRules()) { writeRule(xml, rule); } @@ -283,10 +406,10 @@ public byte[] convertToXmlByteArray(BucketLifecycleConfiguration config) public byte[] convertToXmlByteArray(BucketCrossOriginConfiguration config) throws AmazonClientException { - XmlWriter xml = new XmlWriter(); + final XmlWriter xml = new XmlWriter(); xml.start("CORSConfiguration", "xmlns", Constants.XML_NAMESPACE); - for (CORSRule rule : config.getRules()) { + for (final CORSRule rule : config.getRules()) { writeRule(xml, rule); } @@ -300,48 +423,28 @@ private void writeRule(XmlWriter xml, Rule rule) { if (rule.getId() != null) { xml.start("ID").value(rule.getId()).end(); } - xml.start("Prefix").value(rule.getPrefix()).end(); + writePrefix(xml, rule); xml.start("Status").value(rule.getStatus()).end(); + writeLifecycleFilter(xml, rule.getFilter()); - Transition transition = rule.getTransition(); - if (transition != null) { - xml.start("Transition"); - if (transition.getDate() != null) { - xml.start("Date"); - xml.value(ServiceUtils.formatIso8601Date(transition.getDate())); - xml.end(); + addTransitions(xml, rule.getTransitions()); + addNoncurrentTransitions(xml, rule.getNoncurrentVersionTransitions()); + + if (hasCurrentExpirationPolicy(rule)) { + // The rule attributes below are mutually exclusive, the service + // will throw an error if + // more than one is provided + xml.start("Expiration"); + if (rule.getExpirationInDays() != -1) { + xml.start("Days").value("" + rule.getExpirationInDays()).end(); } - if (transition.getDays() != -1) { - xml.start("Days"); - xml.value(Integer.toString(transition.getDays())); - xml.end(); + if (rule.getExpirationDate() != null) { + xml.start("Date").value(ServiceUtils.formatIso8601Date(rule.getExpirationDate())) + .end(); } - - xml.start("StorageClass"); - xml.value(transition.getStorageClass().toString()); - xml.end(); // - xml.end(); // - } - - NoncurrentVersionTransition ncvTransition = - rule.getNoncurrentVersionTransition(); - if (ncvTransition != null) { - xml.start("NoncurrentVersionTransition"); - if (ncvTransition.getDays() != -1) { - xml.start("NoncurrentDays"); - xml.value(Integer.toString(ncvTransition.getDays())); - xml.end(); + if (rule.isExpiredObjectDeleteMarker() == true) { + xml.start("ExpiredObjectDeleteMarker").value("true").end(); } - - xml.start("StorageClass"); - xml.value(ncvTransition.getStorageClass().toString()); - xml.end(); // - xml.end(); // - } - - if (rule.getExpirationInDays() != -1) { - xml.start("Expiration"); - xml.start("Days").value("" + rule.getExpirationInDays()).end(); xml.end(); // } @@ -354,27 +457,132 @@ private void writeRule(XmlWriter xml, Rule rule) { xml.end(); // } - if (rule.getExpirationDate() != null) { - xml.start("Expiration"); - xml.start("Date").value(ServiceUtils.formatIso8601Date(rule.getExpirationDate())).end(); - xml.end(); // + if (rule.getAbortIncompleteMultipartUpload() != null) { + xml.start("AbortIncompleteMultipartUpload"); + xml.start("DaysAfterInitiation").value(Integer + .toString(rule.getAbortIncompleteMultipartUpload().getDaysAfterInitiation())) + .end(); + xml.end(); // } xml.end(); // } + private void addTransitions(XmlWriter xml, List transitions) { + if (transitions == null || transitions.isEmpty()) { + return; + } + + for (final Transition t : transitions) { + if (t != null) { + xml.start("Transition"); + if (t.getDate() != null) { + xml.start("Date"); + xml.value(ServiceUtils.formatIso8601Date(t.getDate())); + xml.end(); + } + if (t.getDays() != -1) { + xml.start("Days"); + xml.value(Integer.toString(t.getDays())); + xml.end(); + } + + xml.start("StorageClass"); + xml.value(t.getStorageClass().toString()); + xml.end(); // + xml.end(); // + } + } + } + + private void addNoncurrentTransitions(XmlWriter xml, + List transitions) { + if (transitions == null || transitions.isEmpty()) { + return; + } + + for (final NoncurrentVersionTransition t : transitions) { + if (t != null) { + xml.start("NoncurrentVersionTransition"); + if (t.getDays() != -1) { + xml.start("NoncurrentDays"); + xml.value(Integer.toString(t.getDays())); + xml.end(); + } + + xml.start("StorageClass"); + xml.value(t.getStorageClass().toString()); + xml.end(); // + xml.end(); // + } + } + } + + private void writeLifecycleFilter(XmlWriter xml, LifecycleFilter filter) { + if (filter == null) { + return; + } + + xml.start("Filter"); + writeLifecycleFilterPredicate(xml, filter.getPredicate()); + xml.end(); + } + + private void writeLifecycleFilterPredicate(XmlWriter xml, LifecycleFilterPredicate predicate) { + if (predicate == null) { + return; + } + predicate.accept(new LifecyclePredicateVisitorImpl(xml)); + } + + private class LifecyclePredicateVisitorImpl implements LifecyclePredicateVisitor { + private final XmlWriter xml; + + public LifecyclePredicateVisitorImpl(XmlWriter xml) { + this.xml = xml; + } + + @Override + public void visit(LifecyclePrefixPredicate lifecyclePrefixPredicate) { + writePrefix(xml, lifecyclePrefixPredicate.getPrefix()); + } + + @Override + public void visit(LifecycleTagPredicate lifecycleTagPredicate) { + writeTag(xml, lifecycleTagPredicate.getTag()); + } + + @Override + public void visit(LifecycleAndOperator lifecycleAndOperator) { + xml.start("And"); + for (final LifecycleFilterPredicate predicate : lifecycleAndOperator.getOperands()) { + predicate.accept(this); + } + xml.end(); // + } + } + + /** + * @param rule + * @return True if rule has a current expiration () policy set + */ + private boolean hasCurrentExpirationPolicy(Rule rule) { + return rule.getExpirationInDays() != -1 || rule.getExpirationDate() != null + || rule.isExpiredObjectDeleteMarker(); + } + private void writeRule(XmlWriter xml, CORSRule rule) { xml.start("CORSRule"); if (rule.getId() != null) { xml.start("ID").value(rule.getId()).end(); } if (rule.getAllowedOrigins() != null) { - for (String origin : rule.getAllowedOrigins()) { + for (final String origin : rule.getAllowedOrigins()) { xml.start("AllowedOrigin").value(origin).end(); } } if (rule.getAllowedMethods() != null) { - for (AllowedMethods method : rule.getAllowedMethods()) { + for (final AllowedMethods method : rule.getAllowedMethods()) { xml.start("AllowedMethod").value(method.toString()).end(); } } @@ -382,12 +590,12 @@ private void writeRule(XmlWriter xml, CORSRule rule) { xml.start("MaxAgeSeconds").value(Integer.toString(rule.getMaxAgeSeconds())).end(); } if (rule.getExposedHeaders() != null) { - for (String header : rule.getExposedHeaders()) { + for (final String header : rule.getExposedHeaders()) { xml.start("ExposeHeader").value(header).end(); } } if (rule.getAllowedHeaders() != null) { - for (String header : rule.getAllowedHeaders()) { + for (final String header : rule.getAllowedHeaders()) { xml.start("AllowedHeader").value(header).end(); } } @@ -396,7 +604,7 @@ private void writeRule(XmlWriter xml, CORSRule rule) { private void writeRule(XmlWriter xml, RoutingRule rule) { xml.start("RoutingRule"); - RoutingRuleCondition condition = rule.getCondition(); + final RoutingRuleCondition condition = rule.getCondition(); if (condition != null) { xml.start("Condition"); xml.start("KeyPrefixEquals"); @@ -414,7 +622,7 @@ private void writeRule(XmlWriter xml, RoutingRule rule) { } xml.start("Redirect"); - RedirectRule redirect = rule.getRedirect(); + final RedirectRule redirect = rule.getRedirect(); if (redirect != null) { if (redirect.getprotocol() != null) { xml.start("Protocol").value(redirect.getprotocol()).end(); @@ -440,6 +648,7 @@ private void writeRule(XmlWriter xml, RoutingRule rule) { xml.end();// } + /** * Converts the specified {@link BucketTaggingConfiguration} object to an * XML fragment that can be sent to Amazon S3. @@ -453,10 +662,10 @@ private void writeRule(XmlWriter xml, RoutingRule rule) { public byte[] convertToXmlByteArray(BucketTaggingConfiguration config) throws AmazonClientException { - XmlWriter xml = new XmlWriter(); + final XmlWriter xml = new XmlWriter(); xml.start("Tagging"); - for (TagSet tagset : config.getAllTagSets()) { + for (final TagSet tagset : config.getAllTagSets()) { writeRule(xml, tagset); } @@ -465,9 +674,110 @@ public byte[] convertToXmlByteArray(BucketTaggingConfiguration config) return xml.getBytes(); } + /** + * Converts the specified {@link InventoryConfiguration} object to an XML + * fragment that can be sent to Amazon S3. + * + * @param config The {@link InventoryConfiguration} + */ + /* + * + * A2OCNCIEQW9MSG + * s3-object-inventory-list-gamma-us-east-1 + * CSV string + * true + * string configId + * All + * Size LastModifiedDate + * StorageClass ETag + * IsMultipartUploaded ReplicationStatus + * Daily + * + */ + public byte[] convertToXmlByteArray(InventoryConfiguration config) + throws AmazonClientException { + final XmlWriter xml = new XmlWriter(); + xml.start("InventoryConfiguration", "xmlns", Constants.XML_NAMESPACE); + + xml.start("Id").value(config.getId()).end(); + xml.start("IsEnabled").value(String.valueOf(config.isEnabled())).end(); + xml.start("IncludedObjectVersions").value(config.getIncludedObjectVersions()).end(); + + writeInventoryDestination(xml, config.getDestination()); + writeInventoryFilter(xml, config.getInventoryFilter()); + addInventorySchedule(xml, config.getSchedule()); + addInventoryOptionalFields(xml, config.getOptionalFields()); + + xml.end(); // + + return xml.getBytes(); + } + + private void writeInventoryDestination(XmlWriter xml, InventoryDestination destination) { + if (destination == null) { + return; + } + + xml.start("Destination"); + final InventoryS3BucketDestination s3BucketDestination = destination + .getS3BucketDestination(); + if (s3BucketDestination != null) { + xml.start("S3BucketDestination"); + addParameterIfNotNull(xml, "AccountId", s3BucketDestination.getAccountId()); + addParameterIfNotNull(xml, "Bucket", s3BucketDestination.getBucketArn()); + addParameterIfNotNull(xml, "Prefix", s3BucketDestination.getPrefix()); + addParameterIfNotNull(xml, "Format", s3BucketDestination.getFormat()); + xml.end(); // + } + xml.end(); // + } + + private void writeInventoryFilter(XmlWriter xml, InventoryFilter inventoryFilter) { + if (inventoryFilter == null) { + return; + } + + xml.start("Filter"); + writeInventoryFilterPredicate(xml, inventoryFilter.getPredicate()); + xml.end(); + } + + private void writeInventoryFilterPredicate(XmlWriter xml, InventoryFilterPredicate predicate) { + if (predicate == null) { + return; + } + + if (predicate instanceof InventoryPrefixPredicate) { + writePrefix(xml, ((InventoryPrefixPredicate) predicate).getPrefix()); + } + } + + private void addInventorySchedule(XmlWriter xml, InventorySchedule schedule) { + if (schedule == null) { + return; + } + + xml.start("Schedule"); + addParameterIfNotNull(xml, "Frequency", schedule.getFrequency()); + xml.end(); + } + + private void addInventoryOptionalFields(XmlWriter xml, List optionalFields) { + if (isNullOrEmpty(optionalFields)) { + return; + } + + xml.start("OptionalFields"); + for (final String field : optionalFields) { + xml.start("Field").value(field).end(); + } + xml.end(); + } + private void writeRule(XmlWriter xml, TagSet tagset) { xml.start("TagSet"); - for (String key : tagset.getAllTags().keySet()) { + for (final String key : tagset.getAllTags().keySet()) { xml.start("Tag"); xml.start("Key").value(key).end(); xml.start("Value").value(tagset.getTag(key)).end(); @@ -476,4 +786,241 @@ private void writeRule(XmlWriter xml, TagSet tagset) { xml.end(); // } -} + private boolean hasTags(TagSet tagSet) { + return tagSet != null && tagSet.getAllTags() != null && tagSet.getAllTags().size() > 0; + } + + /** + * Converts the specified + * {@link com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration} + * object to an XML fragment that can be sent to Amazon S3. + * + * @param config The + * {@link com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration} + */ + /* + * + * XXX documents/ + * foo bar + * + * 1 + * CSV + * 123456789 + * destination-bucket destination-prefix + * + * + */ + public byte[] convertToXmlByteArray(AnalyticsConfiguration config) + throws AmazonClientException { + final XmlWriter xml = new XmlWriter(); + + xml.start("AnalyticsConfiguration", "xmlns", Constants.XML_NAMESPACE); + + addParameterIfNotNull(xml, "Id", config.getId()); + writeAnalyticsFilter(xml, config.getFilter()); + writeStorageClassAnalysis(xml, config.getStorageClassAnalysis()); + + xml.end(); + + return xml.getBytes(); + } + + private void writeAnalyticsFilter(XmlWriter xml, AnalyticsFilter filter) { + if (filter == null) { + return; + } + + xml.start("Filter"); + writeAnalyticsFilterPredicate(xml, filter.getPredicate()); + xml.end(); + } + + private void writeAnalyticsFilterPredicate(XmlWriter xml, AnalyticsFilterPredicate predicate) { + if (predicate == null) { + return; + } + + predicate.accept(new AnalyticsPredicateVisitorImpl(xml)); + } + + private void writeStorageClassAnalysis(XmlWriter xml, + StorageClassAnalysis storageClassAnalysis) { + if (storageClassAnalysis == null) { + return; + } + + xml.start("StorageClassAnalysis"); + if (storageClassAnalysis.getDataExport() != null) { + final StorageClassAnalysisDataExport dataExport = storageClassAnalysis.getDataExport(); + + xml.start("DataExport"); + + addParameterIfNotNull(xml, "OutputSchemaVersion", dataExport.getOutputSchemaVersion()); + writeAnalyticsExportDestination(xml, dataExport.getDestination()); + + xml.end(); // + } + + xml.end(); // + } + + private void writeAnalyticsExportDestination(XmlWriter xml, + AnalyticsExportDestination destination) { + if (destination == null) { + return; + } + + xml.start("Destination"); + + if (destination.getS3BucketDestination() != null) { + xml.start("S3BucketDestination"); + final AnalyticsS3BucketDestination s3BucketDestination = destination + .getS3BucketDestination(); + addParameterIfNotNull(xml, "Format", s3BucketDestination.getFormat()); + addParameterIfNotNull(xml, "BucketAccountId", s3BucketDestination.getBucketAccountId()); + addParameterIfNotNull(xml, "Bucket", s3BucketDestination.getBucketArn()); + addParameterIfNotNull(xml, "Prefix", s3BucketDestination.getPrefix()); + xml.end(); // + } + + xml.end(); // + } + + private class AnalyticsPredicateVisitorImpl implements AnalyticsPredicateVisitor { + private final XmlWriter xml; + + public AnalyticsPredicateVisitorImpl(XmlWriter xml) { + this.xml = xml; + } + + @Override + public void visit(AnalyticsPrefixPredicate analyticsPrefixPredicate) { + writePrefix(xml, analyticsPrefixPredicate.getPrefix()); + } + + @Override + public void visit(AnalyticsTagPredicate analyticsTagPredicate) { + writeTag(xml, analyticsTagPredicate.getTag()); + } + + @Override + public void visit(AnalyticsAndOperator analyticsAndOperator) { + xml.start("And"); + for (final AnalyticsFilterPredicate predicate : analyticsAndOperator.getOperands()) { + predicate.accept(this); + } + xml.end(); + } + } + + /** + * Converts the specified + * {@link com.amazonaws.services.s3.model.metrics.MetricsConfiguration} + * object to an XML fragment that can be sent to Amazon S3. + * + * @param config The + * {@link com.amazonaws.services.s3.model.metrics.MetricsConfiguration} + * . + */ + /* + * + * metrics-id prefix Project + * Foo documents/ + * foo bar + * + */ + public byte[] convertToXmlByteArray(MetricsConfiguration config) throws AmazonClientException { + final XmlWriter xml = new XmlWriter(); + + xml.start("MetricsConfiguration", "xmlns", Constants.XML_NAMESPACE); + + addParameterIfNotNull(xml, "Id", config.getId()); + writeMetricsFilter(xml, config.getFilter()); + + xml.end(); + + return xml.getBytes(); + } + + private void writeMetricsFilter(XmlWriter xml, MetricsFilter filter) { + if (filter == null) { + return; + } + + xml.start("Filter"); + writeMetricsFilterPredicate(xml, filter.getPredicate()); + xml.end(); + } + + private void writeMetricsFilterPredicate(XmlWriter xml, MetricsFilterPredicate predicate) { + if (predicate == null) { + return; + } + + predicate.accept(new MetricsPredicateVisitorImpl(xml)); + } + + private class MetricsPredicateVisitorImpl implements MetricsPredicateVisitor { + private final XmlWriter xml; + + public MetricsPredicateVisitorImpl(XmlWriter xml) { + this.xml = xml; + } + + @Override + public void visit(MetricsPrefixPredicate metricsPrefixPredicate) { + writePrefix(xml, metricsPrefixPredicate.getPrefix()); + } + + @Override + public void visit(MetricsTagPredicate metricsTagPredicate) { + writeTag(xml, metricsTagPredicate.getTag()); + } + + @Override + public void visit(MetricsAndOperator metricsAndOperator) { + xml.start("And"); + for (final MetricsFilterPredicate predicate : metricsAndOperator.getOperands()) { + predicate.accept(this); + } + xml.end(); + } + } + + private void addParameterIfNotNull(XmlWriter xml, String xmlTagName, String value) { + if (value != null) { + xml.start(xmlTagName).value(value).end(); + } + } + + private void writePrefix(XmlWriter xml, Rule rule) { + // If no filter is set stick with the legacy behavior where we treat a + // null prefix as empty prefix. + if (rule.getFilter() == null) { + xml.start("Prefix").value(rule.getPrefix() == null ? "" : rule.getPrefix()).end(); + } else if (rule.getPrefix() != null) { + throw new IllegalArgumentException( + "Prefix cannot be used with Filter. Use LifecyclePrefixPredicate to create a LifecycleFilter"); + } + } + + private void writePrefix(XmlWriter xml, String prefix) { + addParameterIfNotNull(xml, "Prefix", prefix); + } + + private void writeTag(XmlWriter xml, Tag tag) { + if (tag == null) { + return; + } + xml.start("Tag"); + xml.start("Key").value(tag.getKey()).end(); + xml.start("Value").value(tag.getValue()).end(); + xml.end(); + } + + private boolean isNullOrEmpty(Collection collection) { + return collection == null || collection.isEmpty(); + } + +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/BucketNotificationConfigurationStaxUnmarshaller.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/BucketNotificationConfigurationStaxUnmarshaller.java new file mode 100644 index 0000000000..b03b87104e --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/BucketNotificationConfigurationStaxUnmarshaller.java @@ -0,0 +1,97 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform; + +import com.amazonaws.AmazonClientException; +import com.amazonaws.services.s3.model.BucketNotificationConfiguration; +import com.amazonaws.services.s3.model.NotificationConfiguration; +import com.amazonaws.transform.StaxUnmarshallerContext; +import com.amazonaws.transform.Unmarshaller; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.InputStream; +import java.util.Map.Entry; + +public class BucketNotificationConfigurationStaxUnmarshaller implements + Unmarshaller { + + private static BucketNotificationConfigurationStaxUnmarshaller instance = new BucketNotificationConfigurationStaxUnmarshaller(); + + public static BucketNotificationConfigurationStaxUnmarshaller getInstance() { + return instance; + } + + private static final XmlPullParserFactory xmlPullParserFactory; + static { + try { + xmlPullParserFactory = XmlPullParserFactory.newInstance(); + } catch (final XmlPullParserException xppe) { + throw new AmazonClientException("Couldn't initialize XmlPullParserFactory", xppe); + } + } + + private BucketNotificationConfigurationStaxUnmarshaller() { + } + + @Override + public BucketNotificationConfiguration unmarshall(InputStream inputStream) throws Exception { + + final XmlPullParser xpp = xmlPullParserFactory.newPullParser(); + xpp.setInput(inputStream, null); + + final StaxUnmarshallerContext context = new StaxUnmarshallerContext(xpp, + null); + + final int originalDepth = context.getCurrentDepth(); + int targetDepth = originalDepth + 1; + + if (context.isStartOfDocument()) { + targetDepth += 1; + } + + final BucketNotificationConfiguration config = new BucketNotificationConfiguration(); + + while (true) { + final int xmlEvent = context.nextEvent(); + if (xmlEvent == XmlPullParser.END_DOCUMENT) { + break; + } else if (xmlEvent == XmlPullParser.START_TAG) { + if (context.testExpression("TopicConfiguration", targetDepth)) { + final Entry entry = TopicConfigurationStaxUnmarshaller.getInstance() + .unmarshall(context); + config.addConfiguration(entry.getKey(), entry.getValue()); + } else if (context.testExpression("QueueConfiguration", targetDepth)) { + final Entry entry = QueueConfigurationStaxUnmarshaller.getInstance() + .unmarshall(context); + config.addConfiguration(entry.getKey(), entry.getValue()); + } else if (context.testExpression("CloudFunctionConfiguration", targetDepth)) { + final Entry entry = LambdaConfigurationStaxUnmarshaller.getInstance() + .unmarshall(context); + config.addConfiguration(entry.getKey(), entry.getValue()); + } + } else if (xmlEvent == XmlPullParser.END_TAG) { + if (context.getCurrentDepth() < originalDepth) { + return config; + } + } + } + + return config; + } + +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/FilterRuleStaxUnmarshaller.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/FilterRuleStaxUnmarshaller.java new file mode 100644 index 0000000000..8ccf528873 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/FilterRuleStaxUnmarshaller.java @@ -0,0 +1,67 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform; + +import com.amazonaws.services.s3.model.FilterRule; +import com.amazonaws.transform.SimpleTypeStaxUnmarshallers.StringStaxUnmarshaller; +import com.amazonaws.transform.StaxUnmarshallerContext; +import com.amazonaws.transform.Unmarshaller; + +import org.xmlpull.v1.XmlPullParser; + +class FilterRuleStaxUnmarshaller implements Unmarshaller { + + private static FilterRuleStaxUnmarshaller instance = new FilterRuleStaxUnmarshaller(); + + public static FilterRuleStaxUnmarshaller getInstance() { + return instance; + } + + private FilterRuleStaxUnmarshaller() { + } + + @Override + public FilterRule unmarshall(StaxUnmarshallerContext context) throws Exception { + + final int originalDepth = context.getCurrentDepth(); + int targetDepth = originalDepth + 1; + + if (context.isStartOfDocument()) { + targetDepth += 2; + } + + final FilterRule filter = new FilterRule(); + + while (true) { + final int xmlEvent = context.nextEvent(); + if (xmlEvent == XmlPullParser.END_DOCUMENT) { + break; + } else if (xmlEvent == XmlPullParser.START_TAG) { + if (context.testExpression("Name", targetDepth)) { + filter.setName(StringStaxUnmarshaller.getInstance().unmarshall(context)); + } else if (context.testExpression("Value", targetDepth)) { + filter.setValue(StringStaxUnmarshaller.getInstance().unmarshall(context)); + } + } else if (xmlEvent == XmlPullParser.END_TAG) { + if (context.getCurrentDepth() < originalDepth) { + return filter; + } + } + } + + return filter; + + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/FilterStaxUnmarshaller.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/FilterStaxUnmarshaller.java new file mode 100644 index 0000000000..9780e21462 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/FilterStaxUnmarshaller.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform; + +import com.amazonaws.services.s3.model.Filter; +import com.amazonaws.transform.StaxUnmarshallerContext; +import com.amazonaws.transform.Unmarshaller; + +import org.xmlpull.v1.XmlPullParser; + +class FilterStaxUnmarshaller implements Unmarshaller { + + private static FilterStaxUnmarshaller instance = new FilterStaxUnmarshaller(); + + public static FilterStaxUnmarshaller getInstance() { + return instance; + } + + private FilterStaxUnmarshaller() { + } + + @Override + public Filter unmarshall(StaxUnmarshallerContext context) throws Exception { + final int originalDepth = context.getCurrentDepth(); + int targetDepth = originalDepth + 1; + + if (context.isStartOfDocument()) { + targetDepth += 1; + } + + final Filter filter = new Filter(); + + while (true) { + final int xmlEvent = context.nextEvent(); + if (xmlEvent == XmlPullParser.END_DOCUMENT) { + break; + } else if (xmlEvent == XmlPullParser.START_TAG) { + if (context.testExpression("S3Key", targetDepth)) { + filter.withS3KeyFilter(S3KeyFilterStaxUnmarshaller.getInstance().unmarshall(context)); + } + } else if (xmlEvent == XmlPullParser.END_TAG) { + if (context.getCurrentDepth() < originalDepth) { + return filter; + } + } + } + return filter; + } + +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/LambdaConfigurationStaxUnmarshaller.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/LambdaConfigurationStaxUnmarshaller.java new file mode 100644 index 0000000000..26dc67f2c0 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/LambdaConfigurationStaxUnmarshaller.java @@ -0,0 +1,107 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform; + +import com.amazonaws.services.s3.model.CloudFunctionConfiguration; +import com.amazonaws.services.s3.model.Filter; +import com.amazonaws.services.s3.model.LambdaConfiguration; +import com.amazonaws.services.s3.model.NotificationConfiguration; +import com.amazonaws.transform.SimpleTypeStaxUnmarshallers.StringStaxUnmarshaller; +import com.amazonaws.transform.StaxUnmarshallerContext; +import com.amazonaws.transform.Unmarshaller; + +import org.xmlpull.v1.XmlPullParser; + +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +/** + * CloudFunctionConfiguration is a little different then Topic or Queue Configurations and thus + * doesn't fit nicely as a subclass of {@link NotificationConfigurationStaxUnmarshaller}. A + * CloudFunctionConfiguration element may be unmarshalled into two different classes based on + * presence of the InvocationRole attribute. If InvocationRole is present we unmarshall into the + * legacy {@link CloudFunctionConfiguration}, otherwise we unmarshall into + * {@link LambdaConfiguration}. + */ +class LambdaConfigurationStaxUnmarshaller implements + Unmarshaller, StaxUnmarshallerContext> { + + private static LambdaConfigurationStaxUnmarshaller instance = new LambdaConfigurationStaxUnmarshaller(); + + public static LambdaConfigurationStaxUnmarshaller getInstance() { + return instance; + } + + private LambdaConfigurationStaxUnmarshaller() { + } + + @Override + public Entry unmarshall(StaxUnmarshallerContext context) throws Exception { + final int originalDepth = context.getCurrentDepth(); + int targetDepth = originalDepth + 1; + + if (context.isStartOfDocument()) { + targetDepth += 1; + } + + String id = null; + final List events = new ArrayList(); + Filter filter = null; + String functionArn = null; + String invocationRole = null; + + while (true) { + final int xmlEvent = context.nextEvent(); + if (xmlEvent == XmlPullParser.END_DOCUMENT) { + break; + } else if (xmlEvent == XmlPullParser.START_TAG) { + if (context.testExpression("Id", targetDepth)) { + id = StringStaxUnmarshaller.getInstance().unmarshall(context); + } else if (context.testExpression("Event", targetDepth)) { + events.add(StringStaxUnmarshaller.getInstance().unmarshall(context)); + } else if (context.testExpression("Filter", targetDepth)) { + filter = FilterStaxUnmarshaller.getInstance().unmarshall(context); + } else if (context.testExpression("CloudFunction", targetDepth)) { + functionArn = StringStaxUnmarshaller.getInstance().unmarshall(context); + } else if (context.testExpression("InvocationRole", targetDepth)) { + invocationRole = StringStaxUnmarshaller.getInstance().unmarshall(context); + } + } else if (xmlEvent == XmlPullParser.END_TAG) { + if (context.getCurrentDepth() < originalDepth) { + return createLambdaConfig(id, events, functionArn, invocationRole, filter); + } + } + } + + return null; + } + + private Entry createLambdaConfig(String id, + List events, + String functionArn, + String invocationRole, + Filter filter) { + NotificationConfiguration config; + if (invocationRole == null) { + config = new LambdaConfiguration(functionArn, events.toArray(new String[0])); + } else { + config = new CloudFunctionConfiguration(invocationRole, functionArn, events.toArray(new String[0])); + } + return new SimpleEntry(id, config.withFilter(filter)); + } + +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/NotificationConfigurationStaxUnmarshaller.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/NotificationConfigurationStaxUnmarshaller.java new file mode 100644 index 0000000000..0fb6606911 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/NotificationConfigurationStaxUnmarshaller.java @@ -0,0 +1,102 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform; + +import com.amazonaws.services.s3.model.BucketNotificationConfiguration; +import com.amazonaws.services.s3.model.NotificationConfiguration; +import com.amazonaws.services.s3.model.QueueConfiguration; +import com.amazonaws.services.s3.model.TopicConfiguration; +import com.amazonaws.transform.SimpleTypeStaxUnmarshallers.StringStaxUnmarshaller; +import com.amazonaws.transform.StaxUnmarshallerContext; +import com.amazonaws.transform.Unmarshaller; + +import org.xmlpull.v1.XmlPullParser; + +import java.util.AbstractMap.SimpleEntry; +import java.util.Map.Entry; + +/** + * Base class for unmarshalling into subclasses of {@link NotificationConfiguration}. Current only + * {@link QueueConfiguration} and {@link TopicConfiguration} extend this class. + * + * @param + * Concrete type of {@link NotificationConfiguration} + */ +abstract class NotificationConfigurationStaxUnmarshaller implements + Unmarshaller, StaxUnmarshallerContext> { + + /** + * Id (aka configuration name) isn't modeled on the actual {@link NotificationConfiguration} + * class but as the key name in the map of configurations in + * {@link BucketNotificationConfiguration} + */ + @Override + public Entry unmarshall(StaxUnmarshallerContext context) throws Exception { + final int originalDepth = context.getCurrentDepth(); + int targetDepth = originalDepth + 1; + + if (context.isStartOfDocument()) { + targetDepth += 1; + } + + final T topicConfig = createConfiguration(); + String id = null; + + while (true) { + final int xmlEvent = context.nextEvent(); + if (xmlEvent == XmlPullParser.END_DOCUMENT) { + break; + } else if (xmlEvent == XmlPullParser.START_TAG) { + if (handleXmlEvent(topicConfig, context, targetDepth)) { + // Do nothing, subclass has handled it + } else if (context.testExpression("Id", targetDepth)) { + id = StringStaxUnmarshaller.getInstance().unmarshall(context); + } else if (context.testExpression("Event", targetDepth)) { + topicConfig.addEvent(StringStaxUnmarshaller.getInstance().unmarshall(context)); + } else if (context.testExpression("Filter", targetDepth)) { + topicConfig.setFilter(FilterStaxUnmarshaller.getInstance().unmarshall(context)); + } + } else if (xmlEvent == XmlPullParser.END_TAG) { + if (context.getCurrentDepth() < originalDepth) { + return new SimpleEntry(id, topicConfig); + } + } + } + + return null; + } + + /** + * Factory method to create the appropriate subclass of {@link NotificationConfiguration} + */ + protected abstract T createConfiguration(); + + /** + * Callback to allow subclass first shot at handling an XML event. Only attribute and start + * element events are forwarded to subclasses + * + * @param config + * {@link NotificationConfiguration} object we are unmarshalling into + * @param context + * Context of XML unmarshalling + * @param targetDepth + * expected depth for this level of unmarshalling + * @return True if event has been handled and super class should move on to the next event, + * false otherwise + * @throws Exception + */ + protected abstract boolean handleXmlEvent(T config, StaxUnmarshallerContext context, int targetDepth) + throws Exception; +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/ObjectTaggingXmlFactory.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/ObjectTaggingXmlFactory.java new file mode 100644 index 0000000000..c9382ed0a0 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/ObjectTaggingXmlFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform; + +import com.amazonaws.services.s3.internal.XmlWriter; +import com.amazonaws.services.s3.model.ObjectTagging; +import com.amazonaws.services.s3.model.Tag; + +import java.util.Map; + +public class ObjectTaggingXmlFactory { + public byte[] convertToXmlByteArray(ObjectTagging tagging) { + XmlWriter writer = new XmlWriter(); + writer.start("Tagging").start("TagSet"); + for (Tag tag : tagging.getTagSet()) { + writer.start("Tag"); + writer.start("Key").value(tag.getKey()).end(); + writer.start("Value").value(tag.getValue()).end(); + writer.end(); // + } + writer.end(); // + writer.end(); // + + return writer.getBytes(); + } +} diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/QueueConfigurationStaxUnmarshaller.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/QueueConfigurationStaxUnmarshaller.java new file mode 100644 index 0000000000..b802e996c7 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/QueueConfigurationStaxUnmarshaller.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform; + +import com.amazonaws.services.s3.model.QueueConfiguration; +import com.amazonaws.transform.SimpleTypeStaxUnmarshallers.StringStaxUnmarshaller; +import com.amazonaws.transform.StaxUnmarshallerContext; + +class QueueConfigurationStaxUnmarshaller extends NotificationConfigurationStaxUnmarshaller { + + private static QueueConfigurationStaxUnmarshaller instance = new QueueConfigurationStaxUnmarshaller(); + + public static QueueConfigurationStaxUnmarshaller getInstance() { + return instance; + } + + private QueueConfigurationStaxUnmarshaller() { + } + + protected boolean handleXmlEvent(QueueConfiguration queueConfig, StaxUnmarshallerContext context, int targetDepth) + throws Exception { + if (context.testExpression("Queue", targetDepth)) { + queueConfig.setQueueARN(StringStaxUnmarshaller.getInstance().unmarshall(context)); + return true; + } + return false; + + } + + protected QueueConfiguration createConfiguration() { + return new QueueConfiguration(); + } + +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/S3KeyFilterStaxUnmarshaller.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/S3KeyFilterStaxUnmarshaller.java new file mode 100644 index 0000000000..6097a109b4 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/S3KeyFilterStaxUnmarshaller.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform; + +import com.amazonaws.services.s3.model.S3KeyFilter; +import com.amazonaws.transform.StaxUnmarshallerContext; +import com.amazonaws.transform.Unmarshaller; + +import org.xmlpull.v1.XmlPullParser; + +class S3KeyFilterStaxUnmarshaller implements Unmarshaller { + + private static S3KeyFilterStaxUnmarshaller instance = new S3KeyFilterStaxUnmarshaller(); + + public static S3KeyFilterStaxUnmarshaller getInstance() { + return instance; + } + + private S3KeyFilterStaxUnmarshaller() { + } + + @Override + public S3KeyFilter unmarshall(StaxUnmarshallerContext context) throws Exception { + final int originalDepth = context.getCurrentDepth(); + int targetDepth = originalDepth + 1; + + if (context.isStartOfDocument()) { + targetDepth += 1; + } + + final S3KeyFilter filter = new S3KeyFilter(); + + while (true) { + final int xmlEvent = context.nextEvent(); + if (xmlEvent == XmlPullParser.END_DOCUMENT) { + break; + } else if (xmlEvent == XmlPullParser.START_TAG) { + if (context.testExpression("FilterRule", targetDepth)) { + filter.addFilterRule(FilterRuleStaxUnmarshaller.getInstance().unmarshall(context)); + } + } else if (xmlEvent == XmlPullParser.END_TAG) { + if (context.getCurrentDepth() < originalDepth) { + return filter; + } + } + } + + return filter; + } + +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/TopicConfigurationStaxUnmarshaller.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/TopicConfigurationStaxUnmarshaller.java new file mode 100644 index 0000000000..af7a6c89c6 --- /dev/null +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/TopicConfigurationStaxUnmarshaller.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform; + +import com.amazonaws.services.s3.model.TopicConfiguration; +import com.amazonaws.transform.SimpleTypeStaxUnmarshallers.StringStaxUnmarshaller; +import com.amazonaws.transform.StaxUnmarshallerContext; + +class TopicConfigurationStaxUnmarshaller extends NotificationConfigurationStaxUnmarshaller { + + private static TopicConfigurationStaxUnmarshaller instance = new TopicConfigurationStaxUnmarshaller(); + + public static TopicConfigurationStaxUnmarshaller getInstance() { + return instance; + } + + private TopicConfigurationStaxUnmarshaller() { + } + + protected boolean handleXmlEvent(TopicConfiguration topicConfig, StaxUnmarshallerContext context, int targetDepth) + throws Exception { + if (context.testExpression("Topic", targetDepth)) { + topicConfig.setTopicARN(StringStaxUnmarshaller.getInstance().unmarshall(context)); + return true; + } + return false; + + } + + protected TopicConfiguration createConfiguration() { + return new TopicConfiguration(); + } + +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/Unmarshallers.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/Unmarshallers.java index 505fd1913d..d7deef23fa 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/Unmarshallers.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/Unmarshallers.java @@ -22,18 +22,32 @@ import com.amazonaws.services.s3.model.BucketCrossOriginConfiguration; import com.amazonaws.services.s3.model.BucketLifecycleConfiguration; import com.amazonaws.services.s3.model.BucketLoggingConfiguration; -import com.amazonaws.services.s3.model.BucketNotificationConfiguration; import com.amazonaws.services.s3.model.BucketReplicationConfiguration; import com.amazonaws.services.s3.model.BucketTaggingConfiguration; import com.amazonaws.services.s3.model.BucketVersioningConfiguration; import com.amazonaws.services.s3.model.BucketWebsiteConfiguration; +import com.amazonaws.services.s3.model.DeleteBucketAnalyticsConfigurationResult; +import com.amazonaws.services.s3.model.DeleteBucketInventoryConfigurationResult; +import com.amazonaws.services.s3.model.DeleteBucketMetricsConfigurationResult; +import com.amazonaws.services.s3.model.DeleteObjectTaggingResult; +import com.amazonaws.services.s3.model.GetBucketAnalyticsConfigurationResult; +import com.amazonaws.services.s3.model.GetBucketInventoryConfigurationResult; +import com.amazonaws.services.s3.model.GetBucketMetricsConfigurationResult; +import com.amazonaws.services.s3.model.GetObjectTaggingResult; import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; +import com.amazonaws.services.s3.model.ListBucketAnalyticsConfigurationsResult; +import com.amazonaws.services.s3.model.ListBucketInventoryConfigurationsResult; +import com.amazonaws.services.s3.model.ListBucketMetricsConfigurationsResult; import com.amazonaws.services.s3.model.ListObjectsV2Result; import com.amazonaws.services.s3.model.MultipartUploadListing; import com.amazonaws.services.s3.model.ObjectListing; import com.amazonaws.services.s3.model.Owner; import com.amazonaws.services.s3.model.PartListing; import com.amazonaws.services.s3.model.RequestPaymentConfiguration; +import com.amazonaws.services.s3.model.SetBucketAnalyticsConfigurationResult; +import com.amazonaws.services.s3.model.SetBucketInventoryConfigurationResult; +import com.amazonaws.services.s3.model.SetBucketMetricsConfigurationResult; +import com.amazonaws.services.s3.model.SetObjectTaggingResult; import com.amazonaws.services.s3.model.VersionListing; import com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.CompleteMultipartUploadHandler; import com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.CopyObjectResultHandler; @@ -77,10 +91,19 @@ public Owner unmarshall(InputStream in) throws Exception { */ public static final class ListObjectsUnmarshaller implements Unmarshaller { + + private final boolean shouldSDKDecodeResponse; + + public ListObjectsUnmarshaller(final boolean shouldSDKDecodeResponse) { + this.shouldSDKDecodeResponse = shouldSDKDecodeResponse; + } + @Override public ObjectListing unmarshall(InputStream in) throws Exception { return new XmlResponsesSaxParser() - .parseListBucketObjectsResponse(in).getObjectListing(); + .parseListBucketObjectsResponse(in, + shouldSDKDecodeResponse) + .getObjectListing(); } } @@ -90,10 +113,16 @@ public ObjectListing unmarshall(InputStream in) throws Exception { public static final class ListObjectsV2Unmarshaller implements Unmarshaller { + private final boolean shouldSDKDecodeResponse; + + public ListObjectsV2Unmarshaller(final boolean shouldSDKDecodeResponse) { + this.shouldSDKDecodeResponse = shouldSDKDecodeResponse; + } + @Override public ListObjectsV2Result unmarshall(InputStream in) throws Exception { return new XmlResponsesSaxParser() - .parseListObjectsV2Response(in).getResult(); + .parseListObjectsV2Response(in, shouldSDKDecodeResponse).getResult(); } } @@ -102,10 +131,17 @@ public ListObjectsV2Result unmarshall(InputStream in) throws Exception { */ public static final class VersionListUnmarshaller implements Unmarshaller { + + private final boolean shouldSDKDecodeResponse; + + public VersionListUnmarshaller(final boolean shouldSDKDecodeResponse) { + this.shouldSDKDecodeResponse = shouldSDKDecodeResponse; + } + @Override public VersionListing unmarshall(InputStream in) throws Exception { return new XmlResponsesSaxParser() - .parseListVersionsResponse(in).getListing(); + .parseListVersionsResponse(in, shouldSDKDecodeResponse).getListing(); } } @@ -147,8 +183,9 @@ public String unmarshall(InputStream in) throws Exception { * S3 treats the US location differently, and assumes that if the * reported location is null, then it's a US bucket. */ - if (location == null) + if (location == null) { location = "US"; + } return location; } @@ -190,18 +227,6 @@ public BucketReplicationConfiguration unmarshall(InputStream in) throws Exceptio } } - /** - * Unmarshaller for the BucketNotificationConfiguration XML response. - */ - public static final class BucketNotificationConfigurationUnmarshaller implements - Unmarshaller { - @Override - public BucketNotificationConfiguration unmarshall(InputStream in) throws Exception { - return new XmlResponsesSaxParser() - .parseNotificationConfigurationResponse(in).getConfiguration(); - } - } - /** * Unmarshaller for the BucketTaggingConfiguration XML response. */ @@ -324,4 +349,175 @@ public RequestPaymentConfiguration unmarshall(InputStream in) throws Exception { .parseRequestPaymentConfigurationResponse(in).getConfiguration(); } } -} + + public static final class GetObjectTaggingResponseUnmarshaller + implements Unmarshaller { + + @Override + public GetObjectTaggingResult unmarshall(InputStream in) throws Exception { + return new XmlResponsesSaxParser().parseObjectTaggingResponse(in).getResult(); + } + } + + public static final class SetObjectTaggingResponseUnmarshaller + implements Unmarshaller { + + @Override + public SetObjectTaggingResult unmarshall(InputStream in) throws Exception { + return new SetObjectTaggingResult(); + } + } + + public static final class DeleteObjectTaggingResponseUnmarshaller + implements Unmarshaller { + + @Override + public DeleteObjectTaggingResult unmarshall(InputStream in) throws Exception { + return new DeleteObjectTaggingResult(); + } + } + + /** + * Unmarshaller for the GetBucketAnalyticsConfiguration XML response. + */ + public static final class GetBucketAnalyticsConfigurationUnmarshaller implements + Unmarshaller { + @Override + public GetBucketAnalyticsConfigurationResult unmarshall(InputStream in) throws Exception { + return new XmlResponsesSaxParser().parseGetBucketAnalyticsConfigurationResponse(in) + .getResult(); + } + } + + /** + * Unmarshaller for the ListBucketAnalyticsConfigurations XML response. + */ + public static final class ListBucketAnalyticsConfigurationUnmarshaller implements + Unmarshaller { + @Override + public ListBucketAnalyticsConfigurationsResult unmarshall(InputStream in) throws Exception { + return new XmlResponsesSaxParser().parseListBucketAnalyticsConfigurationResponse(in) + .getResult(); + } + } + + /** + * Unmarshaller for the DeleteBucketAnalyticsConfiguration XML response. + */ + public static final class DeleteBucketAnalyticsConfigurationUnmarshaller implements + Unmarshaller { + @Override + public DeleteBucketAnalyticsConfigurationResult unmarshall(InputStream in) + throws Exception { + return new DeleteBucketAnalyticsConfigurationResult(); + } + } + + /** + * Unmarshaller for the SetBucketAnalyticsConfiguration XML response. + */ + public static final class SetBucketAnalyticsConfigurationUnmarshaller implements + Unmarshaller { + @Override + public SetBucketAnalyticsConfigurationResult unmarshall(InputStream in) throws Exception { + return new SetBucketAnalyticsConfigurationResult(); + } + } + + /** + * Unmarshaller for the GetBucketMetricsConfiguration XML response. + */ + public static final class GetBucketMetricsConfigurationUnmarshaller implements + Unmarshaller { + @Override + public GetBucketMetricsConfigurationResult unmarshall(InputStream in) throws Exception { + return new XmlResponsesSaxParser().parseGetBucketMetricsConfigurationResponse(in) + .getResult(); + } + } + + /** + * Unmarshaller for the ListBucketMetricsConfigurations XML response. + */ + public static final class ListBucketMetricsConfigurationsUnmarshaller implements + Unmarshaller { + @Override + public ListBucketMetricsConfigurationsResult unmarshall(InputStream in) throws Exception { + return new XmlResponsesSaxParser().parseListBucketMetricsConfigurationsResponse(in) + .getResult(); + } + } + + /** + * Unmarshaller for the DeleteBucketMetricsConfiguration XML response. + */ + public static final class DeleteBucketMetricsConfigurationUnmarshaller implements + Unmarshaller { + @Override + public DeleteBucketMetricsConfigurationResult unmarshall(InputStream in) throws Exception { + return new DeleteBucketMetricsConfigurationResult(); + } + } + + /** + * Unmarshaller for the SetBucketMetricsConfiguration XML response. + */ + public static final class SetBucketMetricsConfigurationUnmarshaller implements + Unmarshaller { + @Override + public SetBucketMetricsConfigurationResult unmarshall(InputStream in) throws Exception { + return new SetBucketMetricsConfigurationResult(); + } + } + + /** + * Unmarshaller for the GetBucketInventoryConfiguration XML response. + */ + public static final class GetBucketInventoryConfigurationUnmarshaller implements + Unmarshaller { + + @Override + public GetBucketInventoryConfigurationResult unmarshall(InputStream in) throws Exception { + return new XmlResponsesSaxParser().parseGetBucketInventoryConfigurationResponse(in) + .getResult(); + } + } + + /** + * Unmarshaller for the ListBucketInventoryConfigurations XML response. + */ + public static final class ListBucketInventoryConfigurationsUnmarshaller implements + Unmarshaller { + + @Override + public ListBucketInventoryConfigurationsResult unmarshall(InputStream in) throws Exception { + return new XmlResponsesSaxParser().parseBucketListInventoryConfigurationsResponse(in) + .getResult(); + } + } + + /** + * Unmarshaller for the DeleteBucketInventoryConfiguration XML response. + */ + public static final class DeleteBucketInventoryConfigurationUnmarshaller implements + Unmarshaller { + + @Override + public DeleteBucketInventoryConfigurationResult unmarshall(InputStream in) + throws Exception { + return new DeleteBucketInventoryConfigurationResult(); + } + } + + /** + * Unmarshaller for the SetBucketInventoryConfiguration XML response. + */ + public static final class SetBucketInventoryConfigurationUnmarshaller implements + Unmarshaller { + + @Override + public SetBucketInventoryConfigurationResult unmarshall(InputStream in) throws Exception { + return new SetBucketInventoryConfigurationResult(); + } + } +} \ No newline at end of file diff --git a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/XmlResponsesSaxParser.java b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/XmlResponsesSaxParser.java index 4942a670cb..d24028b7de 100644 --- a/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/XmlResponsesSaxParser.java +++ b/aws-android-sdk-s3/src/main/java/com/amazonaws/services/s3/model/transform/XmlResponsesSaxParser.java @@ -24,8 +24,11 @@ import com.amazonaws.services.s3.internal.Constants; import com.amazonaws.services.s3.internal.DeleteObjectsResponse; import com.amazonaws.services.s3.internal.ObjectExpirationResult; +import com.amazonaws.services.s3.internal.S3RequesterChargedResult; +import com.amazonaws.services.s3.internal.S3VersionResult; import com.amazonaws.services.s3.internal.ServerSideEncryptionResult; import com.amazonaws.services.s3.internal.ServiceUtils; +import com.amazonaws.services.s3.model.AbortIncompleteMultipartUpload; import com.amazonaws.services.s3.model.AccessControlList; import com.amazonaws.services.s3.model.AmazonS3Exception; import com.amazonaws.services.s3.model.Bucket; @@ -36,8 +39,6 @@ import com.amazonaws.services.s3.model.BucketLifecycleConfiguration.Rule; import com.amazonaws.services.s3.model.BucketLifecycleConfiguration.Transition; import com.amazonaws.services.s3.model.BucketLoggingConfiguration; -import com.amazonaws.services.s3.model.BucketNotificationConfiguration; -import com.amazonaws.services.s3.model.BucketNotificationConfiguration.TopicConfiguration; import com.amazonaws.services.s3.model.BucketReplicationConfiguration; import com.amazonaws.services.s3.model.BucketTaggingConfiguration; import com.amazonaws.services.s3.model.BucketVersioningConfiguration; @@ -49,9 +50,16 @@ import com.amazonaws.services.s3.model.CopyObjectResult; import com.amazonaws.services.s3.model.DeleteObjectsResult.DeletedObject; import com.amazonaws.services.s3.model.EmailAddressGrantee; +import com.amazonaws.services.s3.model.GetBucketAnalyticsConfigurationResult; +import com.amazonaws.services.s3.model.GetBucketInventoryConfigurationResult; +import com.amazonaws.services.s3.model.GetBucketMetricsConfigurationResult; +import com.amazonaws.services.s3.model.GetObjectTaggingResult; import com.amazonaws.services.s3.model.Grantee; import com.amazonaws.services.s3.model.GroupGrantee; import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; +import com.amazonaws.services.s3.model.ListBucketAnalyticsConfigurationsResult; +import com.amazonaws.services.s3.model.ListBucketInventoryConfigurationsResult; +import com.amazonaws.services.s3.model.ListBucketMetricsConfigurationsResult; import com.amazonaws.services.s3.model.ListObjectsV2Result; import com.amazonaws.services.s3.model.MultiObjectDeleteException.DeleteError; import com.amazonaws.services.s3.model.MultipartUpload; @@ -71,10 +79,38 @@ import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectSummary; import com.amazonaws.services.s3.model.S3VersionSummary; -import com.amazonaws.services.s3.model.StorageClass; +import com.amazonaws.services.s3.model.Tag; import com.amazonaws.services.s3.model.TagSet; import com.amazonaws.services.s3.model.VersionListing; +import com.amazonaws.services.s3.model.analytics.AnalyticsAndOperator; +import com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration; +import com.amazonaws.services.s3.model.analytics.AnalyticsExportDestination; +import com.amazonaws.services.s3.model.analytics.AnalyticsFilter; +import com.amazonaws.services.s3.model.analytics.AnalyticsFilterPredicate; +import com.amazonaws.services.s3.model.analytics.AnalyticsPrefixPredicate; +import com.amazonaws.services.s3.model.analytics.AnalyticsS3BucketDestination; +import com.amazonaws.services.s3.model.analytics.AnalyticsTagPredicate; +import com.amazonaws.services.s3.model.analytics.StorageClassAnalysis; +import com.amazonaws.services.s3.model.analytics.StorageClassAnalysisDataExport; +import com.amazonaws.services.s3.model.inventory.InventoryConfiguration; +import com.amazonaws.services.s3.model.inventory.InventoryDestination; +import com.amazonaws.services.s3.model.inventory.InventoryFilter; +import com.amazonaws.services.s3.model.inventory.InventoryPrefixPredicate; +import com.amazonaws.services.s3.model.inventory.InventoryS3BucketDestination; +import com.amazonaws.services.s3.model.inventory.InventorySchedule; +import com.amazonaws.services.s3.model.lifecycle.LifecycleAndOperator; +import com.amazonaws.services.s3.model.lifecycle.LifecycleFilter; +import com.amazonaws.services.s3.model.lifecycle.LifecycleFilterPredicate; +import com.amazonaws.services.s3.model.lifecycle.LifecyclePrefixPredicate; +import com.amazonaws.services.s3.model.lifecycle.LifecycleTagPredicate; +import com.amazonaws.services.s3.model.metrics.MetricsAndOperator; +import com.amazonaws.services.s3.model.metrics.MetricsConfiguration; +import com.amazonaws.services.s3.model.metrics.MetricsFilter; +import com.amazonaws.services.s3.model.metrics.MetricsFilterPredicate; +import com.amazonaws.services.s3.model.metrics.MetricsPrefixPredicate; +import com.amazonaws.services.s3.model.metrics.MetricsTagPredicate; import com.amazonaws.util.DateUtils; +import com.amazonaws.util.HttpUtils; import com.amazonaws.util.StringUtils; import org.apache.commons.logging.Log; @@ -118,12 +154,12 @@ public XmlResponsesSaxParser() throws AmazonClientException { // Ensure we can load the XML Reader. try { xr = XMLReaderFactory.createXMLReader(); - } catch (SAXException e) { + } catch (final SAXException e) { System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver"); try { // Try once more... xr = XMLReaderFactory.createXMLReader(); - } catch (SAXException e2) { + } catch (final SAXException e2) { throw new AmazonClientException( "Couldn't initialize a sax driver for the XMLReader", e); } @@ -147,19 +183,19 @@ protected void parseXmlInputStream(DefaultHandler handler, InputStream inputStre log.debug("Parsing XML response document with handler: " + handler.getClass()); } - BufferedReader breader = new BufferedReader(new InputStreamReader(inputStream, + final BufferedReader breader = new BufferedReader(new InputStreamReader(inputStream, Constants.DEFAULT_ENCODING)); xr.setContentHandler(handler); xr.setErrorHandler(handler); xr.parse(new InputSource(breader)); - } catch (IOException e) { + } catch (final IOException e) { throw e; - } catch (Throwable t) { + } catch (final Throwable t) { try { inputStream.close(); - } catch (IOException e) { + } catch (final IOException e) { if (log.isErrorEnabled()) { log.error("Unable to close response InputStream up after XML parse failure", e); } @@ -190,11 +226,11 @@ protected InputStream sanitizeXmlDocument(DefaultHandler handler, InputStream in * into a string buffer, so we can replace troublesome * characters before sending the document to the XML parser. */ - StringBuilder listingDocBuffer = new StringBuilder(); - BufferedReader br = new BufferedReader( + final StringBuilder listingDocBuffer = new StringBuilder(); + final BufferedReader br = new BufferedReader( new InputStreamReader(inputStream, Constants.DEFAULT_ENCODING)); - char[] buf = new char[8192]; + final char[] buf = new char[8192]; int read = -1; while ((read = br.read(buf)) != -1) { listingDocBuffer.append(buf, 0, read); @@ -207,18 +243,18 @@ protected InputStream sanitizeXmlDocument(DefaultHandler handler, InputStream in * misinterpreting 0x0D characters as 0x0A and being unable to * parse the XML. */ - String listingDoc = listingDocBuffer.toString().replaceAll("\r", " "); + final String listingDoc = listingDocBuffer.toString().replaceAll("\r", " "); sanitizedInputStream = new ByteArrayInputStream( listingDoc.getBytes(UTF8)); - } catch (IOException e) { + } catch (final IOException e) { throw e; - } catch (Throwable t) { + } catch (final Throwable t) { try { inputStream.close(); - } catch (IOException e) { + } catch (final IOException e) { if (log.isErrorEnabled()) { log.error( "Unable to close response InputStream after failure sanitizing XML document", @@ -227,7 +263,8 @@ protected InputStream sanitizeXmlDocument(DefaultHandler handler, InputStream in } throw new AmazonClientException( "Failed to sanitize XML document destined for handler " - + handler.getClass(), t); + + handler.getClass(), + t); } return sanitizedInputStream; } @@ -242,10 +279,12 @@ protected InputStream sanitizeXmlDocument(DefaultHandler handler, InputStream in * returns the string the caller passed in. */ private static String checkForEmptyString(String s) { - if (s == null) + if (s == null) { return null; - if (s.length() == 0) + } + if (s.length() == 0) { return null; + } return s; } @@ -262,7 +301,7 @@ private static String checkForEmptyString(String s) { private static int parseInt(String s) { try { return Integer.parseInt(s); - } catch (NumberFormatException nfe) { + } catch (final NumberFormatException nfe) { log.error("Unable to parse integer value '" + s + "'", nfe); } @@ -275,19 +314,44 @@ private static int parseInt(String s) { * and -1 is returned. * * @param s The string to parse and return as a long. - * @return The long value of the specified string, otherwise -1 if there were - * any problems parsing the string as a long. + * @return The long value of the specified string, otherwise -1 if there + * were any problems parsing the string as a long. */ private static long parseLong(String s) { try { return Long.parseLong(s); - } catch (NumberFormatException nfe) { + } catch (final NumberFormatException nfe) { log.error("Unable to parse long value '" + s + "'", nfe); } return -1; } + private static String findAttributeValue( + String qnameToFind, + Attributes attrs) { + if (StringUtils.isBlank(qnameToFind) || attrs == null) { + return null; + } + + for (int i = 0; i < attrs.getLength(); i++) { + final String qname = attrs.getQName(i); + if (qname.trim().equalsIgnoreCase(qnameToFind.trim())) { + return attrs.getValue(i); + } + } + + return null; + } + + /** + * Perform a url decode on the given value if specified. Return value by + * default; + */ + private static String decodeIfSpecified(String value, boolean decode) { + return decode ? HttpUtils.urlDecode(value) : value; + } + /** * Parses a ListBucket response XML document from an input stream. * @@ -296,9 +360,10 @@ private static long parseLong(String s) { * stream. * @throws AmazonClientException */ - public ListBucketHandler parseListBucketObjectsResponse(InputStream inputStream) + public ListBucketHandler parseListBucketObjectsResponse(InputStream inputStream, + final boolean shouldSDKDecodeResponse) throws IOException { - ListBucketHandler handler = new ListBucketHandler(); + final ListBucketHandler handler = new ListBucketHandler(shouldSDKDecodeResponse); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler; } @@ -311,9 +376,10 @@ public ListBucketHandler parseListBucketObjectsResponse(InputStream inputStream) * stream. * @throws AmazonClientException */ - public ListObjectsV2Handler parseListObjectsV2Response(InputStream inputStream) + public ListObjectsV2Handler parseListObjectsV2Response(InputStream inputStream, + final boolean shouldSDKDecodeResponse) throws IOException { - ListObjectsV2Handler handler = new ListObjectsV2Handler(); + final ListObjectsV2Handler handler = new ListObjectsV2Handler(shouldSDKDecodeResponse); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler; @@ -327,9 +393,9 @@ public ListObjectsV2Handler parseListObjectsV2Response(InputStream inputStream) * stream. * @throws AmazonClientException */ - public ListVersionsHandler parseListVersionsResponse(InputStream inputStream) + public ListVersionsHandler parseListVersionsResponse(InputStream inputStream,final boolean shouldSDKDecodeResponse) throws IOException { - ListVersionsHandler handler = new ListVersionsHandler(); + final ListVersionsHandler handler = new ListVersionsHandler(shouldSDKDecodeResponse); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler; } @@ -344,7 +410,7 @@ public ListVersionsHandler parseListVersionsResponse(InputStream inputStream) */ public ListAllMyBucketsHandler parseListMyBucketsResponse(InputStream inputStream) throws IOException { - ListAllMyBucketsHandler handler = new ListAllMyBucketsHandler(); + final ListAllMyBucketsHandler handler = new ListAllMyBucketsHandler(); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler; } @@ -360,7 +426,7 @@ public ListAllMyBucketsHandler parseListMyBucketsResponse(InputStream inputStrea */ public AccessControlListHandler parseAccessControlListResponse(InputStream inputStream) throws IOException { - AccessControlListHandler handler = new AccessControlListHandler(); + final AccessControlListHandler handler = new AccessControlListHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -376,7 +442,7 @@ public AccessControlListHandler parseAccessControlListResponse(InputStream input */ public BucketLoggingConfigurationHandler parseLoggingStatusResponse(InputStream inputStream) throws IOException { - BucketLoggingConfigurationHandler handler = new BucketLoggingConfigurationHandler(); + final BucketLoggingConfigurationHandler handler = new BucketLoggingConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -384,7 +450,7 @@ public BucketLoggingConfigurationHandler parseLoggingStatusResponse(InputStream public BucketLifecycleConfigurationHandler parseBucketLifecycleConfigurationResponse( InputStream inputStream) throws IOException { - BucketLifecycleConfigurationHandler handler = new BucketLifecycleConfigurationHandler(); + final BucketLifecycleConfigurationHandler handler = new BucketLifecycleConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -392,14 +458,14 @@ public BucketLifecycleConfigurationHandler parseBucketLifecycleConfigurationResp public BucketCrossOriginConfigurationHandler parseBucketCrossOriginConfigurationResponse( InputStream inputStream) throws IOException { - BucketCrossOriginConfigurationHandler handler = new BucketCrossOriginConfigurationHandler(); + final BucketCrossOriginConfigurationHandler handler = new BucketCrossOriginConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler; } public String parseBucketLocationResponse(InputStream inputStream) throws IOException { - BucketLocationHandler handler = new BucketLocationHandler(); + final BucketLocationHandler handler = new BucketLocationHandler(); parseXmlInputStream(handler, inputStream); return handler.getLocation(); } @@ -407,7 +473,7 @@ public String parseBucketLocationResponse(InputStream inputStream) public BucketVersioningConfigurationHandler parseVersioningConfigurationResponse( InputStream inputStream) throws IOException { - BucketVersioningConfigurationHandler handler = new BucketVersioningConfigurationHandler(); + final BucketVersioningConfigurationHandler handler = new BucketVersioningConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -415,22 +481,14 @@ public BucketVersioningConfigurationHandler parseVersioningConfigurationResponse public BucketWebsiteConfigurationHandler parseWebsiteConfigurationResponse( InputStream inputStream) throws IOException { - BucketWebsiteConfigurationHandler handler = new BucketWebsiteConfigurationHandler(); - parseXmlInputStream(handler, inputStream); - return handler; - } - - public BucketNotificationConfigurationHandler parseNotificationConfigurationResponse( - InputStream inputStream) - throws IOException { - BucketNotificationConfigurationHandler handler = new BucketNotificationConfigurationHandler(); + final BucketWebsiteConfigurationHandler handler = new BucketWebsiteConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler; } public BucketReplicationConfigurationHandler parseReplicationConfigurationResponse( InputStream inputStream) throws IOException { - BucketReplicationConfigurationHandler handler = new BucketReplicationConfigurationHandler(); + final BucketReplicationConfigurationHandler handler = new BucketReplicationConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -438,7 +496,7 @@ public BucketReplicationConfigurationHandler parseReplicationConfigurationRespon public BucketTaggingConfigurationHandler parseTaggingConfigurationResponse( InputStream inputStream) throws IOException { - BucketTaggingConfigurationHandler handler = new BucketTaggingConfigurationHandler(); + final BucketTaggingConfigurationHandler handler = new BucketTaggingConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -446,21 +504,21 @@ public BucketTaggingConfigurationHandler parseTaggingConfigurationResponse( public BucketAccelerateConfigurationHandler parseAccelerateConfigurationResponse( InputStream inputStream) throws IOException { - BucketAccelerateConfigurationHandler handler = new BucketAccelerateConfigurationHandler(); + final BucketAccelerateConfigurationHandler handler = new BucketAccelerateConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler; } public DeleteObjectsHandler parseDeletedObjectsResult(InputStream inputStream) throws IOException { - DeleteObjectsHandler handler = new DeleteObjectsHandler(); + final DeleteObjectsHandler handler = new DeleteObjectsHandler(); parseXmlInputStream(handler, inputStream); return handler; } public CopyObjectResultHandler parseCopyObjectResponse(InputStream inputStream) throws IOException { - CopyObjectResultHandler handler = new CopyObjectResultHandler(); + final CopyObjectResultHandler handler = new CopyObjectResultHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -468,7 +526,7 @@ public CopyObjectResultHandler parseCopyObjectResponse(InputStream inputStream) public CompleteMultipartUploadHandler parseCompleteMultipartUploadResponse( InputStream inputStream) throws IOException { - CompleteMultipartUploadHandler handler = new CompleteMultipartUploadHandler(); + final CompleteMultipartUploadHandler handler = new CompleteMultipartUploadHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -476,21 +534,76 @@ public CompleteMultipartUploadHandler parseCompleteMultipartUploadResponse( public InitiateMultipartUploadHandler parseInitiateMultipartUploadResponse( InputStream inputStream) throws IOException { - InitiateMultipartUploadHandler handler = new InitiateMultipartUploadHandler(); + final InitiateMultipartUploadHandler handler = new InitiateMultipartUploadHandler(); parseXmlInputStream(handler, inputStream); return handler; } public ListMultipartUploadsHandler parseListMultipartUploadsResponse(InputStream inputStream) throws IOException { - ListMultipartUploadsHandler handler = new ListMultipartUploadsHandler(); + final ListMultipartUploadsHandler handler = new ListMultipartUploadsHandler(); parseXmlInputStream(handler, inputStream); return handler; } public ListPartsHandler parseListPartsResponse(InputStream inputStream) throws IOException { - ListPartsHandler handler = new ListPartsHandler(); + final ListPartsHandler handler = new ListPartsHandler(); + parseXmlInputStream(handler, inputStream); + return handler; + } + + public GetObjectTaggingHandler parseObjectTaggingResponse(InputStream inputStream) + throws IOException { + final GetObjectTaggingHandler handler = new GetObjectTaggingHandler(); + parseXmlInputStream(handler, inputStream); + return handler; + } + + public GetBucketMetricsConfigurationHandler parseGetBucketMetricsConfigurationResponse( + InputStream inputStream) + throws IOException { + final GetBucketMetricsConfigurationHandler handler = new GetBucketMetricsConfigurationHandler(); + parseXmlInputStream(handler, inputStream); + return handler; + } + + public ListBucketMetricsConfigurationsHandler parseListBucketMetricsConfigurationsResponse( + InputStream inputStream) + throws IOException { + final ListBucketMetricsConfigurationsHandler handler = new ListBucketMetricsConfigurationsHandler(); + parseXmlInputStream(handler, inputStream); + return handler; + } + + public GetBucketAnalyticsConfigurationHandler parseGetBucketAnalyticsConfigurationResponse( + InputStream inputStream) + throws IOException { + final GetBucketAnalyticsConfigurationHandler handler = new GetBucketAnalyticsConfigurationHandler(); + parseXmlInputStream(handler, inputStream); + return handler; + } + + public ListBucketAnalyticsConfigurationHandler parseListBucketAnalyticsConfigurationResponse( + InputStream inputStream) + throws IOException { + final ListBucketAnalyticsConfigurationHandler handler = new ListBucketAnalyticsConfigurationHandler(); + parseXmlInputStream(handler, inputStream); + return handler; + } + + public GetBucketInventoryConfigurationHandler parseGetBucketInventoryConfigurationResponse( + InputStream inputStream) + throws IOException { + final GetBucketInventoryConfigurationHandler handler = new GetBucketInventoryConfigurationHandler(); + parseXmlInputStream(handler, inputStream); + return handler; + } + + public ListBucketInventoryConfigurationsHandler parseBucketListInventoryConfigurationsResponse( + InputStream inputStream) + throws IOException { + final ListBucketInventoryConfigurationsHandler handler = new ListBucketInventoryConfigurationsHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -504,7 +617,7 @@ public ListPartsHandler parseListPartsResponse(InputStream inputStream) public RequestPaymentConfigurationHandler parseRequestPaymentConfigurationResponse( InputStream inputStream) throws IOException { - RequestPaymentConfigurationHandler handler = new RequestPaymentConfigurationHandler(); + final RequestPaymentConfigurationHandler handler = new RequestPaymentConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler; } @@ -520,11 +633,16 @@ public RequestPaymentConfigurationHandler parseRequestPaymentConfigurationRespon public static class ListBucketHandler extends AbstractHandler { private final ObjectListing objectListing = new ObjectListing(); + private final boolean shouldSDKDecodeResponse; private S3ObjectSummary currentObject = null; private Owner currentOwner = null; private String lastKey = null; + public ListBucketHandler(final boolean shouldSDKDecodeResponse) { + this.shouldSDKDecodeResponse = shouldSDKDecodeResponse; + } + public ObjectListing getObjectListing() { return objectListing; } @@ -592,25 +710,30 @@ else if (in("ListBucketResult")) { } } else if (name.equals("Prefix")) { - objectListing.setPrefix(checkForEmptyString(getText())); + objectListing.setPrefix(decodeIfSpecified(checkForEmptyString(getText()), + shouldSDKDecodeResponse)); } else if (name.equals("Marker")) { - objectListing.setMarker(checkForEmptyString(getText())); + objectListing.setMarker(decodeIfSpecified(checkForEmptyString(getText()), + shouldSDKDecodeResponse)); } else if (name.equals("NextMarker")) { - objectListing.setNextMarker(getText()); + objectListing + .setNextMarker(decodeIfSpecified(getText(), shouldSDKDecodeResponse)); } else if (name.equals("MaxKeys")) { objectListing.setMaxKeys(parseInt(getText())); } else if (name.equals("Delimiter")) { - objectListing.setDelimiter(checkForEmptyString(getText())); + objectListing.setDelimiter(decodeIfSpecified(checkForEmptyString(getText()), + shouldSDKDecodeResponse)); } else if (name.equals("EncodingType")) { - objectListing.setEncodingType(checkForEmptyString(getText())); + objectListing.setEncodingType( + shouldSDKDecodeResponse ? null : checkForEmptyString(getText())); } else if (name.equals("IsTruncated")) { - String isTruncatedStr = + final String isTruncatedStr = StringUtils.lowerCase(getText()); if (isTruncatedStr.startsWith("false")) { @@ -632,7 +755,7 @@ else if (in("ListBucketResult")) { else if (in("ListBucketResult", "Contents")) { if (name.equals("Key")) { lastKey = getText(); - currentObject.setKey(lastKey); + currentObject.setKey(decodeIfSpecified(lastKey, shouldSDKDecodeResponse)); } else if (name.equals("LastModified")) { currentObject.setLastModified( @@ -665,7 +788,8 @@ else if (in("ListBucketResult", "Contents", "Owner")) { else if (in("ListBucketResult", "CommonPrefixes")) { if (name.equals("Prefix")) { - objectListing.getCommonPrefixes().add(getText()); + objectListing.getCommonPrefixes() + .add(decodeIfSpecified(getText(), shouldSDKDecodeResponse)); } } } @@ -676,11 +800,16 @@ else if (in("ListBucketResult", "CommonPrefixes")) { */ public static class ListObjectsV2Handler extends AbstractHandler { private final ListObjectsV2Result result = new ListObjectsV2Result(); + private final boolean shouldSDKDecodeResponse; private S3ObjectSummary currentObject = null; private Owner currentOwner = null; private String lastKey = null; + public ListObjectsV2Handler(final boolean shouldSDKDecodeResponse) { + this.shouldSDKDecodeResponse = shouldSDKDecodeResponse; + } + public ListObjectsV2Result getResult() { return result; } @@ -744,7 +873,8 @@ else if (in("ListBucketResult")) { } } else if (name.equals("Prefix")) { - result.setPrefix(checkForEmptyString(getText())); + result.setPrefix(decodeIfSpecified(checkForEmptyString(getText()), + shouldSDKDecodeResponse)); } else if (name.equals("MaxKeys")) { result.setMaxKeys(parseInt(getText())); @@ -756,18 +886,19 @@ else if (in("ListBucketResult")) { result.setContinuationToken(getText()); } else if (name.equals("StartAfter")) { - result.setStartAfter(getText()); + result.setStartAfter(decodeIfSpecified(getText(), shouldSDKDecodeResponse)); } else if (name.equals("KeyCount")) { result.setKeyCount(parseInt(getText())); } else if (name.equals("Delimiter")) { - result.setDelimiter(checkForEmptyString(getText())); + result.setDelimiter(decodeIfSpecified(checkForEmptyString(getText()), + shouldSDKDecodeResponse)); } else if (name.equals("EncodingType")) { result.setEncodingType(checkForEmptyString(getText())); } else if (name.equals("IsTruncated")) { - String isTruncatedStr = StringUtils.lowerCase(getText()); + final String isTruncatedStr = StringUtils.lowerCase(getText()); if (isTruncatedStr.startsWith("false")) { result.setTruncated(false); @@ -788,7 +919,7 @@ else if (in("ListBucketResult")) { else if (in("ListBucketResult", "Contents")) { if (name.equals("Key")) { lastKey = getText(); - currentObject.setKey(lastKey); + currentObject.setKey(decodeIfSpecified(lastKey, shouldSDKDecodeResponse)); } else if (name.equals("LastModified")) { currentObject.setLastModified( ServiceUtils.parseIso8601Date(getText())); @@ -820,7 +951,8 @@ else if (in("ListBucketResult", "Contents", "Owner")) { else if (in("ListBucketResult", "CommonPrefixes")) { if (name.equals("Prefix")) { - result.getCommonPrefixes().add(getText()); + result.getCommonPrefixes() + .add(decodeIfSpecified(getText(), shouldSDKDecodeResponse)); } } } @@ -894,7 +1026,7 @@ else if (in("ListAllMyBucketsResult", "Buckets", "Bucket")) { currentBucket.setName(getText()); } else if (name.equals("CreationDate")) { - Date creationDate = DateUtils.parseISO8601Date(getText()); + final Date creationDate = DateUtils.parseISO8601Date(getText()); currentBucket.setCreationDate(creationDate); } } @@ -937,7 +1069,7 @@ protected void doStartElement( else if (in("AccessControlPolicy", "AccessControlList", "Grant")) { if (name.equals("Grantee")) { - String type = XmlResponsesSaxParser + final String type = XmlResponsesSaxParser .findAttributeValue("xsi:type", attrs); if ("AmazonCustomerByEmail".equals(type)) { @@ -1073,7 +1205,7 @@ protected void doStartElement( protected void doEndElement(String uri, String name, String qName) { if (atTopLevel()) { if (name.equals("LocationConstraint")) { - String elementText = getText(); + final String elementText = getText(); if (elementText.length() == 0) { location = null; } else { @@ -1085,7 +1217,7 @@ protected void doEndElement(String uri, String name, String qName) { } public static class CopyObjectResultHandler extends AbstractSSEHandler implements - ObjectExpirationResult { + ObjectExpirationResult, S3RequesterChargedResult, S3VersionResult { // Data items for successful copy private final CopyObjectResult result = new CopyObjectResult(); @@ -1106,10 +1238,12 @@ public Date getLastModified() { return result.getLastModifiedDate(); } + @Override public String getVersionId() { return result.getVersionId(); } + @Override public void setVersionId(String versionId) { result.setVersionId(versionId); } @@ -1158,6 +1292,16 @@ public boolean isErrorResponse() { return receivedErrorResponse; } + @Override + public boolean isRequesterCharged() { + return result.isRequesterCharged(); + } + + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + result.setRequesterCharged(isRequesterCharged); + } + @Override protected void doStartElement( String uri, @@ -1236,10 +1380,15 @@ protected void doEndElement(String uri, String name, String qName) { public static class ListVersionsHandler extends AbstractHandler { private final VersionListing versionListing = new VersionListing(); + private final boolean shouldSDKDecodeResponse; private S3VersionSummary currentVersionSummary; private Owner currentOwner; + public ListVersionsHandler(final boolean shouldSDKDecodeResponse) { + this.shouldSDKDecodeResponse = shouldSDKDecodeResponse; + } + public VersionListing getListing() { return versionListing; } @@ -1284,10 +1433,12 @@ protected void doEndElement( versionListing.setBucketName(getText()); } else if (name.equals("Prefix")) { - versionListing.setPrefix(checkForEmptyString(getText())); + versionListing.setPrefix(decodeIfSpecified(checkForEmptyString(getText()), + shouldSDKDecodeResponse)); } else if (name.equals("KeyMarker")) { - versionListing.setKeyMarker(checkForEmptyString(getText())); + versionListing.setKeyMarker(decodeIfSpecified(checkForEmptyString(getText()), + shouldSDKDecodeResponse)); } else if (name.equals("VersionIdMarker")) { versionListing.setVersionIdMarker(checkForEmptyString( @@ -1297,14 +1448,16 @@ protected void doEndElement( versionListing.setMaxKeys(Integer.parseInt(getText())); } else if (name.equals("Delimiter")) { - versionListing.setDelimiter(checkForEmptyString(getText())); + versionListing.setDelimiter(decodeIfSpecified(checkForEmptyString(getText()), + shouldSDKDecodeResponse)); } else if (name.equals("EncodingType")) { - versionListing.setEncodingType(checkForEmptyString( - getText())); + versionListing.setEncodingType( + shouldSDKDecodeResponse ? null : checkForEmptyString(getText())); } else if (name.equals("NextKeyMarker")) { - versionListing.setNextKeyMarker(getText()); + versionListing.setNextKeyMarker(decodeIfSpecified + (checkForEmptyString(getText()), shouldSDKDecodeResponse)); } else if (name.equals("NextVersionIdMarker")) { versionListing.setNextVersionIdMarker(getText()); @@ -1324,8 +1477,9 @@ protected void doEndElement( else if (in("ListVersionsResult", "CommonPrefixes")) { if (name.equals("Prefix")) { - versionListing.getCommonPrefixes() - .add(checkForEmptyString(getText())); + final String commonPrefix = checkForEmptyString(getText()); + versionListing.getCommonPrefixes().add(shouldSDKDecodeResponse + ? HttpUtils.urlDecode(commonPrefix) : commonPrefix); } } @@ -1333,7 +1487,9 @@ else if (in("ListVersionsResult", "Version") || in("ListVersionsResult", "DeleteMarker")) { if (name.equals("Key")) { - currentVersionSummary.setKey(getText()); + final String text = getText(); + currentVersionSummary + .setKey(decodeIfSpecified(text, shouldSDKDecodeResponse)); } else if (name.equals("VersionId")) { currentVersionSummary.setVersionId(getText()); @@ -1507,7 +1663,7 @@ protected void doEndElement(String uri, String name, String qName) { configuration.setStatus(getText()); } else if (name.equals("MfaDelete")) { - String mfaDeleteStatus = getText(); + final String mfaDeleteStatus = getText(); if (mfaDeleteStatus.equals("Disabled")) { configuration.setMfaDeleteEnabled(false); @@ -1550,22 +1706,26 @@ protected void doEndElement(String uri, String name, String qName) { } /* - * - * http://Example- - * Bucket.s3.amazonaws.com/Example-Object - * Example-Bucket Example-Object - * "3858f62230ac3c915f300c664312c11f-9" - * Or if an error occurred while - * completing: - * InternalError We encountered an internal error. - * Please try again. - * 656c76696e6727732072657175657374 - * Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== + * + * + * http://Example-Bucket.s3.amazonaws.com/Example-Object + * Example-Bucket + * Example-Object + * "3858f62230ac3c915f300c664312c11f-9" + * + * + * Or if an error occurred while completing: + * + * + * + * InternalError + * We encountered an internal error. Please try again. + * 656c76696e6727732072657175657374 + * Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== * */ public static class CompleteMultipartUploadHandler extends AbstractSSEHandler - implements ObjectExpirationResult { + implements ObjectExpirationResult, S3VersionResult, S3RequesterChargedResult { // Successful completion private CompleteMultipartUploadResult result; @@ -1616,6 +1776,36 @@ public void setExpirationTimeRuleId(String expirationTimeRuleId) { } } + @Override + public void setVersionId(String versionId) { + if (result != null) { + result.setVersionId(versionId); + } + } + + @Override + public String getVersionId() { + return result == null ? null : result.getVersionId(); + } + + /** + * @see com.amazonaws.services.s3.model.CompleteMultipartUploadResult#isRequesterCharged() + */ + @Override + public boolean isRequesterCharged() { + return result == null ? false : result.isRequesterCharged(); + } + + /** + * @see com.amazonaws.services.s3.model.CompleteMultipartUploadResult#setRequesterCharged(boolean) + */ + @Override + public void setRequesterCharged(boolean isRequesterCharged) { + if (result != null) { + result.setRequesterCharged(isRequesterCharged); + } + } + public CompleteMultipartUploadResult getCompleteMultipartUploadResult() { return result; } @@ -1677,11 +1867,12 @@ else if (in("Error")) { } /* - * - * example-bucket example-object - * VXBsb2FkIElEIGZvciA2aWWpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA - * + * + * + * example-bucket + * example-object + * VXBsb2FkIElEIGZvciA2aWWpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA + * */ public static class InitiateMultipartUploadHandler extends AbstractHandler { @@ -1718,36 +1909,62 @@ protected void doEndElement(String uri, String name, String qName) { } /* - * HTTP/1.1 200 OK x-amz-id-2: - * Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== - * x-amz-request-id: 656c76696e6727732072657175657374 Date: Tue, 16 Feb 2010 - * 20:34:56 GMT Content-Length: 1330 Connection: keep-alive Server: AmazonS3 - * bucket - * / - * - * my-movie.m2ts - * YW55IGlkZWEgd2h5IGVsdmluZydzIHVwbG9hZCBmYWlsZWQ - * 3 - * true my-divisor - * XMgbGlrZSBlbHZpbmcncyBub3QgaGF2aW5nIG11Y2ggbHVjaw - * b1d16700c70b0b05597d7acd6a3f92be - * delving - * STANDARD Tue, 26 Jan 2010 - * 19:42:19 GMT my-movie.m2ts - * VXBsb2FkIElEIGZvciBlbHZpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA - * b1d16700c70b0b05597d7acd6a3f92be - * delving - * STANDARD Tue, 16 Feb 2010 - * 20:34:56 GMT my-movie.m2ts - * YW55IGlkZWEgd2h5IGVsdmluZydzIHVwbG9hZCBmYWlsZWQ - * b1d16700c70b0b05597d7acd6a3f92be - * delving - * STANDARD Wed, 27 Jan 2010 - * 03:02:01 GMT - * photos/ - * videos/ + * HTTP/1.1 200 OK + * x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== + * x-amz-request-id: 656c76696e6727732072657175657374 + * Date: Tue, 16 Feb 2010 20:34:56 GMT + * Content-Length: 1330 + * Connection: keep-alive + * Server: AmazonS3 + * + * + * + * bucket + * + * / + * + * + * my-movie.m2ts + * YW55IGlkZWEgd2h5IGVsdmluZydzIHVwbG9hZCBmYWlsZWQ + * 3 + * true + * + * my-divisor + * XMgbGlrZSBlbHZpbmcncyBub3QgaGF2aW5nIG11Y2ggbHVjaw + * + * b1d16700c70b0b05597d7acd6a3f92be + * delving + * + * STANDARD + * Tue, 26 Jan 2010 19:42:19 GMT + * + * + * my-movie.m2ts + * VXBsb2FkIElEIGZvciBlbHZpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA + * + * b1d16700c70b0b05597d7acd6a3f92be + * delving + * + * STANDARD + * Tue, 16 Feb 2010 20:34:56 GMT + * + * + * my-movie.m2ts + * YW55IGlkZWEgd2h5IGVsdmluZydzIHVwbG9hZCBmYWlsZWQ + * + * b1d16700c70b0b05597d7acd6a3f92be + * delving + * + * STANDARD + * Wed, 27 Jan 2010 03:02:01 GMT + * + * + * photos/ + * + * + * videos/ + * + * */ public static class ListMultipartUploadsHandler extends AbstractHandler { @@ -1846,28 +2063,45 @@ else if (in("ListMultipartUploadsResult", "Upload", "Owner") } /* - * HTTP/1.1 200 OK x-amz-id-2: - * Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== - * x-amz-request-id: 656c76696e6727732072657175657374 Date: Tue, 16 Feb 2010 - * 20:34:56 GMT Content-Length: 985 Connection: keep-alive Server: AmazonS3 - * - * example-bucket example-object - * XXBsb2FkIElEIGZvciBlbHZpbmcncyVcdS1tb3ZpZS5tMnRzEEEwbG9hZA - * x1x16700c70b0b05597d7ecd6a3f92be - * username - * x1x16700c70b0b05597d7ecd6a3f92be - * username - * STANDARD - * 1 - * 3 2 - * true 2 - * Wed, 27 Jan 2010 03:02:03 GMT - * "7778aef83f66abc1fa1e8477f296d394" 10485760 - * 3 Wed, 27 Jan 2010 - * 03:02:02 GMT - * "aaaa18db4cc2f85cedef654fccc4a4x8" 10485760 - * + * HTTP/1.1 200 OK + * x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== + * x-amz-request-id: 656c76696e6727732072657175657374 + * Date: Tue, 16 Feb 2010 20:34:56 GMT + * Content-Length: 985 + * Connection: keep-alive + * Server: AmazonS3 + * + * + * + * example-bucket + * example-object + * XXBsb2FkIElEIGZvciBlbHZpbmcncyVcdS1tb3ZpZS5tMnRzEEEwbG9hZA + * + * x1x16700c70b0b05597d7ecd6a3f92be + * username + * + * + * x1x16700c70b0b05597d7ecd6a3f92be + * username + * + * STANDARD + * 1 + * 3 + * 2 + * true + * + * 2 + * Wed, 27 Jan 2010 03:02:03 GMT + * "7778aef83f66abc1fa1e8477f296d394" + * 10485760 + * + * + * 3 + * Wed, 27 Jan 2010 03:02:02 GMT + * "aaaa18db4cc2f85cedef654fccc4a4x8" + * 10485760 + * + * */ public static class ListPartsHandler extends AbstractHandler { @@ -1955,52 +2189,10 @@ else if (in("ListPartsResult", "Owner") private Integer parseInteger(String text) { text = checkForEmptyString(getText()); - if (text == null) + if (text == null) { return null; - return Integer.parseInt(text); - } - } - - public static class BucketNotificationConfigurationHandler extends AbstractHandler { - - private final BucketNotificationConfiguration configuration = - new BucketNotificationConfiguration(); - - private String topic; - private String event; - - public BucketNotificationConfiguration getConfiguration() { - return configuration; - } - - @Override - protected void doStartElement( - String uri, - String name, - String qName, - Attributes attrs) { - - } - - @Override - protected void doEndElement(String uri, String name, String qName) { - if (in("NotificationConfiguration", "TopicConfiguration")) { - if (name.equals("Topic")) { - topic = getText(); - } else if (name.equals("Event")) { - event = getText(); - } - } else if (in("NotificationConfiguration")) { - if (name.equals("TopicConfiguration")) { - if (topic != null && event != null) { - configuration.getTopicConfigurations() - .add(new TopicConfiguration(topic, event)); - } - - topic = null; - event = null; - } } + return Integer.parseInt(text); } } @@ -2010,22 +2202,22 @@ protected void doEndElement(String uri, String name, String qName) { * *

      * 
-     *  
-     *      replication-rule-1-1421862858808
-     *      testPrefix1
-     *      Enabled
-     *      
-     *          bucketARN
-     *      
-     *  
-     *  
-     *      replication-rule-2-1421862858808
-     *      testPrefix2
-     *      Disabled
-     *      
-     *          arn:aws:s3:::bucket-dest-replication-integ-test-1421862858808
-     *      
-     *  
+     * 	
+     *   	replication-rule-1-1421862858808
+     *   	testPrefix1
+     *   	Enabled
+     *   	
+     *       	bucketARN
+     *   	
+     *	
+     *	
+     *   	replication-rule-2-1421862858808
+     *   	testPrefix2
+     *   	Disabled
+     *   	
+     *       	arn:aws:s3:::bucket-dest-replication-integ-test-1421862858808
+     *   	
+     *	
      * 
      * 
*/ @@ -2157,19 +2349,97 @@ else if (in("Tagging", "TagSet", "Tag")) { } } + /** + * Handler for unmarshalling the response from GET Object Tagging. + * + * + * + * + * Foo + * 1 + * + * + * Bar + * 2 + * + * + * Baz + * 3 + * + * + * + */ + public static class GetObjectTaggingHandler extends AbstractHandler { + private GetObjectTaggingResult getObjectTaggingResult; + private List tagSet; + private String currentTagValue; + private String currentTagKey; + + public GetObjectTaggingResult getResult() { + return getObjectTaggingResult; + } + + @Override + protected void doStartElement(String uri, String name, String qName, Attributes attrs) { + if (in("Tagging")) { + if (name.equals("TagSet")) { + tagSet = new ArrayList(); + } + } + } + + @Override + protected void doEndElement(String uri, String name, String qName) { + if (in("Tagging")) { + if (name.equals("TagSet")) { + getObjectTaggingResult = new GetObjectTaggingResult(tagSet); + tagSet = null; + } + } + if (in("Tagging", "TagSet")) { + if (name.equals("Tag")) { + tagSet.add(new Tag(currentTagKey, currentTagValue)); + currentTagKey = null; + currentTagValue = null; + } + } else if (in("Tagging", "TagSet", "Tag")) { + if (name.equals("Key")) { + currentTagKey = getText(); + } else if (name.equals("Value")) { + currentTagValue = getText(); + } + } + } + } + /* - * HTTP/1.1 200 OK x-amz-id-2: - * Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== - * x-amz-request-id: 656c76696e6727732072657175657374 Date: Tue, 20 Sep 2012 - * 20:34:56 GMT Content-Type: application/xml Transfer-Encoding: chunked - * Connection: keep-alive Server: AmazonS3 Key - * Version Key - * Version Code - * Message Key - * true - * Version - * + HTTP/1.1 200 OK + x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== + x-amz-request-id: 656c76696e6727732072657175657374 + Date: Tue, 20 Sep 2012 20:34:56 GMT + Content-Type: application/xml + Transfer-Encoding: chunked + Connection: keep-alive + Server: AmazonS3 + + + + + Key + Version + + + Key + Version + Code + Message + + + Key + true + Version + + */ public static class DeleteObjectsHandler extends AbstractHandler { @@ -2244,24 +2514,77 @@ else if (in("DeleteResult", "Error")) { } } - /* - * HTTP/1.1 200 OK x-amz-id-2: - * Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== - * x-amz-request-id: 656c76696e6727732072657175657374 Date: Tue, 20 Sep 2012 - * 20:34:56 GMT Content-Length: xxx Connection: keep-alive Server: AmazonS3 - * logs-rule logs/ - * Enabled 30 - * GLACIER - * 365 - * 7 GLACIER - * - * 14 - * image-rule image/ - * Enabled - * 2012-12-31T00:00:00.000Z - * GLACIER - * 2020-12-31T00:00:00.000Z - * + /** + * HTTP/1.1 200 OK + x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== + x-amz-request-id: 656c76696e6727732072657175657374 + Date: Tue, 20 Sep 2012 20:34:56 GMT + Content-Length: xxx + Connection: keep-alive + Server: AmazonS3 + + + + logs-rule + logs/ + Enabled + + logs/ + + key1 + value1 + + + logs/ + + key1 + value1 + + + key1 + value1 + + + + + 30 + STANDARD_IA + + + 90 + GLACIER + + + 365 + + + 7 + STANDARD_IA + + + 14 + GLACIER + + + 365 + + + + image-rule + image/ + Enabled + + 2012-12-31T00:00:00.000Z + GLACIER + + + 2020-12-31T00:00:00.000Z + + + 10 + + + */ public static class BucketLifecycleConfigurationHandler extends AbstractHandler { @@ -2271,6 +2594,11 @@ public static class BucketLifecycleConfigurationHandler extends AbstractHandler private Rule currentRule; private Transition currentTransition; private NoncurrentVersionTransition currentNcvTransition; + private AbortIncompleteMultipartUpload abortIncompleteMultipartUpload; + private LifecycleFilter currentFilter; + private List andOperandsList; + private String currentTagKey; + private String currentTagValue; public BucketLifecycleConfiguration getConfiguration() { return configuration; @@ -2292,6 +2620,14 @@ protected void doStartElement( currentTransition = new Transition(); } else if (name.equals("NoncurrentVersionTransition")) { currentNcvTransition = new NoncurrentVersionTransition(); + } else if (name.equals("AbortIncompleteMultipartUpload")) { + abortIncompleteMultipartUpload = new AbortIncompleteMultipartUpload(); + } else if (name.equals("Filter")) { + currentFilter = new LifecycleFilter(); + } + } else if (in("LifecycleConfiguration", "Rule", "Filter")) { + if (name.equals("And")) { + andOperandsList = new ArrayList(); } } } @@ -2316,31 +2652,37 @@ else if (in("LifecycleConfiguration", "Rule")) { currentRule.setStatus(getText()); } else if (name.equals("Transition")) { - currentRule.setTransition(currentTransition); + currentRule.addTransition(currentTransition); currentTransition = null; } else if (name.equals("NoncurrentVersionTransition")) { - currentRule.setNoncurrentVersionTransition( + currentRule.addNoncurrentVersionTransition( currentNcvTransition); currentNcvTransition = null; + } else if (name.equals("AbortIncompleteMultipartUpload")) { + currentRule.setAbortIncompleteMultipartUpload(abortIncompleteMultipartUpload); + abortIncompleteMultipartUpload = null; + } else if (name.equals("Filter")) { + currentRule.setFilter(currentFilter); + currentFilter = null; } } else if (in("LifecycleConfiguration", "Rule", "Expiration")) { if (name.equals("Date")) { - currentRule.setExpirationDate( - ServiceUtils.parseIso8601Date(getText())); + currentRule.setExpirationDate(ServiceUtils.parseIso8601Date(getText())); } else if (name.equals("Days")) { - currentRule.setExpirationInDays( - Integer.parseInt(getText())); + currentRule.setExpirationInDays(Integer.parseInt(getText())); + } else if (name.equals("ExpiredObjectDeleteMarker")) { + if ("true".equals(getText())) { + currentRule.setExpiredObjectDeleteMarker(true); + } } } else if (in("LifecycleConfiguration", "Rule", "Transition")) { if (name.equals("StorageClass")) { - currentTransition.setStorageClass( - StorageClass.fromValue(getText())); - + currentTransition.setStorageClass(getText()); } else if (name.equals("Date")) { currentTransition.setDate( ServiceUtils.parseIso8601Date(getText())); @@ -2359,27 +2701,79 @@ else if (in("LifecycleConfiguration", "Rule", "NoncurrentVersionExpiration")) { else if (in("LifecycleConfiguration", "Rule", "NoncurrentVersionTransition")) { if (name.equals("StorageClass")) { - currentNcvTransition.setStorageClass( - StorageClass.fromValue(getText())); - + currentNcvTransition.setStorageClass(getText()); } else if (name.equals("NoncurrentDays")) { currentNcvTransition.setDays(Integer.parseInt(getText())); } } + + else if (in("LifecycleConfiguration", "Rule", "AbortIncompleteMultipartUpload")) { + if (name.equals("DaysAfterInitiation")) { + abortIncompleteMultipartUpload + .setDaysAfterInitiation(Integer.parseInt(getText())); + } + } + + else if (in("LifecycleConfiguration", "Rule", "Filter")) { + if (name.equals("Prefix")) { + currentFilter.setPredicate(new LifecyclePrefixPredicate(getText())); + } else if (name.equals("Tag")) { + currentFilter.setPredicate( + new LifecycleTagPredicate(new Tag(currentTagKey, currentTagValue))); + currentTagKey = null; + currentTagValue = null; + } else if (name.equals("And")) { + currentFilter.setPredicate(new LifecycleAndOperator(andOperandsList)); + andOperandsList = null; + } + } + + else if (in("LifecycleConfiguration", "Rule", "Filter", "Tag")) { + if (name.equals("Key")) { + currentTagKey = getText(); + } else if (name.equals("Value")) { + currentTagValue = getText(); + } + } + + else if (in("LifecycleConfiguration", "Rule", "Filter", "And")) { + if (name.equals("Prefix")) { + andOperandsList.add(new LifecyclePrefixPredicate(getText())); + } else if (name.equals("Tag")) { + andOperandsList.add( + new LifecycleTagPredicate(new Tag(currentTagKey, currentTagValue))); + currentTagKey = null; + currentTagValue = null; + } + } + + else if (in("LifecycleConfiguration", "Rule", "Filter", "And", "Tag")) { + if (name.equals("Key")) { + currentTagKey = getText(); + } else if (name.equals("Value")) { + currentTagValue = getText(); + } + } } } /* - * HTTP/1.1 200 OK x-amz-id-2: - * Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== - * x-amz-request-id: 656c76696e6727732072657175657374 Date: Tue, 20 Sep 2011 - * 20:34:56 GMT Content-Length: Some Length Connection: keep-alive Server: - * AmazonS3 - * http://www.foobar.com - * GET 3000 - * x-amz-server-side-encryption - * - */ + HTTP/1.1 200 OK + x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== + x-amz-request-id: 656c76696e6727732072657175657374 + Date: Tue, 20 Sep 2011 20:34:56 GMT + Content-Length: Some Length + Connection: keep-alive + Server: AmazonS3 + + + http://www.foobar.com + GET + 3000 + x-amz-server-side-encryption + + + */ public static class BucketCrossOriginConfigurationHandler extends AbstractHandler { private final BucketCrossOriginConfiguration configuration = @@ -2466,17 +2860,803 @@ protected void doEndElement(String uri, String name, String qName) { } } - private static String findAttributeValue( - String qnameToFind, - Attributes attrs) { + /* + HTTP/1.1 200 OK + x-amz-id-2: ITnGT1y4RyTmXa3rPi4hklTXouTf0hccUjo0iCPjz6FnfIutBj3M7fPGlWO2SEWp + x-amz-request-id: 51991C342C575321 + Date: Wed, 14 May 2014 02:11:22 GMT + Server: AmazonS3 + Content-Length: ... + + + + metrics-id + + + + logs-rule + logs/ + Enabled + + 30 + SOME_FUTURE_STORAGE_CLASS + + + 365 + + + 7 + SOME_FUTURE_STORAGE_CLASS + + + 14 + + + + prefix + + key1 + value1 + + + key2 + value2 + + + + + + + + + + + + + + + + + + + + + + key1 + value1 + + + + + + + + + prefix + + key1 + value1 + + + + + + + + + + + key0 + value0 + + + key1 + value1 + + + key2 + value2 + + + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/multiObjectDelete/testResponses/errorResponse.xml b/aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/multiObjectDelete/testResponses/errorResponse.xml new file mode 100644 index 0000000000..6bf7f71ecb --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/multiObjectDelete/testResponses/errorResponse.xml @@ -0,0 +1,12 @@ + + + + object1 + 123 + + + object2 + AccessDenied + Access Denied + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/multiObjectDelete/testResponses/fullResponse.xml b/aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/multiObjectDelete/testResponses/fullResponse.xml new file mode 100644 index 0000000000..503de3eae6 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/com/amazonaws/services/s3/multiObjectDelete/testResponses/fullResponse.xml @@ -0,0 +1,18 @@ + + + + key1 + Version1 + + + key2 + Version2 + Code + Message + + + key3 + true + Version3 + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseBasicTags.xml b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseBasicTags.xml new file mode 100644 index 0000000000..915b3d9dba --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseBasicTags.xml @@ -0,0 +1,6 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseChildTagError.xml b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseChildTagError.xml new file mode 100644 index 0000000000..8a02f98ab4 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseChildTagError.xml @@ -0,0 +1,7 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + TestErrorResponse + diff --git a/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseChildTags.xml b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseChildTags.xml new file mode 100644 index 0000000000..09769efad7 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseChildTags.xml @@ -0,0 +1,11 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + + AdditionalDetails2 + AdditionalDetails3 + AdditionalDetails4 + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseNoErrorTag.xml b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseNoErrorTag.xml new file mode 100644 index 0000000000..c0fa18ba15 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseNoErrorTag.xml @@ -0,0 +1,8 @@ +TestCode +TestMessage +TestRequestId +TestExtendedRequestId +AdditionalDetails1 +AdditionalDetails2 +AdditionalDetails3 +AdditionalDetails4 diff --git a/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseRepeatedXml.xml b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseRepeatedXml.xml new file mode 100644 index 0000000000..86b94fc3b8 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseRepeatedXml.xml @@ -0,0 +1,12 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + + + TestCode1 + TestMessage1 + TestRequestId1 + TestExtendedRequestId1 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseRootElementAsFoo.xml b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseRootElementAsFoo.xml new file mode 100644 index 0000000000..ad171804ae --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseRootElementAsFoo.xml @@ -0,0 +1,8 @@ + + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseWithAdditionalDetails.xml b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseWithAdditionalDetails.xml new file mode 100644 index 0000000000..49e7c63b21 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseWithAdditionalDetails.xml @@ -0,0 +1,10 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + AdditionalDetails1 + AdditionalDetails2 + AdditionalDetails3 + AdditionalDetails4 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseXMLNotProperFormat.xml b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseXMLNotProperFormat.xml new file mode 100644 index 0000000000..e5edd73689 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/errorResponse/ErrorResponseXMLNotProperFormat.xml @@ -0,0 +1,10 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + AdditionalDetails1 + AdditionalDetails2 + AdditionalDetails3 + AdditionalDetails4 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/marshalling/AnalyticsConfiguration.xml b/aws-android-sdk-s3/src/test/java/resources/marshalling/AnalyticsConfiguration.xml new file mode 100644 index 0000000000..af6055b514 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/marshalling/AnalyticsConfiguration.xml @@ -0,0 +1,25 @@ + + analytics-id + + + documents/ + + foo + bar + + + + + + V_1 + + + CSV + 123456789 + arn:aws:s3:::destination-bucket + destination-prefix + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/marshalling/InventoryConfiguration.xml b/aws-android-sdk-s3/src/test/java/resources/marshalling/InventoryConfiguration.xml new file mode 100644 index 0000000000..5fb6e096cf --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/marshalling/InventoryConfiguration.xml @@ -0,0 +1,24 @@ + + inventory-id + + + accountId + arn:aws:s3:::bucket + CSV + prefix + + + true + + prefix + + All + + LastModifiedDate + StorageClass + ReplicationStatus + + + Daily + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/marshalling/MetricsConfiguration.xml b/aws-android-sdk-s3/src/test/java/resources/marshalling/MetricsConfiguration.xml new file mode 100644 index 0000000000..00d1217082 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/marshalling/MetricsConfiguration.xml @@ -0,0 +1,16 @@ + + metrics-id + + + documents/ + + foo + bar + + + + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/marshalling/RestoreObjectWithTier.xml b/aws-android-sdk-s3/src/test/java/resources/marshalling/RestoreObjectWithTier.xml new file mode 100644 index 0000000000..319b1d1460 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/marshalling/RestoreObjectWithTier.xml @@ -0,0 +1,6 @@ + + 1 + + Bulk + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/unmarshalling/AnalyticsConfiguration.xml b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/AnalyticsConfiguration.xml new file mode 100644 index 0000000000..af6055b514 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/AnalyticsConfiguration.xml @@ -0,0 +1,25 @@ + + analytics-id + + + documents/ + + foo + bar + + + + + + V_1 + + + CSV + 123456789 + arn:aws:s3:::destination-bucket + destination-prefix + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/unmarshalling/InventoryConfiguration.xml b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/InventoryConfiguration.xml new file mode 100644 index 0000000000..5fb6e096cf --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/InventoryConfiguration.xml @@ -0,0 +1,24 @@ + + inventory-id + + + accountId + arn:aws:s3:::bucket + CSV + prefix + + + true + + prefix + + All + + LastModifiedDate + StorageClass + ReplicationStatus + + + Daily + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketAnalyticsConfigurationsResponse.xml b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketAnalyticsConfigurationsResponse.xml new file mode 100644 index 0000000000..0ff5cfe333 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketAnalyticsConfigurationsResponse.xml @@ -0,0 +1,51 @@ + + + XXX + + + documents/ + + foo + bar + + + + + + V_1 + + + CSV + 123456789 + arn:aws:s3:::destination-bucket + destination-prefix + + + + + + + YYY + + prefix + + + + V_2 + + + JSON + 123 + arn:aws:s3:::bucket2 + prefix2 + + + + + + true + token1 + token2 + + + diff --git a/aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketInventoryConfigurationsResponse.xml b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketInventoryConfigurationsResponse.xml new file mode 100644 index 0000000000..3bdc508095 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketInventoryConfigurationsResponse.xml @@ -0,0 +1,33 @@ + + + + + accountId + arn:aws:s3:::bucket + CSV + prefix + + + true + + prefix + + configId + All + + LastModifiedDate + StorageClass + ReplicationStatus + + + Daily + + + + + configId2 + + true + token1 + token2 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketMetricsConfigurationsResponse.xml b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketMetricsConfigurationsResponse.xml new file mode 100644 index 0000000000..d40446e682 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/ListBucketMetricsConfigurationsResponse.xml @@ -0,0 +1,26 @@ + + + id1 + + + documents/ + + foo + bar + + + + + + id2 + + + key + value + + + + true + token1 + token2 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/java/resources/unmarshalling/MetricsConfiguration.xml b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/MetricsConfiguration.xml new file mode 100644 index 0000000000..00d1217082 --- /dev/null +++ b/aws-android-sdk-s3/src/test/java/resources/unmarshalling/MetricsConfiguration.xml @@ -0,0 +1,16 @@ + + metrics-id + + + documents/ + + foo + bar + + + + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/CloudFunctionConfiguration.xml b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/CloudFunctionConfiguration.xml new file mode 100644 index 0000000000..154c4824f4 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/CloudFunctionConfiguration.xml @@ -0,0 +1,21 @@ + + + CloudFunctionConfigId + some-cloud-function-arn + some-cloud-invoc-role + SomeEvent1 + SomeEvent2 + + + + Prefix + some-prefix + + + Suffix + some-suffix + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/GetObjectTagsResponse.xml b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/GetObjectTagsResponse.xml new file mode 100644 index 0000000000..566bc526da --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/GetObjectTagsResponse.xml @@ -0,0 +1,16 @@ + + + + Foo + 1 + + + Bar + 2 + + + Baz + 3 + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/LambdaConfiguration.xml b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/LambdaConfiguration.xml new file mode 100644 index 0000000000..1236705c8b --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/LambdaConfiguration.xml @@ -0,0 +1,20 @@ + + + LambdaConfigId + some-lambda-function-arn + SomeEvent1 + SomeEvent2 + + + + Prefix + some-prefix + + + Suffix + some-suffix + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/QueueConfiguration.xml b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/QueueConfiguration.xml new file mode 100644 index 0000000000..2276ebce8a --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/QueueConfiguration.xml @@ -0,0 +1,20 @@ + + + QueueConfigId + some-queue-arn + SomeEvent1 + SomeEvent2 + + + + Prefix + some-prefix + + + Suffix + some-suffix + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/TopicConfiguration.xml b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/TopicConfiguration.xml new file mode 100644 index 0000000000..d219074a8b --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/TopicConfiguration.xml @@ -0,0 +1,20 @@ + + + TopicConfigId + some-topic-arn + SomeEvent1 + SomeEvent2 + + + + Prefix + some-prefix + + + Suffix + some-suffix + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/lifecycle-configuration-unknown-storage-class.xml b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/lifecycle-configuration-unknown-storage-class.xml new file mode 100644 index 0000000000..288b7d2514 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/com/amazonaws/services/s3/model/transform/lifecycle-configuration-unknown-storage-class.xml @@ -0,0 +1,92 @@ + + + + logs-rule + logs/ + Enabled + + 30 + SOME_FUTURE_STORAGE_CLASS + + + 365 + + + 7 + SOME_FUTURE_STORAGE_CLASS + + + 14 + + + + prefix + + key1 + value1 + + + key2 + value2 + + + + + + + + + + + + + + + + + + + + + + key1 + value1 + + + + + + + + + prefix + + key1 + value1 + + + + + + + + + + + key0 + value0 + + + key1 + value1 + + + key2 + value2 + + + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseBasicTags.xml b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseBasicTags.xml new file mode 100644 index 0000000000..915b3d9dba --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseBasicTags.xml @@ -0,0 +1,6 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseChildTagError.xml b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseChildTagError.xml new file mode 100644 index 0000000000..8a02f98ab4 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseChildTagError.xml @@ -0,0 +1,7 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + TestErrorResponse + diff --git a/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseChildTags.xml b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseChildTags.xml new file mode 100644 index 0000000000..09769efad7 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseChildTags.xml @@ -0,0 +1,11 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + + AdditionalDetails2 + AdditionalDetails3 + AdditionalDetails4 + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseNoErrorTag.xml b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseNoErrorTag.xml new file mode 100644 index 0000000000..c0fa18ba15 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseNoErrorTag.xml @@ -0,0 +1,8 @@ +TestCode +TestMessage +TestRequestId +TestExtendedRequestId +AdditionalDetails1 +AdditionalDetails2 +AdditionalDetails3 +AdditionalDetails4 diff --git a/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseRepeatedXml.xml b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseRepeatedXml.xml new file mode 100644 index 0000000000..86b94fc3b8 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseRepeatedXml.xml @@ -0,0 +1,12 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + + + TestCode1 + TestMessage1 + TestRequestId1 + TestExtendedRequestId1 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseRootElementAsFoo.xml b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseRootElementAsFoo.xml new file mode 100644 index 0000000000..ad171804ae --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseRootElementAsFoo.xml @@ -0,0 +1,8 @@ + + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseWithAdditionalDetails.xml b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseWithAdditionalDetails.xml new file mode 100644 index 0000000000..49e7c63b21 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseWithAdditionalDetails.xml @@ -0,0 +1,10 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + AdditionalDetails1 + AdditionalDetails2 + AdditionalDetails3 + AdditionalDetails4 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseXMLNotProperFormat.xml b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseXMLNotProperFormat.xml new file mode 100644 index 0000000000..e5edd73689 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/errorResponse/ErrorResponseXMLNotProperFormat.xml @@ -0,0 +1,10 @@ + + TestCode + TestMessage + TestRequestId + TestExtendedRequestId + AdditionalDetails1 + AdditionalDetails2 + AdditionalDetails3 + AdditionalDetails4 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/marshalling/AnalyticsConfiguration.xml b/aws-android-sdk-s3/src/test/resources/resources/marshalling/AnalyticsConfiguration.xml new file mode 100644 index 0000000000..af6055b514 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/marshalling/AnalyticsConfiguration.xml @@ -0,0 +1,25 @@ + + analytics-id + + + documents/ + + foo + bar + + + + + + V_1 + + + CSV + 123456789 + arn:aws:s3:::destination-bucket + destination-prefix + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/marshalling/InventoryConfiguration.xml b/aws-android-sdk-s3/src/test/resources/resources/marshalling/InventoryConfiguration.xml new file mode 100644 index 0000000000..5fb6e096cf --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/marshalling/InventoryConfiguration.xml @@ -0,0 +1,24 @@ + + inventory-id + + + accountId + arn:aws:s3:::bucket + CSV + prefix + + + true + + prefix + + All + + LastModifiedDate + StorageClass + ReplicationStatus + + + Daily + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/marshalling/MetricsConfiguration.xml b/aws-android-sdk-s3/src/test/resources/resources/marshalling/MetricsConfiguration.xml new file mode 100644 index 0000000000..00d1217082 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/marshalling/MetricsConfiguration.xml @@ -0,0 +1,16 @@ + + metrics-id + + + documents/ + + foo + bar + + + + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/marshalling/RestoreObjectWithTier.xml b/aws-android-sdk-s3/src/test/resources/resources/marshalling/RestoreObjectWithTier.xml new file mode 100644 index 0000000000..319b1d1460 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/marshalling/RestoreObjectWithTier.xml @@ -0,0 +1,6 @@ + + 1 + + Bulk + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/AnalyticsConfiguration.xml b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/AnalyticsConfiguration.xml new file mode 100644 index 0000000000..af6055b514 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/AnalyticsConfiguration.xml @@ -0,0 +1,25 @@ + + analytics-id + + + documents/ + + foo + bar + + + + + + V_1 + + + CSV + 123456789 + arn:aws:s3:::destination-bucket + destination-prefix + + + + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/InventoryConfiguration.xml b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/InventoryConfiguration.xml new file mode 100644 index 0000000000..5fb6e096cf --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/InventoryConfiguration.xml @@ -0,0 +1,24 @@ + + inventory-id + + + accountId + arn:aws:s3:::bucket + CSV + prefix + + + true + + prefix + + All + + LastModifiedDate + StorageClass + ReplicationStatus + + + Daily + + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketAnalyticsConfigurationsResponse.xml b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketAnalyticsConfigurationsResponse.xml new file mode 100644 index 0000000000..0ff5cfe333 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketAnalyticsConfigurationsResponse.xml @@ -0,0 +1,51 @@ + + + XXX + + + documents/ + + foo + bar + + + + + + V_1 + + + CSV + 123456789 + arn:aws:s3:::destination-bucket + destination-prefix + + + + + + + YYY + + prefix + + + + V_2 + + + JSON + 123 + arn:aws:s3:::bucket2 + prefix2 + + + + + + true + token1 + token2 + + + diff --git a/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketInventoryConfigurationsResponse.xml b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketInventoryConfigurationsResponse.xml new file mode 100644 index 0000000000..3bdc508095 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketInventoryConfigurationsResponse.xml @@ -0,0 +1,33 @@ + + + + + accountId + arn:aws:s3:::bucket + CSV + prefix + + + true + + prefix + + configId + All + + LastModifiedDate + StorageClass + ReplicationStatus + + + Daily + + + + + configId2 + + true + token1 + token2 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketMetricsConfigurationsResponse.xml b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketMetricsConfigurationsResponse.xml new file mode 100644 index 0000000000..d40446e682 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/ListBucketMetricsConfigurationsResponse.xml @@ -0,0 +1,26 @@ + + + id1 + + + documents/ + + foo + bar + + + + + + id2 + + + key + value + + + + true + token1 + token2 + \ No newline at end of file diff --git a/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/MetricsConfiguration.xml b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/MetricsConfiguration.xml new file mode 100644 index 0000000000..00d1217082 --- /dev/null +++ b/aws-android-sdk-s3/src/test/resources/resources/unmarshalling/MetricsConfiguration.xml @@ -0,0 +1,16 @@ + + metrics-id + + + documents/ + + foo + bar + + + + + + + + \ No newline at end of file diff --git a/aws-android-sdk-sdb/pom.xml b/aws-android-sdk-sdb/pom.xml index 2484ea1d30..32138af01b 100644 --- a/aws-android-sdk-sdb/pom.xml +++ b/aws-android-sdk-sdb/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,7 +20,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 junit diff --git a/aws-android-sdk-ses/pom.xml b/aws-android-sdk-ses/pom.xml index 8588f03cd7..018a79f15e 100644 --- a/aws-android-sdk-ses/pom.xml +++ b/aws-android-sdk-ses/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,7 +20,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 diff --git a/aws-android-sdk-sns/pom.xml b/aws-android-sdk-sns/pom.xml index fced59e6a4..2030cf6d78 100644 --- a/aws-android-sdk-sns/pom.xml +++ b/aws-android-sdk-sns/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,13 +20,13 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 com.amazonaws aws-android-sdk-sqs false - 2.3.9 + 2.4.0 diff --git a/aws-android-sdk-sqs/pom.xml b/aws-android-sdk-sqs/pom.xml index 45fd97afa2..859ff57d68 100644 --- a/aws-android-sdk-sqs/pom.xml +++ b/aws-android-sdk-sqs/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.3.9 + 2.4.0 @@ -20,7 +20,7 @@ com.amazonaws aws-android-sdk-core false - 2.3.9 + 2.4.0 diff --git a/pom.xml b/pom.xml index 47b2021a09..deece71ef2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ aws-android-sdk-pom pom AWS SDK for Android - 2.3.9 + 2.4.0 The Amazon Web Services SDK for Android provides Android APIs for building software on AWS’ cost-effective, scalable, and reliable infrastructure products. The AWS SDK for Android allows developers to code against APIs for all of Amazon's infrastructure web services (Amazon S3, Amazon EC2, Amazon SQS, Auto Scaling, etc). http://aws.amazon.com/sdkforandroid