com.amazonaws.mobileconnectors.s3.transfermanager.internal.CopyCallable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aws-sdk-android Show documentation
Show all versions of aws-sdk-android Show documentation
Gradle project for AWS SDK Android
The newest version!
/*
* Copyright 2010-2015 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.mobileconnectors.s3.transfermanager.internal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.amazonaws.Request;
import com.amazonaws.event.ProgressEvent;
import com.amazonaws.event.ProgressListenerCallbackExecutor;
import com.amazonaws.event.ProgressListenerChain;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.Headers;
import com.amazonaws.services.s3.model.AbortMultipartUploadRequest;
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.InitiateMultipartUploadRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PartETag;
import com.amazonaws.services.s3.model.StorageClass;
import com.amazonaws.mobileconnectors.s3.transfermanager.TransferManager;
import com.amazonaws.mobileconnectors.s3.transfermanager.TransferManagerConfiguration;
import com.amazonaws.mobileconnectors.s3.transfermanager.Transfer.TransferState;
import com.amazonaws.mobileconnectors.s3.transfermanager.model.CopyResult;
import com.amazonaws.util.DateUtils;
/**
* An implementation of the Callable interface that helps
* TransferManager
in carrying out the copy requests. The call
* method checks if multi-part copy request can be carried out on the Amazon S3
* object. This is done by checking the size of the Amazon S3 object being
* copied.
*
*
* If the size of the object is greater than the preferred size limit, then copy
* operation is carried out as multi part copy requests to the server.
*
*
* If the size of the object is less than the preferred size limit, the copy
* operation is carried out in a single request where the Amazon S3 object is
* transferred as one chunk from the source bucket to the destination bucket.
*
*
*/
public class CopyCallable implements Callable {
/**
* A reference to the Amazon S3 client using which copy or copy part
* requests are initiated.
*/
private final AmazonS3 s3;
/** Thread pool used during multi-part copy is performed. */
private final ExecutorService threadPool;
/** A reference to the original copy request received. */
private final CopyObjectRequest copyObjectRequest;
/** Upload id to be used when sending copy part requests. */
private String multipartUploadId;
/** Metadata of the object in the source bucket to be copied. */
private final ObjectMetadata metadata;
private final CopyImpl copy;
private static final Log log = LogFactory.getLog(CopyCallable.class);
/**
* TransferManager
configuration that provides details on when
* to use multi-part copy, part size etc.,
*/
private final TransferManagerConfiguration configuration;
/**
* A list of future objects to be returned when multi-part copy is
* initiated.
*/
private final List> futures = new ArrayList>();
private final ProgressListenerCallbackExecutor progressListenerChainCallbackExecutor;
public CopyCallable(TransferManager transferManager,
ExecutorService threadPool, CopyImpl copy,
CopyObjectRequest copyObjectRequest, ObjectMetadata metadata,
ProgressListenerChain progressListenerChain) {
this.s3 = transferManager.getAmazonS3Client();
this.configuration = transferManager.getConfiguration();
this.threadPool = threadPool;
this.copyObjectRequest = copyObjectRequest;
this.metadata = metadata;
this.progressListenerChainCallbackExecutor = ProgressListenerCallbackExecutor
.wrapListener(progressListenerChain);
this.copy = copy;
}
List> getFutures() {
return futures;
}
String getMultipartUploadId() {
return multipartUploadId;
}
/**
* Returns true if this CopyCallable is processing a multi-part copy.
*
* @return True if this CopyCallable is processing a multi-part copy.
*/
public boolean isMultipartCopy() {
return (metadata.getContentLength() > configuration
.getMultipartCopyThreshold());
}
public CopyResult call() throws Exception {
copy.setState(TransferState.InProgress);
if (isMultipartCopy()) {
fireProgressEvent(ProgressEvent.STARTED_EVENT_CODE);
copyInParts();
return null;
} else {
return copyInOneChunk();
}
}
/**
* Performs the copy of the Amazon S3 object from source bucket to
* destination bucket. The Amazon S3 object is copied to destination in one
* single request.
*
* @returns CopyResult response information from the server.
*/
private CopyResult copyInOneChunk() {
CopyObjectResult copyObjectResult = s3.copyObject(copyObjectRequest);
CopyResult copyResult = new CopyResult();
copyResult.setSourceBucketName(copyObjectRequest.getSourceBucketName());
copyResult.setSourceKey(copyObjectRequest.getSourceKey());
copyResult.setDestinationBucketName(copyObjectRequest
.getDestinationBucketName());
copyResult.setDestinationKey(copyObjectRequest.getDestinationKey());
copyResult.setETag(copyObjectResult.getETag());
copyResult.setVersionId(copyObjectResult.getVersionId());
return copyResult;
}
/**
* Performs the copy of an Amazon S3 object from source bucket to
* destination bucket as multiple copy part requests. The information about
* the part to be copied is specified in the request as a byte range
* (first-last)
*
* @throws Exception
* Any Exception that occurs while carrying out the request.
*/
private void copyInParts() throws Exception {
final String bucketName = copyObjectRequest.getDestinationBucketName();
final String key = copyObjectRequest.getDestinationKey();
multipartUploadId = initiateMultipartUpload(copyObjectRequest);
long optimalPartSize = getOptimalPartSize(metadata.getContentLength());
try {
CopyPartRequestFactory requestFactory = new CopyPartRequestFactory(
copyObjectRequest, multipartUploadId, optimalPartSize,
metadata.getContentLength());
copyPartsInParallel(requestFactory);
} catch (Exception e) {
fireProgressEvent(ProgressEvent.FAILED_EVENT_CODE);
try {
s3.abortMultipartUpload(new AbortMultipartUploadRequest(
bucketName, key, multipartUploadId));
} catch (Exception e2) {
log.info(
"Unable to abort multipart upload, you may need to manually remove uploaded parts: "
+ e2.getMessage(), e2);
}
throw e;
}
}
/**
* Computes and returns the optimal part size for the copy operation.
*/
private long getOptimalPartSize(long contentLengthOfSource) {
long optimalPartSize = TransferManagerUtils
.calculateOptimalPartSizeForCopy(copyObjectRequest,
configuration, contentLengthOfSource);
log.debug("Calculated optimal part size: " + optimalPartSize);
return optimalPartSize;
}
/**
* Submits a callable for each part to be copied to our thread pool and
* records its corresponding Future.
*/
private void copyPartsInParallel(CopyPartRequestFactory requestFactory) {
while (requestFactory.hasMoreRequests()) {
if (threadPool.isShutdown())
throw new CancellationException(
"TransferManager has been shutdown");
CopyPartRequest request = requestFactory.getNextCopyPartRequest();
futures.add(threadPool.submit(new CopyPartCallable(s3, request)));
}
}
/**
* Initiates a multipart upload and returns the upload id
*/
private String initiateMultipartUpload(CopyObjectRequest copyObjectRequest) {
InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(
copyObjectRequest.getDestinationBucketName(),
copyObjectRequest.getDestinationKey()).withCannedACL(
copyObjectRequest.getCannedAccessControlList());
if (copyObjectRequest.getAccessControlList() != null) {
initiateMultipartUploadRequest
.setAccessControlList(copyObjectRequest
.getAccessControlList());
}
if (copyObjectRequest.getStorageClass() != null) {
initiateMultipartUploadRequest.setStorageClass(StorageClass
.fromValue(copyObjectRequest.getStorageClass()));
}
if (copyObjectRequest.getDestinationSSECustomerKey() != null) {
initiateMultipartUploadRequest.setSSECustomerKey(
copyObjectRequest.getDestinationSSECustomerKey());
}
ObjectMetadata newObjectMetadata = copyObjectRequest.getNewObjectMetadata();
if(newObjectMetadata == null){
newObjectMetadata = new ObjectMetadata();
}
if(newObjectMetadata.getContentType() == null){
newObjectMetadata.setContentType(metadata.getContentType());
}
initiateMultipartUploadRequest.setObjectMetadata(newObjectMetadata);
populateMetadataWithEncryptionParams(metadata,newObjectMetadata);
String uploadId = s3.initiateMultipartUpload(
initiateMultipartUploadRequest).getUploadId();
log.debug("Initiated new multipart upload: " + uploadId);
return uploadId;
}
private void fireProgressEvent(final int eventType) {
if (progressListenerChainCallbackExecutor == null)
return;
ProgressEvent event = new ProgressEvent(0);
event.setEventCode(eventType);
progressListenerChainCallbackExecutor.progressChanged(event);
}
private void populateMetadataWithEncryptionParams(ObjectMetadata source, ObjectMetadata destination) {
Map userMetadataSource = source.getUserMetadata();
Map userMetadataDestination = destination.getUserMetadata();
String[] headersToCopy = { Headers.CRYPTO_CEK_ALGORITHM,
Headers.CRYPTO_IV, Headers.CRYPTO_KEY, Headers.CRYPTO_KEY_V2,
Headers.CRYPTO_KEYWRAP_ALGORITHM, Headers.CRYPTO_TAG_LENGTH,
Headers.MATERIALS_DESCRIPTION,
Headers.UNENCRYPTED_CONTENT_LENGTH,
Headers.UNENCRYPTED_CONTENT_MD5 };
if (userMetadataSource != null) {
if(userMetadataDestination == null){
userMetadataDestination= new HashMap();
destination.setUserMetadata(userMetadataDestination);
}
String headerValue;
for(String header : headersToCopy){
headerValue = userMetadataSource.get(header);
if(headerValue != null){
userMetadataDestination.put(header, headerValue);
}
}
}
}
}