private File createTestFile(int length, int offset) throws IOException {
    File ret = createTestFile();

    byte[] data = new byte[length];
    rand.nextBytes(data);

    long crc32 = FileUtils.calcCRC(data);

    File baseDir = ret.getParentFile();
    String fileName = ret.getName();

    WriteRequest request = new WriteRequest();
    request.setBaseDir(baseDir);
    request.setFileName(fileName);
    request.setData(data);
    request.setOffset(offset);
    request.setLength(length);

    WriteResponse response = FileUtils.writeBlock(request);
    if (!response.isSuccess() || crc32 != response.getCrc32()) {
      throw new IOException(
          String.format("Failed creating temporary file: ", ret.getAbsolutePath()));
    }

    return ret;
  }
  private KeyStore getKeyStore(String fileName, String password)
      throws GeneralSecurityException, IOException {
    KeyStore ret = KeyStore.getInstance(KeyStore.getDefaultType());

    if (fileName != null) {
      File file = FileUtils.canonicalFile(new File(fileName));
      if (file.canRead()) {
        InputStream in = new FileInputStream(file);
        try {
          ret.load(in, new Password(password).toString().toCharArray());
        } finally {
          in.close();
        }
      }
    }

    return ret;
  }
  private void run(String[] args) {
    try {
      cli = new CommandLineUtils(getClass());
      cli.useAllow().useBaseDir().useClient().useSync().useTimeZone();

      if (cli.parseArgs(args)) {
        if (cli.isHelp()) {
          cli.displayHelp();
        } else {
          blockSize = FileUtils.MIN_BLOCK_SIZE;

          RemoteClient remoteClient =
              RemoteClient.builder()
                  .ssl(cli.hasSsl())
                  .host(cli.getHost())
                  .port(cli.getPort())
                  .build();
          try {
            BlockReader localReader = new BlockReaderLocalImpl();
            BlockWriter localWriter = new BlockWriterLocalImpl();
            BlockReader remoteReader = new BlockReaderRemoteImpl(remoteClient);
            BlockWriter remoteWriter = new BlockWriterRemoteImpl(remoteClient);
            HashRequester remoteHasher = new HashRequesterRemoteImpl(remoteClient);
            DirectoryLister remoteLister = new DirectoryListerRemoteImpl(remoteClient);

            testMakeDirectory(remoteLister, REMOTE_PATH);

            for (int xx = 0; xx < 5; xx++) {
              File readFile = createTestFile(blockSize + rand.nextInt(blockSize * 5), 0);

              testCopyFile(
                  localReader,
                  readFile.getName(),
                  remoteWriter,
                  fileName,
                  readFile.getParentFile());

              testHash(remoteHasher, REMOTE_PATH);

              File writeFile = createTestFile();

              testCopyFile(
                  remoteReader,
                  fileName,
                  localWriter,
                  writeFile.getName(),
                  writeFile.getParentFile());

              assertTrue(
                  "Files different after copy local", FileUtils.compareFiles(writeFile, readFile));

              testListDirectory(remoteLister, REMOTE_PATH);

              testDeleteFile(remoteWriter, fileName, writeFile.getParentFile());

              readFile.delete();
              writeFile.delete();
            }

            testDeleteDirectory(remoteLister, REMOTE_PATH);
          } finally {
            remoteClient.finished();
          }
        }
      }
    } catch (Throwable ee) {
      logger.error(String.format("Error running test: %s", ee.getMessage()), ee);
    }
  }
  @Override
  public ReadResponse readBlock(ReadRequest request) {
    ReadResponse ret = new ReadResponse();

    if (remoteClient == null) {
      throw new IllegalStateException(
          String.format(
              "%s missing %s", getClass().getSimpleName(), RemoteClient.class.getSimpleName()));
    }

    ret.setRequest(request);

    String uri =
        remoteClient.getURI(String.format("%s/%s", Constants.FILE_PATH, request.getFileName()));

    WebResource webResource =
        remoteClient
            .getClient()
            .resource(uri)
            .queryParam(Constants.OFFSET_PARAM, String.valueOf(request.getOffset()))
            .queryParam(Constants.BLOCK_SIZE_PARAM, String.valueOf(request.getBlockSize()));
    logger.debug(webResource.toString());
    Builder builder = webResource.accept(MediaType.APPLICATION_OCTET_STREAM);
    if (FileUtils.isCompress()) {
      builder = builder.header(Constants.CONTENT_ENCODED_HEADER, Constants.DEFLATE);
    }

    ClientResponse clientResponse = builder.get(ClientResponse.class);
    try {
      remoteClient.checkForException(clientResponse);

      if (clientResponse.getStatus() != HttpStatus.OK_200) {
        throw new ErrorReadingBlockException(
            String.format(
                "Failed GET %s: HttpStatus: %d/%s",
                uri, clientResponse.getStatus(), clientResponse.getStatusInfo()));
      }

      logger.debug(clientResponse.getHeaders().toString());

      ret.setLength(
          Integer.parseInt(clientResponse.getHeaders().getFirst(Constants.LENGTH_HEADER)));
      int contentLength = Integer.parseInt(clientResponse.getHeaders().getFirst("Content-Length"));

      byte[] block = null;
      int compressed = -1;
      if (clientResponse.getHeaders().containsKey(Constants.COMPRESSED_HEADER)) {
        compressed =
            Integer.parseInt(clientResponse.getHeaders().getFirst(Constants.COMPRESSED_HEADER));
        if (contentLength != compressed) {
          throw new ErrorReadingBlockException(
              String.format(
                  "Failed GET %s: Compressed Content-Length: %d/%s",
                  uri, contentLength, compressed));
        }

        Inflated inflated =
            CompressionUtils.inflate(getDataByEntity(clientResponse, compressed), ret.getLength());
        if (inflated == null || inflated.getLength() != ret.getLength()) {
          throw new ErrorInflatingBlockException("Error inflating compressed read response");
        }

        block = inflated.getData();
      } else {
        if (contentLength != ret.getLength()) {
          throw new ErrorReadingBlockException(
              String.format(
                  "Failed GET %s: Content-Length: %d/%s", uri, contentLength, ret.getLength()));
        }

        block = getDataByEntity(clientResponse, ret.getLength());
      }

      ret.setCrc32(Long.parseLong(clientResponse.getHeaders().getFirst(Constants.CRC_HEADER)));
      ret.setEof(Boolean.parseBoolean(clientResponse.getHeaders().getFirst(Constants.EOF_HEADER)));
      ret.setSuccess(
          Boolean.parseBoolean(clientResponse.getHeaders().getFirst(Constants.SUCCESS_HEADER)));
      ret.setData(block);
    } finally {
      clientResponse.close();
    }

    return ret;
  }