Bug 732768 - Eliminate shared state and concurrency problems in tests. r=nalexander

This commit is contained in:
Richard Newman 2012-03-05 20:53:14 -08:00
parent 4b3ef26701
commit bb48da370c
11 changed files with 295 additions and 241 deletions

View File

@ -1,39 +1,6 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Android Sync Client.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Richard Newman <rnewman@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.middleware;
@ -43,16 +10,12 @@ import java.util.concurrent.ExecutorService;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.crypto.CryptoException;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
import org.mozilla.gecko.sync.repositories.RecordFactory;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionGuidsSinceDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
import org.mozilla.gecko.sync.repositories.domain.Record;
/**
@ -94,14 +57,12 @@ import org.mozilla.gecko.sync.repositories.domain.Record;
* @author rnewman
*
*/
public class Crypto5MiddlewareRepositorySession extends RepositorySession {
public class Crypto5MiddlewareRepositorySession extends MiddlewareRepositorySession {
private KeyBundle keyBundle;
private RepositorySession inner;
private RecordFactory recordFactory;
public Crypto5MiddlewareRepositorySession(RepositorySession session, Crypto5MiddlewareRepository repository, RecordFactory recordFactory) {
super(repository);
this.inner = session;
super(session, repository);
this.keyBundle = repository.keyBundle;
this.recordFactory = recordFactory;
}
@ -180,13 +141,6 @@ public class Crypto5MiddlewareRepositorySession extends RepositorySession {
return new DecryptingTransformingFetchDelegate(inner, this.keyBundle, this.recordFactory);
}
@Override
public void guidsSince(long timestamp,
RepositorySessionGuidsSinceDelegate delegate) {
// TODO: need to do anything here?
inner.guidsSince(timestamp, delegate);
}
@Override
public void fetchSince(long timestamp,
RepositorySessionFetchRecordsDelegate delegate) {
@ -195,7 +149,7 @@ public class Crypto5MiddlewareRepositorySession extends RepositorySession {
@Override
public void fetch(String[] guids,
RepositorySessionFetchRecordsDelegate delegate) {
RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException {
inner.fetch(guids, makeUnwrappingDelegate(delegate));
}
@ -230,92 +184,4 @@ public class Crypto5MiddlewareRepositorySession extends RepositorySession {
// Allow the inner session to do delegate handling.
inner.store(rec);
}
@Override
public void wipe(RepositorySessionWipeDelegate delegate) {
inner.wipe(delegate);
}
@Override
public void storeDone() {
inner.storeDone();
}
@Override
public void storeDone(long storeEnd) {
inner.storeDone(storeEnd);
}
public class Crypto5MiddlewareRepositorySessionBeginDelegate implements RepositorySessionBeginDelegate {
private Crypto5MiddlewareRepositorySession outerSession;
private RepositorySessionBeginDelegate next;
public Crypto5MiddlewareRepositorySessionBeginDelegate(Crypto5MiddlewareRepositorySession outerSession, RepositorySessionBeginDelegate next) {
this.outerSession = outerSession;
this.next = next;
}
@Override
public void onBeginFailed(Exception ex) {
next.onBeginFailed(ex);
}
@Override
public void onBeginSucceeded(RepositorySession session) {
outerSession.setStatus(SessionStatus.ACTIVE);
next.onBeginSucceeded(outerSession);
}
@Override
public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) {
return this;
}
}
public void begin(RepositorySessionBeginDelegate delegate) {
inner.begin(new Crypto5MiddlewareRepositorySessionBeginDelegate(this, delegate));
}
public class Crypto5MiddlewareRepositorySessionFinishDelegate implements RepositorySessionFinishDelegate {
private Crypto5MiddlewareRepositorySession outerSession;
private RepositorySessionFinishDelegate next;
public Crypto5MiddlewareRepositorySessionFinishDelegate(Crypto5MiddlewareRepositorySession outerSession, RepositorySessionFinishDelegate next) {
this.outerSession = outerSession;
this.next = next;
}
@Override
public void onFinishFailed(Exception ex) {
next.onFinishFailed(ex);
}
@Override
public void onFinishSucceeded(RepositorySession session, RepositorySessionBundle bundle) {
outerSession.setStatus(SessionStatus.DONE);
next.onFinishSucceeded(outerSession, bundle);
}
@Override
public RepositorySessionFinishDelegate deferredFinishDelegate(ExecutorService executor) {
return this;
}
}
@Override
public void finish(RepositorySessionFinishDelegate delegate) {
inner.finish(new Crypto5MiddlewareRepositorySessionFinishDelegate(this, delegate));
}
@Override
public void abort() {
setStatus(SessionStatus.ABORTED);
inner.abort();
}
@Override
public void abort(RepositorySessionFinishDelegate delegate) {
this.status = SessionStatus.DONE; // TODO: ABORTED?
inner.abort(new Crypto5MiddlewareRepositorySessionFinishDelegate(this, delegate));
}
}

View File

@ -51,6 +51,5 @@ public abstract class MiddlewareRepository extends Repository {
public RepositorySessionCreationDelegate deferredCreationDelegate() {
return this;
}
}
}

View File

@ -0,0 +1,164 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.middleware;
import java.util.concurrent.ExecutorService;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionGuidsSinceDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
public abstract class MiddlewareRepositorySession extends RepositorySession {
private static final String LOG_TAG = "MiddlewareSession";
protected final RepositorySession inner;
public MiddlewareRepositorySession(RepositorySession innerSession, MiddlewareRepository repository) {
super(repository);
this.inner = innerSession;
}
@Override
public void wipe(RepositorySessionWipeDelegate delegate) {
inner.wipe(delegate);
}
public class MiddlewareRepositorySessionBeginDelegate implements RepositorySessionBeginDelegate {
private MiddlewareRepositorySession outerSession;
private RepositorySessionBeginDelegate next;
public MiddlewareRepositorySessionBeginDelegate(MiddlewareRepositorySession outerSession, RepositorySessionBeginDelegate next) {
this.outerSession = outerSession;
this.next = next;
}
@Override
public void onBeginFailed(Exception ex) {
next.onBeginFailed(ex);
}
@Override
public void onBeginSucceeded(RepositorySession session) {
next.onBeginSucceeded(outerSession);
}
@Override
public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) {
final RepositorySessionBeginDelegate deferred = next.deferredBeginDelegate(executor);
return new RepositorySessionBeginDelegate() {
@Override
public void onBeginSucceeded(RepositorySession session) {
if (inner != session) {
Logger.warn(LOG_TAG, "Got onBeginSucceeded for session " + session + ", not our inner session!");
}
deferred.onBeginSucceeded(outerSession);
}
@Override
public void onBeginFailed(Exception ex) {
deferred.onBeginFailed(ex);
}
@Override
public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) {
return this;
}
};
}
}
public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException {
inner.begin(new MiddlewareRepositorySessionBeginDelegate(this, delegate));
}
public class MiddlewareRepositorySessionFinishDelegate implements RepositorySessionFinishDelegate {
private final MiddlewareRepositorySession outerSession;
private final RepositorySessionFinishDelegate next;
public MiddlewareRepositorySessionFinishDelegate(MiddlewareRepositorySession outerSession, RepositorySessionFinishDelegate next) {
this.outerSession = outerSession;
this.next = next;
}
@Override
public void onFinishFailed(Exception ex) {
next.onFinishFailed(ex);
}
@Override
public void onFinishSucceeded(RepositorySession session, RepositorySessionBundle bundle) {
next.onFinishSucceeded(outerSession, bundle);
}
@Override
public RepositorySessionFinishDelegate deferredFinishDelegate(ExecutorService executor) {
return this;
}
}
@Override
public void finish(RepositorySessionFinishDelegate delegate) throws InactiveSessionException {
inner.finish(new MiddlewareRepositorySessionFinishDelegate(this, delegate));
}
@Override
public synchronized void ensureActive() throws InactiveSessionException {
inner.ensureActive();
}
@Override
public synchronized boolean isActive() {
return inner.isActive();
}
@Override
public synchronized SessionStatus getStatus() {
return inner.getStatus();
}
@Override
public synchronized void setStatus(SessionStatus status) {
inner.setStatus(status);
}
@Override
public synchronized void transitionFrom(SessionStatus from, SessionStatus to)
throws InvalidSessionTransitionException {
inner.transitionFrom(from, to);
}
@Override
public void abort() {
inner.abort();
}
@Override
public void abort(RepositorySessionFinishDelegate delegate) {
inner.abort(new MiddlewareRepositorySessionFinishDelegate(this, delegate));
}
@Override
public void guidsSince(long timestamp, RepositorySessionGuidsSinceDelegate delegate) {
// TODO: need to do anything here?
inner.guidsSince(timestamp, delegate);
}
@Override
public void storeDone() {
inner.storeDone();
}
@Override
public void storeDone(long storeEnd) {
inner.storeDone(storeEnd);
}
}

