use of ch.cyberduck.core.threading.TransferBackgroundActionState in project cyberduck by iterate-ch.
the class AbstractTransferWorker method transfer.
/**
* @param item File to transfer
* @param action Transfer action for existing files
*/
public Future<TransferStatus> transfer(final TransferItem item, final TransferAction action) throws BackgroundException {
if (this.isCanceled()) {
throw new TransferCanceledException();
}
// Only transfer if accepted by filter and stored in table with transfer status
if (table.containsKey(item)) {
// Overall transfer status
final TransferStatus status = table.get(item);
// Handle submit of one or more segments
final List<TransferStatus> segments = status.getSegments();
for (final TransferStatus segment : segments) {
if (segment.isComplete()) {
continue;
}
this.submit(new RetryTransferCallable(transfer.getSource()) {
@Override
public TransferStatus call() throws BackgroundException {
status.validate();
// Transfer
// Do transfer with retry
this.transferSegment(segment);
final Session<?> source = borrow(Connection.source);
final Session<?> destination = borrow(Connection.destination);
try {
// Determine transfer filter implementation from selected overwrite action
final TransferPathFilter filter = transfer.filter(source, destination, action, progress);
// Post process of file.
filter.complete(segment.getRename().remote != null ? segment.getRename().remote : item.remote, segment.getRename().local != null ? segment.getRename().local : item.local, options, segment, progress);
} finally {
release(source, Connection.source, null);
release(destination, Connection.destination, null);
}
// Recursive
if (item.remote.isDirectory()) {
if (!cache.isCached(item)) {
log.warn(String.format("Missing entry for %s in cache", item));
}
for (TransferItem f : cache.get(item)) {
// Recursive
transfer(f, action);
}
cache.remove(item);
}
return segment;
}
private void transferSegment(final TransferStatus segment) throws BackgroundException {
if (log.isDebugEnabled()) {
log.debug(String.format("Transfer item %s with status %s", item, segment));
}
final Session<?> s = borrow(Connection.source);
final Session<?> d = borrow(Connection.destination);
final BytecountStreamListener counter = new BytecountStreamListener(stream);
try {
transfer.transfer(s, d, segment.getRename().remote != null ? segment.getRename().remote : item.remote, segment.getRename().local != null ? segment.getRename().local : item.local, options, status, segment, connect, progress, counter);
} catch (BackgroundException e) {
release(s, Connection.source, e);
release(d, Connection.destination, e);
log.warn(String.format("Failure %s transferring %s", item, e));
// Determine if we should retry depending on failure type
if (this.retry(e, progress, new TransferBackgroundActionState(status))) {
final Session<?> source = borrow(Connection.source);
final Session<?> destination = borrow(Connection.destination);
try {
final TransferPathFilter filter = transfer.filter(source, destination, TransferAction.resume, progress);
if (filter.accept(item.remote, item.local, new TransferStatus().exists(true))) {
if (log.isDebugEnabled()) {
log.debug(String.format("Retry transfer of %s", item));
}
final TransferStatus retry = filter.prepare(item.remote, item.local, new TransferStatus().exists(true), progress);
// Add bytes retrieved but not yet counted
stream.recv(retry.getOffset() - counter.getRecv());
stream.sent(retry.getOffset() - counter.getSent());
transfer.addTransferred(retry.getOffset() - counter.getSent());
// Retry immediately
log.info(String.format("Retry %s with transfer status %s", item, retry));
this.transferSegment(segment.withLength(retry.getLength()).withOffset(retry.getOffset()).append(retry.isAppend()));
return;
}
} finally {
release(source, Connection.source, null);
release(destination, Connection.destination, null);
}
}
if (log.isDebugEnabled()) {
log.debug(String.format("Cancel retry for %s", item));
}
segment.setFailure();
// Prompt to continue or abort for application errors
if (error.prompt(item, segment, e, table.size())) {
// Continue
log.warn(String.format("Ignore transfer failure %s", e));
} else {
throw new TransferCanceledException(e);
}
} finally {
release(s, Connection.source, null);
release(d, Connection.destination, null);
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("RetryTransferCallable{");
sb.append("item=").append(item);
sb.append(", status=").append(segment);
sb.append('}');
return sb.toString();
}
});
}
return this.submit(new TransferCallable() {
@Override
public TransferStatus call() throws BackgroundException {
status.validate();
if (status.isSegmented()) {
// Await completion of all segments
boolean complete = true;
for (TransferStatus segment : segments) {
if (!segment.await()) {
log.warn(String.format("Failure to complete segment %s.", segment));
complete = false;
}
}
if (complete) {
final Session<?> source = borrow(Connection.source);
final Session<?> destination = borrow(Connection.destination);
try {
// Determine transfer filter implementation from selected overwrite action
final TransferPathFilter filter = transfer.filter(source, destination, action, progress);
// Concatenate segments with completed status set
filter.complete(status.getRename().remote != null ? status.getRename().remote : item.remote, status.getRename().local != null ? status.getRename().local : item.local, options, status.complete(), progress);
} finally {
release(source, Connection.source, null);
release(destination, Connection.destination, null);
}
} else {
log.warn(String.format("Skip concatenating segments for failed transfer %s", status));
status.setFailure();
}
}
return status;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("TransferCallable{");
sb.append("item=").append(item);
sb.append(", status=").append(status);
sb.append('}');
return sb.toString();
}
});
} else {
log.warn(String.format("Skip file %s with unknown transfer status", item));
}
return ConcurrentUtils.constantFuture(null);
}
Aggregations