public void run() {
      upload.setState(Transfer.State.ACTIVE);
      final DefaultTransferResource transferResource =
          new DefaultTransferResource(repository.getUrl(), path, file, upload.getTrace());

      try {
        final String uri = validateUri(path);

        final CompletionHandler completionHandler =
            new CompletionHandler(transferResource, httpClient, logger, RequestType.PUT) {
              @Override
              public void onThrowable(Throwable t) {
                try {
                  super.onThrowable(t);
                  if (Exception.class.isAssignableFrom(t.getClass())) {
                    exception = Exception.class.cast(t);
                  } else {
                    exception = new Exception(t);
                  }

                  if (listener != null) {
                    listener.transferFailed(
                        newEvent(transferResource, exception, RequestType.PUT, EventType.FAILED));
                  }
                } finally {
                  latch.countDown();
                }
              }

              @Override
              public Response onCompleted(Response r) throws Exception {
                try {
                  Response response = super.onCompleted(r);
                  handleResponseCode(uri, response.getStatusCode(), response.getStatusText());

                  httpClient
                      .getConfig()
                      .executorService()
                      .execute(
                          new Runnable() {
                            public void run() {
                              try {
                                uploadChecksums(file, uri);
                              } catch (Exception ex) {
                                exception = ex;
                              } finally {
                                latch.countDown();
                              }
                            }
                          });

                  return r;
                } catch (Exception ex) {
                  exception = ex;
                  throw ex;
                }
              }
            };

        if (listener != null) {

          completionHandler.addTransferListener(listener);
          listener.transferInitiated(
              newEvent(transferResource, null, RequestType.PUT, EventType.INITIATED));
        }

        if (file == null) {
          throw new IllegalArgumentException("no source file specified for upload");
        }
        transferResource.setContentLength(file.length());

        httpClient
            .preparePut(uri)
            .setHeaders(headers)
            .setBody(new ProgressingFileBodyGenerator(file, completionHandler))
            .execute(completionHandler);
      } catch (Exception e) {
        try {
          if (listener != null) {
            listener.transferFailed(
                newEvent(transferResource, e, RequestType.PUT, EventType.FAILED));
          }
          exception = e;
        } finally {
          latch.countDown();
        }
      }
    }
    public void run() {
      download.setState(Transfer.State.ACTIVE);
      final String uri = validateUri(path);
      final TransferResource transferResource =
          new DefaultTransferResource(repository.getUrl(), path, file, download.getTrace());
      final boolean ignoreChecksum = RepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals(checksumPolicy);
      CompletionHandler completionHandler = null;

      final FileLockCompanion fileLockCompanion =
          (file != null)
              ? createOrGetTmpFile(file.getPath(), allowResumable)
              : new FileLockCompanion(null, null);

      try {
        long length = 0;
        if (fileLockCompanion.getFile() != null) {
          fileProcessor.mkdirs(fileLockCompanion.getFile().getParentFile());
        }

        // Position the file to the end in case we are resuming an aborded download.
        final RandomAccessFile resumableFile =
            fileLockCompanion.getFile() == null
                ? null
                : new RandomAccessFile(fileLockCompanion.getFile(), "rw");
        if (resumableFile != null) {
          length = resumableFile.length();
        }

        FluentCaseInsensitiveStringsMap headers = new FluentCaseInsensitiveStringsMap();
        if (!useCache) {
          headers.add("Pragma", "no-cache");
        }
        headers.add("Accept", "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2");
        headers.replaceAll(AsyncRepositoryConnector.this.headers);

        Request request = null;
        final AtomicInteger maxRequestTry = new AtomicInteger();
        AsyncHttpClient client = httpClient;
        final AtomicBoolean closeOnComplete = new AtomicBoolean(false);

        /**
         * If length > 0, it means we are resuming a interrupted download. If that's the case, we
         * can't re-use the current httpClient because compression is enabled, and supporting
         * compression per request is not supported in ahc and may never has it could have a
         * performance impact.
         */
        if (length > 0) {
          AsyncHttpClientConfig config = createConfig(session, repository, false);
          client = new AsyncHttpClient(new NettyAsyncHttpProvider(config));
          request = client.prepareGet(uri).setRangeOffset(length).setHeaders(headers).build();
          closeOnComplete.set(true);
        } else {
          request = httpClient.prepareGet(uri).setHeaders(headers).build();
        }

        final Request activeRequest = request;
        final AsyncHttpClient activeHttpClient = client;
        completionHandler =
            new CompletionHandler(transferResource, httpClient, logger, RequestType.GET) {
              private final AtomicBoolean seekEndOnFile = new AtomicBoolean(false);

              private final AtomicBoolean handleTmpFile = new AtomicBoolean(true);

              private final AtomicBoolean localException = new AtomicBoolean(false);

              /** {@inheritDoc} */
              @Override
              public STATE onHeadersReceived(final HttpResponseHeaders headers) throws Exception {

                FluentCaseInsensitiveStringsMap h = headers.getHeaders();
                String rangeByteValue = h.getFirstValue("Content-Range");
                // Make sure the server acceptance of the range requests headers
                if (rangeByteValue != null && rangeByteValue.compareToIgnoreCase("none") != 0) {
                  seekEndOnFile.set(true);
                }
                return super.onHeadersReceived(headers);
              }

              @Override
              public void onThrowable(Throwable t) {
                try {
                  logger.debug("onThrowable", t);
                  /**
                   * If an IOException occurs, let's try to resume the request based on how much
                   * bytes has been so far downloaded. Fail after IOException.
                   */
                  if (!disableResumeSupport
                      && !localException.get()
                      && maxRequestTry.get() < maxIOExceptionRetry
                      && IOException.class.isAssignableFrom(t.getClass())) {
                    logger.debug("Trying to recover from an IOException " + activeRequest);
                    maxRequestTry.incrementAndGet();
                    Request newRequest =
                        new RequestBuilder(activeRequest)
                            .setRangeOffset(resumableFile.length())
                            .build();
                    activeHttpClient.executeRequest(newRequest, this);
                    deleteFile.set(false);
                    return;
                  }
                  localException.set(false);

                  if (closeOnComplete.get()) {
                    activeHttpClient.close();
                  }

                  super.onThrowable(t);
                  if (Exception.class.isAssignableFrom(t.getClass())) {
                    exception = Exception.class.cast(t);
                  } else {
                    exception = new Exception(t);
                  }
                  fireTransferFailed();
                } catch (Throwable ex) {
                  logger.debug("Unexpected exception", ex);
                } finally {
                  if (resumableFile != null) {
                    try {
                      resumableFile.close();
                    } catch (IOException ex) {
                    }
                  }
                  deleteFile(fileLockCompanion);

                  latch.countDown();
                  removeListeners();
                }
              }

              private void removeListeners() {
                removeTransferListener(listener);
              }

              public STATE onBodyPartReceived(final HttpResponseBodyPart content) throws Exception {
                if (status() != null
                    && (status().getStatusCode() == 200 || status().getStatusCode() == 206)) {
                  byte[] bytes = content.getBodyPartBytes();
                  try {
                    // If the content-range header was present, save the bytes at the end of the
                    // file
                    // as we are resuming an existing download.
                    if (seekEndOnFile.get()) {
                      resumableFile.seek(fileLockCompanion.getFile().length());

                      // No need to seek again.
                      seekEndOnFile.set(false);
                    }
                    resumableFile.write(bytes);
                  } catch (IOException ex) {
                    logger.debug("onBodyPartReceived", ex);
                    exception = ex;
                    localException.set(true);
                    throw ex;
                  }
                }
                return super.onBodyPartReceived(content);
              }

              @Override
              public Response onCompleted(Response r) throws Exception {
                try {
                  deleteFile.set(true);
                  try {
                    resumableFile.close();
                  } catch (IOException ex) {
                  }

                  final Response response = super.onCompleted(r);

                  handleResponseCode(uri, response.getStatusCode(), response.getStatusText());

                  if (!ignoreChecksum) {
                    activeHttpClient
                        .getConfig()
                        .executorService()
                        .execute(
                            new Runnable() {
                              public void run() {
                                try {
                                  try {
                                    Map<String, Object> checksums =
                                        ChecksumUtils.calc(
                                            fileLockCompanion.getFile(), checksumAlgos.keySet());
                                    if (!verifyChecksum(
                                            file, uri, (String) checksums.get("SHA-1"), ".sha1")
                                        && !verifyChecksum(
                                            file, uri, (String) checksums.get("MD5"), ".md5")) {
                                      throw new ChecksumFailureException(
                                          "Checksum validation failed"
                                              + ", no checksums available from the repository");
                                    }
                                  } catch (ChecksumFailureException e) {
                                    if (RepositoryPolicy.CHECKSUM_POLICY_FAIL.equals(
                                        checksumPolicy)) {
                                      throw e;
                                    }
                                    if (listener != null) {
                                      listener.transferCorrupted(
                                          newEvent(
                                              transferResource,
                                              e,
                                              RequestType.GET,
                                              EventType.CORRUPTED));
                                    }
                                  }
                                } catch (Exception ex) {
                                  exception = ex;
                                } finally {
                                  if (exception == null) {
                                    try {
                                      rename(fileLockCompanion.getFile(), file);
                                      releaseLock(fileLockCompanion);
                                    } catch (IOException e) {
                                      exception = e;
                                    }
                                  } else {
                                    deleteFile(fileLockCompanion);
                                  }

                                  latch.countDown();
                                  if (closeOnComplete.get()) {
                                    activeHttpClient.close();
                                  }
                                }
                              }
                            });
                  } else {

                    rename(fileLockCompanion.getFile(), file);
                    releaseLock(fileLockCompanion);
                    handleTmpFile.set(false);

                    // asyncHttpClient.close may takes time before all connections get closed.
                    // We unlatch first.
                    latch.countDown();
                    if (closeOnComplete.get()) {
                      activeHttpClient.close();
                    }
                  }
                  removeListeners();

                  return response;
                } catch (Exception ex) {
                  exception = ex;
                  localException.set(true);
                  throw ex;
                } finally {
                  try {
                    if (handleTmpFile.get() && fileLockCompanion.getFile() != null) {
                      if (exception != null) {
                        deleteFile(fileLockCompanion);
                      } else if (ignoreChecksum) {
                        rename(fileLockCompanion.getFile(), file);
                        releaseLock(fileLockCompanion);
                      }
                    }
                  } catch (IOException ex) {
                    exception = ex;
                  }
                }
              }
            };

        try {
          if (file == null) {
            if (!resourceExist(uri)) {
              throw new ResourceDoesNotExistException(
                  "Could not find " + uri + " in " + repository.getUrl());
            }
            latch.countDown();
          } else {
            if (listener != null) {
              completionHandler.addTransferListener(listener);
              listener.transferInitiated(
                  newEvent(transferResource, null, RequestType.GET, EventType.INITIATED));
            }

            activeHttpClient.executeRequest(request, completionHandler);
          }
        } catch (Exception ex) {
          try {
            if (resumableFile != null) {
              resumableFile.close();
            }
          } catch (IOException ex2) {
          }
          deleteFile(fileLockCompanion);
          exception = ex;
          latch.countDown();
        }
      } catch (Throwable t) {
        deleteFile(fileLockCompanion);
        try {
          if (Exception.class.isAssignableFrom(t.getClass())) {
            exception = Exception.class.cast(t);
          } else {
            exception = new Exception(t);
          }
          if (listener != null) {
            listener.transferFailed(
                newEvent(transferResource, exception, RequestType.GET, EventType.FAILED));
          }
        } finally {
          latch.countDown();
        }
      }
    }