in clearcase-common/src/jetbrains/buildServer/vcs/clearcase/Util.java [521:619]
private void copyFile(File source, File... destinations) throws IOException {
// some initial logging
if (LOGGER.isDebugEnabled()) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Copying file \"");
stringBuilder.append(source.toString());
stringBuilder.append("\" to the following destinations:\n");
for (File destination : destinations) {
stringBuilder.append(destination.getPath());
stringBuilder.append('\n');
}
LOGGER.info(stringBuilder.toString());
}
// ensure that all destination files exist before starting the transfer
// processing
for (File destination : destinations) {
if (!destination.exists()) {
destination.getParentFile().mkdirs();
destination.createNewFile();
}
}
// quick return when source is an empty file
sourceLength = source.length();
if (sourceLength == 0) {
return;
}
// create a Transferrer thread for every destination
int destinationCount = destinations.length;
final Transferrer[] transferrers = new Transferrer[destinationCount];
for (int i = 0; i < destinationCount; i++) {
transferrers[i] = new Transferrer(new FileInputStream(source).getChannel(), new FileOutputStream(destinations[i]).getChannel());
}
barrier = new CyclicBarrier(destinationCount, new Runnable() {
public void run() {
// inform property listeners about copied data volume
position += transferVolume;
copiedBytes += transferVolume;
propertyChangeSupport.firePropertyChange(BYTE_COUNTER_PROPERTY, oldCopiedBytes, copiedBytes);
oldCopiedBytes = copiedBytes;
// determine next slice size before releasing the barrier
long stop = System.currentTimeMillis();
long time = stop - sliceStartTime;
LOGGER.debug("time = " + NUMBER_FORMAT.format(time) + " ms");
if (time != 0) {
// bandwidth = transferVolume / time
// newSlice = bandwith * WANTED_TIME
long newSlice = (transferVolume * WANTED_TIME) / time;
// just using newSlice here leads to overmodulation
// doubling or halving is the slower (and probably better)
// approach
long doubleSlice = 2 * slice;
long halfSlice = slice / 2;
if (newSlice > doubleSlice) {
slice = doubleSlice;
} else if ((newSlice < halfSlice) && (halfSlice > 0)) {
slice = halfSlice;
}
transferVolume = Math.min(slice, sourceLength - position);
LOGGER.debug("\nslice = " + NUMBER_FORMAT.format(slice) + " Byte\ntransferVolume = " + NUMBER_FORMAT.format(transferVolume) + " Byte");
}
sliceStartTime = System.currentTimeMillis();
}
});
// start the transfer process
position = 0;
transferVolume = Math.min(slice, sourceLength);
LOGGER.debug("\nslice = " + NUMBER_FORMAT.format(slice) + " Byte\ntransferVolume = " + NUMBER_FORMAT.format(transferVolume) + " Byte");
sliceStartTime = System.currentTimeMillis();
//TODO: Is that ok to use executor service creation
ExecutorService executorService = ExecutorsFactory.newExecutor("Clearcase copy file");
try {
ExecutorCompletionService<Void> completionService = new ExecutorCompletionService<Void>(executorService);
for (Transferrer transferrer : transferrers) {
completionService.submit(transferrer, null);
}
// wait until all transferrers completed their execution
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < destinationCount; i++) {
try {
completionService.take();
} catch (InterruptedException ex) {
LOGGER.error(ex.getMessage(), ex);
}
}
} finally {
executorService.shutdown();
}
}