public static void setETag(URIConverter uriConverter, URI file, String eTag) {
   try {
     if (eTag != null) {
       BaseUtil.writeFile(
           uriConverter, null, file.appendFileExtension("etag"), eTag.getBytes("UTF-8"));
     } else {
       BaseUtil.deleteFile(uriConverter, null, file);
     }
   } catch (IORuntimeException ex) {
     // If we can't write the ETag, perhaps some other process is writing it, but it's expected to
     // write the same ETag value.
   } catch (UnsupportedEncodingException ex) {
     // All systems support UTF-8.
   }
 }
  public static String getETag(URIConverter uriConverter, URI file) {
    if (uriConverter.exists(file, null)) {
      URI eTagFile = file.appendFileExtension("etag");
      if (uriConverter.exists(eTagFile, null)) {
        try {
          return new String(BaseUtil.readFile(uriConverter, null, eTagFile), "UTF-8");
        } catch (IORuntimeException ex) {
          // If we can't read the ETag, we'll just return null.
        } catch (UnsupportedEncodingException ex) {
          // All systems support UTF-8.
        }
      }
    }

    return null;
  }
  @Override
  public InputStream createInputStream(URI uri, Map<?, ?> options) throws IOException {
    if (TEST_IO_EXCEPTION) {
      File folder = new File(CACHE_FOLDER.toFileString());
      if (folder.isDirectory()) {
        System.out.println("Deleting cache folder: " + folder);
        IOUtil.deleteBestEffort(folder);
      }

      throw new IOException("Simulated network problem");
    }

    CacheHandling cacheHandling = getCacheHandling(options);
    URIConverter uriConverter = getURIConverter(options);
    URI cacheURI = getCacheFile(uri);
    String eTag =
        cacheHandling == CacheHandling.CACHE_IGNORE ? null : getETag(uriConverter, cacheURI);
    String expectedETag = cacheHandling == CacheHandling.CACHE_IGNORE ? null : getExpectedETag(uri);
    if (expectedETag != null
        || cacheHandling == CacheHandling.CACHE_ONLY
        || cacheHandling == CacheHandling.CACHE_WITHOUT_ETAG_CHECKING) {
      if (cacheHandling == CacheHandling.CACHE_ONLY
              || cacheHandling == CacheHandling.CACHE_WITHOUT_ETAG_CHECKING
          ? eTag != null
          : expectedETag.equals(eTag)) {
        try {
          setExpectedETag(uri, expectedETag);
          return uriConverter.createInputStream(cacheURI, options);
        } catch (IOException ex) {
          // Perhaps another JVM is busy writing this file.
          // Proceed as if it doesn't exit.
        }
      }
    }

    String username;
    String password;

    String uriString = uri.toString();
    Proxy proxy = ProxySetupHelper.getProxy(uriString);
    if (proxy != null) {
      username = proxy.getUsername();
      password = proxy.getPassword();
    } else {
      username = null;
      password = null;
    }

    IContainer container = createContainer();

    AuthorizationHandler authorizatonHandler = getAuthorizatonHandler(options);
    Authorization authorization = getAuthorizaton(options);
    int triedReauthorization = 0;
    for (int i = 0; ; ++i) {
      IRetrieveFileTransferContainerAdapter fileTransfer =
          container.getAdapter(IRetrieveFileTransferContainerAdapter.class);

      if (proxy != null) {
        fileTransfer.setProxy(proxy);

        if (username != null) {
          fileTransfer.setConnectContextForAuthentication(
              ConnectContextFactory.createUsernamePasswordConnectContext(username, password));
        } else if (password != null) {
          fileTransfer.setConnectContextForAuthentication(
              ConnectContextFactory.createPasswordConnectContext(password));
        }
      }

      FileTransferListener transferListener = new FileTransferListener(eTag);

      try {
        FileTransferID fileTransferID =
            new FileTransferID(new FileTransferNamespace(), IOUtil.newURI(uriString));
        Map<Object, Object> requestOptions = new HashMap<Object, Object>();
        requestOptions.put(IRetrieveFileTransferOptions.CONNECT_TIMEOUT, 10000);
        requestOptions.put(IRetrieveFileTransferOptions.READ_TIMEOUT, 10000);
        if (authorization != null && authorization.isAuthorized()) {
          requestOptions.put(
              IRetrieveFileTransferOptions.REQUEST_HEADERS,
              Collections.singletonMap("Authorization", authorization.getAuthorization()));
        }

        fileTransfer.sendRetrieveRequest(fileTransferID, transferListener, requestOptions);
      } catch (IncomingFileTransferException ex) {
        throw new IOExceptionWithCause(ex);
      }
      try {
        transferListener.receiveLatch.await();
      } catch (InterruptedException ex) {
        throw new IOExceptionWithCause(ex);
      }

      if (transferListener.exception != null) {
        if (!(transferListener.exception instanceof UserCancelledException)) {
          if (transferListener.exception.getCause() instanceof SocketTimeoutException && i <= 2) {
            continue;
          }

          if (authorizatonHandler != null
              && transferListener.exception instanceof IncomingFileTransferException) {
            // We assume contents can be accessed via the github API
            // https://developer.github.com/v3/repos/contents/#get-contents
            // That API, for security reasons, does not return HTTP_UNAUTHORIZED, so we need this
            // special case for that host.
            IncomingFileTransferException incomingFileTransferException =
                (IncomingFileTransferException) transferListener.exception;
            int errorCode = incomingFileTransferException.getErrorCode();
            if (errorCode == HttpURLConnection.HTTP_UNAUTHORIZED
                || API_GITHUB_HOST.equals(getHost(uri))
                    && errorCode == HttpURLConnection.HTTP_NOT_FOUND) {
              if (authorization == null) {
                authorization = authorizatonHandler.authorize(uri);
                if (authorization.isAuthorized()) {
                  --i;
                  continue;
                }
              }

              if (!authorization.isUnauthorizeable() && triedReauthorization++ < 3) {
                authorization = authorizatonHandler.reauthorize(uri, authorization);
                if (authorization.isAuthorized()) {
                  --i;
                  continue;
                }
              }
            }
          }
        }

        if (!CacheHandling.CACHE_IGNORE.equals(cacheHandling)
            && uriConverter.exists(cacheURI, options)
            && (!(transferListener.exception instanceof IncomingFileTransferException)
                || ((IncomingFileTransferException) transferListener.exception).getErrorCode()
                    != HttpURLConnection.HTTP_NOT_FOUND)) {
          setExpectedETag(uri, transferListener.eTag == null ? eTag : transferListener.eTag);
          return uriConverter.createInputStream(cacheURI, options);
        }

        throw new IOExceptionWithCause(transferListener.exception);
      }

      byte[] bytes = transferListener.out.toByteArray();

      // In the case of the Github API, the bytes will be JSON that contains a "content" pair
      // containing the Base64 encoding of the actual contents.
      if (API_GITHUB_HOST.equals(getHost(uri))) {
        // Find the start tag in the JSON value.
        String value = new String(bytes, "UTF-8");
        int start = value.indexOf(CONTENT_TAG);
        if (start != -1) {
          // Find the ending quote of the encoded contents.
          start += CONTENT_TAG.length();
          int end = value.indexOf('"', start);
          if (end != -1) {
            // The content is delimited by \n so split on that during the conversion.
            String content = value.substring(start, end);
            String[] split = content.split("\\\\n");

            // Write the converted bytes to a new stream and process those bytes instead.
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            for (String line : split) {
              byte[] binary = XMLTypeFactory.eINSTANCE.createBase64Binary(line);
              out.write(binary);
            }

            out.close();
            bytes = out.toByteArray();
          }
        }
      }

      try {
        BaseUtil.writeFile(uriConverter, options, cacheURI, bytes);
      } catch (IORuntimeException ex) {
        // Ignore attempts to write out to the cache file.
        // This may collide with another JVM doing exactly the same thing.
        transferListener.eTag = null;
      } finally {
        setETag(uriConverter, cacheURI, transferListener.eTag);
      }

      setExpectedETag(uri, transferListener.eTag);
      Map<Object, Object> response = getResponse(options);
      if (response != null) {
        response.put(URIConverter.RESPONSE_TIME_STAMP_PROPERTY, transferListener.lastModified);
      }

      ETagMirror etagMirror = (ETagMirror) options.get(ETagMirror.OPTION_ETAG_MIRROR);
      if (etagMirror != null) {
        etagMirror.cacheUpdated(uri);
      }

      return new ByteArrayInputStream(bytes);
    }
  }