View File

@ -80,7 +80,7 @@ public abstract class RepositorySession {
Logger.trace(LOG_TAG, message);
}
protected SessionStatus status = SessionStatus.UNSTARTED;
private SessionStatus status = SessionStatus.UNSTARTED;
protected Repository repository;
protected RepositorySessionStoreDelegate delegate;
@ -109,7 +109,7 @@ public abstract class RepositorySession {
public abstract void guidsSince(long timestamp, RepositorySessionGuidsSinceDelegate delegate);
public abstract void fetchSince(long timestamp, RepositorySessionFetchRecordsDelegate delegate);
public abstract void fetch(String[] guids, RepositorySessionFetchRecordsDelegate delegate);
public abstract void fetch(String[] guids, RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException;
public abstract void fetchAll(RepositorySessionFetchRecordsDelegate delegate);
/**
@ -182,21 +182,19 @@ public abstract class RepositorySession {
*
*/
protected void sharedBegin() throws InvalidSessionTransitionException {
if (this.status == SessionStatus.UNSTARTED) {
this.status = SessionStatus.ACTIVE;
} else {
Logger.error(LOG_TAG, "Tried to begin() an already active or finished session");
Logger.debug(LOG_TAG, "Shared begin.");
if (delegateQueue.isShutdown()) {
throw new InvalidSessionTransitionException(null);
}
if (storeWorkQueue.isShutdown()) {
throw new InvalidSessionTransitionException(null);
}
this.transitionFrom(SessionStatus.UNSTARTED, SessionStatus.ACTIVE);
}
public void begin(RepositorySessionBeginDelegate delegate) {
try {
sharedBegin();
delegate.deferredBeginDelegate(delegateQueue).onBeginSucceeded(this);
} catch (Exception e) {
delegate.deferredBeginDelegate(delegateQueue).onBeginFailed(e);
}
public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException {
sharedBegin();
delegate.deferredBeginDelegate(delegateQueue).onBeginSucceeded(this);
}
protected RepositorySessionBundle getBundle() {
@ -231,43 +229,85 @@ public abstract class RepositorySession {
* @param delegate
*/
public void abort(RepositorySessionFinishDelegate delegate) {
this.status = SessionStatus.DONE; // TODO: ABORTED?
this.abort();
delegate.deferredFinishDelegate(delegateQueue).onFinishSucceeded(this, this.getBundle(null));
}
public void finish(final RepositorySessionFinishDelegate delegate) {
if (this.status == SessionStatus.ACTIVE) {
this.status = SessionStatus.DONE;
delegate.deferredFinishDelegate(delegateQueue).onFinishSucceeded(this, this.getBundle(null));
} else {
Logger.error(LOG_TAG, "Tried to finish() an unstarted or already finished session");
Exception e = new InvalidSessionTransitionException(null);
delegate.deferredFinishDelegate(delegateQueue).onFinishFailed(e);
}
Logger.info(LOG_TAG, "Shutting down work queues.");
// storeWorkQueue.shutdown();
// delegateQueue.shutdown();
}
public boolean isActive() {
return status == SessionStatus.ACTIVE;
}
public SessionStatus getStatus() {
return status;
}
public void setStatus(SessionStatus status) {
this.status = status;
}
public void abort() {
// TODO: do something here.
status = SessionStatus.ABORTED;
this.setStatus(SessionStatus.ABORTED);
try {
storeWorkQueue.shutdown();
} catch (Exception e) {
Logger.error(LOG_TAG, "Caught exception shutting down store work queue.", e);
}
try {
delegateQueue.shutdown();
} catch (Exception e) {
Logger.error(LOG_TAG, "Caught exception shutting down delegate queue.", e);
}
}
public void finish(final RepositorySessionFinishDelegate delegate) throws InactiveSessionException {
try {
this.transitionFrom(SessionStatus.ACTIVE, SessionStatus.DONE);
delegate.deferredFinishDelegate(delegateQueue).onFinishSucceeded(this, this.getBundle(null));
} catch (InvalidSessionTransitionException e) {
Logger.error(LOG_TAG, "Tried to finish() an unstarted or already finished session");
InactiveSessionException ex = new InactiveSessionException(null);
ex.initCause(e);
throw ex;
}
Logger.info(LOG_TAG, "Shutting down work queues.");
storeWorkQueue.shutdown();
delegateQueue.shutdown();
}
/**
* Run the provided command if we're active and our delegate queue
* is not shut down.
*
* @param command
* @throws InactiveSessionException
*/
protected synchronized void executeDelegateCommand(Runnable command)
throws InactiveSessionException {
if (!isActive() || delegateQueue.isShutdown()) {
throw new InactiveSessionException(null);
}
delegateQueue.execute(command);
}
public synchronized void ensureActive() throws InactiveSessionException {
if (!isActive()) {
throw new InactiveSessionException(null);
}
}
public synchronized boolean isActive() {
return status == SessionStatus.ACTIVE;
}
public synchronized SessionStatus getStatus() {
return status;
}
public synchronized void setStatus(SessionStatus status) {
this.status = status;
}
public synchronized void transitionFrom(SessionStatus from, SessionStatus to) throws InvalidSessionTransitionException {
if (from == null || this.status == from) {
Logger.trace(LOG_TAG, "Successfully transitioning from " + this.status + " to " + to);
this.status = to;
return;
}
Logger.warn(LOG_TAG, "Wanted to transition from " + from + " but in state " + this.status);
throw new InvalidSessionTransitionException(null);
}
/**
* Produce a record that is some combination of the remote and local records
* provided.

View File

@ -24,7 +24,7 @@ public abstract class StoreTrackingRepositorySession extends RepositorySession {
}
@Override
public void begin(RepositorySessionBeginDelegate delegate) {
public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException {
RepositorySessionBeginDelegate deferredDelegate = delegate.deferredBeginDelegate(delegateQueue);
try {
super.sharedBegin();
@ -74,7 +74,7 @@ public abstract class StoreTrackingRepositorySession extends RepositorySession {
}
@Override
public void finish(RepositorySessionFinishDelegate delegate) {
public void finish(RepositorySessionFinishDelegate delegate) throws InactiveSessionException {
super.finish(delegate);
this.storeTracker = null;
}

View File

@ -1,40 +1,6 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Android Sync Client.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jason Voll <jvoll@mozilla.com>
* Richard Newman <rnewman@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.repositories.android;
@ -48,7 +14,8 @@ public class AndroidBrowserBookmarksRepository extends AndroidBrowserRepository
@Override
protected void sessionCreator(RepositorySessionCreationDelegate delegate, Context context) {
AndroidBrowserBookmarksRepositorySession session = new AndroidBrowserBookmarksRepositorySession(AndroidBrowserBookmarksRepository.this, context);
delegate.onSessionCreated(session);
final RepositorySessionCreationDelegate deferredCreationDelegate = delegate.deferredCreationDelegate();
deferredCreationDelegate.onSessionCreated(session);
}
@Override

View File

@ -16,6 +16,8 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
import org.mozilla.gecko.sync.repositories.NoGuidForIdException;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.ParentNotFoundException;
@ -478,7 +480,7 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo
}
@Override
public void begin(RepositorySessionBeginDelegate delegate) {
public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException {
// Check for the existence of special folders
// and insert them if they don't exist.
Cursor cur;
@ -526,7 +528,7 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo
}
@Override
public void finish(RepositorySessionFinishDelegate delegate) {
public void finish(RepositorySessionFinishDelegate delegate) throws InactiveSessionException {
// Override finish to do this check; make sure all records
// needing re-parenting have been re-parented.
if (needsReparenting != 0) {

View File

@ -141,14 +141,9 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos
}
@Override
public void begin(RepositorySessionBeginDelegate delegate) {
public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException {
RepositorySessionBeginDelegate deferredDelegate = delegate.deferredBeginDelegate(delegateQueue);
try {
super.sharedBegin();
} catch (InvalidSessionTransitionException e) {
deferredDelegate.onBeginFailed(e);
return;
}
super.sharedBegin();
try {
// We do this check here even though it results in one extra call to the DB
@ -241,9 +236,9 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos
@Override
public void fetch(String[] guids,
RepositorySessionFetchRecordsDelegate delegate) {
RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException {
FetchRunnable command = new FetchRunnable(guids, now(), null, delegate);
delegateQueue.execute(command);
executeDelegateCommand(command);
}
abstract class FetchingRunnable implements Runnable {
@ -289,7 +284,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos
}
}
class FetchRunnable extends FetchingRunnable {
public class FetchRunnable extends FetchingRunnable {
private String[] guids;
private long end;
private RecordFilter filter;
@ -392,6 +387,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos
@Override
public void run() {
if (!isActive()) {
Logger.warn(LOG_TAG, "AndroidBrowserRepositorySession is inactive. Store failing.");
delegate.onRecordStoreFailed(new InactiveSessionException(null));
return;
}

View File

@ -42,6 +42,7 @@ import java.util.concurrent.ExecutorService;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.ThreadPool;
import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.delegates.DeferredRepositorySessionBeginDelegate;
@ -166,8 +167,9 @@ class RecordsChannel implements
/**
* Begin both sessions, invoking flow() when done.
* @throws InvalidSessionTransitionException
*/
public void beginAndFlow() {
public void beginAndFlow() throws InvalidSessionTransitionException {
Logger.info(LOG_TAG, "Beginning source.");
source.begin(this);
}
@ -251,7 +253,11 @@ class RecordsChannel implements
public void onBeginSucceeded(RepositorySession session) {
if (session == source) {
Logger.info(LOG_TAG, "Source session began. Beginning sink session.");
sink.begin(this);
try {
sink.begin(this);
} catch (InvalidSessionTransitionException e) {
onBeginFailed(e);
}
}
if (session == sink) {
Logger.info(LOG_TAG, "Sink session began. Beginning flow.");

View File

@ -40,6 +40,8 @@ package org.mozilla.gecko.sync.synchronizer;
import java.util.concurrent.ExecutorService;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
import org.mozilla.gecko.sync.repositories.delegates.DeferrableRepositorySessionCreationDelegate;
@ -170,7 +172,11 @@ implements RecordsChannelDelegate,
};
final RecordsChannel channelAToB = new RecordsChannel(this.sessionA, this.sessionB, channelDelegate);
info("Starting A to B flow. Channel is " + channelAToB);
channelAToB.beginAndFlow();
try {
channelAToB.beginAndFlow();
} catch (InvalidSessionTransitionException e) {
onFlowBeginFailed(channelAToB, e);
}
}
@Override
@ -183,7 +189,11 @@ implements RecordsChannelDelegate,
flowBToACompleted = true;
// Finish the two sessions.
this.sessionA.finish(this);
try {
this.sessionA.finish(this);
} catch (InactiveSessionException e) {
this.onFinishFailed(e);
}
}
@Override
@ -297,7 +307,11 @@ implements RecordsChannelDelegate,
if (this.sessionB != null) {
info("Finishing session B.");
// On to the next.
this.sessionB.finish(this);
try {
this.sessionB.finish(this);
} catch (InactiveSessionException e) {
this.onFinishFailed(e);
}
}
} else if (session == sessionB) {
if (flowBToACompleted) {

File diff suppressed because one or more lines are too long