diff --git a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/ContactMailboxDownloadWorker.java b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/ContactMailboxDownloadWorker.java index a70f18d69..3922ebccf 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/ContactMailboxDownloadWorker.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/ContactMailboxDownloadWorker.java @@ -1,73 +1,23 @@ package org.briarproject.bramble.mailbox; -import org.briarproject.bramble.api.Cancellable; +import org.briarproject.bramble.api.mailbox.MailboxFolderId; import org.briarproject.bramble.api.mailbox.MailboxProperties; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver; import org.briarproject.bramble.mailbox.MailboxApi.ApiException; import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile; -import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException; -import org.briarproject.bramble.mailbox.TorReachabilityMonitor.TorReachabilityObserver; -import java.io.File; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Queue; -import java.util.logging.Logger; -import javax.annotation.Nullable; -import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; -import static java.util.logging.Level.INFO; -import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; -import static org.briarproject.bramble.util.LogUtils.logException; @ThreadSafe @NotNullByDefault -class ContactMailboxDownloadWorker implements MailboxWorker, - ConnectivityObserver, TorReachabilityObserver { - - /** - * When the worker is started it waits for a connectivity check, then - * starts its first download cycle: checking the inbox, downloading and - * deleting any files, and checking again until the inbox is empty. - *
- * The worker then waits for our Tor hidden service to be reachable before
- * starting its second download cycle. This ensures that if a contact
- * tried and failed to connect to our hidden service before it was
- * reachable, and therefore uploaded a file to the mailbox instead, we'll
- * find the file in the second download cycle.
- */
- private enum State {
- CREATED,
- CONNECTIVITY_CHECK,
- DOWNLOAD_CYCLE_1,
- WAITING_FOR_TOR,
- DOWNLOAD_CYCLE_2,
- FINISHED,
- DESTROYED
- }
-
- private static final Logger LOG =
- getLogger(ContactMailboxDownloadWorker.class.getName());
-
- private final ConnectivityChecker connectivityChecker;
- private final TorReachabilityMonitor torReachabilityMonitor;
- private final MailboxApiCaller mailboxApiCaller;
- private final MailboxApi mailboxApi;
- private final MailboxFileManager mailboxFileManager;
- private final MailboxProperties mailboxProperties;
- private final Object lock = new Object();
-
- @GuardedBy("lock")
- private State state = State.CREATED;
-
- @GuardedBy("lock")
- @Nullable
- private Cancellable apiCall = null;
+class ContactMailboxDownloadWorker extends MailboxDownloadWorker {
ContactMailboxDownloadWorker(
ConnectivityChecker connectivityChecker,
@@ -76,57 +26,14 @@ class ContactMailboxDownloadWorker implements MailboxWorker,
MailboxApi mailboxApi,
MailboxFileManager mailboxFileManager,
MailboxProperties mailboxProperties) {
+ super(connectivityChecker, torReachabilityMonitor, mailboxApiCaller,
+ mailboxApi, mailboxFileManager, mailboxProperties);
if (mailboxProperties.isOwner()) throw new IllegalArgumentException();
- this.connectivityChecker = connectivityChecker;
- this.torReachabilityMonitor = torReachabilityMonitor;
- this.mailboxApiCaller = mailboxApiCaller;
- this.mailboxApi = mailboxApi;
- this.mailboxFileManager = mailboxFileManager;
- this.mailboxProperties = mailboxProperties;
}
@Override
- public void start() {
- LOG.info("Started");
- synchronized (lock) {
- // Don't allow the worker to be reused
- if (state != State.CREATED) return;
- state = State.CONNECTIVITY_CHECK;
- }
- // Avoid leaking observer in case destroy() is called concurrently
- // before observer is added
- connectivityChecker.checkConnectivity(mailboxProperties, this);
- boolean destroyed;
- synchronized (lock) {
- destroyed = state == State.DESTROYED;
- }
- if (destroyed) connectivityChecker.removeObserver(this);
- }
-
- @Override
- public void destroy() {
- LOG.info("Destroyed");
- Cancellable apiCall;
- synchronized (lock) {
- state = State.DESTROYED;
- apiCall = this.apiCall;
- this.apiCall = null;
- }
- if (apiCall != null) apiCall.cancel();
- connectivityChecker.removeObserver(this);
- torReachabilityMonitor.removeObserver(this);
- }
-
- @Override
- public void onConnectivityCheckSucceeded() {
- LOG.info("Connectivity check succeeded");
- synchronized (lock) {
- if (state != State.CONNECTIVITY_CHECK) return;
- state = State.DOWNLOAD_CYCLE_1;
- // Start first download cycle
- apiCall = mailboxApiCaller.retryWithBackoff(
- new SimpleApiCall(this::apiCallListInbox));
- }
+ protected ApiCall createApiCallForDownloadCycle() {
+ return new SimpleApiCall(this::apiCallListInbox);
}
private void apiCallListInbox() throws IOException, ApiException {
@@ -134,110 +41,18 @@ class ContactMailboxDownloadWorker implements MailboxWorker,
if (state == State.DESTROYED) return;
}
LOG.info("Listing inbox");
- List
+ * The worker then waits for our Tor hidden service to be reachable before
+ * starting its second download cycle. This ensures that if a contact
+ * tried and failed to connect to our hidden service before it was
+ * reachable, and therefore uploaded a file to the mailbox instead, we'll
+ * find the file in the second download cycle.
+ */
+ protected enum State {
+ CREATED,
+ CONNECTIVITY_CHECK,
+ DOWNLOAD_CYCLE_1,
+ WAITING_FOR_TOR,
+ DOWNLOAD_CYCLE_2,
+ FINISHED,
+ DESTROYED
+ }
+
+ protected static final Logger LOG =
+ getLogger(MailboxDownloadWorker.class.getName());
+
+ private final ConnectivityChecker connectivityChecker;
+ private final TorReachabilityMonitor torReachabilityMonitor;
+ protected final MailboxApiCaller mailboxApiCaller;
+ protected final MailboxApi mailboxApi;
+ private final MailboxFileManager mailboxFileManager;
+ protected final MailboxProperties mailboxProperties;
+ protected final Object lock = new Object();
+
+ @GuardedBy("lock")
+ protected State state = State.CREATED;
+
+ @GuardedBy("lock")
+ @Nullable
+ protected Cancellable apiCall = null;
+
+ /**
+ * Creates the API call that starts the worker's download cycle.
+ */
+ protected abstract ApiCall createApiCallForDownloadCycle();
+
+ MailboxDownloadWorker(
+ ConnectivityChecker connectivityChecker,
+ TorReachabilityMonitor torReachabilityMonitor,
+ MailboxApiCaller mailboxApiCaller,
+ MailboxApi mailboxApi,
+ MailboxFileManager mailboxFileManager,
+ MailboxProperties mailboxProperties) {
+ this.connectivityChecker = connectivityChecker;
+ this.torReachabilityMonitor = torReachabilityMonitor;
+ this.mailboxApiCaller = mailboxApiCaller;
+ this.mailboxApi = mailboxApi;
+ this.mailboxFileManager = mailboxFileManager;
+ this.mailboxProperties = mailboxProperties;
+ }
+
+ @Override
+ public void start() {
+ LOG.info("Started");
+ synchronized (lock) {
+ // Don't allow the worker to be reused
+ if (state != State.CREATED) return;
+ state = State.CONNECTIVITY_CHECK;
+ }
+ // Avoid leaking observer in case destroy() is called concurrently
+ // before observer is added
+ connectivityChecker.checkConnectivity(mailboxProperties, this);
+ boolean destroyed;
+ synchronized (lock) {
+ destroyed = state == State.DESTROYED;
+ }
+ if (destroyed) connectivityChecker.removeObserver(this);
+ }
+
+ @Override
+ public void destroy() {
+ LOG.info("Destroyed");
+ Cancellable apiCall;
+ synchronized (lock) {
+ state = State.DESTROYED;
+ apiCall = this.apiCall;
+ this.apiCall = null;
+ }
+ if (apiCall != null) apiCall.cancel();
+ connectivityChecker.removeObserver(this);
+ torReachabilityMonitor.removeObserver(this);
+ }
+
+ @Override
+ public void onConnectivityCheckSucceeded() {
+ LOG.info("Connectivity check succeeded");
+ synchronized (lock) {
+ if (state != State.CONNECTIVITY_CHECK) return;
+ state = State.DOWNLOAD_CYCLE_1;
+ // Start first download cycle
+ apiCall = mailboxApiCaller.retryWithBackoff(
+ createApiCallForDownloadCycle());
+ }
+ }
+
+ void onDownloadCycleFinished() {
+ boolean addObserver = false;
+ synchronized (lock) {
+ if (state == State.DOWNLOAD_CYCLE_1) {
+ LOG.info("First download cycle finished");
+ state = State.WAITING_FOR_TOR;
+ apiCall = null;
+ addObserver = true;
+ } else if (state == State.DOWNLOAD_CYCLE_2) {
+ LOG.info("Second download cycle finished");
+ state = State.FINISHED;
+ apiCall = null;
+ }
+ }
+ if (addObserver) {
+ // Avoid leaking observer in case destroy() is called concurrently
+ // before observer is added
+ torReachabilityMonitor.addOneShotObserver(this);
+ boolean destroyed;
+ synchronized (lock) {
+ destroyed = state == State.DESTROYED;
+ }
+ if (destroyed) torReachabilityMonitor.removeObserver(this);
+ }
+ }
+
+ void downloadNextFile(Queue
- * The worker then waits for our Tor hidden service to be reachable before
- * starting its second download cycle. This ensures that if a contact
- * tried and failed to connect to our hidden service before it was
- * reachable, and therefore uploaded a file to the mailbox instead, we'll
- * find the file in the second download cycle.
- */
- private enum State {
- CREATED,
- CONNECTIVITY_CHECK,
- DOWNLOAD_CYCLE_1,
- WAITING_FOR_TOR,
- DOWNLOAD_CYCLE_2,
- FINISHED,
- DESTROYED
- }
+class OwnMailboxDownloadWorker extends MailboxDownloadWorker {
/**
* The maximum number of files that will be downloaded before checking
* again for folders with available files. This ensures that if a file
* arrives during a download cycle, its folder will be checked within a
- * reasonable amount of time even if another folder has a very large number
- * of files.
+ * reasonable amount of time even if some other folder has a very large
+ * number of files.
*
* Package access for testing.
*/
static final int MAX_ROUND_ROBIN_FILES = 1000;
- private static final Logger LOG =
- getLogger(OwnMailboxDownloadWorker.class.getName());
-
- private final ConnectivityChecker connectivityChecker;
- private final TorReachabilityMonitor torReachabilityMonitor;
- private final MailboxApiCaller mailboxApiCaller;
- private final MailboxApi mailboxApi;
- private final MailboxFileManager mailboxFileManager;
- private final MailboxProperties mailboxProperties;
- private final Object lock = new Object();
-
- @GuardedBy("lock")
- private State state = State.CREATED;
-
- @GuardedBy("lock")
- @Nullable
- private Cancellable apiCall = null;
-
OwnMailboxDownloadWorker(
ConnectivityChecker connectivityChecker,
TorReachabilityMonitor torReachabilityMonitor,
@@ -95,57 +42,14 @@ class OwnMailboxDownloadWorker implements MailboxWorker, ConnectivityObserver,
MailboxApi mailboxApi,
MailboxFileManager mailboxFileManager,
MailboxProperties mailboxProperties) {
+ super(connectivityChecker, torReachabilityMonitor, mailboxApiCaller,
+ mailboxApi, mailboxFileManager, mailboxProperties);
if (!mailboxProperties.isOwner()) throw new IllegalArgumentException();
- this.connectivityChecker = connectivityChecker;
- this.torReachabilityMonitor = torReachabilityMonitor;
- this.mailboxApiCaller = mailboxApiCaller;
- this.mailboxApi = mailboxApi;
- this.mailboxFileManager = mailboxFileManager;
- this.mailboxProperties = mailboxProperties;
}
@Override
- public void start() {
- LOG.info("Started");
- synchronized (lock) {
- // Don't allow the worker to be reused
- if (state != State.CREATED) return;
- state = State.CONNECTIVITY_CHECK;
- }
- // Avoid leaking observer in case destroy() is called concurrently
- // before observer is added
- connectivityChecker.checkConnectivity(mailboxProperties, this);
- boolean destroyed;
- synchronized (lock) {
- destroyed = state == State.DESTROYED;
- }
- if (destroyed) connectivityChecker.removeObserver(this);
- }
-
- @Override
- public void destroy() {
- LOG.info("Destroyed");
- Cancellable apiCall;
- synchronized (lock) {
- state = State.DESTROYED;
- apiCall = this.apiCall;
- this.apiCall = null;
- }
- if (apiCall != null) apiCall.cancel();
- connectivityChecker.removeObserver(this);
- torReachabilityMonitor.removeObserver(this);
- }
-
- @Override
- public void onConnectivityCheckSucceeded() {
- LOG.info("Connectivity check succeeded");
- synchronized (lock) {
- if (state != State.CONNECTIVITY_CHECK) return;
- state = State.DOWNLOAD_CYCLE_1;
- // Start first download cycle
- apiCall = mailboxApiCaller.retryWithBackoff(
- new SimpleApiCall(this::apiCallListFolders));
- }
+ protected ApiCall createApiCallForDownloadCycle() {
+ return new SimpleApiCall(this::apiCallListFolders);
}
private void apiCallListFolders() throws IOException, ApiException {
@@ -159,32 +63,6 @@ class OwnMailboxDownloadWorker implements MailboxWorker, ConnectivityObserver,
else listNextFolder(new LinkedList<>(folders), new HashMap<>());
}
- private void onDownloadCycleFinished() {
- boolean addObserver = false;
- synchronized (lock) {
- if (state == State.DOWNLOAD_CYCLE_1) {
- LOG.info("First download cycle finished");
- state = State.WAITING_FOR_TOR;
- apiCall = null;
- addObserver = true;
- } else if (state == State.DOWNLOAD_CYCLE_2) {
- LOG.info("Second download cycle finished");
- state = State.FINISHED;
- apiCall = null;
- }
- }
- if (addObserver) {
- // Avoid leaking observer in case destroy() is called concurrently
- // before observer is added
- torReachabilityMonitor.addOneShotObserver(this);
- boolean destroyed;
- synchronized (lock) {
- destroyed = state == State.DESTROYED;
- }
- if (destroyed) torReachabilityMonitor.removeObserver(this);
- }
- }
-
private void listNextFolder(Queue