com.qcloud.cos.transfer.ResumableDownloadMonitor Maven / Gradle / Ivy
/*
* Copyright 2010-2019 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.
* According to cos feature, we modify some class,comment, field name, etc.
*/
package com.qcloud.cos.transfer;
import static com.qcloud.cos.event.SDKProgressPublisher.publishProgress;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import com.qcloud.cos.event.ProgressEventType;
import com.qcloud.cos.event.ProgressListenerChain;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.transfer.Transfer.TransferState;
import com.qcloud.cos.utils.CRC64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ResumableDownloadMonitor implements Callable, TransferMonitor {
private static final Logger log = LoggerFactory.getLogger(ResumableDownloadMonitor.class);
private final ProgressListenerChain listener;
private final ResumableDownloadSubmitter downloadSubmitter;
private final DownloadImpl transfer;
private final PersistableResumeDownload downloadRecord;
private final File destFile;
private final FileChannel destFileChannel;
private final List> futures =
Collections.synchronizedList(new ArrayList>());
private boolean isDownloadDone = false;
private AtomicReference> futureReference = new AtomicReference>(null);
public Future getFuture() {
return futureReference.get();
}
private synchronized void cancelFuture() {
futureReference.get().cancel(true);
}
ResumableDownloadMonitor(ProgressListenerChain listener,
ResumableDownloadSubmitter downloadSubmitter, DownloadImpl transfer,
PersistableResumeDownload downloadRecord,
File destFile, FileChannel destFileChannel) {
this.listener = listener;
this.downloadSubmitter = downloadSubmitter;
this.transfer = transfer;
this.downloadRecord = downloadRecord;
this.destFile = destFile;
this.destFileChannel = destFileChannel;
}
public static ResumableDownloadMonitor create(ProgressListenerChain listener,
ResumableDownloadSubmitter downloadSubmitter, DownloadImpl transfer,
ExecutorService threadPool, PersistableResumeDownload downloadRecord,
File destFile, FileChannel destFileChannel) {
ResumableDownloadMonitor monitor = new ResumableDownloadMonitor(listener, downloadSubmitter,
transfer, downloadRecord, destFile, destFileChannel);
monitor.futureReference.compareAndSet(null, threadPool.submit(monitor));
return monitor;
}
public synchronized boolean isDone() {
return isDownloadDone;
}
private synchronized void markAllDone() {
isDownloadDone = true;
}
@Override
public File call() throws Exception {
downloadSubmitter.submit();
futures.addAll(downloadSubmitter.getFutures());
List downloadParts = new ArrayList();
downloadParts.addAll(downloadSubmitter.getSkippedParts());
try {
for (Future future : futures) {
try {
downloadParts.add(future.get());
} catch (Exception e) {
throw new CosClientException("range download got exception: "+ e.getCause().getMessage() + e.getMessage());
}
}
// download finished.
downloadRecord.getDumpFile().delete();
destFileChannel.close();
if ((downloadRecord.getCrc64ecma() != null) && !downloadRecord.getCrc64ecma().isEmpty()) {
checkCRC(downloadParts);
}
downloadComplete();
return destFile;
} catch (CancellationException e) {
transfer.setState(TransferState.Canceled);
publishProgress(listener, ProgressEventType.TRANSFER_CANCELED_EVENT);
throw new CosClientException("Download canceled");
} catch (Exception e) {
downloadFailed();
throw e;
}
}
void downloadComplete() {
markAllDone();
transfer.setState(TransferState.Completed);
publishProgress(listener, ProgressEventType.TRANSFER_COMPLETED_EVENT);
}
void downloadFailed() {
transfer.setState(TransferState.Failed);
publishProgress(listener, ProgressEventType.TRANSFER_FAILED_EVENT);
}
private void cancelFutures() {
cancelFuture();
if (futures.size() == 0) {
futures.addAll(downloadSubmitter.getFutures());
}
for (Future f : futures) {
f.cancel(true);
}
futures.clear();
}
void performAbort() {
cancelFutures();
publishProgress(listener, ProgressEventType.TRANSFER_CANCELED_EVENT);
}
void checkCRC(List downloadParts) throws IOException {
Collections.sort(downloadParts, new Comparator(){
@Override
public int compare(DownloadPart part1, DownloadPart part2) {
return (int)(part1.start - part2.start);
}
});
CRC64 crc64 = new CRC64();
for (DownloadPart part : downloadParts) {
crc64 = CRC64.combine(crc64, new CRC64(part.crc64), part.getContentLength());
}
long crc64Download = crc64.getValue();
long crc64Cos = crc64ToLong(downloadRecord.getCrc64ecma());
log.debug("download crc " + crc64Download + " cos crc " + crc64Cos);
if (crc64Download != crc64Cos) {
destFile.delete();
throw new CosClientException("download file has diff crc64 with cos file, cos: " +
crc64Cos + " downloaded: " + crc64Download);
}
}
long crc64ToLong(String crc64) {
if (crc64.charAt(0) == '-') {
return negativeCrc64ToLong(crc64);
} else {
return positiveCrc64ToLong(crc64);
}
}
long positiveCrc64ToLong(String strCrc64) {
BigInteger crc64 = new BigInteger(strCrc64);
BigInteger maxLong = new BigInteger(Long.toString(Long.MAX_VALUE));
int maxCnt = 0;
while (crc64.compareTo(maxLong) > 0) {
crc64 = crc64.subtract(maxLong);
maxCnt++;
}
return crc64.longValue() + Long.MAX_VALUE * maxCnt;
}
long negativeCrc64ToLong(String strCrc64) {
BigInteger crc64 = new BigInteger(strCrc64);
BigInteger minLong = new BigInteger(Long.toString(Long.MIN_VALUE));
int minCnt = 0;
while (crc64.compareTo(minLong) < 0) {
crc64 = crc64.subtract(minLong);
minCnt++;
}
return crc64.longValue() + Long.MIN_VALUE * minCnt;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy