package org.mozilla.gecko.sync; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.mozilla.gecko.sync.delegates.JSONRecordFetchDelegate; import org.mozilla.gecko.sync.net.SyncStorageRecordRequest; import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate; import org.mozilla.gecko.sync.net.SyncStorageResponse; /** * An object which fetches a chunk of JSON from a URI, using certain credentials, * and informs its delegate of the result. */ public class JSONRecordFetcher { private static final long DEFAULT_AWAIT_TIMEOUT_MSEC = 2 * 60 * 1000; // Two minutes. private static final String LOG_TAG = "JSONRecordFetcher"; protected final String credentials; protected final String uri; protected JSONRecordFetchDelegate delegate; public JSONRecordFetcher(final String uri, final String credentials) { this.uri = uri; this.credentials = credentials; } protected String getURI() { return this.uri; } private class JSONFetchHandler implements SyncStorageRequestDelegate { // SyncStorageRequestDelegate methods for fetching. public String credentials() { return credentials; } public String ifUnmodifiedSince() { return null; } public void handleRequestSuccess(SyncStorageResponse response) { if (response.wasSuccessful()) { try { delegate.handleSuccess(response.jsonObjectBody()); } catch (Exception e) { handleRequestError(e); } return; } handleRequestFailure(response); } @Override public void handleRequestFailure(SyncStorageResponse response) { delegate.handleFailure(response); } @Override public void handleRequestError(Exception ex) { delegate.handleError(ex); } } public void fetch(final JSONRecordFetchDelegate delegate) { this.delegate = delegate; try { final SyncStorageRecordRequest r = new SyncStorageRecordRequest(this.getURI()); r.delegate = new JSONFetchHandler(); r.get(); } catch (Exception e) { delegate.handleError(e); } } private class LatchedJSONRecordFetchDelegate implements JSONRecordFetchDelegate { public ExtendedJSONObject body = null; public Exception exception = null; private CountDownLatch latch; public LatchedJSONRecordFetchDelegate(CountDownLatch latch) { this.latch = latch; } @Override public void handleFailure(SyncStorageResponse response) { this.exception = new HTTPFailureException(response); latch.countDown(); } @Override public void handleError(Exception e) { this.exception = e; latch.countDown(); } @Override public void handleSuccess(ExtendedJSONObject body) { this.body = body; latch.countDown(); } } /** * Fetch the info record, blocking until it returns. * @return the info record. */ public ExtendedJSONObject fetchBlocking() throws HTTPFailureException, Exception { CountDownLatch latch = new CountDownLatch(1); LatchedJSONRecordFetchDelegate delegate = new LatchedJSONRecordFetchDelegate(latch); this.delegate = delegate; this.fetch(delegate); // Sanity wait: the resource itself will time out and throw after two // minutes, so we just want to avoid coding errors causing us to block // endlessly. if (!latch.await(DEFAULT_AWAIT_TIMEOUT_MSEC, TimeUnit.MILLISECONDS)) { Logger.warn(LOG_TAG, "Interrupted fetching info record."); throw new InterruptedException("info fetch timed out."); } if (delegate.body != null) { return delegate.body; } if (delegate.exception != null) { throw delegate.exception; } throw new Exception("Unknown error."); } }