private void copyFile()

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();
        }
      }