Bug 730626 - Implement resetting. r=nalexander, a=blocking-fennec

This commit is contained in:
Richard Newman 2012-04-21 21:15:27 -07:00
Родитель 75ea8ab159
Коммит 46a49ddccd
22 изменённых файлов: 725 добавлений и 278 удалений

Просмотреть файл

@ -0,0 +1,15 @@
/* 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;
public class EngineSettings {
public final String syncID;
public final int version;
public EngineSettings(final String syncID, final int version) {
this.syncID = syncID;
this.version = version;
}
}

Просмотреть файл

@ -131,6 +131,30 @@ public class ExtendedJSONObject {
return (String) this.get(key);
}
/**
* Return an Integer if the value for this key is an Integer, Long, or String
* that can be parsed as a base 10 Integer.
* Passes through null.
*
* @throws NumberFormatException
*/
public Integer getIntegerSafely(String key) throws NumberFormatException {
Object val = this.object.get(key);
if (val == null) {
return null;
}
if (val instanceof Integer) {
return (Integer) val;
}
if (val instanceof Long) {
return new Integer(((Long) val).intValue());
}
if (val instanceof String) {
return Integer.parseInt((String) val, 10);
}
throw new NumberFormatException("Expecting Integer, got " + val.getClass());
}
/**
* Return a server timestamp value as milliseconds since epoch.
* @param key

Просмотреть файл

@ -8,7 +8,11 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
@ -31,9 +35,7 @@ import org.mozilla.gecko.sync.net.SyncStorageRequest;
import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
import org.mozilla.gecko.sync.net.SyncStorageResponse;
import org.mozilla.gecko.sync.stage.AndroidBrowserBookmarksServerSyncStage;
import org.mozilla.gecko.sync.stage.FormHistoryServerSyncStage;
import org.mozilla.gecko.sync.stage.AndroidBrowserHistoryServerSyncStage;
import org.mozilla.gecko.sync.stage.PasswordsServerSyncStage;
import org.mozilla.gecko.sync.stage.CheckPreconditionsStage;
import org.mozilla.gecko.sync.stage.CompletedStage;
import org.mozilla.gecko.sync.stage.EnsureClusterURLStage;
@ -41,9 +43,11 @@ import org.mozilla.gecko.sync.stage.EnsureCrypto5KeysStage;
import org.mozilla.gecko.sync.stage.FennecTabsServerSyncStage;
import org.mozilla.gecko.sync.stage.FetchInfoCollectionsStage;
import org.mozilla.gecko.sync.stage.FetchMetaGlobalStage;
import org.mozilla.gecko.sync.stage.FormHistoryServerSyncStage;
import org.mozilla.gecko.sync.stage.GlobalSyncStage;
import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
import org.mozilla.gecko.sync.stage.NoSuchStageException;
import org.mozilla.gecko.sync.stage.PasswordsServerSyncStage;
import org.mozilla.gecko.sync.stage.SyncClientsEngineStage;
import android.content.Context;
@ -152,6 +156,8 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
registerCommands();
prepareStages();
// TODO: data-driven plan for the sync, referring to prepareStages.
}
protected void registerCommands() {
@ -160,14 +166,32 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
processor.registerCommand("resetEngine", new CommandRunner() {
@Override
public void executeCommand(List<String> args) {
resetClient(new String[] { args.get(0) });
HashSet<String> names = new HashSet<String>();
names.add(args.get(0));
resetStagesByName(names);
}
});
processor.registerCommand("resetAll", new CommandRunner() {
@Override
public void executeCommand(List<String> args) {
resetClient(null);
resetAllStages();
}
});
processor.registerCommand("wipeEngine", new CommandRunner() {
@Override
public void executeCommand(List<String> args) {
HashSet<String> names = new HashSet<String>();
names.add(args.get(0));
wipeStagesByName(names);
}
});
processor.registerCommand("wipeAll", new CommandRunner() {
@Override
public void executeCommand(List<String> args) {
wipeAllStages();
}
});
@ -180,24 +204,31 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
}
protected void prepareStages() {
stages = new HashMap<Stage, GlobalSyncStage>();
stages.put(Stage.checkPreconditions, new CheckPreconditionsStage());
stages.put(Stage.ensureClusterURL, new EnsureClusterURLStage());
stages.put(Stage.fetchInfoCollections, new FetchInfoCollectionsStage());
stages.put(Stage.fetchMetaGlobal, new FetchMetaGlobalStage());
stages.put(Stage.ensureKeysStage, new EnsureCrypto5KeysStage());
stages.put(Stage.syncClientsEngine, new SyncClientsEngineStage());
HashMap<Stage, GlobalSyncStage> stages = new HashMap<Stage, GlobalSyncStage>();
// TODO: more stages.
stages.put(Stage.syncTabs, new FennecTabsServerSyncStage());
stages.put(Stage.syncPasswords, new PasswordsServerSyncStage());
stages.put(Stage.syncBookmarks, new AndroidBrowserBookmarksServerSyncStage());
stages.put(Stage.syncHistory, new AndroidBrowserHistoryServerSyncStage());
stages.put(Stage.syncFormHistory, new FormHistoryServerSyncStage());
stages.put(Stage.completed, new CompletedStage());
stages.put(Stage.checkPreconditions, new CheckPreconditionsStage(this));
stages.put(Stage.ensureClusterURL, new EnsureClusterURLStage(this));
stages.put(Stage.fetchInfoCollections, new FetchInfoCollectionsStage(this));
stages.put(Stage.fetchMetaGlobal, new FetchMetaGlobalStage(this));
stages.put(Stage.ensureKeysStage, new EnsureCrypto5KeysStage(this));
stages.put(Stage.syncClientsEngine, new SyncClientsEngineStage(this));
stages.put(Stage.syncTabs, new FennecTabsServerSyncStage(this));
stages.put(Stage.syncPasswords, new PasswordsServerSyncStage(this));
stages.put(Stage.syncBookmarks, new AndroidBrowserBookmarksServerSyncStage(this));
stages.put(Stage.syncHistory, new AndroidBrowserHistoryServerSyncStage(this));
stages.put(Stage.syncFormHistory, new FormHistoryServerSyncStage(this));
stages.put(Stage.completed, new CompletedStage(this));
this.stages = Collections.unmodifiableMap(stages);
}
protected GlobalSyncStage getStageByName(Stage next) throws NoSuchStageException {
public GlobalSyncStage getSyncStageByName(String name) throws NoSuchStageException {
return getSyncStageByName(Stage.byName(name));
}
public GlobalSyncStage getSyncStageByName(Stage next) throws NoSuchStageException {
GlobalSyncStage stage = stages.get(next);
if (stage == null) {
throw new NoSuchStageException(next);
@ -205,6 +236,32 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
return stage;
}
public Collection<GlobalSyncStage> getSyncStagesByEnum(Collection<Stage> enums) {
ArrayList<GlobalSyncStage> out = new ArrayList<GlobalSyncStage>();
for (Stage name : enums) {
try {
GlobalSyncStage stage = this.getSyncStageByName(name);
out.add(stage);
} catch (NoSuchStageException e) {
Logger.warn(LOG_TAG, "Unable to find stage with name " + name);
}
}
return out;
}
public Collection<GlobalSyncStage> getSyncStagesByName(Collection<String> names) {
ArrayList<GlobalSyncStage> out = new ArrayList<GlobalSyncStage>();
for (String name : names) {
try {
GlobalSyncStage stage = this.getSyncStageByName(name);
out.add(stage);
} catch (NoSuchStageException e) {
Logger.warn(LOG_TAG, "Unable to find stage with name " + name);
}
}
return out;
}
/**
* Advance and loop around the stages of a sync.
* @param current
@ -232,7 +289,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
Stage next = nextStage(this.currentState);
GlobalSyncStage nextStage;
try {
nextStage = this.getStageByName(next);
nextStage = this.getSyncStageByName(next);
} catch (NoSuchStageException e) {
this.abort(e, "No such stage " + next);
return;
@ -240,7 +297,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
this.currentState = next;
Logger.info(LOG_TAG, "Running next stage " + next + " (" + nextStage + ")...");
try {
nextStage.execute(this);
nextStage.execute();
} catch (Exception ex) {
Logger.warn(LOG_TAG, "Caught exception " + ex + " running stage " + next);
this.abort(ex, "Uncaught exception in stage.");
@ -435,10 +492,9 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
String localSyncID = this.getSyncID();
if (!remoteSyncID.equals(localSyncID)) {
// Sync ID has changed. Reset timestamps and fetch new keys.
resetClient(null);
resetAllStages();
config.purgeCryptoKeys();
config.syncID = remoteSyncID;
// TODO TODO TODO
}
config.persistToPrefs();
advance();
@ -486,7 +542,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
@Override
public void onWiped(long timestamp) {
session.resetClient(null);
session.resetAllStages();
session.config.purgeCryptoKeys();
session.config.persistToPrefs();
@ -558,9 +614,26 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
freshStartDelegate.onFreshStartFailed(e);
}
});
}
// Note that we do not yet implement wipeRemote: it's only necessary for
// first sync options.
// -- reset local stages, wipe server for each stage *except* clients
// (stages only, not whole server!), send wipeEngine commands to each client.
//
// Similarly for startOver (because we don't receive that notification).
// -- remove client data from server, reset local stages, clear keys, reset
// backoff, clear all prefs, discard credentials.
//
// Change passphrase: wipe entire server, reset client to force upload, sync.
//
// When an engine is disabled: wipe its collections on the server, reupload
// meta/global.
//
// On syncing each stage: if server has engine version 0 or old, wipe server,
// reset client to prompt reupload.
// If sync ID mismatch: take that syncID and reset client.
private void wipeServer(final CredentialsSource credentials, final WipeServerDelegate wipeDelegate) {
SyncStorageRequest request;
final GlobalSession self = this;
@ -609,16 +682,62 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
request.delete();
}
/**
* Reset our state. Clear our sync ID, reset each engine, drop any
* cached records.
*/
private void resetClient(String[] engines) {
if (engines == null) {
// Set `engines` to be *all* the engines.
public void wipeAllStages() {
Logger.info(LOG_TAG, "Wiping all stages.");
// Includes "clients".
this.wipeStagesByEnum(Stage.getNamedStages());
}
public static void wipeStages(Collection<GlobalSyncStage> stages) {
for (GlobalSyncStage stage : stages) {
try {
Logger.info(LOG_TAG, "Wiping " + stage);
stage.wipeLocal();
} catch (Exception e) {
Logger.error(LOG_TAG, "Ignoring wipe failure for stage " + stage, e);
}
}
}
public void wipeStagesByEnum(Collection<Stage> stages) {
GlobalSession.wipeStages(this.getSyncStagesByEnum(stages));
}
public void wipeStagesByName(Collection<String> names) {
GlobalSession.wipeStages(this.getSyncStagesByName(names));
}
public void resetAllStages() {
Logger.info(LOG_TAG, "Resetting all stages.");
// Includes "clients".
this.resetStagesByEnum(Stage.getNamedStages());
}
public static void resetStages(Collection<GlobalSyncStage> stages) {
for (GlobalSyncStage stage : stages) {
try {
Logger.info(LOG_TAG, "Resetting " + stage);
stage.resetLocal();
} catch (Exception e) {
Logger.error(LOG_TAG, "Ignoring reset failure for stage " + stage, e);
}
}
}
public void resetStagesByEnum(Collection<Stage> stages) {
GlobalSession.resetStages(this.getSyncStagesByEnum(stages));
}
public void resetStagesByName(Collection<String> names) {
for (String name : names) {
try {
GlobalSyncStage stage = this.getSyncStageByName(name);
Logger.info(LOG_TAG, "Resetting " + name + "(" + stage + ")");
stage.resetLocal();
} catch (NoSuchStageException e) {
Logger.warn(LOG_TAG, "Cannot reset stage " + name + ": no such stage.");
}
}
// TODO: futz with config?!
// TODO: engines?!
}
/**
@ -635,21 +754,40 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
* Otherwise, returns true if there is an entry for this engine in the
* meta/global "engines" object.
*
* @param engineName
* @param engineName the name to check (e.g., "bookmarks").
* @param engineSettings
* if non-null, verify that the server engine settings are congruent
* with this, throwing the appropriate MetaGlobalException if not.
* @return
* true if the engine with the provided name is present in the
* meta/global "engines" object.
* meta/global "engines" object, and verification passed.
*
* @throws MetaGlobalException
*/
public boolean engineIsEnabled(String engineName) throws MetaGlobalException {
public boolean engineIsEnabled(String engineName, EngineSettings engineSettings) throws MetaGlobalException {
if (this.config.metaGlobal == null) {
throw new MetaGlobalNotSetException();
}
if (this.config.metaGlobal.engines == null) {
throw new MetaGlobalMissingEnginesException();
}
return this.config.metaGlobal.engines.get(engineName) != null;
ExtendedJSONObject engineEntry;
try {
engineEntry = this.config.metaGlobal.engines.getObject(engineName);
} catch (NonObjectJSONException e) {
Logger.error(LOG_TAG, "Engine field for " + engineName + " in meta/global is not an object.");
throw new MetaGlobalMissingEnginesException();
}
if (engineEntry == null) {
Logger.debug(LOG_TAG, "Engine " + engineName + " not enabled: no meta/global entry.");
return false;
}
if (engineSettings != null) {
MetaGlobal.verifyEngineSettings(engineEntry, engineSettings);
}
return true;
}
public ClientsDataDelegate getClientsDelegate() {

Просмотреть файл

@ -8,6 +8,10 @@ import java.io.IOException;
import java.net.URISyntaxException;
import org.json.simple.parser.ParseException;
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalMalformedSyncIDException;
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalMalformedVersionException;
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalStaleClientSyncIDException;
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalStaleClientVersionException;
import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
@ -105,6 +109,55 @@ public class MetaGlobal implements SyncStorageRequestDelegate {
this.engines = engines;
}
/**
* Returns if the server settings and local settings match.
* Throws a specific exception if that's not the case.
*/
public static void verifyEngineSettings(ExtendedJSONObject engineEntry,
EngineSettings engineSettings)
throws MetaGlobalMalformedVersionException, MetaGlobalMalformedSyncIDException, MetaGlobalStaleClientVersionException, MetaGlobalStaleClientSyncIDException {
if (engineEntry == null) {
throw new IllegalArgumentException("engineEntry cannot be null.");
}
if (engineSettings == null) {
throw new IllegalArgumentException("engineSettings cannot be null.");
}
try {
Integer version = engineEntry.getIntegerSafely("version");
if (version == null ||
version.intValue() == 0) {
// Invalid version. Wipe the server.
throw new MetaGlobalException.MetaGlobalMalformedVersionException();
}
if (version > engineSettings.version) {
// We're out of date.
throw new MetaGlobalException.MetaGlobalStaleClientVersionException(version);
}
try {
String syncID = engineEntry.getString("syncID");
if (syncID == null) {
// No syncID. This should never happen. Wipe the server.
throw new MetaGlobalException.MetaGlobalMalformedSyncIDException();
}
if (!syncID.equals(engineSettings.syncID)) {
// Our syncID is wrong. Reset client and take the server syncID.
throw new MetaGlobalException.MetaGlobalStaleClientSyncIDException(syncID);
}
// Great!
return;
} catch (ClassCastException e) {
// Malformed syncID on the server. Wipe the server.
throw new MetaGlobalException.MetaGlobalMalformedSyncIDException();
}
} catch (NumberFormatException e) {
// Invalid version. Wipe the server.
throw new MetaGlobalException.MetaGlobalMalformedVersionException();
}
}
public String getSyncID() {
return syncID;
}

Просмотреть файл

@ -1,42 +1,37 @@
/* ***** 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;
public class MetaGlobalException extends SyncException {
private static final long serialVersionUID = -6182315615113508925L;
public static class MetaGlobalMalformedSyncIDException extends MetaGlobalException {
private static final long serialVersionUID = 1L;
}
public static class MetaGlobalMalformedVersionException extends MetaGlobalException {
private static final long serialVersionUID = 1L;
}
public static class MetaGlobalOutdatedVersionException extends MetaGlobalException {
private static final long serialVersionUID = 1L;
}
public static class MetaGlobalStaleClientVersionException extends MetaGlobalException {
private static final long serialVersionUID = 1L;
public final int serverVersion;
public MetaGlobalStaleClientVersionException(final int version) {
this.serverVersion = version;
}
}
public static class MetaGlobalStaleClientSyncIDException extends MetaGlobalException {
private static final long serialVersionUID = 1L;
public final String serverSyncID;
public MetaGlobalStaleClientSyncIDException(final String syncID) {
this.serverSyncID = syncID;
}
}
}

Просмотреть файл

@ -786,13 +786,13 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo
@Override
public void run() {
try {
// Clear our queued deletions.
deletionManager.clear();
super.run();
} catch (Exception ex) {
delegate.onWipeFailed(ex);
return;
}
// Clear our queued deletions.
deletionManager.clear();
}
}

Просмотреть файл

@ -0,0 +1,28 @@
/* 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.stage;
import org.mozilla.gecko.sync.GlobalSession;
/**
* This is simply a stage that is not responsible for synchronizing repositories.
*/
public abstract class AbstractNonRepositorySyncStage implements GlobalSyncStage {
protected final GlobalSession session;
public AbstractNonRepositorySyncStage(GlobalSession session) {
this.session = session;
}
@Override
public void resetLocal() {
// Do nothing.
}
@Override
public void wipeLocal() {
// Do nothing.
}
}

Просмотреть файл

@ -1,44 +1,12 @@
/* ***** 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.stage;
import java.net.URISyntaxException;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.MetaGlobalException;
import org.mozilla.gecko.sync.repositories.ConstrainedServer11Repository;
@ -56,6 +24,10 @@ public class AndroidBrowserBookmarksServerSyncStage extends ServerSyncStage {
private static final String BOOKMARKS_SORT = "index";
private static final long BOOKMARKS_REQUEST_LIMIT = 5000; // Sanity limit.
public AndroidBrowserBookmarksServerSyncStage(GlobalSession session) {
super(session);
}
@Override
protected String getCollection() {
return "bookmarks";
@ -87,7 +59,7 @@ public class AndroidBrowserBookmarksServerSyncStage extends ServerSyncStage {
@Override
protected boolean isEnabled() throws MetaGlobalException {
if (session == null || session.getContext() == null) {
if (session.getContext() == null) {
return false;
}
boolean migrated = FennecControlHelper.areBookmarksMigrated(session.getContext());

Просмотреть файл

@ -39,6 +39,7 @@ package org.mozilla.gecko.sync.stage;
import java.net.URISyntaxException;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.MetaGlobalException;
import org.mozilla.gecko.sync.repositories.ConstrainedServer11Repository;
@ -56,6 +57,10 @@ public class AndroidBrowserHistoryServerSyncStage extends ServerSyncStage {
private static final String HISTORY_SORT = "index";
private static final long HISTORY_REQUEST_LIMIT = 250;
public AndroidBrowserHistoryServerSyncStage(GlobalSession session) {
super(session);
}
@Override
protected String getCollection() {
return "history";

Просмотреть файл

@ -1,46 +1,18 @@
/* ***** 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.stage;
import org.mozilla.gecko.sync.GlobalSession;
public class CheckPreconditionsStage implements GlobalSyncStage {
public void execute(GlobalSession session) throws NoSuchStageException {
public class CheckPreconditionsStage extends AbstractNonRepositorySyncStage {
public CheckPreconditionsStage(GlobalSession session) {
super(session);
}
@Override
public void execute() throws NoSuchStageException {
session.advance();
}
}

Просмотреть файл

@ -1,51 +1,22 @@
/* ***** 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.stage;
import org.mozilla.gecko.sync.GlobalSession;
public class CompletedStage implements GlobalSyncStage {
public class CompletedStage extends AbstractNonRepositorySyncStage {
public CompletedStage(GlobalSession session) {
super(session);
}
@Override
public void execute(GlobalSession session) throws NoSuchStageException {
public void execute() throws NoSuchStageException {
// TODO: Update tracking timestamps, close connections, etc.
// TODO: call clean() on each Repository in the sync constellation.
session.completeSync();
}
}

Просмотреть файл

@ -24,7 +24,11 @@ import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
public class EnsureClusterURLStage implements GlobalSyncStage {
public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
public EnsureClusterURLStage(GlobalSession session) {
super(session);
}
public interface ClusterURLFetchDelegate {
/**
* 200 - Success.
@ -172,7 +176,7 @@ public class EnsureClusterURLStage implements GlobalSyncStage {
resource.get();
}
public void execute(final GlobalSession session) throws NoSuchStageException {
public void execute() throws NoSuchStageException {
final URI oldClusterURL = session.config.getClusterURL();
final boolean wantNodeAssignment = session.callback.wantNodeAssignment();

Просмотреть файл

@ -23,16 +23,20 @@ import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
import org.mozilla.gecko.sync.net.SyncStorageResponse;
public class EnsureCrypto5KeysStage implements GlobalSyncStage, SyncStorageRequestDelegate, KeyUploadDelegate {
public class EnsureCrypto5KeysStage
extends AbstractNonRepositorySyncStage
implements SyncStorageRequestDelegate, KeyUploadDelegate {
public EnsureCrypto5KeysStage(GlobalSession session) {
super(session);
}
private static final String LOG_TAG = "EnsureC5KeysStage";
private static final String CRYPTO_COLLECTION = "crypto";
protected GlobalSession session;
protected boolean retrying = false;
@Override
public void execute(GlobalSession session) throws NoSuchStageException {
this.session = session;
public void execute() throws NoSuchStageException {
InfoCollections infoCollections = session.config.infoCollections;
if (infoCollections == null) {
session.abort(null, "No info/collections set in EnsureCrypto5KeysStage.");
@ -161,7 +165,7 @@ public class EnsureCrypto5KeysStage implements GlobalSyncStage, SyncStorageReque
Logger.debug(LOG_TAG, "New keys uploaded. Starting stage again to fetch them.");
try {
retrying = true;
this.execute(this.session);
this.execute();
} catch (NoSuchStageException e) {
session.abort(e, "No such stage.");
}

Просмотреть файл

@ -6,6 +6,7 @@ package org.mozilla.gecko.sync.stage;
import org.mozilla.gecko.sync.repositories.domain.TabsRecord;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.repositories.RecordFactory;
import org.mozilla.gecko.sync.repositories.Repository;
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
@ -14,6 +15,10 @@ import org.mozilla.gecko.sync.repositories.domain.Record;
public class FennecTabsServerSyncStage extends ServerSyncStage {
private static final String COLLECTION = "tabs";
public FennecTabsServerSyncStage(GlobalSession session) {
super(session);
}
public class FennecTabsRecordFactory extends RecordFactory {
@Override
public Record createRecord(Record record) {

Просмотреть файл

@ -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.stage;
@ -46,15 +13,14 @@ import org.mozilla.gecko.sync.net.SyncStorageResponse;
import android.util.Log;
public class FetchInfoCollectionsStage implements GlobalSyncStage {
public class FetchInfoCollectionsStage extends AbstractNonRepositorySyncStage {
private static final String LOG_TAG = "FetchInfoCollStage";
public class StageInfoCollectionsDelegate implements InfoCollectionsDelegate {
public FetchInfoCollectionsStage(GlobalSession session) {
super(session);
}
private GlobalSession session;
public StageInfoCollectionsDelegate(GlobalSession session) {
this.session = session;
}
public class StageInfoCollectionsDelegate implements InfoCollectionsDelegate {
@Override
public void handleSuccess(InfoCollections global) {
@ -77,9 +43,9 @@ public class FetchInfoCollectionsStage implements GlobalSyncStage {
}
@Override
public void execute(GlobalSession session) throws NoSuchStageException {
public void execute() throws NoSuchStageException {
try {
session.fetchInfoCollections(new StageInfoCollectionsDelegate(session));
session.fetchInfoCollections(new StageInfoCollectionsDelegate());
} catch (URISyntaxException e) {
session.abort(e, "Invalid URI.");
}

Просмотреть файл

@ -12,7 +12,11 @@ import org.mozilla.gecko.sync.PersistedMetaGlobal;
import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
import org.mozilla.gecko.sync.net.SyncStorageResponse;
public class FetchMetaGlobalStage implements GlobalSyncStage {
public class FetchMetaGlobalStage extends AbstractNonRepositorySyncStage {
public FetchMetaGlobalStage(GlobalSession session) {
super(session);
}
private static final String LOG_TAG = "FetchMetaGlobalStage";
private static final String META_COLLECTION = "meta";
@ -51,7 +55,7 @@ public class FetchMetaGlobalStage implements GlobalSyncStage {
}
@Override
public void execute(GlobalSession session) throws NoSuchStageException {
public void execute() throws NoSuchStageException {
InfoCollections infoCollections = session.config.infoCollections;
if (infoCollections == null) {
session.abort(null, "No info/collections set in FetchMetaGlobalStage.");

Просмотреть файл

@ -7,6 +7,7 @@ package org.mozilla.gecko.sync.stage;
import java.net.URISyntaxException;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.repositories.ConstrainedServer11Repository;
import org.mozilla.gecko.sync.repositories.RecordFactory;
import org.mozilla.gecko.sync.repositories.Repository;
@ -21,9 +22,13 @@ public class FormHistoryServerSyncStage extends ServerSyncStage {
private static final String FORM_HISTORY_SORT = "index";
private static final long FORM_HISTORY_REQUEST_LIMIT = 5000; // Sanity limit.
public FormHistoryServerSyncStage(GlobalSession session) {
super(session);
}
@Override
public void execute(org.mozilla.gecko.sync.GlobalSession session) throws NoSuchStageException {
super.execute(session);
public void execute() throws NoSuchStageException {
super.execute();
}
@Override

Просмотреть файл

@ -4,7 +4,12 @@
package org.mozilla.gecko.sync.stage;
import org.mozilla.gecko.sync.GlobalSession;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
public interface GlobalSyncStage {
public static enum Stage {
@ -18,19 +23,59 @@ public interface GlobalSyncStage {
ensureSpecialRecords,
updateEngineTimestamps,
*/
syncClientsEngine,
syncClientsEngine("clients"),
/*
processFirstSyncPref,
processClientCommands,
updateEnabledEngines,
*/
syncTabs,
syncPasswords,
syncBookmarks,
syncHistory,
syncFormHistory,
completed,
syncTabs("tabs"),
syncPasswords("passwords"),
syncBookmarks("bookmarks"),
syncHistory("history"),
syncFormHistory("forms"),
completed;
// Maintain a mapping from names ("bookmarks") to Stage enumerations (syncBookmarks).
private static final Map<String, Stage> named = new HashMap<String, Stage>();
static {
for (Stage s : EnumSet.allOf(Stage.class)) {
if (s.getRepositoryName() != null) {
named.put(s.getRepositoryName(), s);
}
}
}
public static Stage byName(final String name) {
if (name == null) {
return null;
}
return named.get(name);
}
/**
* @return an immutable collection of Stages.
*/
public static Collection<Stage> getNamedStages() {
return Collections.unmodifiableCollection(named.values());
}
// Each Stage tracks its repositoryName.
private final String repositoryName;
public String getRepositoryName() {
return repositoryName;
}
private Stage() {
this.repositoryName = null;
}
private Stage(final String name) {
this.repositoryName = name;
}
}
public void execute(GlobalSession session) throws NoSuchStageException;
public void execute() throws NoSuchStageException;
public void resetLocal();
public void wipeLocal() throws Exception;
}

Просмотреть файл

@ -5,6 +5,7 @@
package org.mozilla.gecko.sync.stage;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.repositories.RecordFactory;
import org.mozilla.gecko.sync.repositories.Repository;
import org.mozilla.gecko.sync.repositories.android.PasswordsRepositorySession;
@ -12,11 +13,15 @@ import org.mozilla.gecko.sync.repositories.domain.PasswordRecord;
import org.mozilla.gecko.sync.repositories.domain.Record;
public class PasswordsServerSyncStage extends ServerSyncStage {
public PasswordsServerSyncStage(GlobalSession session) {
super(session);
}
@Override
protected String getCollection() {
return "passwords";
}
@Override
protected String getEngineName() {
return "passwords";
@ -41,5 +46,4 @@ public class PasswordsServerSyncStage extends ServerSyncStage {
return r;
}
}
}

Просмотреть файл

@ -6,6 +6,7 @@ package org.mozilla.gecko.sync.stage;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutorService;
import org.json.simple.parser.ParseException;
import org.mozilla.gecko.sync.GlobalSession;
@ -17,12 +18,22 @@ import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.SynchronizerConfiguration;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.middleware.Crypto5MiddlewareRepository;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
import org.mozilla.gecko.sync.repositories.RecordFactory;
import org.mozilla.gecko.sync.repositories.Repository;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
import org.mozilla.gecko.sync.repositories.Server11Repository;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
import org.mozilla.gecko.sync.synchronizer.Synchronizer;
import org.mozilla.gecko.sync.synchronizer.SynchronizerDelegate;
import android.content.Context;
/**
* Fetch from a server collection into a local repository, encrypting
* and decrypting along the way.
@ -34,8 +45,16 @@ public abstract class ServerSyncStage implements
GlobalSyncStage,
SynchronizerDelegate {
protected GlobalSession session;
protected String LOG_TAG = "ServerSyncStage";
protected static final String LOG_TAG = "ServerSyncStage";
protected final GlobalSession session;
public ServerSyncStage(GlobalSession session) {
if (session == null) {
throw new IllegalArgumentException("session must not be null.");
}
this.session = session;
}
/**
* Override these in your subclasses.
@ -44,8 +63,11 @@ public abstract class ServerSyncStage implements
* @throws MetaGlobalException
*/
protected boolean isEnabled() throws MetaGlobalException {
return session.engineIsEnabled(this.getEngineName());
// TODO: pass EngineSettings here to check syncID and storage version.
// Catch the subclasses of MetaGlobalException to trigger various resets and wipes.
return session.engineIsEnabled(this.getEngineName(), null);
}
protected abstract String getCollection();
protected abstract String getEngineName();
protected abstract Repository getLocalRepository();
@ -77,6 +99,14 @@ public abstract class ServerSyncStage implements
return this.getCollection() + ".";
}
protected SynchronizerConfiguration getConfig() throws NonObjectJSONException, IOException, ParseException {
return new SynchronizerConfiguration(session.config.getBranch(bundlePrefix()));
}
protected void persistConfig(SynchronizerConfiguration synchronizerConfiguration) {
synchronizerConfiguration.persist(session.config.getBranch(bundlePrefix()));
}
public Synchronizer getConfiguredSynchronizer(GlobalSession session) throws NoCollectionKeysSetException, URISyntaxException, NonObjectJSONException, IOException, ParseException {
Repository remote = wrappedServerRepo();
@ -84,7 +114,7 @@ public abstract class ServerSyncStage implements
synchronizer.repositoryA = remote;
synchronizer.repositoryB = this.getLocalRepository();
SynchronizerConfiguration config = new SynchronizerConfiguration(session.config.getBranch(bundlePrefix()));
SynchronizerConfiguration config = this.getConfig();
synchronizer.load(config);
// TODO: should wipe in either direction?
@ -93,11 +123,178 @@ public abstract class ServerSyncStage implements
}
@Override
public void execute(GlobalSession session) throws NoSuchStageException {
public void resetLocal() {
// Clear both timestamps.
SynchronizerConfiguration config;
try {
config = this.getConfig();
} catch (Exception e) {
Logger.warn(LOG_TAG, "Unable to reset " + this + ": fetching config failed.", e);
return;
}
config.localBundle.setTimestamp(0L);
config.remoteBundle.setTimestamp(0L);
Logger.info(LOG_TAG, "Reset timestamps for " + this);
persistConfig(config);
}
// Not thread-safe. Use with caution.
private class WipeWaiter {
public boolean sessionSucceeded = true;
public boolean wipeSucceeded = true;
public Exception error;
public void notify(Exception e, boolean sessionSucceeded) {
this.sessionSucceeded = sessionSucceeded;
this.wipeSucceeded = false;
this.error = e;
this.notify();
}
}
/**
* Synchronously wipe this stage by instantiating a local repository session
* and wiping that.
*
* Logs and rethrows an exception on failure.
*/
@Override
public void wipeLocal() throws Exception {
// Reset, then clear data.
this.resetLocal();
final WipeWaiter monitor = new WipeWaiter();
final Context context = session.getContext();
final Repository r = this.getLocalRepository();
final Runnable doWipe = new Runnable() {
@Override
public void run() {
r.createSession(new RepositorySessionCreationDelegate() {
@Override
public void onSessionCreated(final RepositorySession session) {
try {
session.begin(new RepositorySessionBeginDelegate() {
@Override
public void onBeginSucceeded(final RepositorySession session) {
session.wipe(new RepositorySessionWipeDelegate() {
@Override
public void onWipeSucceeded() {
try {
session.finish(new RepositorySessionFinishDelegate() {
@Override
public void onFinishSucceeded(RepositorySession session,
RepositorySessionBundle bundle) {
// Hurrah.
synchronized (monitor) {
monitor.notify();
}
}
@Override
public void onFinishFailed(Exception ex) {
// Assume that no finish => no wipe.
synchronized (monitor) {
monitor.notify(ex, true);
}
}
@Override
public RepositorySessionFinishDelegate deferredFinishDelegate(ExecutorService executor) {
return this;
}
});
} catch (InactiveSessionException e) {
// Cannot happen. Call for safety.
synchronized (monitor) {
monitor.notify(e, true);
}
}
}
@Override
public void onWipeFailed(Exception ex) {
session.abort();
synchronized (monitor) {
monitor.notify(ex, true);
}
}
@Override
public RepositorySessionWipeDelegate deferredWipeDelegate(ExecutorService executor) {
return this;
}
});
}
@Override
public void onBeginFailed(Exception ex) {
session.abort();
synchronized (monitor) {
monitor.notify(ex, true);
}
}
@Override
public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) {
return this;
}
});
} catch (InvalidSessionTransitionException e) {
session.abort();
synchronized (monitor) {
monitor.notify(e, true);
}
}
}
@Override
public void onSessionCreateFailed(Exception ex) {
synchronized (monitor) {
monitor.notify(ex, false);
}
}
@Override
public RepositorySessionCreationDelegate deferredCreationDelegate() {
return this;
}
}, context);
}
};
final Thread wiping = new Thread(doWipe);
synchronized (monitor) {
wiping.start();
try {
monitor.wait();
} catch (InterruptedException e) {
Logger.error(LOG_TAG, "Wipe interrupted.");
}
}
if (!monitor.sessionSucceeded) {
Logger.error(LOG_TAG, "Failed to create session for wipe.");
throw monitor.error;
}
if (!monitor.wipeSucceeded) {
Logger.error(LOG_TAG, "Failed to wipe session.");
throw monitor.error;
}
Logger.info(LOG_TAG, "Wiping stage complete.");
}
@Override
public void execute() throws NoSuchStageException {
final String name = getEngineName();
Logger.debug(LOG_TAG, "Starting execute for " + name);
this.session = session;
try {
if (!this.isEnabled()) {
Logger.info(LOG_TAG, "Stage " + name + " disabled; skipping.");
@ -141,7 +338,7 @@ public abstract class ServerSyncStage implements
SynchronizerConfiguration synchronizerConfiguration = synchronizer.save();
if (synchronizerConfiguration != null) {
synchronizerConfiguration.persist(session.config.getBranch(bundlePrefix()));
persistConfig(synchronizerConfiguration);
} else {
Logger.warn(LOG_TAG, "Didn't get configuration from synchronizer after success");
}

Просмотреть файл

@ -41,16 +41,44 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
public static final int CLIENTS_TTL_REFRESH = 604800000; // 7 days
public static final int MAX_UPLOAD_FAILURE_COUNT = 5;
protected GlobalSession session;
protected final GlobalSession session;
protected final ClientRecordFactory factory = new ClientRecordFactory();
protected ClientUploadDelegate clientUploadDelegate;
protected ClientDownloadDelegate clientDownloadDelegate;
// Be sure to use this safely via getClientsDatabaseAccessor/closeDataAccessor.
protected ClientsDatabaseAccessor db;
protected volatile boolean shouldWipe;
protected volatile boolean commandsProcessedShouldUpload;
protected final AtomicInteger uploadAttemptsCount = new AtomicInteger();
public SyncClientsEngineStage(GlobalSession session) {
if (session == null) {
throw new IllegalArgumentException("session must not be null.");
}
this.session = session;
}
protected int getClientsCount() {
return getClientsDatabaseAccessor().clientsCount();
}
protected synchronized ClientsDatabaseAccessor getClientsDatabaseAccessor() {
if (db == null) {
db = new ClientsDatabaseAccessor(session.getContext());
}
return db;
}
protected synchronized void closeDataAccessor() {
if (db == null) {
return;
}
db.close();
db = null;
}
/**
* The following two delegates, ClientDownloadDelegate and ClientUploadDelegate
* are both triggered in a chain, starting when execute() calls
@ -93,11 +121,11 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
final int clientsCount;
try {
clientsCount = db.clientsCount();
clientsCount = getClientsCount();
} finally {
// Close the database to clear cached readableDatabase/writableDatabase
// after we've completed our last transaction (db.store()).
db.close();
closeDataAccessor();
}
Logger.debug(LOG_TAG, "Database contains " + clientsCount + " clients.");
@ -119,7 +147,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
session.abort(new HTTPFailureException(response), "Client download failed.");
} finally {
// Close the database upon failure.
db.close();
closeDataAccessor();
}
}
@ -131,7 +159,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
session.abort(ex, "Failure fetching client record.");
} finally {
// Close the database upon error.
db.close();
closeDataAccessor();
}
}
@ -250,10 +278,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
}
@Override
public void execute(GlobalSession session) throws NoSuchStageException {
this.session = session;
init();
public void execute() throws NoSuchStageException {
if (shouldDownload()) {
downloadClientRecords(); // Will kick off upload, too
} else {
@ -261,6 +286,24 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
}
}
@Override
public void resetLocal() {
// Clear timestamps and local data.
session.config.persistServerClientRecordTimestamp(0L);
session.getClientsDelegate().setClientsCount(0);
try {
getClientsDatabaseAccessor().wipe();
} finally {
closeDataAccessor();
}
}
@Override
public void wipeLocal() throws Exception {
// Nothing more to do.
this.resetLocal();
}
protected ClientRecord newLocalClientRecord(ClientsDataDelegate delegate) {
final String ourGUID = delegate.getAccountGUID();
final String ourName = delegate.getClientName();
@ -270,10 +313,6 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
return r;
}
protected void init() {
db = new ClientsDatabaseAccessor(session.getContext());
}
// TODO: Bug 726055 - More considered handling of when to sync.
protected boolean shouldDownload() {
// Ask info/collections whether a download is needed.
@ -309,7 +348,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
// TODO: Bug 715792 - Process commands here.
for (int i = 0; i < commands.size(); i++) {
processor.processCommand(new ExtendedJSONObject((JSONObject)commands.get(i)));
processor.processCommand(new ExtendedJSONObject((JSONObject) commands.get(i)));
}
}
@ -370,6 +409,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
}
protected void wipeAndStore(ClientRecord record) {
ClientsDatabaseAccessor db = getClientsDatabaseAccessor();
if (shouldWipe) {
db.wipe();
shouldWipe = false;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны