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