/* * Copyright 2010-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; import static com.amazonaws.util.LengthCheckInputStream.EXCLUDE_SKIPPED_BYTES; import static com.amazonaws.util.LengthCheckInputStream.INCLUDE_SKIPPED_BYTES; import com.amazonaws.AbortedException; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.AmazonServiceException.ErrorType; import com.amazonaws.AmazonWebServiceClient; import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.AmazonWebServiceResponse; import com.amazonaws.ClientConfiguration; import com.amazonaws.DefaultRequest; 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; import com.amazonaws.auth.Presigner; import com.amazonaws.auth.Signer; import com.amazonaws.auth.SignerFactory; import com.amazonaws.event.ProgressEvent; import com.amazonaws.event.ProgressListener; import com.amazonaws.event.ProgressListenerCallbackExecutor; import com.amazonaws.event.ProgressReportingInputStream; import com.amazonaws.handlers.HandlerChainFactory; import com.amazonaws.handlers.RequestHandler2; import com.amazonaws.http.ExecutionContext; import com.amazonaws.http.HttpClient; import com.amazonaws.http.HttpMethodName; import com.amazonaws.http.HttpResponseHandler; import com.amazonaws.http.UrlHttpClient; import com.amazonaws.internal.StaticCredentialsProvider; import com.amazonaws.metrics.AwsSdkMetrics; import com.amazonaws.metrics.RequestMetricCollector; import com.amazonaws.regions.RegionUtils; import com.amazonaws.services.s3.internal.AWSS3V4Signer; import com.amazonaws.services.s3.internal.BucketNameUtils; import com.amazonaws.services.s3.internal.Constants; import com.amazonaws.services.s3.internal.DeleteObjectsResponse; import com.amazonaws.services.s3.internal.DigestValidationInputStream; import com.amazonaws.services.s3.internal.InputSubstream; import com.amazonaws.services.s3.internal.MD5DigestCalculatingInputStream; import com.amazonaws.services.s3.internal.ObjectExpirationHeaderHandler; import com.amazonaws.services.s3.internal.RepeatableFileInputStream; import com.amazonaws.services.s3.internal.ResponseHeaderHandlerChain; import com.amazonaws.services.s3.internal.S3ErrorResponseHandler; import com.amazonaws.services.s3.internal.S3ExecutionContext; 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.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.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.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.transform.AclXmlFactory; import com.amazonaws.services.s3.model.transform.BucketConfigurationXmlFactory; import com.amazonaws.services.s3.model.transform.HeadBucketResultHandler; import com.amazonaws.services.s3.model.transform.MultiObjectDeleteXmlFactory; import com.amazonaws.services.s3.model.transform.RequestPaymentConfigurationXmlFactory; import com.amazonaws.services.s3.model.transform.RequestXmlFactory; import com.amazonaws.services.s3.model.transform.Unmarshallers; import com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.CompleteMultipartUploadHandler; import com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.CopyObjectResultHandler; import com.amazonaws.services.s3.util.Mimetypes; import com.amazonaws.transform.Unmarshaller; import com.amazonaws.util.AWSRequestMetrics; import com.amazonaws.util.AWSRequestMetrics.Field; import com.amazonaws.util.AwsHostNameUtils; import com.amazonaws.util.Base64; import com.amazonaws.util.BinaryUtils; import com.amazonaws.util.DateUtils; import com.amazonaws.util.HttpUtils; import com.amazonaws.util.LengthCheckInputStream; import com.amazonaws.util.Md5Utils; import com.amazonaws.util.ServiceClientHolderInputStream; import com.amazonaws.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; /** * <p> * Provides the client for accessing the Amazon S3 web service. * </p> * <p> * Amazon S3 provides storage for the Internet, and is designed to make * web-scale computing easier for developers. * </p> * <p> * The Amazon S3 Java Client provides a simple interface that can be used to * store and retrieve any amount of data, at any time, from anywhere on the web. * It gives any developer access to the same highly scalable, reliable, secure, * fast, inexpensive infrastructure that Amazon uses to run its own global * network of web sites. The service aims to maximize benefits of scale and to * pass those benefits on to developers. * </p> * <p> * For more information about Amazon S3, please see <a * href="http://aws.amazon.com/s3"> http://aws.amazon.com/s3</a> * </p> */ public class AmazonS3Client extends AmazonWebServiceClient implements AmazonS3 { public static final String S3_SERVICE_NAME = "s3"; private static final String S3_SIGNER = "S3SignerType"; private static final String S3_V4_SIGNER = "AWSS3V4SignerType"; /** Shared logger for client events */ private static Log log = LogFactory.getLog(AmazonS3Client.class); static { // Enable S3 specific predefined request metrics. AwsSdkMetrics.addAll(Arrays.asList(S3ServiceMetric.values())); // Register S3-specific signers. SignerFactory.registerSigner(S3_SIGNER, S3Signer.class); SignerFactory.registerSigner(S3_V4_SIGNER, AWSS3V4Signer.class); } /** Responsible for handling error responses from all S3 service calls. */ private final S3ErrorResponseHandler errorResponseHandler = new S3ErrorResponseHandler(); /** Shared response handler for operations with no response. */ private final S3XmlResponseHandler<Void> voidResponseHandler = new S3XmlResponseHandler<Void>( null); /** Shared factory for converting configuration objects to XML */ private static final BucketConfigurationXmlFactory bucketConfigurationXmlFactory = new BucketConfigurationXmlFactory(); /** * Shared factory for converting request payment configuration objects to * XML */ private static final RequestPaymentConfigurationXmlFactory requestPaymentConfigurationXmlFactory = new RequestPaymentConfigurationXmlFactory(); /** S3 specific client configuration options */ private S3ClientOptions clientOptions = new S3ClientOptions(); /** Provider for AWS credentials. */ private final AWSCredentialsProvider awsCredentialsProvider; /** * The S3 client region that is set by either (a) calling * setRegion/configureRegion OR (b) calling setEndpoint with a * region-specific S3 endpoint. This region string will be used for signing * requests sent by this client. */ volatile String clientRegion; /** * Constructs a new client to invoke service methods on Amazon S3. A * credentials provider chain will be used that searches for credentials in * this order: * <ul> * <li>Environment Variables - AWS_ACCESS_KEY_ID and AWS_SECRET_KEY</li> * <li>Java System Properties - aws.accessKeyId and aws.secretKey</li> * <li>Credential profiles file at the default location (~/.aws/credentials) * shared by all AWS SDKs and the AWS CLI</li> * <li>Instance Profile Credentials - delivered through the Amazon EC2 * metadata service</li> * </ul> * <p> * If no credentials are found in the chain, this client will attempt to * work in an anonymous mode where requests aren't signed. Only a subset of * the Amazon S3 API will work with anonymous <i>(i.e. unsigned)</i> * requests, but this can prove useful in some situations. For example: * <ul> * <li>If an Amazon S3 bucket has {@link Permission#Read} permission for the * {@link GroupGrantee#AllUsers} group, anonymous clients can call * {@link #listObjects(String)} to see what objects are stored in a bucket.</li> * <li>If an object has {@link Permission#Read} permission for the * {@link GroupGrantee#AllUsers} group, anonymous clients can call * {@link #getObject(String, String)} and * {@link #getObjectMetadata(String, String)} to pull object content and * metadata.</li> * <li>If a bucket has {@link Permission#Write} permission for the * {@link GroupGrantee#AllUsers} group, anonymous clients can upload objects * to the bucket.</li> * </ul> * </p> * <p> * You can force the client to operate in an anonymous mode, and skip the * credentials provider chain, by passing in <code>null</code> for the * credentials. * </p> * * @see AmazonS3Client#AmazonS3Client(AWSCredentials) * @see AmazonS3Client#AmazonS3Client(AWSCredentials, ClientConfiguration) */ @Deprecated public AmazonS3Client() { this(new DefaultAWSCredentialsProviderChain()); } /** * Constructs a new Amazon S3 client using the specified AWS credentials to * access Amazon S3. * * @param awsCredentials The AWS credentials to use when making requests to * Amazon S3 with this client. * @see AmazonS3Client#AmazonS3Client() * @see AmazonS3Client#AmazonS3Client(AWSCredentials, ClientConfiguration) */ public AmazonS3Client(AWSCredentials awsCredentials) { this(awsCredentials, new ClientConfiguration()); } /** * Constructs a new Amazon S3 client using the specified AWS credentials and * client configuration to access Amazon S3. * * @param awsCredentials The AWS credentials to use when making requests to * Amazon S3 with this client. * @param clientConfiguration The client configuration options controlling * how this client connects to Amazon S3 (e.g. proxy settings, * retry counts, etc). * @see AmazonS3Client#AmazonS3Client() * @see AmazonS3Client#AmazonS3Client(AWSCredentials) */ public AmazonS3Client(AWSCredentials awsCredentials, ClientConfiguration clientConfiguration) { this(new StaticCredentialsProvider(awsCredentials), clientConfiguration); } /** * Constructs a new Amazon S3 client using the specified AWS credentials * provider to access Amazon S3. * * @param credentialsProvider The AWS credentials provider which will * provide credentials to authenticate requests with AWS * services. */ public AmazonS3Client(AWSCredentialsProvider credentialsProvider) { this(credentialsProvider, new ClientConfiguration()); } /** * Constructs a new Amazon S3 client using the specified AWS credentials and * client configuration to access Amazon S3. * * @param credentialsProvider The AWS credentials provider which will * provide credentials to authenticate requests with AWS * services. * @param clientConfiguration The client configuration options controlling * how this client connects to Amazon S3 (e.g. proxy settings, * retry counts, etc). */ public AmazonS3Client(AWSCredentialsProvider credentialsProvider, ClientConfiguration clientConfiguration) { this(credentialsProvider, clientConfiguration, new UrlHttpClient(clientConfiguration)); } /** * Constructs a new Amazon S3 client using the specified AWS credentials, * client configuration and request metric collector to access Amazon S3. * * @param credentialsProvider The AWS credentials provider which will * provide credentials to authenticate requests with AWS * services. * @param clientConfiguration The client configuration options controlling * how this client connects to Amazon S3 (e.g. proxy settings, * retry counts, etc). * @param requestMetricCollector request metric collector */ @Deprecated public AmazonS3Client(AWSCredentialsProvider credentialsProvider, ClientConfiguration clientConfiguration, RequestMetricCollector requestMetricCollector) { super(clientConfiguration, new UrlHttpClient(clientConfiguration), requestMetricCollector); this.awsCredentialsProvider = credentialsProvider; init(); } /** * Constructs a new Amazon S3 client using the specified AWS credentials, * client configuration and request metric collector to access Amazon S3. * * @param credentialsProvider The AWS credentials provider which will * provide credentials to authenticate requests with AWS * services. * @param httpClient * @param clientConfiguration The client configuration options controlling * how this client connects to Amazon S3 (e.g. proxy settings, * retry counts, etc). * @param requestMetricCollector request metric collector */ public AmazonS3Client(AWSCredentialsProvider credentialsProvider, ClientConfiguration clientConfiguration, HttpClient httpClient) { super(clientConfiguration, httpClient); this.awsCredentialsProvider = credentialsProvider; init(); } /** * Constructs a new client using the specified client configuration to * access Amazon S3. A credentials provider chain will be used that searches * for credentials in this order: * <ul> * <li>Environment Variables - AWS_ACCESS_KEY_ID and AWS_SECRET_KEY</li> * <li>Java System Properties - aws.accessKeyId and aws.secretKey</li> * <li>Instance Profile Credentials - delivered through the Amazon EC2 * metadata service</li> * </ul> * <p> * If no credentials are found in the chain, this client will attempt to * work in an anonymous mode where requests aren't signed. Only a subset of * the Amazon S3 API will work with anonymous <i>(i.e. unsigned)</i> * requests, but this can prove useful in some situations. For example: * <ul> * <li>If an Amazon S3 bucket has {@link Permission#Read} permission for the * {@link GroupGrantee#AllUsers} group, anonymous clients can call * {@link #listObjects(String)} to see what objects are stored in a bucket.</li> * <li>If an object has {@link Permission#Read} permission for the * {@link GroupGrantee#AllUsers} group, anonymous clients can call * {@link #getObject(String, String)} and * {@link #getObjectMetadata(String, String)} to pull object content and * metadata.</li> * <li>If a bucket has {@link Permission#Write} permission for the * {@link GroupGrantee#AllUsers} group, anonymous clients can upload objects * to the bucket.</li> * </ul> * </p> * <p> * You can force the client to operate in an anonymous mode, and skip the * credentials provider chain, by passing in <code>null</code> for the * credentials. * </p> * * @param clientConfiguration The client configuration options controlling * how this client connects to Amazon S3 (e.g. proxy settings, * retry counts, etc). * @see AmazonS3Client#AmazonS3Client(AWSCredentials) * @see AmazonS3Client#AmazonS3Client(AWSCredentials, ClientConfiguration) */ public AmazonS3Client(ClientConfiguration clientConfiguration) { this(new DefaultAWSCredentialsProviderChain(), clientConfiguration); } private void init() { // calling this.setEndpoint(...) will also modify the signer accordingly setEndpoint(Constants.S3_HOSTNAME); HandlerChainFactory chainFactory = new HandlerChainFactory(); requestHandler2s.addAll(chainFactory.newRequestHandlerChain( "/com/amazonaws/services/s3/request.handlers")); requestHandler2s.addAll(chainFactory.newRequestHandler2Chain( "/com/amazonaws/services/s3/request.handler2s")); } @Override public void setEndpoint(String endpoint) { if (endpoint.endsWith(Constants.S3_ACCELERATE_HOSTNAME)) { throw new IllegalStateException( "To enable accelerate mode, please use AmazonS3Client.setS3ClientOptions(S3ClientOptions.builder().setAccelerateModeEnabled(true).build());"); } super.setEndpoint(endpoint); /* * Extract the region string from the endpoint if it's not known to be a * global S3 endpoint. */ if (!endpoint.endsWith(Constants.S3_HOSTNAME)) { clientRegion = AwsHostNameUtils.parseRegionName(this.endpoint.getHost(), S3_SERVICE_NAME); } } @Override public void setRegion(com.amazonaws.regions.Region region) { super.setRegion(region); /* * We need to preserve the user provided region. This is because the * region might be mapped to a global s3 endpoint (e.g. when the client * is in accelerate mode), in which case we won't be able to extract the * region back from the endpoint during request signing phase. */ clientRegion = region.getName(); } /** * <p> * Override the default S3 client options for this client. Also set the * endpoint to s3-accelerate if such is specified in the S3 client options. * </p> * * @param clientOptions The S3 client options to use. */ @Override public void setS3ClientOptions(S3ClientOptions clientOptions) { this.clientOptions = new S3ClientOptions(clientOptions); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#listNextBatchOfVersions(com.amazonaws * .services.s3.model.S3VersionListing) */ @Override public VersionListing listNextBatchOfVersions(VersionListing previousVersionListing) throws AmazonClientException, AmazonServiceException { assertParameterNotNull( previousVersionListing, "The previous version listing parameter must be specified when listing the next batch of versions in a bucket"); if (!previousVersionListing.isTruncated()) { VersionListing emptyListing = new VersionListing(); emptyListing.setBucketName(previousVersionListing.getBucketName()); emptyListing.setDelimiter(previousVersionListing.getDelimiter()); emptyListing.setKeyMarker(previousVersionListing.getNextKeyMarker()); emptyListing.setVersionIdMarker(previousVersionListing.getNextVersionIdMarker()); emptyListing.setMaxKeys(previousVersionListing.getMaxKeys()); emptyListing.setPrefix(previousVersionListing.getPrefix()); emptyListing.setEncodingType(previousVersionListing.getEncodingType()); emptyListing.setTruncated(false); return emptyListing; } return listVersions(new ListVersionsRequest( previousVersionListing.getBucketName(), previousVersionListing.getPrefix(), previousVersionListing.getNextKeyMarker(), previousVersionListing.getNextVersionIdMarker(), previousVersionListing.getDelimiter(), new Integer(previousVersionListing.getMaxKeys())) .withEncodingType(previousVersionListing.getEncodingType())); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#listVersions(java.lang.String, * java.lang.String) */ @Override public VersionListing listVersions(String bucketName, String prefix) throws AmazonClientException, AmazonServiceException { return listVersions(new ListVersionsRequest(bucketName, prefix, null, null, null, null)); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#listVersions(java.lang.String, * java.lang.String, java.lang.String, java.lang.String, java.lang.String, * java.lang.Integer) */ @Override public VersionListing listVersions(String bucketName, String prefix, String keyMarker, String versionIdMarker, String delimiter, Integer maxKeys) throws AmazonClientException, AmazonServiceException { ListVersionsRequest request = new ListVersionsRequest() .withBucketName(bucketName) .withPrefix(prefix) .withDelimiter(delimiter) .withKeyMarker(keyMarker) .withVersionIdMarker(versionIdMarker) .withMaxResults(maxKeys); return listVersions(request); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#listVersions(com.amazonaws.services * .s3.model.ListVersionsRequest) */ @Override public VersionListing listVersions(ListVersionsRequest listVersionsRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(listVersionsRequest.getBucketName(), "The bucket name parameter must be specified when listing versions in a bucket"); Request<ListVersionsRequest> 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()); if (listVersionsRequest.getMaxResults() != null && listVersionsRequest.getMaxResults().intValue() >= 0) request.addParameter("max-keys", listVersionsRequest.getMaxResults().toString()); if (listVersionsRequest.getEncodingType() != null) request.addParameter("encoding-type", listVersionsRequest.getEncodingType()); return invoke(request, new Unmarshallers.VersionListUnmarshaller(), listVersionsRequest.getBucketName(), null); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#listObjects(java.lang.String) */ @Override public ObjectListing listObjects(String bucketName) throws AmazonClientException, AmazonServiceException { return listObjects(new ListObjectsRequest(bucketName, null, null, null, null)); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#listObjects(java.lang.String, * java.lang.String) */ @Override public ObjectListing listObjects(String bucketName, String prefix) throws AmazonClientException, AmazonServiceException { return listObjects(new ListObjectsRequest(bucketName, prefix, null, null, null)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#listObjects(com.amazonaws.services * .s3.model.ListObjectsRequest) */ @Override public ObjectListing listObjects(ListObjectsRequest listObjectsRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(listObjectsRequest.getBucketName(), "The bucket name parameter must be specified when listing objects in a bucket"); Request<ListObjectsRequest> 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()); if (listObjectsRequest.getMaxKeys() != null && 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(), listObjectsRequest.getBucketName(), null); } @Override public ListObjectsV2Result listObjectsV2(String bucketName) throws AmazonClientException, AmazonServiceException { return listObjectsV2(new ListObjectsV2Request().withBucketName(bucketName)); } @Override public ListObjectsV2Result listObjectsV2(String bucketName, String prefix) throws AmazonClientException, AmazonServiceException { return listObjectsV2( new ListObjectsV2Request().withBucketName(bucketName).withPrefix(prefix)); } @Override public ListObjectsV2Result listObjectsV2(ListObjectsV2Request listObjectsV2Request) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(listObjectsV2Request.getBucketName(), "The bucket name parameter must be specified when listing objects in a bucket"); Request<ListObjectsV2Request> 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", 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(), listObjectsV2Request.getBucketName(), null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#listNextBatchOfObjects(com.amazonaws * .services.s3.model.S3ObjectListing) */ @Override public ObjectListing listNextBatchOfObjects(ObjectListing previousObjectListing) throws AmazonClientException, AmazonServiceException { assertParameterNotNull( previousObjectListing, "The previous object listing parameter must be specified when listing the next batch of objects in a bucket"); if (!previousObjectListing.isTruncated()) { ObjectListing emptyListing = new ObjectListing(); emptyListing.setBucketName(previousObjectListing.getBucketName()); emptyListing.setDelimiter(previousObjectListing.getDelimiter()); emptyListing.setMarker(previousObjectListing.getNextMarker()); emptyListing.setMaxKeys(previousObjectListing.getMaxKeys()); emptyListing.setPrefix(previousObjectListing.getPrefix()); emptyListing.setEncodingType(previousObjectListing.getEncodingType()); emptyListing.setTruncated(false); return emptyListing; } return listObjects(new ListObjectsRequest( previousObjectListing.getBucketName(), previousObjectListing.getPrefix(), previousObjectListing.getNextMarker(), previousObjectListing.getDelimiter(), new Integer(previousObjectListing.getMaxKeys())) .withEncodingType(previousObjectListing.getEncodingType())); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#getS3AccountOwner() */ @Override public Owner getS3AccountOwner() throws AmazonClientException, AmazonServiceException { Request<ListBucketsRequest> request = createRequest(null, null, new ListBucketsRequest(), HttpMethodName.GET); return invoke(request, new Unmarshallers.ListBucketsOwnerUnmarshaller(), null, null); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#listBuckets() */ @Override public List<Bucket> listBuckets(ListBucketsRequest listBucketsRequest) throws AmazonClientException, AmazonServiceException { Request<ListBucketsRequest> request = createRequest(null, null, listBucketsRequest, HttpMethodName.GET); return invoke(request, new Unmarshallers.ListBucketsUnmarshaller(), null, null); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#listBuckets() */ @Override public List<Bucket> listBuckets() throws AmazonClientException, AmazonServiceException { return listBuckets(new ListBucketsRequest()); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketLocation(com.amazonaws.services * .s3.AmazonS3Client.GetBucketLocationRequest) */ @Override public String getBucketLocation(GetBucketLocationRequest getBucketLocationRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(getBucketLocationRequest, "The request parameter must be specified when requesting a bucket's location"); String bucketName = getBucketLocationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting a bucket's location"); Request<GetBucketLocationRequest> request = createRequest(bucketName, null, getBucketLocationRequest, HttpMethodName.GET); request.addParameter("location", null); return invoke(request, new Unmarshallers.BucketLocationUnmarshaller(), bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketLocation(java.lang.String) */ @Override public String getBucketLocation(String bucketName) throws AmazonClientException, AmazonServiceException { return getBucketLocation(new GetBucketLocationRequest(bucketName)); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#createBucketjava.lang.String) */ @Override public Bucket createBucket(String bucketName) throws AmazonClientException, AmazonServiceException { return createBucket(new CreateBucketRequest(bucketName)); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#createBucket(java.lang.String, * com.amazonaws.services.s3.model.Region) */ @Override public Bucket createBucket(String bucketName, Region region) throws AmazonClientException, AmazonServiceException { return createBucket(new CreateBucketRequest(bucketName, region)); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#createBucket(java.lang.String, * java.lang.String) */ @Override public Bucket createBucket(String bucketName, String region) throws AmazonClientException, AmazonServiceException { return createBucket(new CreateBucketRequest(bucketName, region)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#createBucket(com.amazonaws.services * .s3.model.CreateBucketRequest) */ @Override public Bucket createBucket(CreateBucketRequest createBucketRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(createBucketRequest, "The CreateBucketRequest parameter must be specified when creating a bucket"); String bucketName = createBucketRequest.getBucketName(); String region = createBucketRequest.getRegion(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when creating a bucket"); if (bucketName != null) bucketName = bucketName.trim(); BucketNameUtils.validateBucketName(bucketName); Request<CreateBucketRequest> request = createRequest(bucketName, null, createBucketRequest, HttpMethodName.PUT); if (createBucketRequest.getAccessControlList() != null) { addAclHeaders(request, createBucketRequest.getAccessControlList()); } else if (createBucketRequest.getCannedAcl() != null) { request.addHeader(Headers.S3_CANNED_ACL, createBucketRequest.getCannedAcl().toString()); } /* * If we're talking to a region-specific endpoint other than the US, we * *must* specify a location constraint. Try to derive the region from * the endpoint. */ if (!(this.endpoint.getHost().equals(Constants.S3_HOSTNAME)) && (region == null || region.isEmpty())) { try { region = RegionUtils .getRegionByEndpoint(this.endpoint.getHost()) .getName(); } catch (IllegalArgumentException exception) { // Endpoint does not correspond to a known region; send the // request with no location constraint and hope for the best. } } /* * We can only send the CreateBucketConfiguration if we're *not* * creating a bucket in the US region. */ if (region != null && !StringUtils.upperCase(region).equals(Region.US_Standard.toString())) { XmlWriter xml = new XmlWriter(); xml.start("CreateBucketConfiguration", "xmlns", Constants.XML_NAMESPACE); xml.start("LocationConstraint").value(region).end(); xml.end(); byte[] bytes = xml.getBytes(); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); } invoke(request, voidResponseHandler, bucketName, null); return new Bucket(bucketName); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#getObjectAcl(java.lang.String, * java.lang.String) */ @Override public AccessControlList getObjectAcl(String bucketName, String key) throws AmazonClientException, AmazonServiceException { return getObjectAcl(bucketName, key, null); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#getObjectAcl(java.lang.String, * java.lang.String, java.lang.String) */ @Override public AccessControlList getObjectAcl(String bucketName, String key, String versionId) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting an object's ACL"); assertParameterNotNull(key, "The key parameter must be specified when requesting an object's ACL"); return getAcl(bucketName, key, versionId, null); } @Override public void setObjectAcl(String bucketName, String key, AccessControlList acl) throws AmazonClientException, AmazonServiceException { setObjectAcl(bucketName, key, null, acl); } @Override public void setObjectAcl(String bucketName, String key, CannedAccessControlList acl) throws AmazonClientException, AmazonServiceException { setObjectAcl(bucketName, key, null, acl); } @Override public void setObjectAcl(String bucketName, String key, String versionId, AccessControlList acl) throws AmazonClientException, AmazonServiceException { setObjectAcl0(bucketName, key, versionId, acl, null); } /** * Same as {@link #setObjectAcl(String, String, String, AccessControlList)} * but allows specifying a request metric collector. */ 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)); } @Override public void setObjectAcl(String bucketName, String key, String versionId, CannedAccessControlList acl) throws AmazonClientException, AmazonServiceException { setObjectAcl0(bucketName, key, versionId, acl, null); } /** * Same as * {@link #setObjectAcl(String, String, String, CannedAccessControlList)} * but allows specifying a request metric collector. */ public void setObjectAcl(String bucketName, String key, String versionId, CannedAccessControlList acl, RequestMetricCollector requestMetricCollector) { setObjectAcl0(bucketName, key, versionId, acl, requestMetricCollector); } private void setObjectAcl0(String bucketName, String key, String versionId, CannedAccessControlList 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)); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#getBucketAcl(java.lang.String) */ @Override public AccessControlList getBucketAcl(String bucketName) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting a bucket's ACL"); return getAcl(bucketName, null, null, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketAcl(com.amazonaws.services * .s3.GetBucketAclRequest) */ @Override public AccessControlList getBucketAcl(GetBucketAclRequest getBucketAclRequest) throws AmazonClientException, AmazonServiceException { 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); } @Override public void setBucketAcl(String bucketName, AccessControlList acl) throws AmazonClientException, AmazonServiceException { setBucketAcl0(bucketName, acl, null); } /** * Same as {@link #setBucketAcl(String, AccessControlList)} but allows * specifying a request metric collector. */ public void setBucketAcl(String bucketName, AccessControlList acl, RequestMetricCollector requestMetricCollector) { setBucketAcl0(bucketName, acl, requestMetricCollector); } private void setBucketAcl0(String bucketName, AccessControlList acl, RequestMetricCollector requestMetricCollector) { assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting a bucket's ACL"); assertParameterNotNull(acl, "The ACL parameter must be specified when setting a bucket's ACL"); setAcl(bucketName, null, null, acl, new GenericBucketRequest(bucketName) .withRequestMetricCollector(requestMetricCollector)); } @Override public void setBucketAcl(SetBucketAclRequest setBucketAclRequest) throws AmazonClientException, AmazonServiceException { String bucketName = setBucketAclRequest.getBucketName(); AccessControlList acl = setBucketAclRequest.getAcl(); 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); } else if (cannedAcl != null) { setAcl(bucketName, null, null, cannedAcl, setBucketAclRequest); } else { assertParameterNotNull(null, "The ACL parameter must be specified when setting a bucket's ACL"); } } @Override public void setBucketAcl(String bucketName, CannedAccessControlList acl) throws AmazonClientException, AmazonServiceException { setBucketAcl0(bucketName, acl, null); } /** * Same as {@link #setBucketAcl(String, CannedAccessControlList)} but allows * specifying a request metric collector. */ public void setBucketAcl(String bucketName, CannedAccessControlList acl, RequestMetricCollector requestMetricCollector) throws AmazonClientException, AmazonServiceException { setBucketAcl0(bucketName, acl, requestMetricCollector); } private void setBucketAcl0(String bucketName, CannedAccessControlList acl, RequestMetricCollector col) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting a bucket's ACL"); assertParameterNotNull(acl, "The ACL parameter must be specified when setting a bucket's ACL"); setAcl(bucketName, null, null, acl, new GenericBucketRequest(bucketName) .withRequestMetricCollector(col)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getObjectMetadata(java.lang.String, * java.lang.String) */ @Override public ObjectMetadata getObjectMetadata(String bucketName, String key) throws AmazonClientException, AmazonServiceException { return getObjectMetadata(new GetObjectMetadataRequest(bucketName, key)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getObjectMetadata(com.amazonaws.services * .s3.model.GetObjectMetadataRequest) */ @Override public ObjectMetadata getObjectMetadata(GetObjectMetadataRequest getObjectMetadataRequest) throws AmazonClientException, AmazonServiceException { 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(); 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<GetObjectMetadataRequest> request = createRequest(bucketName, key, getObjectMetadataRequest, HttpMethodName.HEAD); if (versionId != null) request.addParameter("versionId", versionId); populateSseCpkRequestParameters(request, getObjectMetadataRequest.getSSECustomerKey()); return invoke(request, new S3MetadataResponseHandler(), bucketName, key); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#getObject(java.lang.String, * java.lang.String) */ @Override public S3Object getObject(String bucketName, String key) throws AmazonClientException, AmazonServiceException { return getObject(new GetObjectRequest(bucketName, key)); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#doesBucketExist(java.lang.String) */ @Override public boolean doesBucketExist(String bucketName) throws AmazonClientException, AmazonServiceException { try { headBucket(new HeadBucketRequest(bucketName)); return true; } catch (AmazonServiceException ase) { // A redirect error or a forbidden error means the bucket exists. So // returning true. if ((ase.getStatusCode() == Constants.BUCKET_REDIRECT_STATUS_CODE) || (ase.getStatusCode() == Constants.BUCKET_ACCESS_FORBIDDEN_STATUS_CODE)) { return true; } if (ase.getStatusCode() == Constants.NO_SUCH_BUCKET_STATUS_CODE) { return false; } throw ase; } } @Override public boolean doesObjectExist(String bucketName, String objectName) throws AmazonServiceException, AmazonClientException { try { getObjectMetadata(bucketName, objectName); return true; } catch (AmazonS3Exception e) { if (e.getStatusCode() == 404) { return false; } throw e; } } @Override public HeadBucketResult headBucket(HeadBucketRequest headBucketRequest) throws AmazonClientException, AmazonServiceException { String bucketName = headBucketRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucketName parameter must be specified."); Request<HeadBucketRequest> request = createRequest(bucketName, null, headBucketRequest, HttpMethodName.HEAD); return invoke(request, new HeadBucketResultHandler(), bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#changeStorageClass(java.lang.String, * java.lang.String, java.lang.String) */ @Override public void changeObjectStorageClass(String bucketName, String key, StorageClass newStorageClass) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(bucketName, "The bucketName parameter must be specified when changing an object's storage class"); assertParameterNotNull(key, "The key parameter must be specified when changing an object's storage class"); assertParameterNotNull(newStorageClass, "The newStorageClass parameter must be specified when changing an object's storage class"); copyObject(new CopyObjectRequest(bucketName, key, bucketName, key) .withStorageClass(newStorageClass.toString())); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setObjectRedirectLocation(java.lang * .String, java.lang.String, java.lang.String) */ @Override public void setObjectRedirectLocation(String bucketName, String key, String newRedirectLocation) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(bucketName, "The bucketName parameter must be specified when changing an object's storage class"); assertParameterNotNull(key, "The key parameter must be specified when changing an object's storage class"); assertParameterNotNull(newRedirectLocation, "The newStorageClass parameter must be specified when changing an object's storage class"); copyObject(new CopyObjectRequest(bucketName, key, bucketName, key) .withRedirectLocation(newRedirectLocation)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getObject(com.amazonaws.services.s3 * .model.GetObjectRequest) */ @Override public S3Object getObject(GetObjectRequest getObjectRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(getObjectRequest, "The GetObjectRequest parameter must be specified when requesting an object"); assertParameterNotNull(getObjectRequest.getBucketName(), "The bucket name parameter must be specified when requesting an object"); assertParameterNotNull(getObjectRequest.getKey(), "The key parameter must be specified when requesting an object"); Request<GetObjectRequest> request = createRequest(getObjectRequest.getBucketName(), getObjectRequest.getKey(), getObjectRequest, HttpMethodName.GET); if (getObjectRequest.getVersionId() != null) { request.addParameter("versionId", getObjectRequest.getVersionId()); } // Range long[] range = getObjectRequest.getRange(); if (range != null) { String rangeHeader = "bytes=" + Long.toString(range[0]) + "-"; if (range[1] >= 0) { /* * Negative value is invalid per S3 range get and will result in * downloading the entire object. Leaving last byte empty so as * to resume download from range[0]. */ rangeHeader += Long.toString(range[1]); } request.addHeader(Headers.RANGE, rangeHeader); } if (getObjectRequest.isRequesterPays()) { request.addHeader(Headers.REQUESTER_PAYS_HEADER, Constants.REQUESTER_PAYS); } addResponseHeaderParameters(request, getObjectRequest.getResponseHeaders()); addDateHeader(request, Headers.GET_OBJECT_IF_MODIFIED_SINCE, getObjectRequest.getModifiedSinceConstraint()); addDateHeader(request, Headers.GET_OBJECT_IF_UNMODIFIED_SINCE, getObjectRequest.getUnmodifiedSinceConstraint()); addStringListHeader(request, Headers.GET_OBJECT_IF_MATCH, getObjectRequest.getMatchingETagConstraints()); addStringListHeader(request, Headers.GET_OBJECT_IF_NONE_MATCH, getObjectRequest.getNonmatchingETagConstraints()); // Populate the SSE-CPK parameters to the request header populateSseCpkRequestParameters(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 .wrapListener(progressListener); try { S3Object s3Object = invoke(request, new S3ObjectResponseHandler(), getObjectRequest.getBucketName(), getObjectRequest.getKey()); /* * TODO: For now, it's easiest to set there here in the client, but * we could push this back into the response handler with a little * more work. */ s3Object.setBucketName(getObjectRequest.getBucketName()); s3Object.setKey(getObjectRequest.getKey()); InputStream input = s3Object.getObjectContent(); // Hold a reference to this client while the InputStream is still // around - otherwise a finalizer in the HttpClient may reset the // underlying TCP connection out from under us. input = new ServiceClientHolderInputStream(input, this); // If someone is interested in progress updates, wrap the input // stream in a filter that will trigger progress reports. if (progressListenerCallbackExecutor != null) { @SuppressWarnings("resource") ProgressReportingInputStream progressReportingInputStream = new ProgressReportingInputStream( input, progressListenerCallbackExecutor); progressReportingInputStream.setFireCompletedEvent(true); input = progressReportingInputStream; fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.STARTED_EVENT_CODE); } // The Etag header contains a server-side MD5 of the object. If // we're downloading the whole object, by default we wrap the // stream in a validator that calculates an MD5 of the downloaded // bytes and complains if what we received doesn't match the Etag. if (!ServiceUtils.skipMd5CheckPerRequest(getObjectRequest) && !ServiceUtils.skipMd5CheckPerResponse(s3Object.getObjectMetadata())) { byte[] serverSideHash = null; 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"); input = new DigestValidationInputStream(input, digest, serverSideHash); } catch (NoSuchAlgorithmException e) { log.warn("No MD5 digest algorithm available. Unable to calculate " + "checksum and verify data integrity.", e); } } } else { // Ensures the data received from S3 has the same length as the // expected content-length input = new LengthCheckInputStream(input, s3Object.getObjectMetadata().getContentLength(), // expected // length INCLUDE_SKIPPED_BYTES); // bytes received from S3 are // all included even if skipped } // Re-wrap within an S3ObjectInputStream. Explicitly do not collect // metrics here because we know we're ultimately wrapping another // S3ObjectInputStream which will take care of that. s3Object.setObjectContent(new S3ObjectInputStream(input)); return s3Object; } catch (AmazonS3Exception ase) { /* * If the request failed because one of the specified constraints * was not met (ex: matching ETag, modified since date, etc.), then * return null, so that users don't have to wrap their code in * try/catch blocks and check for this status code if they want to * use constraints. */ if (ase.getStatusCode() == 412 || ase.getStatusCode() == 304) { fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.CANCELED_EVENT_CODE); return null; } fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.FAILED_EVENT_CODE); throw ase; } } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getObject(com.amazonaws.services.s3 * .model.GetObjectRequest, java.io.File) */ @Override public ObjectMetadata getObject(final GetObjectRequest getObjectRequest, File destinationFile) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(destinationFile, "The destination file parameter must be specified when downloading an object directly to a file"); boolean mode = ServiceUtils.OVERWRITE_MODE; if (getObjectRequest.getRange() != null && getObjectRequest.getRange()[0] > 0) { mode = ServiceUtils.APPEND_MODE; } S3Object s3Object = ServiceUtils.retryableDownloadS3ObjectToFile(destinationFile, new ServiceUtils.RetryableS3DownloadTask() { @Override public S3Object getS3ObjectStream() { return getObject(getObjectRequest); } @Override public boolean needIntegrityCheck() { return !ServiceUtils.skipMd5CheckPerRequest(getObjectRequest); } }, mode); // getObject can return null if constraints were specified but not met if (s3Object == null) return null; return s3Object.getObjectMetadata(); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#deleteBucket(java.lang.String) */ @Override public void deleteBucket(String bucketName) throws AmazonClientException, AmazonServiceException { deleteBucket(new DeleteBucketRequest(bucketName)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucket(com.amazonaws.services * .s3.model.DeleteBucketRequest) */ @Override public void deleteBucket(DeleteBucketRequest deleteBucketRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(deleteBucketRequest, "The DeleteBucketRequest parameter must be specified when deleting a bucket"); String bucketName = deleteBucketRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting a bucket"); Request<DeleteBucketRequest> request = createRequest(bucketName, null, deleteBucketRequest, HttpMethodName.DELETE); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#putObject(java.lang.String, * java.lang.String, java.io.File) */ @Override public PutObjectResult putObject(String bucketName, String key, File file) throws AmazonClientException, AmazonServiceException { return putObject(new PutObjectRequest(bucketName, key, file) .withMetadata(new ObjectMetadata())); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#putObject(java.lang.String, * java.lang.String, java.io.InputStream, * com.amazonaws.services.s3.model.S3ObjectMetadata) */ @Override public PutObjectResult putObject(String bucketName, String key, InputStream input, ObjectMetadata metadata) throws AmazonClientException, AmazonServiceException { return putObject(new PutObjectRequest(bucketName, key, input, metadata)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#putObject(com.amazonaws.services.s3 * .model.PutObjectRequest) */ @Override public PutObjectResult putObject(PutObjectRequest putObjectRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(putObjectRequest, "The PutObjectRequest parameter must be specified when uploading an object"); String bucketName = putObjectRequest.getBucketName(); String key = putObjectRequest.getKey(); ObjectMetadata metadata = putObjectRequest.getMetadata(); InputStream input = putObjectRequest.getInputStream(); /* * This is compatible with progress listener set by either the legacy * method PutObjectRequest#setProgressListener or the new method * PutObjectRequest#setGeneralProgressListener. */ ProgressListener progressListener = putObjectRequest.getGeneralProgressListener(); ProgressListenerCallbackExecutor progressListenerCallbackExecutor = ProgressListenerCallbackExecutor .wrapListener(progressListener); if (metadata == null) metadata = new ObjectMetadata(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when uploading an object"); assertParameterNotNull(key, "The key parameter must be specified when uploading an object"); final boolean skipContentMd5Check = ServiceUtils .skipMd5CheckPerRequest(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(); // Always set the content length, even if it's already set metadata.setContentLength(file.length()); final boolean calculateMD5 = metadata.getContentMD5() == null; // Only set the content type if it hasn't already been set if (metadata.getContentType() == null) { metadata.setContentType(Mimetypes.getInstance().getMimetype(file)); } if (calculateMD5 && !skipContentMd5Check) { try { String contentMd5_b64 = Md5Utils.md5AsBase64(file); metadata.setContentMD5(contentMd5_b64); } catch (Exception e) { throw new AmazonClientException( "Unable to calculate MD5 hash: " + e.getMessage(), e); } } try { input = new RepeatableFileInputStream(file); } catch (FileNotFoundException fnfe) { throw new AmazonClientException("Unable to find file to upload", fnfe); } } Request<PutObjectRequest> request = createRequest(bucketName, key, putObjectRequest, HttpMethodName.PUT); if (putObjectRequest.getAccessControlList() != null) { addAclHeaders(request, putObjectRequest.getAccessControlList()); } else if (putObjectRequest.getCannedAcl() != null) { request.addHeader(Headers.S3_CANNED_ACL, putObjectRequest.getCannedAcl().toString()); } if (putObjectRequest.getStorageClass() != null) { request.addHeader(Headers.STORAGE_CLASS, putObjectRequest.getStorageClass()); } if (putObjectRequest.getRedirectLocation() != null) { request.addHeader(Headers.REDIRECT_LOCATION, putObjectRequest.getRedirectLocation()); if (input == null) { setZeroContentLength(request); input = new ByteArrayInputStream(new byte[0]); } } // Populate the SSE-CPK parameters to the request header populateSseCpkRequestParameters(request, putObjectRequest.getSSECustomerKey()); // Use internal interface to differentiate 0 from unset. final Long contentLength = (Long) metadata.getRawMetadataValue(Headers.CONTENT_LENGTH); if (contentLength == null) { /* * There's nothing we can do except for let the HTTP client buffer * the input stream contents if the caller doesn't tell us how much * data to expect in a stream since we have to explicitly tell * Amazon S3 how much we're sending before we start sending any of * it. */ if (!input.markSupported()) { 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); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bais.available())); input = bais; } else { long len = calculateContentLength(input); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(len)); } } else { final long expectedLength = contentLength.longValue(); if (expectedLength >= 0) { // Performs length check on the underlying data stream. // For S3 encryption client, the underlying data stream here // refers to the cipher-text data stream (ie not the underlying // plain-text data stream which in turn may have been wrapped // with it's own length check input stream.) @SuppressWarnings("resource") LengthCheckInputStream lcis = new LengthCheckInputStream( input, expectedLength, // expected data length to be uploaded EXCLUDE_SKIPPED_BYTES); input = lcis; request.addHeader(Headers.CONTENT_LENGTH, contentLength.toString()); } } if (progressListenerCallbackExecutor != null) { input = new ProgressReportingInputStream(input, progressListenerCallbackExecutor); fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.STARTED_EVENT_CODE); } MD5DigestCalculatingInputStream md5DigestStream = null; if (metadata.getContentMD5() == null && !skipContentMd5Check) { /* * If the user hasn't set the content MD5, then we don't want to * buffer the whole stream in memory just to calculate it. Instead, * we can calculate it on the fly and validate it with the returned * ETag from the object upload. */ input = md5DigestStream = new MD5DigestCalculatingInputStream(input); } if (metadata.getContentType() == null) { /* * Default to the "application/octet-stream" if the user hasn't * specified a content type. */ metadata.setContentType(Mimetypes.MIMETYPE_OCTET_STREAM); } populateRequestMetadata(request, metadata); request.setContent(input); /* * Enable 100-continue support for PUT operations, since this is where * we're potentially uploading large amounts of data and want to find * out as early as possible if an operation will fail. We don't want to * do this for all operations since it will cause extra latency in the * network interaction. */ request.addHeader("Expect", "100-continue"); ObjectMetadata returnedMetadata = null; try { returnedMetadata = invoke(request, new S3MetadataResponseHandler(), bucketName, key); } catch (AmazonClientException ace) { fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.FAILED_EVENT_CODE); throw ace; } finally { try { input.close(); } catch (AbortedException ignore) { } catch (Exception e) { log.debug("Unable to cleanly close input stream: " + e.getMessage(), e); } } String contentMd5 = metadata.getContentMD5(); if (md5DigestStream != null) { contentMd5 = BinaryUtils.toBase64(md5DigestStream.getMd5Digest()); } if (returnedMetadata != null && contentMd5 != null && !skipContentMd5Check) { byte[] clientSideHash = BinaryUtils.fromBase64(contentMd5); byte[] serverSideHash = BinaryUtils.fromHex(returnedMetadata.getETag()); if (!Arrays.equals(clientSideHash, serverSideHash)) { fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.FAILED_EVENT_CODE); throw new AmazonClientException( "Unable to verify integrity of data upload. " + "Client calculated content hash didn't match hash calculated by Amazon S3. " + "You may need to delete the data stored in Amazon S3."); } } fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.COMPLETED_EVENT_CODE); PutObjectResult result = new PutObjectResult(); result.setETag(returnedMetadata.getETag()); 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); return result; } /** * Calculate the content length of a mark supported input stream. * * @param is input stream * @return length of the input stream */ private long calculateContentLength(InputStream is) { long len = 0; byte[] buf = new byte[8 * 1024]; int read; is.mark(-1); try { while ((read = is.read(buf)) != -1) { len += read; } is.reset(); } catch (IOException ioe) { throw new AmazonClientException("Could not calculate content length.", ioe); } return len; } /** * Sets the acccess control headers for the request given. */ private static void addAclHeaders(Request<? extends AmazonWebServiceRequest> request, AccessControlList acl) { Set<Grant> grants = acl.getGrants(); Map<Permission, Collection<Grantee>> grantsByPermission = new HashMap<Permission, Collection<Grantee>>(); for (Grant grant : grants) { if (!grantsByPermission.containsKey(grant.getPermission())) { grantsByPermission.put(grant.getPermission(), new LinkedList<Grantee>()); } grantsByPermission.get(grant.getPermission()).add(grant.getGrantee()); } for (Permission permission : Permission.values()) { if (grantsByPermission.containsKey(permission)) { Collection<Grantee> grantees = grantsByPermission.get(permission); boolean seenOne = false; StringBuilder granteeString = new StringBuilder(); for (Grantee grantee : grantees) { if (!seenOne) seenOne = true; else granteeString.append(", "); granteeString.append(grantee.getTypeIdentifier()).append("=").append("\"") .append(grantee.getIdentifier()).append("\""); } request.addHeader(permission.getHeaderName(), granteeString.toString()); } } } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#copyObject(java.lang.String, * java.lang.String, java.lang.String, java.lang.String) */ @Override public CopyObjectResult copyObject(String sourceBucketName, String sourceKey, String destinationBucketName, String destinationKey) throws AmazonClientException, AmazonServiceException { return copyObject(new CopyObjectRequest(sourceBucketName, sourceKey, destinationBucketName, destinationKey)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#copyObject(com.amazonaws.services. * s3.model.CopyObjectRequest) */ @Override public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(copyObjectRequest.getSourceBucketName(), "The source bucket name must be specified when copying an object"); assertParameterNotNull(copyObjectRequest.getSourceKey(), "The source object key must be specified when copying an object"); assertParameterNotNull(copyObjectRequest.getDestinationBucketName(), "The destination bucket name must be specified when copying an object"); assertParameterNotNull(copyObjectRequest.getDestinationKey(), "The destination object key must be specified when copying an object"); String destinationKey = copyObjectRequest.getDestinationKey(); String destinationBucketName = copyObjectRequest.getDestinationBucketName(); Request<CopyObjectRequest> request = createRequest(destinationBucketName, destinationKey, copyObjectRequest, HttpMethodName.PUT); populateRequestWithCopyObjectParameters(request, copyObjectRequest); /* * 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 * remote server thinks there's more data to pull. */ setZeroContentLength(request); CopyObjectResultHandler copyObjectResultHandler = null; try { @SuppressWarnings("unchecked") ResponseHeaderHandlerChain<CopyObjectResultHandler> handler = new ResponseHeaderHandlerChain<CopyObjectResultHandler>( // xml payload unmarshaller new Unmarshallers.CopyObjectUnmarshaller(), // header handlers new ServerSideEncryptionHeaderHandler<CopyObjectResultHandler>(), new S3VersionHeaderHandler(), new ObjectExpirationHeaderHandler<CopyObjectResultHandler>()); copyObjectResultHandler = invoke(request, handler, destinationBucketName, destinationKey); } catch (AmazonS3Exception ase) { /* * If the request failed because one of the specified constraints * was not met (ex: matching ETag, modified since date, etc.), then * return null, so that users don't have to wrap their code in * try/catch blocks and check for this status code if they want to * use constraints. */ if (ase.getStatusCode() == Constants.FAILED_PRECONDITION_STATUS_CODE) { return null; } throw ase; } /* * CopyObject has two failure modes: 1 - An HTTP error code is returned * and the error is processed like any other error response. 2 - An HTTP * 200 OK code is returned, but the response content contains an XML * error response. This makes it very difficult for the client runtime * to cleanly detect this case and handle it like any other error * response. We could extend the runtime to have a more * flexible/customizable definition of success/error (per request), but * 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(); AmazonS3Exception ase = new AmazonS3Exception(errorMessage); ase.setErrorCode(errorCode); ase.setErrorType(ErrorType.Service); ase.setRequestId(requestId); ase.setExtendedRequestId(hostId); ase.setServiceName(request.getServiceName()); ase.setStatusCode(200); throw ase; } // TODO: Might be nice to create this in our custom // S3VersionHeaderHandler 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()); return copyObjectResult; } /** * Copies a source object to a part of a multipart upload. To copy an * object, the caller's account must have read access to the source object * and write access to the destination bucket. </p> * <p> * If constraints are specified in the <code>CopyPartRequest</code> (e.g. * {@link CopyPartRequest#setMatchingETagConstraints(List)}) and are not * satisfied when Amazon S3 receives the request, this method returns * <code>null</code>. This method returns a non-null result under all other * circumstances. * </p> * * @param copyPartRequest The request object containing all the options for * copying an Amazon S3 object. * @return A {@link CopyPartResult} object containing the information * returned by Amazon S3 about the newly created object, or * <code>null</code> if constraints were specified that weren't met * when Amazon S3 attempted to copy the 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#copyObject(CopyObjectRequest) * @see AmazonS3#initiateMultipartUpload(InitiateMultipartUploadRequest) */ @Override public CopyPartResult copyPart(CopyPartRequest copyPartRequest) { assertParameterNotNull(copyPartRequest.getSourceBucketName(), "The source bucket name must be specified when copying a part"); assertParameterNotNull(copyPartRequest.getSourceKey(), "The source object key must be specified when copying a part"); assertParameterNotNull(copyPartRequest.getDestinationBucketName(), "The destination bucket name must be specified when copying a part"); assertParameterNotNull(copyPartRequest.getUploadId(), "The upload id must be specified when copying a part"); assertParameterNotNull(copyPartRequest.getDestinationKey(), "The destination object key must be specified when copying a part"); assertParameterNotNull(copyPartRequest.getPartNumber(), "The part number must be specified when copying a part"); String destinationKey = copyPartRequest.getDestinationKey(); String destinationBucketName = copyPartRequest.getDestinationBucketName(); Request<CopyPartRequest> request = createRequest(destinationBucketName, destinationKey, copyPartRequest, HttpMethodName.PUT); populateRequestWithCopyPartParameters(request, copyPartRequest); request.addParameter("uploadId", copyPartRequest.getUploadId()); request.addParameter("partNumber", Integer.toString(copyPartRequest.getPartNumber())); /* * 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 * remote server thinks there's more data to pull. */ setZeroContentLength(request); CopyObjectResultHandler copyObjectResultHandler = null; try { @SuppressWarnings("unchecked") ResponseHeaderHandlerChain<CopyObjectResultHandler> handler = new ResponseHeaderHandlerChain<CopyObjectResultHandler>( // xml payload unmarshaller new Unmarshallers.CopyObjectUnmarshaller(), // header handlers new ServerSideEncryptionHeaderHandler<CopyObjectResultHandler>(), new S3VersionHeaderHandler()); copyObjectResultHandler = invoke(request, handler, destinationBucketName, destinationKey); } catch (AmazonS3Exception ase) { /* * If the request failed because one of the specified constraints * was not met (ex: matching ETag, modified since date, etc.), then * return null, so that users don't have to wrap their code in * try/catch blocks and check for this status code if they want to * use constraints. */ if (ase.getStatusCode() == Constants.FAILED_PRECONDITION_STATUS_CODE) { return null; } throw ase; } /* * CopyPart has two failure modes: 1 - An HTTP error code is returned * and the error is processed like any other error response. 2 - An HTTP * 200 OK code is returned, but the response content contains an XML * error response. This makes it very difficult for the client runtime * to cleanly detect this case and handle it like any other error * response. We could extend the runtime to have a more * flexible/customizable definition of success/error (per request), but * 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(); AmazonS3Exception ase = new AmazonS3Exception(errorMessage); ase.setErrorCode(errorCode); ase.setErrorType(ErrorType.Service); ase.setRequestId(requestId); ase.setExtendedRequestId(hostId); ase.setServiceName(request.getServiceName()); ase.setStatusCode(200); throw ase; } CopyPartResult copyPartResult = new CopyPartResult(); copyPartResult.setETag(copyObjectResultHandler.getETag()); copyPartResult.setPartNumber(copyPartRequest.getPartNumber()); copyPartResult.setLastModifiedDate(copyObjectResultHandler.getLastModified()); copyPartResult.setVersionId(copyObjectResultHandler.getVersionId()); copyPartResult.setSSEAlgorithm(copyObjectResultHandler.getSSEAlgorithm()); copyPartResult.setSSECustomerAlgorithm(copyObjectResultHandler.getSSECustomerAlgorithm()); copyPartResult.setSSECustomerKeyMd5(copyObjectResultHandler.getSSECustomerKeyMd5()); return copyPartResult; } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#deleteObject(java.lang.String, * java.lang.String) */ @Override public void deleteObject(String bucketName, String key) throws AmazonClientException, AmazonServiceException { deleteObject(new DeleteObjectRequest(bucketName, key)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteObject(com.amazonaws.services * .s3.DeleteObjectRequest) */ @Override public void deleteObject(DeleteObjectRequest deleteObjectRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(deleteObjectRequest, "The delete object request must be specified when deleting an object"); assertParameterNotNull(deleteObjectRequest.getBucketName(), "The bucket name must be specified when deleting an object"); assertParameterNotNull(deleteObjectRequest.getKey(), "The key must be specified when deleting an object"); Request<DeleteObjectRequest> request = createRequest(deleteObjectRequest.getBucketName(), deleteObjectRequest.getKey(), deleteObjectRequest, HttpMethodName.DELETE); invoke(request, voidResponseHandler, deleteObjectRequest.getBucketName(), deleteObjectRequest.getKey()); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteObjects(com.amazonaws.services * .s3.model.DeleteObjectsRequest) */ @Override public DeleteObjectsResult deleteObjects(DeleteObjectsRequest deleteObjectsRequest) { Request<DeleteObjectsRequest> request = createRequest(deleteObjectsRequest.getBucketName(), null, deleteObjectsRequest, HttpMethodName.POST); request.addParameter("delete", null); if (deleteObjectsRequest.getMfa() != null) { populateRequestWithMfaDetails(request, deleteObjectsRequest.getMfa()); } 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); request.addHeader("Content-MD5", md5Base64); } catch (Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } DeleteObjectsResponse response = invoke(request, new Unmarshallers.DeleteObjectsResultUnmarshaller(), deleteObjectsRequest.getBucketName(), null); /* * If the result was only partially successful, throw an exception */ if (!response.getErrors().isEmpty()) { throw new MultiObjectDeleteException(response.getErrors(), response.getDeletedObjects()); } return new DeleteObjectsResult(response.getDeletedObjects()); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteObjectVersion(java.lang.String, * java.lang.String, java.lang.String) */ @Override public void deleteVersion(String bucketName, String key, String versionId) throws AmazonClientException, AmazonServiceException { deleteVersion(new DeleteVersionRequest(bucketName, key, versionId)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteVersion(com.amazonaws.services * .s3.model.DeleteVersionRequest) */ @Override public void deleteVersion(DeleteVersionRequest deleteVersionRequest) throws AmazonClientException, AmazonServiceException { 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(); assertParameterNotNull(bucketName, "The bucket name must be specified when deleting a version"); assertParameterNotNull(key, "The key must be specified when deleting a version"); assertParameterNotNull(versionId, "The version ID must be specified when deleting a version"); Request<DeleteVersionRequest> request = createRequest(bucketName, key, deleteVersionRequest, HttpMethodName.DELETE); if (versionId != null) request.addParameter("versionId", versionId); if (deleteVersionRequest.getMfa() != null) { populateRequestWithMfaDetails(request, deleteVersionRequest.getMfa()); } invoke(request, voidResponseHandler, bucketName, key); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketVersioningConfiguration(com * .amazonaws.services.s3.model.SetBucketVersioningConfigurationRequest) */ @Override public void setBucketVersioningConfiguration( SetBucketVersioningConfigurationRequest setBucketVersioningConfigurationRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull( setBucketVersioningConfigurationRequest, "The SetBucketVersioningConfigurationRequest object must be specified when setting versioning configuration"); String bucketName = setBucketVersioningConfigurationRequest.getBucketName(); BucketVersioningConfiguration versioningConfiguration = setBucketVersioningConfigurationRequest .getVersioningConfiguration(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting versioning configuration"); assertParameterNotNull(versioningConfiguration, "The bucket versioning parameter must be specified when setting versioning configuration"); if (versioningConfiguration.isMfaDeleteEnabled() != null) { assertParameterNotNull( setBucketVersioningConfigurationRequest.getMfa(), "The MFA parameter must be specified when changing MFA Delete status in the versioning configuration"); } Request<SetBucketVersioningConfigurationRequest> request = createRequest(bucketName, null, setBucketVersioningConfigurationRequest, HttpMethodName.PUT); request.addParameter("versioning", null); if (versioningConfiguration.isMfaDeleteEnabled() != null) { if (setBucketVersioningConfigurationRequest.getMfa() != null) { populateRequestWithMfaDetails(request, setBucketVersioningConfigurationRequest.getMfa()); } } byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(versioningConfiguration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketVersioningConfiguration(java * .lang.String) */ @Override public BucketVersioningConfiguration getBucketVersioningConfiguration(String bucketName) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(bucketName, "The bucket name parameter must be specified when querying versioning configuration"); Request<GenericBucketRequest> request = createRequest(bucketName, null, new GenericBucketRequest(bucketName), HttpMethodName.GET); request.addParameter("versioning", null); return invoke(request, new Unmarshallers.BucketVersioningConfigurationUnmarshaller(), bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketWebsiteConfiguration(java * .lang.String) */ @Override public BucketWebsiteConfiguration getBucketWebsiteConfiguration(String bucketName) throws AmazonClientException, AmazonServiceException { return getBucketWebsiteConfiguration(new GetBucketWebsiteConfigurationRequest(bucketName)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketWebsiteConfiguration(com. * amazonaws.services.s3.model.GetBucketWebsiteConfigurationRequest) */ @Override public BucketWebsiteConfiguration getBucketWebsiteConfiguration( GetBucketWebsiteConfigurationRequest getBucketWebsiteConfigurationRequest) throws AmazonClientException, AmazonServiceException { String bucketName = getBucketWebsiteConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting a bucket's website configuration"); Request<GetBucketWebsiteConfigurationRequest> request = createRequest(bucketName, null, getBucketWebsiteConfigurationRequest, HttpMethodName.GET); request.addParameter("website", null); request.addHeader("Content-Type", "application/xml"); try { return invoke(request, new Unmarshallers.BucketWebsiteConfigurationUnmarshaller(), bucketName, null); } catch (AmazonServiceException ase) { if (ase.getStatusCode() == 404) return null; throw ase; } } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketLifecycleConfiguration(java * .lang.String) */ @Override public BucketLifecycleConfiguration getBucketLifecycleConfiguration(String bucketName) { Request<GenericBucketRequest> request = createRequest(bucketName, null, new GenericBucketRequest(bucketName), HttpMethodName.GET); request.addParameter("lifecycle", null); try { return invoke(request, new Unmarshallers.BucketLifecycleConfigurationUnmarshaller(), bucketName, null); } catch (AmazonServiceException ase) { switch (ase.getStatusCode()) { case 404: return null; default: throw ase; } } } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketLifecycleConfiguration(java * .lang.String, * com.amazonaws.services.s3.model.BucketLifecycleConfiguration) */ @Override public void setBucketLifecycleConfiguration(String bucketName, BucketLifecycleConfiguration bucketLifecycleConfiguration) { setBucketLifecycleConfiguration(new SetBucketLifecycleConfigurationRequest(bucketName, bucketLifecycleConfiguration)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketLifecycleConfiguration(com * .amazonaws.services.s3.model.SetBucketLifecycleConfigurationRequest) */ @Override public void setBucketLifecycleConfiguration( SetBucketLifecycleConfigurationRequest setBucketLifecycleConfigurationRequest) { assertParameterNotNull(setBucketLifecycleConfigurationRequest, "The set bucket lifecycle configuration request object must be specified."); String bucketName = setBucketLifecycleConfigurationRequest.getBucketName(); BucketLifecycleConfiguration bucketLifecycleConfiguration = setBucketLifecycleConfigurationRequest .getLifecycleConfiguration(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting bucket lifecycle configuration."); assertParameterNotNull( bucketLifecycleConfiguration, "The lifecycle configuration parameter must be specified when setting bucket lifecycle configuration."); Request<SetBucketLifecycleConfigurationRequest> request = createRequest(bucketName, null, setBucketLifecycleConfigurationRequest, HttpMethodName.PUT); request.addParameter("lifecycle", null); 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); request.addHeader("Content-MD5", md5Base64); } catch (Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketLifecycleConfiguration * (java.lang.String) */ @Override public void deleteBucketLifecycleConfiguration(String bucketName) { deleteBucketLifecycleConfiguration(new DeleteBucketLifecycleConfigurationRequest(bucketName)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketLifecycleConfiguration * (com. * amazonaws.services.s3.model.DeleteBucketLifecycleConfigurationRequest) */ @Override public void deleteBucketLifecycleConfiguration( DeleteBucketLifecycleConfigurationRequest deleteBucketLifecycleConfigurationRequest) { assertParameterNotNull(deleteBucketLifecycleConfigurationRequest, "The delete bucket lifecycle configuration request object must be specified."); String bucketName = deleteBucketLifecycleConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting bucket lifecycle configuration."); Request<DeleteBucketLifecycleConfigurationRequest> request = createRequest(bucketName, null, deleteBucketLifecycleConfigurationRequest, HttpMethodName.DELETE); request.addParameter("lifecycle", null); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketCrossOriginConfiguration( * java.lang.String) */ @Override public BucketCrossOriginConfiguration getBucketCrossOriginConfiguration(String bucketName) { Request<GenericBucketRequest> request = createRequest(bucketName, null, new GenericBucketRequest(bucketName), HttpMethodName.GET); request.addParameter("cors", null); try { return invoke(request, new Unmarshallers.BucketCrossOriginConfigurationUnmarshaller(), bucketName, null); } catch (AmazonServiceException ase) { switch (ase.getStatusCode()) { case 404: return null; default: throw ase; } } } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketCrossOriginConfiguration( * java.lang.String, * com.amazonaws.services.s3.model.BucketCrossOriginConfiguration) */ @Override public void setBucketCrossOriginConfiguration(String bucketName, BucketCrossOriginConfiguration bucketCrossOriginConfiguration) { setBucketCrossOriginConfiguration(new SetBucketCrossOriginConfigurationRequest(bucketName, bucketCrossOriginConfiguration)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketCrossOriginConfiguration( * com.amazonaws.services.s3.model.SetBucketCrossOriginConfigurationRequest) */ @Override public void setBucketCrossOriginConfiguration( SetBucketCrossOriginConfigurationRequest setBucketCrossOriginConfigurationRequest) { assertParameterNotNull(setBucketCrossOriginConfigurationRequest, "The set bucket cross origin configuration request object must be specified."); String bucketName = setBucketCrossOriginConfigurationRequest.getBucketName(); BucketCrossOriginConfiguration bucketCrossOriginConfiguration = setBucketCrossOriginConfigurationRequest .getCrossOriginConfiguration(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting bucket cross origin configuration."); assertParameterNotNull( bucketCrossOriginConfiguration, "The cross origin configuration parameter must be specified when setting bucket cross origin configuration."); Request<SetBucketCrossOriginConfigurationRequest> request = createRequest(bucketName, null, setBucketCrossOriginConfigurationRequest, HttpMethodName.PUT); request.addParameter("cors", null); 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); request.addHeader("Content-MD5", md5Base64); } catch (Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketCrossOriginConfiguration * (java.lang.String) */ @Override public void deleteBucketCrossOriginConfiguration(String bucketName) { deleteBucketCrossOriginConfiguration(new DeleteBucketCrossOriginConfigurationRequest( bucketName)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketCrossOriginConfiguration * (com * .amazonaws.services.s3.model.DeleteBucketCrossOriginConfigurationRequest) */ @Override public void deleteBucketCrossOriginConfiguration( DeleteBucketCrossOriginConfigurationRequest deleteBucketCrossOriginConfigurationRequest) { assertParameterNotNull(deleteBucketCrossOriginConfigurationRequest, "The delete bucket cross origin configuration request object must be specified."); String bucketName = deleteBucketCrossOriginConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting bucket cross origin configuration."); Request<DeleteBucketCrossOriginConfigurationRequest> request = createRequest(bucketName, null, deleteBucketCrossOriginConfigurationRequest, HttpMethodName.DELETE); request.addParameter("cors", null); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketTaggingConfiguration(java * .lang.String) */ @Override public BucketTaggingConfiguration getBucketTaggingConfiguration(String bucketName) { Request<GenericBucketRequest> request = createRequest(bucketName, null, new GenericBucketRequest(bucketName), HttpMethodName.GET); request.addParameter("tagging", null); try { return invoke(request, new Unmarshallers.BucketTaggingConfigurationUnmarshaller(), bucketName, null); } catch (AmazonServiceException ase) { switch (ase.getStatusCode()) { case 404: return null; default: throw ase; } } } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketTaggingConfiguration(java * .lang.String, * com.amazonaws.services.s3.model.BucketLifecycleConfiguration) */ @Override public void setBucketTaggingConfiguration(String bucketName, BucketTaggingConfiguration bucketTaggingConfiguration) { setBucketTaggingConfiguration(new SetBucketTaggingConfigurationRequest(bucketName, bucketTaggingConfiguration)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketTaggingConfiguration(com. * amazonaws.services.s3.model.SetBucketTaggingConfigurationRequest) */ @Override public void setBucketTaggingConfiguration( SetBucketTaggingConfigurationRequest setBucketTaggingConfigurationRequest) { assertParameterNotNull(setBucketTaggingConfigurationRequest, "The set bucket tagging configuration request object must be specified."); String bucketName = setBucketTaggingConfigurationRequest.getBucketName(); BucketTaggingConfiguration bucketTaggingConfiguration = setBucketTaggingConfigurationRequest .getTaggingConfiguration(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting bucket tagging configuration."); assertParameterNotNull(bucketTaggingConfiguration, "The tagging configuration parameter must be specified when setting bucket tagging configuration."); Request<SetBucketTaggingConfigurationRequest> request = createRequest(bucketName, null, setBucketTaggingConfigurationRequest, HttpMethodName.PUT); request.addParameter("tagging", null); 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); request.addHeader("Content-MD5", md5Base64); } catch (Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketTaggingConfiguration(java * .lang.String) */ @Override public void deleteBucketTaggingConfiguration(String bucketName) { deleteBucketTaggingConfiguration(new DeleteBucketTaggingConfigurationRequest(bucketName)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketTaggingConfiguration(com * .amazonaws.services.s3.model.DeleteBucketTaggingConfigurationRequest) */ @Override public void deleteBucketTaggingConfiguration( DeleteBucketTaggingConfigurationRequest deleteBucketTaggingConfigurationRequest) { assertParameterNotNull(deleteBucketTaggingConfigurationRequest, "The delete bucket tagging configuration request object must be specified."); String bucketName = deleteBucketTaggingConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting bucket tagging configuration."); Request<DeleteBucketTaggingConfigurationRequest> request = createRequest(bucketName, null, deleteBucketTaggingConfigurationRequest, HttpMethodName.DELETE); request.addParameter("tagging", null); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketWebsiteConfiguration(java * .lang.String, com.amazonaws.services.s3.model.BucketWebsiteConfiguration) */ @Override public void setBucketWebsiteConfiguration(String bucketName, BucketWebsiteConfiguration configuration) throws AmazonClientException, AmazonServiceException { setBucketWebsiteConfiguration(new SetBucketWebsiteConfigurationRequest(bucketName, configuration)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketWebsiteConfiguration(com. * amazonaws.services.s3.model.SetBucketWebsiteConfigurationRequest) */ @Override public void setBucketWebsiteConfiguration( SetBucketWebsiteConfigurationRequest setBucketWebsiteConfigurationRequest) throws AmazonClientException, AmazonServiceException { String bucketName = setBucketWebsiteConfigurationRequest.getBucketName(); BucketWebsiteConfiguration configuration = setBucketWebsiteConfigurationRequest .getConfiguration(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting a bucket's website configuration"); assertParameterNotNull( configuration, "The bucket website configuration parameter must be specified when setting a bucket's website configuration"); if (configuration.getRedirectAllRequestsTo() == null) { assertParameterNotNull( configuration.getIndexDocumentSuffix(), "The bucket website configuration parameter must specify the index document suffix when setting a bucket's website configuration"); } Request<SetBucketWebsiteConfigurationRequest> request = createRequest(bucketName, null, setBucketWebsiteConfigurationRequest, HttpMethodName.PUT); request.addParameter("website", null); request.addHeader("Content-Type", "application/xml"); byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(configuration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketWebsiteConfiguration(java * .lang.String) */ @Override public void deleteBucketWebsiteConfiguration(String bucketName) throws AmazonClientException, AmazonServiceException { deleteBucketWebsiteConfiguration(new DeleteBucketWebsiteConfigurationRequest(bucketName)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketWebsiteConfiguration(com * .amazonaws.services.s3.model.DeleteBucketWebsiteConfigurationRequest) */ @Override public void deleteBucketWebsiteConfiguration( DeleteBucketWebsiteConfigurationRequest deleteBucketWebsiteConfigurationRequest) throws AmazonClientException, AmazonServiceException { String bucketName = deleteBucketWebsiteConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when deleting a bucket's website configuration"); Request<DeleteBucketWebsiteConfigurationRequest> request = createRequest(bucketName, null, deleteBucketWebsiteConfigurationRequest, HttpMethodName.DELETE); request.addParameter("website", null); request.addHeader("Content-Type", "application/xml"); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketNotificationConfiguration * (java * .lang.String,com.amazonaws.services.s3.model.BucketNotificationConfiguration * ) */ @Override public void setBucketNotificationConfiguration(String bucketName, BucketNotificationConfiguration bucketNotificationConfiguration) throws AmazonClientException, AmazonServiceException { setBucketNotificationConfiguration(new SetBucketNotificationConfigurationRequest( bucketName, bucketNotificationConfiguration)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketNotificationConfiguration * (com. * amazonaws.services.s3.model.SetBucketNotificationConfigurationRequest) */ @Override public void setBucketNotificationConfiguration( SetBucketNotificationConfigurationRequest setBucketNotificationConfigurationRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(setBucketNotificationConfigurationRequest, "The set bucket notification configuration request object must be specified."); String bucketName = setBucketNotificationConfigurationRequest.getBucketName(); BucketNotificationConfiguration bucketNotificationConfiguration = setBucketNotificationConfigurationRequest .getNotificationConfiguration(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting bucket notification configuration."); assertParameterNotNull( bucketNotificationConfiguration, "The notification configuration parameter must be specified when setting bucket notification configuration."); Request<SetBucketNotificationConfigurationRequest> request = createRequest(bucketName, null, setBucketNotificationConfigurationRequest, HttpMethodName.PUT); request.addParameter("notification", null); byte[] bytes = bucketConfigurationXmlFactory .convertToXmlByteArray(bucketNotificationConfiguration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketNotificationConfiguration * (java.lang.String) */ @Override public BucketNotificationConfiguration getBucketNotificationConfiguration(String bucketName) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(bucketName, "The bucket name parameter must be specified when querying notification configuration"); Request<GenericBucketRequest> request = createRequest(bucketName, null, new GenericBucketRequest(bucketName), HttpMethodName.GET); request.addParameter("notification", null); return invoke(request, new Unmarshallers.BucketNotificationConfigurationUnmarshaller(), bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketLoggingConfiguration(java * .lang.String) */ @Override public BucketLoggingConfiguration getBucketLoggingConfiguration(String bucketName) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(bucketName, "The bucket name parameter must be specified when requesting a bucket's logging status"); Request<GenericBucketRequest> request = createRequest(bucketName, null, new GenericBucketRequest(bucketName), HttpMethodName.GET); request.addParameter("logging", null); return invoke(request, new Unmarshallers.BucketLoggingConfigurationnmarshaller(), bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketLoggingConfiguration(com. * amazonaws.services.s3.SetBucketLoggingConfigurationRequest) */ @Override public void setBucketLoggingConfiguration( SetBucketLoggingConfigurationRequest setBucketLoggingConfigurationRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull( setBucketLoggingConfigurationRequest, "The set bucket logging configuration request object must be specified when enabling server access logging"); String bucketName = setBucketLoggingConfigurationRequest.getBucketName(); BucketLoggingConfiguration loggingConfiguration = setBucketLoggingConfigurationRequest .getLoggingConfiguration(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when enabling server access logging"); assertParameterNotNull(loggingConfiguration, "The logging configuration parameter must be specified when enabling server access logging"); Request<SetBucketLoggingConfigurationRequest> request = createRequest(bucketName, null, setBucketLoggingConfigurationRequest, HttpMethodName.PUT); request.addParameter("logging", null); byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(loggingConfiguration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); invoke(request, voidResponseHandler, bucketName, null); } @Override public BucketAccelerateConfiguration getBucketAccelerateConfiguration( String bucketName) throws AmazonServiceException, AmazonClientException { return getBucketAccelerateConfiguration(new GetBucketAccelerateConfigurationRequest( bucketName)); } @Override public BucketAccelerateConfiguration getBucketAccelerateConfiguration( GetBucketAccelerateConfigurationRequest getBucketAccelerateConfigurationRequest) throws AmazonServiceException, AmazonClientException { assertParameterNotNull(getBucketAccelerateConfigurationRequest, "getBucketAccelerateConfigurationRequest must be specified."); String bucketName = getBucketAccelerateConfigurationRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when querying accelerate configuration"); Request<GetBucketAccelerateConfigurationRequest> request = createRequest(bucketName, null, getBucketAccelerateConfigurationRequest, HttpMethodName.GET); request.addParameter("accelerate", null); return invoke(request, new Unmarshallers.BucketAccelerateConfigurationUnmarshaller(), bucketName, null); } @Override public void setBucketAccelerateConfiguration(String bucketName, BucketAccelerateConfiguration accelerateConfiguration) throws AmazonServiceException, AmazonClientException { setBucketAccelerateConfiguration(new SetBucketAccelerateConfigurationRequest( bucketName, accelerateConfiguration)); } @Override public void setBucketAccelerateConfiguration( SetBucketAccelerateConfigurationRequest setBucketAccelerateConfigurationRequest) throws AmazonServiceException, AmazonClientException { assertParameterNotNull(setBucketAccelerateConfigurationRequest, "setBucketAccelerateConfigurationRequest must be specified"); String bucketName = setBucketAccelerateConfigurationRequest.getBucketName(); BucketAccelerateConfiguration accelerateConfiguration = setBucketAccelerateConfigurationRequest .getAccelerateConfiguration(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when setting accelerate configuration."); assertParameterNotNull(accelerateConfiguration, "The bucket accelerate configuration parameter must be specified."); assertParameterNotNull(accelerateConfiguration.getStatus(), "The status parameter must be specified when updating bucket accelerate configuration."); Request<SetBucketAccelerateConfigurationRequest> request = createRequest( bucketName, null, setBucketAccelerateConfigurationRequest, HttpMethodName.PUT); request.addParameter("accelerate", null); byte[] bytes = bucketConfigurationXmlFactory.convertToXmlByteArray(accelerateConfiguration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#getBucketPolicy(java.lang.String) */ @Override public BucketPolicy getBucketPolicy(String bucketName) throws AmazonClientException, AmazonServiceException { return getBucketPolicy(new GetBucketPolicyRequest(bucketName)); } /* * (non-Javadoc) * @see com.amazonaws.services.s3.AmazonS3#setBucketPolicy(java.lang.String, * java.lang.String) */ @Override public void setBucketPolicy(String bucketName, String policyText) throws AmazonClientException, AmazonServiceException { 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<GenericBucketRequest> request = createRequest(bucketName, null, new GenericBucketRequest(bucketName), HttpMethodName.PUT); request.addParameter("policy", null); byte[] bytes = ServiceUtils.toByteArray(policyText); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketPolicy(java.lang.String) */ @Override public void deleteBucketPolicy(String bucketName) throws AmazonClientException, AmazonServiceException { deleteBucketPolicy(new DeleteBucketPolicyRequest(bucketName)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getBucketPolicy(com.amazonaws.services * .s3.model.GetBucketPolicyRequest) */ @Override public BucketPolicy getBucketPolicy( GetBucketPolicyRequest getBucketPolicyRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(getBucketPolicyRequest, "The request object must be specified when getting a bucket policy"); String bucketName = getBucketPolicyRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name must be specified when getting a bucket policy"); Request<GetBucketPolicyRequest> request = createRequest(bucketName, null, getBucketPolicyRequest, HttpMethodName.GET); request.addParameter("policy", null); BucketPolicy result = new BucketPolicy(); try { String policyText = invoke(request, new S3StringResponseHandler(), bucketName, null); result.setPolicyText(policyText); return result; } catch (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")) return result; throw ase; } } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#setBucketPolicy(com.amazonaws.services * .s3.model.SetBucketPolicyRequest) */ @Override public void setBucketPolicy(SetBucketPolicyRequest setBucketPolicyRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(setBucketPolicyRequest, "The request object must be specified when setting a bucket policy"); String bucketName = setBucketPolicyRequest.getBucketName(); 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<SetBucketPolicyRequest> request = createRequest(bucketName, null, setBucketPolicyRequest, HttpMethodName.PUT); request.addParameter("policy", null); byte[] bytes = ServiceUtils.toByteArray(policyText); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#deleteBucketPolicy(com.amazonaws.services * .s3.model.DeleteBucketPolicyRequest) */ @Override public void deleteBucketPolicy( DeleteBucketPolicyRequest deleteBucketPolicyRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(deleteBucketPolicyRequest, "The request object must be specified when deleting a bucket policy"); String bucketName = deleteBucketPolicyRequest.getBucketName(); assertParameterNotNull(bucketName, "The bucket name must be specified when deleting a bucket policy"); Request<DeleteBucketPolicyRequest> request = createRequest(bucketName, null, deleteBucketPolicyRequest, HttpMethodName.DELETE); request.addParameter("policy", null); invoke(request, voidResponseHandler, bucketName, null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#generatePresignedUrl(java.lang.String, * java.lang.String, java.util.Date) */ @Override public URL generatePresignedUrl(String bucketName, String key, Date expiration) throws AmazonClientException { return generatePresignedUrl(bucketName, key, expiration, HttpMethod.GET); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#generatePresignedUrl(java.lang.String, * java.lang.String, java.util.Date, com.amazonaws.HttpMethod) */ @Override public URL generatePresignedUrl(String bucketName, String key, Date expiration, HttpMethod method) throws AmazonClientException { GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, key, method); request.setExpiration(expiration); return generatePresignedUrl(request); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#generatePresignedUrl(com.amazonaws * .services.s3.model.GeneratePresignedUrlRequest) */ @Override public URL generatePresignedUrl(GeneratePresignedUrlRequest generatePresignedUrlRequest) throws AmazonClientException { assertParameterNotNull(generatePresignedUrlRequest, "The request parameter must be specified when generating a pre-signed URL"); String bucketName = generatePresignedUrlRequest.getBucketName(); String key = generatePresignedUrlRequest.getKey(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when generating a pre-signed URL"); assertParameterNotNull(generatePresignedUrlRequest.getMethod(), "The HTTP method request parameter must be specified when generating a pre-signed URL"); if (generatePresignedUrlRequest.getExpiration() == null) { generatePresignedUrlRequest.setExpiration( new Date(System.currentTimeMillis() + 1000 * 60 * 15)); } HttpMethodName httpMethod = HttpMethodName.valueOf(generatePresignedUrlRequest.getMethod() .toString()); // If the key starts with a slash character itself, the following method // will actually add another slash before the resource path to prevent // 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<GeneratePresignedUrlRequest> request = createRequest(bucketName, key, generatePresignedUrlRequest, httpMethod); for (Entry<String, String> entry : generatePresignedUrlRequest.getRequestParameters() .entrySet()) { request.addParameter(entry.getKey(), entry.getValue()); } if (generatePresignedUrlRequest.getContentType() != null) { request.addHeader(Headers.CONTENT_TYPE, generatePresignedUrlRequest.getContentType()); } if (generatePresignedUrlRequest.getContentMd5() != null) { request.addHeader(Headers.CONTENT_MD5, generatePresignedUrlRequest.getContentMd5()); } populateSseCpkRequestParameters(request, generatePresignedUrlRequest.getSSECustomerKey()); addResponseHeaderParameters(request, generatePresignedUrlRequest.getResponseHeaders()); Signer signer = createSigner(request, bucketName, key); if (signer instanceof Presigner) { // If we have a signer which knows how to presign requests, // delegate directly to it. ((Presigner) signer).presignRequest( request, awsCredentialsProvider.getCredentials(), generatePresignedUrlRequest.getExpiration() ); } else { // Otherwise use the default presigning method, which is hardcoded // to use QueryStringSigner. presignRequest( request, generatePresignedUrlRequest.getMethod(), bucketName, key, generatePresignedUrlRequest.getExpiration(), null); } // Remove the leading slash (if any) in the resource-path return ServiceUtils.convertRequestToUrl(request, true); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#abortMultipartUpload(com.amazonaws * .services.s3.model.AbortMultipartUploadRequest) */ @Override public void abortMultipartUpload(AbortMultipartUploadRequest abortMultipartUploadRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(abortMultipartUploadRequest, "The request parameter must be specified when aborting a multipart upload"); assertParameterNotNull(abortMultipartUploadRequest.getBucketName(), "The bucket name parameter must be specified when aborting a multipart upload"); assertParameterNotNull(abortMultipartUploadRequest.getKey(), "The key parameter must be specified when aborting a multipart upload"); assertParameterNotNull(abortMultipartUploadRequest.getUploadId(), "The upload ID parameter must be specified when aborting a multipart upload"); String bucketName = abortMultipartUploadRequest.getBucketName(); String key = abortMultipartUploadRequest.getKey(); Request<AbortMultipartUploadRequest> request = createRequest(bucketName, key, abortMultipartUploadRequest, HttpMethodName.DELETE); request.addParameter("uploadId", abortMultipartUploadRequest.getUploadId()); invoke(request, voidResponseHandler, bucketName, key); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#completeMultipartUpload(com.amazonaws * .services.s3.model.CompleteMultipartUploadRequest) */ @Override public CompleteMultipartUploadResult completeMultipartUpload( CompleteMultipartUploadRequest completeMultipartUploadRequest) throws AmazonClientException, AmazonServiceException { 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(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when completing a multipart upload"); assertParameterNotNull(key, "The key parameter must be specified when completing a multipart upload"); assertParameterNotNull(uploadId, "The upload ID parameter must be specified when completing a multipart upload"); assertParameterNotNull(completeMultipartUploadRequest.getPartETags(), "The part ETags parameter must be specified when completing a multipart upload"); Request<CompleteMultipartUploadRequest> request = createRequest(bucketName, key, completeMultipartUploadRequest, HttpMethodName.POST); request.addParameter("uploadId", uploadId); byte[] xml = RequestXmlFactory.convertToXmlByteArray(completeMultipartUploadRequest .getPartETags()); request.addHeader("Content-Type", "text/plain"); request.addHeader("Content-Length", String.valueOf(xml.length)); request.setContent(new ByteArrayInputStream(xml)); @SuppressWarnings("unchecked") ResponseHeaderHandlerChain<CompleteMultipartUploadHandler> responseHandler = new ResponseHeaderHandlerChain<CompleteMultipartUploadHandler>( // xml payload unmarshaller new Unmarshallers.CompleteMultipartUploadResultUnmarshaller(), // header handlers new ServerSideEncryptionHeaderHandler<CompleteMultipartUploadHandler>(), new ObjectExpirationHeaderHandler<CompleteMultipartUploadHandler>()); 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(); } } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#initiateMultipartUpload(com.amazonaws * .services.s3.model.InitiateMultipartUploadRequest) */ @Override public InitiateMultipartUploadResult initiateMultipartUpload( InitiateMultipartUploadRequest initiateMultipartUploadRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(initiateMultipartUploadRequest, "The request parameter must be specified when initiating a multipart upload"); assertParameterNotNull(initiateMultipartUploadRequest.getBucketName(), "The bucket name parameter must be specified when initiating a multipart upload"); assertParameterNotNull(initiateMultipartUploadRequest.getKey(), "The key parameter must be specified when initiating a multipart upload"); Request<InitiateMultipartUploadRequest> request = createRequest( initiateMultipartUploadRequest.getBucketName(), initiateMultipartUploadRequest.getKey(), initiateMultipartUploadRequest, HttpMethodName.POST); request.addParameter("uploads", null); if (initiateMultipartUploadRequest.getStorageClass() != null) request.addHeader(Headers.STORAGE_CLASS, initiateMultipartUploadRequest .getStorageClass().toString()); if (initiateMultipartUploadRequest.getRedirectLocation() != null) { request.addHeader(Headers.REDIRECT_LOCATION, initiateMultipartUploadRequest.getRedirectLocation()); } if (initiateMultipartUploadRequest.getAccessControlList() != null) { addAclHeaders(request, initiateMultipartUploadRequest.getAccessControlList()); } else if (initiateMultipartUploadRequest.getCannedACL() != null) { request.addHeader(Headers.S3_CANNED_ACL, initiateMultipartUploadRequest.getCannedACL() .toString()); } if (initiateMultipartUploadRequest.objectMetadata != null) { populateRequestMetadata(request, initiateMultipartUploadRequest.objectMetadata); } // Populate the SSE-CPK parameters to the request header populateSseCpkRequestParameters(request, initiateMultipartUploadRequest.getSSECustomerKey()); // Be careful that we don't send the object's total size as the content // length for the InitiateMultipartUpload request. setZeroContentLength(request); // Set the request content to be empty (but not null) to force the // runtime to pass // any query params in the query string and not the request body, to // keep S3 happy. request.setContent(new ByteArrayInputStream(new byte[0])); @SuppressWarnings("unchecked") ResponseHeaderHandlerChain<InitiateMultipartUploadResult> responseHandler = new ResponseHeaderHandlerChain<InitiateMultipartUploadResult>( // xml payload unmarshaller new Unmarshallers.InitiateMultipartUploadResultUnmarshaller(), // header handlers new ServerSideEncryptionHeaderHandler<InitiateMultipartUploadResult>()); return invoke(request, responseHandler, initiateMultipartUploadRequest.getBucketName(), initiateMultipartUploadRequest.getKey()); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#listMultipartUploads(com.amazonaws * .services.s3.model.ListMultipartUploadsRequest) */ @Override public MultipartUploadListing listMultipartUploads( ListMultipartUploadsRequest listMultipartUploadsRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(listMultipartUploadsRequest, "The request parameter must be specified when listing multipart uploads"); assertParameterNotNull(listMultipartUploadsRequest.getBucketName(), "The bucket name parameter must be specified when listing multipart uploads"); Request<ListMultipartUploadsRequest> request = createRequest( listMultipartUploadsRequest.getBucketName(), null, listMultipartUploadsRequest, HttpMethodName.GET); request.addParameter("uploads", null); if (listMultipartUploadsRequest.getKeyMarker() != null) request.addParameter("key-marker", listMultipartUploadsRequest.getKeyMarker()); if (listMultipartUploadsRequest.getMaxUploads() != null) request.addParameter("max-uploads", listMultipartUploadsRequest.getMaxUploads() .toString()); if (listMultipartUploadsRequest.getUploadIdMarker() != null) request.addParameter("upload-id-marker", listMultipartUploadsRequest.getUploadIdMarker()); if (listMultipartUploadsRequest.getDelimiter() != null) request.addParameter("delimiter", listMultipartUploadsRequest.getDelimiter()); if (listMultipartUploadsRequest.getPrefix() != null) request.addParameter("prefix", listMultipartUploadsRequest.getPrefix()); if (listMultipartUploadsRequest.getEncodingType() != null) request.addParameter("encoding-type", listMultipartUploadsRequest.getEncodingType()); return invoke(request, new Unmarshallers.ListMultipartUploadsResultUnmarshaller(), listMultipartUploadsRequest.getBucketName(), null); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#listParts(com.amazonaws.services.s3 * .model.ListPartsRequest) */ @Override public PartListing listParts(ListPartsRequest listPartsRequest) throws AmazonClientException, AmazonServiceException { assertParameterNotNull(listPartsRequest, "The request parameter must be specified when listing parts"); assertParameterNotNull(listPartsRequest.getBucketName(), "The bucket name parameter must be specified when listing parts"); assertParameterNotNull(listPartsRequest.getKey(), "The key parameter must be specified when listing parts"); assertParameterNotNull(listPartsRequest.getUploadId(), "The upload ID parameter must be specified when listing parts"); Request<ListPartsRequest> request = createRequest(listPartsRequest.getBucketName(), listPartsRequest.getKey(), listPartsRequest, HttpMethodName.GET); request.addParameter("uploadId", listPartsRequest.getUploadId()); if (listPartsRequest.getMaxParts() != null) request.addParameter("max-parts", listPartsRequest.getMaxParts().toString()); if (listPartsRequest.getPartNumberMarker() != null) request.addParameter("part-number-marker", listPartsRequest.getPartNumberMarker() .toString()); if (listPartsRequest.getEncodingType() != null) request.addParameter("encoding-type", listPartsRequest.getEncodingType()); return invoke(request, new Unmarshallers.ListPartsResultUnmarshaller(), listPartsRequest.getBucketName(), listPartsRequest.getKey()); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#uploadPart(com.amazonaws.services. * s3.model.UploadPartRequest) */ @Override public UploadPartResult uploadPart(UploadPartRequest uploadPartRequest) throws AmazonClientException, AmazonServiceException { 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(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when uploading a part"); assertParameterNotNull(key, "The key parameter must be specified when uploading a part"); assertParameterNotNull(uploadId, "The upload ID parameter must be specified when uploading a part"); assertParameterNotNull(partNumber, "The part number parameter must be specified when uploading a part"); assertParameterNotNull(partSize, "The part size parameter must be specified when uploading a part"); Request<UploadPartRequest> request = createRequest(bucketName, key, uploadPartRequest, HttpMethodName.PUT); request.addParameter("uploadId", uploadId); request.addParameter("partNumber", Integer.toString(partNumber)); addHeaderIfNotNull(request, Headers.CONTENT_MD5, uploadPartRequest.getMd5Digest()); request.addHeader(Headers.CONTENT_LENGTH, Long.toString(partSize)); request.addHeader("Expect", "100-continue"); // Populate the SSE-CPK parameters to the request header populateSseCpkRequestParameters(request, uploadPartRequest.getSSECustomerKey()); InputStream inputStream = null; if (uploadPartRequest.getInputStream() != null) { inputStream = uploadPartRequest.getInputStream(); } else if (uploadPartRequest.getFile() != null) { try { inputStream = new InputSubstream(new RepeatableFileInputStream( uploadPartRequest.getFile()), uploadPartRequest.getFileOffset(), partSize, true); } catch (FileNotFoundException e) { throw new IllegalArgumentException("The specified file doesn't exist", e); } } else { throw new IllegalArgumentException( "A File or InputStream must be specified when uploading part"); } MD5DigestCalculatingInputStream md5DigestStream = null; if (uploadPartRequest.getMd5Digest() == null && !ServiceUtils.skipMd5CheckPerRequest(uploadPartRequest)) { /* * If the user hasn't set the content MD5, then we don't want to * buffer the whole stream in memory just to calculate it. Instead, * we can calculate it on the fly and validate it with the returned * ETag from the object upload. */ inputStream = md5DigestStream = new MD5DigestCalculatingInputStream(inputStream); } /* * This is compatible with progress listener set by either the legacy * method UploadPartRequest#setProgressListener or the new method * UploadPartRequest#setGeneralProgressListener. */ ProgressListener progressListener = uploadPartRequest.getGeneralProgressListener(); ProgressListenerCallbackExecutor progressListenerCallbackExecutor = ProgressListenerCallbackExecutor .wrapListener(progressListener); if (progressListenerCallbackExecutor != null) { inputStream = new ProgressReportingInputStream(inputStream, progressListenerCallbackExecutor); fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.PART_STARTED_EVENT_CODE); } try { request.setContent(inputStream); 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()); if (!Arrays.equals(clientSideHash, serverSideHash)) { throw new AmazonClientException( "Unable to verify integrity of data upload. " + "Client calculated content hash didn't match hash calculated by Amazon S3. " + "You may need to delete the data stored in Amazon S3."); } } fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.PART_COMPLETED_EVENT_CODE); 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()); return result; } catch (AmazonClientException ace) { fireProgressEvent(progressListenerCallbackExecutor, ProgressEvent.PART_FAILED_EVENT_CODE); // Leaving this here in case anyone is depending on it, but it's // inconsistent with other methods which only generate one of // COMPLETED_EVENT_CODE or FAILED_EVENT_CODE. // fireProgressEvent(progressListenerCallbackExecutor, // ProgressEvent.PART_COMPLETED_EVENT_CODE); throw ace; } finally { if (inputStream != null) { try { inputStream.close(); } catch (Exception e) { } } } } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#getResponseMetadataForRequest(com. * amazonaws.AmazonWebServiceRequest) */ @Override public S3ResponseMetadata getCachedResponseMetadata(AmazonWebServiceRequest request) { return (S3ResponseMetadata) client.getResponseMetadataForRequest(request); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#restoreObject(com.amazonaws.services * .s3.model.RestoreObjectRequest) */ @Override public void restoreObject(RestoreObjectRequest restoreObjectRequest) throws AmazonServiceException { String bucketName = restoreObjectRequest.getBucketName(); String key = restoreObjectRequest.getKey(); String versionId = restoreObjectRequest.getVersionId(); int expirationIndays = restoreObjectRequest.getExpirationInDays(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified when copying a glacier object"); assertParameterNotNull(key, "The key parameter must be specified when copying a glacier object"); if (expirationIndays == -1) { throw new IllegalArgumentException( "The expiration in days parameter must be specified when copying a glacier object"); } Request<RestoreObjectRequest> request = createRequest(bucketName, key, restoreObjectRequest, HttpMethodName.POST); request.addParameter("restore", null); if (versionId != null) { request.addParameter("versionId", versionId); } 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); request.addHeader("Content-MD5", md5Base64); } catch (Exception e) { throw new AmazonClientException("Couldn't compute md5 sum", e); } invoke(request, voidResponseHandler, bucketName, key); } /** * (non-Javadoc) * * @see * com.amazonaws.services.s3.AmazonS3#copyGlacierObject((java.lang.String * , java.lang.String, int) */ @Override public void restoreObject(String bucketName, String key, int expirationInDays) throws AmazonServiceException { restoreObject(new RestoreObjectRequest(bucketName, key, expirationInDays)); } /* * Private Interface */ /** * <p> * Asserts that the specified parameter value is not <code>null</code> and * if it is, throws an <code>IllegalArgumentException</code> with the * specified error message. * </p> * * @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. * * @param progressListenerCallbackExecutor The listener callback executor. * @param eventType The type of event to fire. */ private void fireProgressEvent( final ProgressListenerCallbackExecutor progressListenerCallbackExecutor, final int eventType) { if (progressListenerCallbackExecutor == null) return; ProgressEvent event = new ProgressEvent(0); event.setEventCode(eventType); progressListenerCallbackExecutor.progressChanged(event); } /** * <p> * Gets the Amazon S3 {@link AccessControlList} (ACL) for the specified * resource. (bucket if only the bucketName parameter is specified, * otherwise the object with the specified key in the bucket). * </p> * * @param bucketName The name of the bucket whose ACL should be returned if * the key parameter is not specified, otherwise the bucket * containing the specified key. * @param key The object key whose ACL should be retrieve. If not specified, * the bucket's ACL is returned. * @param versionId The version ID of the object version whose ACL is being * retrieved. * @param originalRequest The original, user facing request object. * @return The S3 ACL for the specified resource. */ private AccessControlList getAcl(String bucketName, String key, String versionId, AmazonWebServiceRequest originalRequest) { if (originalRequest == null) originalRequest = new GenericBucketRequest(bucketName); Request<AmazonWebServiceRequest> request = createRequest(bucketName, key, originalRequest, HttpMethodName.GET); request.addParameter("acl", null); if (versionId != null) request.addParameter("versionId", versionId); return invoke(request, new Unmarshallers.AccessControlListUnmarshaller(), bucketName, key); } /** * Sets the Canned ACL for the specified resource in S3. If only bucketName * is specified, the canned ACL will be applied to the bucket, otherwise if * bucketName and key are specified, the canned 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 * 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 cannedAcl The canned ACL to apply to the resource. * @param originalRequest The original, user facing request object. */ private void setAcl(String bucketName, String key, String versionId, CannedAccessControlList cannedAcl, AmazonWebServiceRequest originalRequest) { if (originalRequest == null) originalRequest = new GenericBucketRequest(bucketName); Request<AmazonWebServiceRequest> request = createRequest(bucketName, key, originalRequest, HttpMethodName.PUT); request.addParameter("acl", null); request.addHeader(Headers.S3_CANNED_ACL, cannedAcl.toString()); if (versionId != null) request.addParameter("versionId", versionId); invoke(request, voidResponseHandler, bucketName, key); } /** * Sets the ACL for the specified resource in S3. If only bucketName is * 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 * 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. */ private void setAcl(String bucketName, String key, String versionId, AccessControlList acl, AmazonWebServiceRequest originalRequest) { if (originalRequest == null) originalRequest = new GenericBucketRequest(bucketName); Request<AmazonWebServiceRequest> request = createRequest(bucketName, key, originalRequest, HttpMethodName.PUT); request.addParameter("acl", null); if (versionId != null) request.addParameter("versionId", versionId); byte[] aclAsXml = new AclXmlFactory().convertToXmlByteArray(acl); request.addHeader("Content-Type", "text/plain"); request.addHeader("Content-Length", String.valueOf(aclAsXml.length)); request.setContent(new ByteArrayInputStream(aclAsXml)); invoke(request, voidResponseHandler, bucketName, key); } /** * Returns a "complete" S3 specific signer, taking into the S3 bucket, key, * and the current S3 client configuration into account. */ 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; } 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 { v4Signer.setRegionName(regionOverride); } 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 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; } // Upgrade to Sigv4 if using non-default region. if (!endpoint.getHost().endsWith(Constants.S3_HOSTNAME)) { return true; } // Go with the default (SigV4 only if we know we're talking to an // endpoint that requires SigV4). return false; } /** * Pre-signs the specified request, using a signature query-string * parameter. * * @param request The request to sign. * @param methodName The HTTP method (GET, PUT, DELETE, HEAD) for the * specified request. * @param bucketName The name of the bucket involved in the request. If the * request is not an operation on a bucket this parameter should * be null. * @param key The object key involved in the request. If the request is not * an operation on an object, this parameter should be null. * @param expiration The time at which the signed request is no longer * valid, and will stop working. * @param subResource The optional sub-resource being requested as part of * the request (e.g. "location", "acl", "logging", or "torrent"). */ protected <T> void presignRequest(Request<T> request, HttpMethod methodName, String bucketName, String key, Date expiration, String subResource) { // Run any additional request handlers if present beforeRequest(request); String resourcePath = "/" + ((bucketName != null) ? bucketName + "/" : "") + ((key != null) ? HttpUtils.urlEncode(key, true) : "") + ((subResource != null) ? "?" + subResource : ""); // Make sure the resource-path for signing does not contain // any consecutive "/"s. // Note that we should also follow the same rule to escape // consecutive "/"s when generating the presigned URL. // See ServiceUtils#convertRequestToUrl(...) resourcePath = resourcePath.replaceAll("(?<=/)/", "%2F"); AWSCredentials credentials = awsCredentialsProvider.getCredentials(); AmazonWebServiceRequest originalRequest = request.getOriginalRequest(); if (originalRequest != null && originalRequest.getRequestCredentials() != null) { credentials = originalRequest.getRequestCredentials(); } new S3QueryStringSigner(methodName.toString(), resourcePath, expiration).sign(request, credentials); // The Amazon S3 DevPay token header is a special exception and can be // safely moved // from the request's headers into the query string to ensure that it // 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); request.addParameter(Headers.SECURITY_TOKEN, value); request.getHeaders().remove(Headers.SECURITY_TOKEN); } } private <T> void beforeRequest(Request<T> request) { if (requestHandler2s != null) { for (RequestHandler2 requestHandler2 : requestHandler2s) { requestHandler2.beforeRequest(request); } } } /** * Converts the current endpoint set for this client into virtual addressing * style, by placing the name of the specified bucket before the S3 service * endpoint. * * @param bucketName The name of the bucket to use in the virtual addressing * style of the returned URI. * @return A new URI, creating from the current service endpoint URI and the * specified bucket. */ private URI convertToVirtualHostEndpoint(String bucketName) { try { return new URI(endpoint.getScheme() + "://" + bucketName + "." + endpoint.getAuthority()); } catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid bucket name: " + bucketName, e); } } /** * <p> * Populates the specified request object with the appropriate headers from * the {@link ObjectMetadata} object. * </p> * * @param request The request to populate with headers. * @param metadata The metadata containing the header information to include * in the request. */ protected static void populateRequestMetadata(Request<?> request, ObjectMetadata metadata) { Map<String, Object> rawMetadata = metadata.getRawMetadata(); if (rawMetadata.get(Headers.SERVER_SIDE_ENCRYPTION_KMS_KEY_ID) != null && !ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION.equals(rawMetadata .get(Headers.SERVER_SIDE_ENCRYPTION))) { throw new IllegalArgumentException( "If you specify a KMS key id for server side encryption, you must also set the SSEAlgorithm to ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION"); } if (rawMetadata != null) { for (Entry<String, Object> entry : rawMetadata.entrySet()) { request.addHeader(entry.getKey(), entry.getValue().toString()); } } Date httpExpiresDate = metadata.getHttpExpiresDate(); if (httpExpiresDate != null) { request.addHeader(Headers.EXPIRES, DateUtils.formatRFC822Date(httpExpiresDate)); } Map<String, String> userMetadata = metadata.getUserMetadata(); if (userMetadata != null) { for (Entry<String, String> entry : userMetadata.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (key != null) key = key.trim(); if (value != null) value = value.trim(); request.addHeader(Headers.S3_USER_METADATA_PREFIX + key, value); } } } /** * <p> * Populates the specified request with the specified Multi-Factor * Authentication (MFA) details. This includes the MFA header with device * serial number and generated token. Since all requests which include the * MFA header must be sent over HTTPS, this operation also configures the * request object to use HTTPS instead of HTTP. * </p> * * @param request The request to populate. * @param mfa The Multi-Factor Authentication information. */ private void populateRequestWithMfaDetails(Request<?> request, MultiFactorAuthentication mfa) { if (mfa == null) return; String endpoint = request.getEndpoint().toString(); if (endpoint.startsWith("http://")) { 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"); } request.addHeader(Headers.S3_MFA, mfa.getDeviceSerialNumber() + " " + mfa.getToken()); } /** * <p> * Populates the specified request with the numerous options available in * <code>CopyObjectRequest</code>. * </p> * * @param request The request to populate with headers to represent all the * options expressed in the <code>CopyObjectRequest</code> * object. * @param copyObjectRequest The object containing all the options for * copying an object in Amazon S3. */ private static void populateRequestWithCopyObjectParameters( Request<? extends AmazonWebServiceRequest> request, CopyObjectRequest copyObjectRequest) { String copySourceHeader = "/" + HttpUtils.urlEncode(copyObjectRequest.getSourceBucketName(), true) + "/" + HttpUtils.urlEncode(copyObjectRequest.getSourceKey(), true); if (copyObjectRequest.getSourceVersionId() != null) { copySourceHeader += "?versionId=" + copyObjectRequest.getSourceVersionId(); } request.addHeader("x-amz-copy-source", copySourceHeader); addDateHeader(request, Headers.COPY_SOURCE_IF_MODIFIED_SINCE, copyObjectRequest.getModifiedSinceConstraint()); addDateHeader(request, Headers.COPY_SOURCE_IF_UNMODIFIED_SINCE, copyObjectRequest.getUnmodifiedSinceConstraint()); addStringListHeader(request, Headers.COPY_SOURCE_IF_MATCH, copyObjectRequest.getMatchingETagConstraints()); addStringListHeader(request, Headers.COPY_SOURCE_IF_NO_MATCH, copyObjectRequest.getNonmatchingETagConstraints()); if (copyObjectRequest.getAccessControlList() != null) { addAclHeaders(request, copyObjectRequest.getAccessControlList()); } else if (copyObjectRequest.getCannedAccessControlList() != null) { request.addHeader(Headers.S3_CANNED_ACL, copyObjectRequest.getCannedAccessControlList().toString()); } if (copyObjectRequest.getStorageClass() != null) { request.addHeader(Headers.STORAGE_CLASS, copyObjectRequest.getStorageClass()); } if (copyObjectRequest.getRedirectLocation() != null) { request.addHeader(Headers.REDIRECT_LOCATION, copyObjectRequest.getRedirectLocation()); } 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()); } /** * <p> * Populates the specified request with the numerous options available in * <code>CopyObjectRequest</code>. * </p> * * @param request The request to populate with headers to represent all the * options expressed in the <code>CopyPartRequest</code> object. * @param copyPartRequest The object containing all the options for copying * an object in Amazon S3. */ private static void populateRequestWithCopyPartParameters(Request<?> request, CopyPartRequest copyPartRequest) { String copySourceHeader = "/" + HttpUtils.urlEncode(copyPartRequest.getSourceBucketName(), true) + "/" + HttpUtils.urlEncode(copyPartRequest.getSourceKey(), true); if (copyPartRequest.getSourceVersionId() != null) { copySourceHeader += "?versionId=" + copyPartRequest.getSourceVersionId(); } request.addHeader("x-amz-copy-source", copySourceHeader); addDateHeader(request, Headers.COPY_SOURCE_IF_MODIFIED_SINCE, copyPartRequest.getModifiedSinceConstraint()); addDateHeader(request, Headers.COPY_SOURCE_IF_UNMODIFIED_SINCE, copyPartRequest.getUnmodifiedSinceConstraint()); addStringListHeader(request, Headers.COPY_SOURCE_IF_MATCH, copyPartRequest.getMatchingETagConstraints()); addStringListHeader(request, Headers.COPY_SOURCE_IF_NO_MATCH, copyPartRequest.getNonmatchingETagConstraints()); if (copyPartRequest.getFirstByte() != null && copyPartRequest.getLastByte() != null) { 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()); } /** * <p> * Populates the specified request with the numerous attributes available in * <code>SSEWithCustomerKeyRequest</code>. * </p> * * @param request The request to populate with headers to represent all the * options expressed in the * <code>ServerSideEncryptionWithCustomerKeyRequest</code> * object. * @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) return; addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM, sseKey.getAlgorithm()); addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY, sseKey.getKey()); addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, sseKey.getMd5()); // Calculate the MD5 hash of the encryption key and fill it in the // 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); request.addHeader(Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, Md5Utils.md5AsBase64(encryptionKey)); } } private static void populateSourceSseCpkRequestParameters(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, sseKey.getAlgorithm()); addHeaderIfNotNull(request, Headers.COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY, sseKey.getKey()); addHeaderIfNotNull(request, Headers.COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, sseKey.getMd5()); // Calculate the MD5 hash of the encryption key and fill it in the // 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); request.addHeader(Headers.COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, Md5Utils.md5AsBase64(encryptionKey)); } } /** * 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. */ private static void addHeaderIfNotNull(Request<?> request, String header, String value) { if (value != null) { request.addHeader(header, value); } } /** * <p> * Adds the specified date header in RFC 822 date format to the specified * request. This method will not add a date header if the specified date * value is <code>null</code>. * </p> * * @param request The request to add the header to. * @param header The header name. * @param value The header value. */ private static void addDateHeader(Request<?> request, String header, Date value) { if (value != null) { request.addHeader(header, ServiceUtils.formatRfc822Date(value)); } } /** * <p> * Adds the specified string list header, joined together separated with * commas, to the specified request. This method will not add a string list * header if the specified values are <code>null</code> or empty. * </p> * * @param request The request to add the header to. * @param header The header name. * @param values The list of strings to join together for the header value. */ private static void addStringListHeader(Request<?> request, String header, List<String> values) { if (values != null && !values.isEmpty()) { request.addHeader(header, ServiceUtils.join(values)); } } /** * <p> * Adds response headers parameters to the request given, if non-null. * </p> * * @param request The request to add the response header parameters to. * @param responseHeaders The full set of response headers to add, or null * for none. */ private static void addResponseHeaderParameters(Request<?> request, ResponseHeaderOverrides responseHeaders) { if (responseHeaders != null) { if (responseHeaders.getCacheControl() != null) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CACHE_CONTROL, responseHeaders.getCacheControl()); } if (responseHeaders.getContentDisposition() != null) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_DISPOSITION, responseHeaders.getContentDisposition()); } if (responseHeaders.getContentEncoding() != null) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_ENCODING, responseHeaders.getContentEncoding()); } if (responseHeaders.getContentLanguage() != null) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_LANGUAGE, responseHeaders.getContentLanguage()); } if (responseHeaders.getContentType() != null) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_TYPE, responseHeaders.getContentType()); } if (responseHeaders.getExpires() != null) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_EXPIRES, responseHeaders.getExpires()); } } } /** * Returns the URL to the key in the bucket given, using the client's scheme * and endpoint. Returns null if the given bucket and key cannot be * converted to a URL. */ public String getResourceUrl(String bucketName, String key) { try { return getUrl(bucketName, key).toString(); } catch (Exception e) { return null; } } /** * Returns an URL for the object stored in the specified bucket and key. * <p> * 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. */ public URL getUrl(String bucketName, String key) { Request<?> request = new DefaultRequest<Object>(Constants.S3_SERVICE_NAME); configRequest(request, bucketName, key); return ServiceUtils.convertRequestToUrl(request); } public Region getRegion() { String authority = super.endpoint.getAuthority(); if (Constants.S3_HOSTNAME.equals(authority)) { return Region.US_Standard; } Matcher m = Region.S3_REGIONAL_ENDPOINT_PATTERN.matcher(authority); if (m.matches()) { return Region.fromValue(m.group(1)); } else { throw new IllegalStateException("S3 client with invalid S3 endpoint configured"); } } /** * Creates and initializes a new request object for the specified S3 * resource. This method is responsible for determining the right way to * address resources. For example, bucket names that are not DNS addressable * cannot be addressed in V2, virtual host, style, and instead must use V1, * path style. The returned request object has the service name, endpoint * and resource path correctly populated. Callers can take the request, add * any additional headers or parameters, then sign and execute the request. * * @param bucketName An optional parameter indicating the name of the bucket * containing the resource involved in the request. * @param key An optional parameter indicating the key under which the * desired resource is stored in the specified bucket. * @param originalRequest The original request, as created by the user. * @param httpMethod The HTTP method to use when sending the request. * @return A new request object, populated with endpoint, resource path, and * service name, ready for callers to populate any additional * headers or parameters, and execute. */ protected <X extends AmazonWebServiceRequest> Request<X> createRequest(String bucketName, String key, X originalRequest, HttpMethodName httpMethod) { Request<X> request = new DefaultRequest<X>(originalRequest, Constants.S3_SERVICE_NAME); request.setHttpMethod(httpMethod); configRequest(request, bucketName, key); return request; } /** * 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. */ 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 : "")); } } } 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; } private <X, Y extends AmazonWebServiceRequest> X invoke(Request<Y> request, Unmarshaller<X, InputStream> unmarshaller, String bucketName, String key) { return invoke(request, new S3XmlResponseHandler<X>(unmarshaller), bucketName, key); } @Override protected final ExecutionContext createExecutionContext(AmazonWebServiceRequest req) { boolean isMetricsEnabled = isRequestMetricsEnabled(req) || isProfilingEnabled(); return new S3ExecutionContext(requestHandler2s, isMetricsEnabled, this); } private <X, Y extends AmazonWebServiceRequest> X invoke(Request<Y> request, HttpResponseHandler<AmazonWebServiceResponse<X>> responseHandler, String bucket, String key) { AmazonWebServiceRequest originalRequest = request.getOriginalRequest(); ExecutionContext executionContext = createExecutionContext(originalRequest); 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 // timing measurement should start as close to the top of the call // stack of the service client method as possible) // but definitely a safe compromise for S3 at least for now. // We can incrementally make it more elaborate should the need arise // for individual method. awsRequestMetrics.startEvent(Field.ClientExecuteTime); Response<X> response = null; try { request.setTimeOffset(timeOffset); /* * The string we sign needs to include the exact headers that we * send with the request, but the client runtime layer adds the * Content-Type header before the request is sent if one isn't set, * so we have to set something here otherwise the request will fail. */ if (!request.getHeaders().containsKey(Headers.CONTENT_TYPE)) { request.addHeader(Headers.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=utf-8"); } AWSCredentials credentials = awsCredentialsProvider.getCredentials(); if (originalRequest.getRequestCredentials() != null) { credentials = originalRequest.getRequestCredentials(); } executionContext.setSigner(createSigner(request, bucket, key)); executionContext.setCredentials(credentials); response = client.execute(request, responseHandler, errorResponseHandler, executionContext); return response.getAwsResponse(); } finally { endClientExecution(awsRequestMetrics, request, response); } } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#enableRequesterPays(java.lang.String) */ @Override public void enableRequesterPays(String bucketName) { RequestPaymentConfiguration configuration = new RequestPaymentConfiguration( Payer.Requester); setBucketRequestPayment(new SetRequestPaymentConfigurationRequest( bucketName, configuration)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#disableRequesterPays(java.lang.String) */ @Override public void disableRequesterPays(String bucketName) { RequestPaymentConfiguration configuration = new RequestPaymentConfiguration( Payer.BucketOwner); setBucketRequestPayment(new SetRequestPaymentConfigurationRequest( bucketName, configuration)); } /* * (non-Javadoc) * @see * com.amazonaws.services.s3.AmazonS3#isRequesterPaysEnabled(java.lang.String * ) */ @Override public boolean isRequesterPaysEnabled(String bucketName) { RequestPaymentConfiguration configuration = getBucketRequestPayment(new GetRequestPaymentConfigurationRequest( bucketName)); return (configuration.getPayer() == Payer.Requester); } /** * Sets the request payment configuration for a given Amazon S3 bucket. This * operation can be done only by the owner of the Amazon S3 bucket. * <p> * When the request payment configuration for a Amazon S3 bucket is set to * <code>Requester</code>, the requester instead of the bucket owner pays * the cost of the request and the data download from the bucket. The bucket * owner always pays the cost of storing data. */ private void setBucketRequestPayment( SetRequestPaymentConfigurationRequest setRequestPaymentConfigurationRequest) { String bucketName = setRequestPaymentConfigurationRequest .getBucketName(); RequestPaymentConfiguration configuration = setRequestPaymentConfigurationRequest .getConfiguration(); assertParameterNotNull(bucketName, "The bucket name parameter must be specified while setting the Requester Pays."); assertParameterNotNull( configuration, "The request payment configuration parameter must be specified when setting the Requester Pays."); Request<SetRequestPaymentConfigurationRequest> request = createRequest( bucketName, null, setRequestPaymentConfigurationRequest, HttpMethodName.PUT); request.addParameter("requestPayment", null); request.addHeader("Content-Type", "application/xml"); byte[] bytes = requestPaymentConfigurationXmlFactory .convertToXmlByteArray(configuration); request.addHeader(Headers.CONTENT_LENGTH, String.valueOf(bytes.length)); request.setContent(new ByteArrayInputStream(bytes)); invoke(request, voidResponseHandler, bucketName, null); } /** * Retrieves the request payment configuration for a given Amazon S3 bucket. * <p> * When the request payment configuration for a Amazon S3 bucket is * <code>Requester</code>, the requester instead of the bucket owner pays * the cost of the request and the data download from the bucket. The bucket * owner always pays the cost of storing data. */ private RequestPaymentConfiguration getBucketRequestPayment( GetRequestPaymentConfigurationRequest getRequestPaymentConfigurationRequest) { String bucketName = getRequestPaymentConfigurationRequest .getBucketName(); assertParameterNotNull( bucketName, "The bucket name parameter must be specified while getting the Request Payment Configuration."); Request<GetRequestPaymentConfigurationRequest> request = createRequest( bucketName, null, getRequestPaymentConfigurationRequest, HttpMethodName.GET); request.addParameter("requestPayment", null); request.addHeader("Content-Type", "application/xml"); return invoke(request, new Unmarshallers.RequestPaymentConfigurationUnmarshaller(), bucketName, null); } private void setZeroContentLength(Request<?> req) { // https://github.com/aws/aws-sdk-java/pull/215 // http://aws.amazon.com/articles/1109#14 req.addHeader(Headers.CONTENT_LENGTH, String.valueOf(0)); } /** * The input stream doesn't support mark, and the content length is unknown. * So we buffer the content of the input stream up to 256k. If it exceeds * the buffer size, an AmazonClientException will be thrown. * * @param is input stream * @return a byte array input stream */ private ByteArrayInputStream toByteArray(InputStream is) { // 256k buffer int size = 1024 * 256; byte[] buf = new byte[size]; int len = 0; try { int available = size; int read; while (available > 0 && (read = is.read(buf, len, available)) != -1) { len += read; available -= read; } if (is.read() != -1) { throw new AmazonClientException("Input stream exceeds 256k buffer."); } is.close(); } catch (IOException ioe) { throw new AmazonClientException("Failed to read from inputstream", ioe); } return new ByteArrayInputStream(buf, 0, len); } @Override public void setBucketReplicationConfiguration(String bucketName, BucketReplicationConfiguration configuration) throws AmazonServiceException, AmazonClientException { setBucketReplicationConfiguration(new SetBucketReplicationConfigurationRequest( bucketName, configuration)); } @Override public void setBucketReplicationConfiguration( SetBucketReplicationConfigurationRequest setBucketReplicationConfigurationRequest) throws AmazonServiceException, AmazonClientException { assertParameterNotNull(setBucketReplicationConfigurationRequest, "The set bucket replication configuration request object must be specified."); final String bucketName = setBucketReplicationConfigurationRequest .getBucketName(); final BucketReplicationConfiguration bucketReplicationConfiguration = setBucketReplicationConfigurationRequest .getReplicationConfiguration(); assertParameterNotNull( bucketName, "The bucket name parameter must be specified when setting replication configuration."); assertParameterNotNull( bucketReplicationConfiguration, "The replication configuration parameter must be specified when setting replication configuration."); Request<SetBucketReplicationConfigurationRequest> request = createRequest( bucketName, null, setBucketReplicationConfigurationRequest, HttpMethodName.PUT); request.addParameter("replication", null); final byte[] bytes = bucketConfigurationXmlFactory .convertToXmlByteArray(bucketReplicationConfiguration); request.addHeader("Content-Length", String.valueOf(bytes.length)); request.addHeader("Content-Type", "application/xml"); request.setContent(new ByteArrayInputStream(bytes)); try { request.addHeader("Content-MD5", BinaryUtils.toBase64(Md5Utils.computeMD5Hash(bytes))); } catch (Exception e) { throw new AmazonClientException( "Not able to compute MD5 of the replication rule configuration. Exception Message : " + e.getMessage(), e); } invoke(request, voidResponseHandler, bucketName, null); } @Override public BucketReplicationConfiguration getBucketReplicationConfiguration( String bucketName) throws AmazonServiceException, AmazonClientException { return getBucketReplicationConfiguration( new GetBucketReplicationConfigurationRequest(bucketName)); } @Override public BucketReplicationConfiguration getBucketReplicationConfiguration( GetBucketReplicationConfigurationRequest getBucketReplicationConfigurationRequest) throws AmazonServiceException, AmazonClientException { assertParameterNotNull( getBucketReplicationConfigurationRequest, "The bucket request parameter must be specified when retrieving replication configuration"); String bucketName = getBucketReplicationConfigurationRequest.getBucketName(); assertParameterNotNull( bucketName, "The bucket request must specify a bucket name when retrieving replication configuration"); Request<GetBucketReplicationConfigurationRequest> request = createRequest(bucketName, null, getBucketReplicationConfigurationRequest, HttpMethodName.GET); request.addParameter("replication", null); return invoke(request, new Unmarshallers.BucketReplicationConfigurationUnmarshaller(), bucketName, null); } @Override public void deleteBucketReplicationConfiguration(String bucketName) throws AmazonServiceException, AmazonClientException { deleteBucketReplicationConfiguration( new DeleteBucketReplicationConfigurationRequest(bucketName)); } @Override public void deleteBucketReplicationConfiguration( DeleteBucketReplicationConfigurationRequest deleteBucketReplicationConfigurationRequest) throws AmazonServiceException, AmazonClientException { final String bucketName = deleteBucketReplicationConfigurationRequest.getBucketName(); assertParameterNotNull( bucketName, "The bucket name parameter must be specified when deleting replication configuration"); Request<DeleteBucketReplicationConfigurationRequest> request = createRequest(bucketName, null, deleteBucketReplicationConfigurationRequest, HttpMethodName.DELETE); request.addParameter("replication", null); invoke(request, voidResponseHandler, bucketName, null); } }