// TODO potential concurrency problem -- late joiner may be added after failure public synchronized void addLateJoiner(final CacheRequest request) { if (cancelled) { request.notifyFailure(RequestFailureType.CANCELLED, null, null, "Cancelled"); return; } if (!request.setDownload(this)) { notifyAllOnFailure(RequestFailureType.CANCELLED, null, null, "Cancelled"); return; } if (request.isJson != initiator.isJson) { BugReportActivity.handleGlobalError( request.context, "Late joiner disagrees with initiator on JSON type"); return; } lateJoiners.add(request); if (request.isHigherPriorityThan(highestPriorityReq)) { highestPriorityReq = request; } if (request.isJson) { if (value != null) request.notifyJsonParseStarted(value, RRTime.utcCurrentTimeMillis(), session, false); } }
private synchronized void notifyAllDownloadStarted() { initiator.notifyDownloadStarted(); for (final CacheRequest req : lateJoiners) { req.notifyDownloadStarted(); } }
private synchronized void notifyAllOnProgress(final long bytesRead, final long bytesTotal) { initiator.notifyProgress(bytesRead, bytesTotal); for (final CacheRequest req : lateJoiners) { req.notifyProgress(bytesRead, bytesTotal); } }
private synchronized void notifyAllOnJsonParseStarted( final JsonValue value, final long timestamp, final UUID session) { initiator.notifyJsonParseStarted(value, timestamp, session, false); for (final CacheRequest req : lateJoiners) { req.notifyJsonParseStarted(value, timestamp, session, false); } }
private synchronized void notifyAllOnSuccess( final CacheManager.ReadableCacheFile cacheFile, final long timestamp, final UUID session, final String mimetype) { initiator.notifySuccess(cacheFile, timestamp, session, false, mimetype); for (final CacheRequest req : lateJoiners) { req.notifySuccess(cacheFile, timestamp, session, false, mimetype); } }
private synchronized void notifyAllOnFailure( final RequestFailureType type, final Throwable t, final StatusLine status, final String readableMessage) { initiator.notifyFailure(type, t, status, readableMessage); for (final CacheRequest req : lateJoiners) { req.notifyFailure(type, t, status, readableMessage); } }
private void handleRequest(final CacheRequest request) { if (request.url == null) { request.notifyFailure( RequestFailureType.MALFORMED_URL, new NullPointerException("URL was null"), null, "URL was null"); return; } switch (request.downloadType) { case NEVER: { final LinkedList<CacheEntry> result = dbManager.select(request.url, request.user.username, request.requestSession); if (result.size() == 0) { request.notifyFailure( RequestFailureType.CACHE_MISS, null, null, "Could not find this data in the cache"); } else { final CacheEntry entry = mostRecentFromList(result); handleCacheEntryFound(entry, request); } break; } case IF_NECESSARY: { final LinkedList<CacheEntry> result = dbManager.select(request.url, request.user.username, request.requestSession); if (result.size() == 0) { queueDownload(request); } else { final CacheEntry entry = mostRecentFromList(result); handleCacheEntryFound(entry, request); } break; } case FORCE: queueDownload(request); break; } }
private Response cacheWritingResponse(CacheRequest cacherequest, Response response) { ac ac1; if (cacherequest != null) { if ((ac1 = cacherequest.body()) != null) { cacherequest = new _cls2(); return response.newBuilder().body(new RealResponseBody(response.headers(), r.a(cacherequest))).build(); } } return response; }
public synchronized void add(final CacheRequest request, final CacheManager manager) { final RequestIdentifier identifier = request.createIdentifier(); // Is in progress? If so, add late joiner. or, cancel if requested. if (downloadsInProgress.containsKey(identifier)) { if (request.cancelExisting) { downloadsInProgress.get(identifier).cancel(); } else { downloadsInProgress.get(identifier).addLateJoiner(request); return; } } // Is the priority <= 0? If so, spin up a new thread and run immediately. if (request.priority <= 0) { final CacheDownload download; if (downloadsQueued.containsKey(identifier)) { download = downloadsQueued.remove(identifier); download.addLateJoiner(request); } else { download = new CacheDownload(request, manager, this); } downloadsInProgress.put(identifier, download); new CacheDownloadThread(this, download, true); return; } // Is in queue? If so, add late joiner if (downloadsQueued.containsKey(identifier)) { downloadsQueued.get(identifier).addLateJoiner(request); return; } // Otherwise, add to queue and notify all downloadsQueued.put(identifier, new CacheDownload(request, manager, this)); notifyAll(); }
public CacheDownload( final CacheRequest initiator, final CacheManager manager, final PrioritisedDownloadQueue queue) { this.initiator = initiator; this.manager = manager; this.queue = queue; highestPriorityReq = initiator; if (!initiator.setDownload(this)) { cancel(); } if (initiator.requestSession != null) { session = initiator.requestSession; } else { session = UUID.randomUUID(); } }
public RequestIdentifier createIdentifier() { return initiator.createIdentifier(); }
public synchronized boolean isHigherPriorityThan(final CacheDownload another) { return highestPriorityReq.isHigherPriorityThan(another.highestPriorityReq); }
private void downloadGet(final HttpClient httpClient) { httpGet = new HttpGet(initiator.url); if (initiator.isJson) httpGet.setHeader("Accept-Encoding", "gzip"); final HttpContext localContext = new BasicHttpContext(); localContext.setAttribute(ClientContext.COOKIE_STORE, initiator.getCookies()); final HttpResponse response; final StatusLine status; try { if (cancelled) { notifyAllOnFailure(RequestFailureType.CANCELLED, null, null, "Cancelled"); return; } response = httpClient.execute(httpGet, localContext); status = response.getStatusLine(); } catch (Throwable t) { t.printStackTrace(); notifyAllOnFailure(RequestFailureType.CONNECTION, t, null, "Unable to open a connection"); return; } if (status.getStatusCode() != 200) { notifyAllOnFailure( RequestFailureType.REQUEST, null, status, String.format("HTTP error %d (%s)", status.getStatusCode(), status.getReasonPhrase())); return; } if (cancelled) { notifyAllOnFailure(RequestFailureType.CANCELLED, null, null, "Cancelled"); return; } final HttpEntity entity = response.getEntity(); if (entity == null) { notifyAllOnFailure( RequestFailureType.CONNECTION, null, status, "Did not receive a valid HTTP response"); return; } final InputStream is; try { is = entity.getContent(); mimetype = entity.getContentType() == null ? null : entity.getContentType().getValue(); } catch (Throwable t) { t.printStackTrace(); notifyAllOnFailure( RequestFailureType.CONNECTION, t, status, "Could not open an input stream"); return; } final NotifyOutputStream cacheOs; if (initiator.cache) { try { cacheFile = manager.openNewCacheFile(initiator, session, mimetype); cacheOs = cacheFile.getOutputStream(); } catch (IOException e) { e.printStackTrace(); notifyAllOnFailure(RequestFailureType.STORAGE, e, null, "Could not access the local cache"); return; } } else { cacheOs = null; } final long contentLength = entity.getContentLength(); if (initiator.isJson) { final InputStream bis; if (initiator.cache) { final CachingInputStream cis = new CachingInputStream( is, cacheOs, new CachingInputStream.BytesReadListener() { public void onBytesRead(final long total) { notifyAllOnProgress(total, contentLength); } }); bis = new BufferedInputStream(cis, 8 * 1024); } else { bis = new BufferedInputStream(is, 8 * 1024); } final JsonValue value; try { value = new JsonValue(bis); value.buildInNewThread(); } catch (Throwable t) { t.printStackTrace(); notifyAllOnFailure(RequestFailureType.PARSE, t, null, "Error parsing the JSON stream"); return; } synchronized (this) { this.value = value; notifyAllOnJsonParseStarted(value, RRTime.utcCurrentTimeMillis(), session); } try { value.join(); } catch (Throwable t) { t.printStackTrace(); notifyAllOnFailure(RequestFailureType.PARSE, t, null, "Error parsing the JSON stream"); return; } success = true; } else { if (!initiator.cache) { BugReportActivity.handleGlobalError( initiator.context, "Cache disabled for non-JSON request"); return; } try { final byte[] buf = new byte[8 * 1024]; int bytesRead; long totalBytesRead = 0; while ((bytesRead = is.read(buf)) > 0) { totalBytesRead += bytesRead; cacheOs.write(buf, 0, bytesRead); notifyAllOnProgress(totalBytesRead, contentLength); } cacheOs.flush(); cacheOs.close(); success = true; } catch (Throwable t) { t.printStackTrace(); notifyAllOnFailure( RequestFailureType.CONNECTION, t, null, "The connection was interrupted"); } } }
// TODO merge with downloadGet private void downloadPost(final HttpClient httpClient) { final HttpPost httpPost = new HttpPost(initiator.url); try { httpPost.setEntity(new UrlEncodedFormEntity(initiator.postFields, HTTP.UTF_8)); } catch (UnsupportedEncodingException e) { BugReportActivity.handleGlobalError(initiator.context, e); return; } final HttpContext localContext = new BasicHttpContext(); localContext.setAttribute(ClientContext.COOKIE_STORE, initiator.getCookies()); final HttpResponse response; final StatusLine status; try { response = httpClient.execute(httpPost, localContext); status = response.getStatusLine(); } catch (Throwable t) { t.printStackTrace(); notifyAllOnFailure(RequestFailureType.CONNECTION, t, null, "Unable to open a connection"); return; } if (status.getStatusCode() != 200) { notifyAllOnFailure( RequestFailureType.REQUEST, null, status, String.format("HTTP error %d (%s)", status.getStatusCode(), status.getReasonPhrase())); return; } final HttpEntity entity = response.getEntity(); if (entity == null) { notifyAllOnFailure( RequestFailureType.CONNECTION, null, status, "Did not receive a valid HTTP response"); return; } final InputStream is; try { is = entity.getContent(); } catch (Throwable t) { t.printStackTrace(); notifyAllOnFailure( RequestFailureType.CONNECTION, t, status, "Could not open an input stream"); return; } if (initiator.isJson) { final BufferedInputStream bis = new BufferedInputStream(is, 8 * 1024); final JsonValue value; try { value = new JsonValue(bis); value.buildInNewThread(); } catch (Throwable t) { t.printStackTrace(); notifyAllOnFailure(RequestFailureType.PARSE, t, null, "Error parsing the JSON stream"); return; } synchronized (this) { this.value = value; notifyAllOnJsonParseStarted(value, RRTime.utcCurrentTimeMillis(), session); } try { value.join(); } catch (Throwable t) { t.printStackTrace(); notifyAllOnFailure(RequestFailureType.PARSE, t, null, "Error parsing the JSON stream"); return; } success = true; } else { throw new RuntimeException("POST requests must be for JSON values"); } }
private void queueDownload(final CacheRequest request) { request.notifyDownloadNecessary(); downloadQueue.add(request, CacheManager.this); }