зеркало из https://github.com/mozilla/gecko-dev.git
Bug 736007 - avoid Server11RepositorySession NullPointerException. r=rnewman
This commit is contained in:
Родитель
e914039a82
Коммит
a1c8f01682
|
@ -115,6 +115,16 @@ public class SyncResponse {
|
|||
return ExtendedJSONObject.parse(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the body as a <b>non-null</b> <code>ExtendedJSONObject</code>.
|
||||
*
|
||||
* @return A non-null <code>ExtendedJSONObject</code>.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* @throws IOException
|
||||
* @throws ParseException
|
||||
* @throws NonObjectJSONException
|
||||
*/
|
||||
public ExtendedJSONObject jsonObjectBody() throws IllegalStateException,
|
||||
IOException, ParseException,
|
||||
NonObjectJSONException {
|
||||
|
|
|
@ -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.repositories;
|
||||
|
||||
|
@ -51,6 +18,7 @@ import org.mozilla.gecko.sync.CryptoRecord;
|
|||
import org.mozilla.gecko.sync.DelayedWorkTracker;
|
||||
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||
import org.mozilla.gecko.sync.HTTPFailureException;
|
||||
import org.mozilla.gecko.sync.Logger;
|
||||
import org.mozilla.gecko.sync.UnexpectedJSONException;
|
||||
import org.mozilla.gecko.sync.crypto.KeyBundle;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageCollectionRequest;
|
||||
|
@ -64,7 +32,6 @@ import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDeleg
|
|||
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
|
||||
import org.mozilla.gecko.sync.repositories.domain.Record;
|
||||
|
||||
import android.util.Log;
|
||||
import ch.boye.httpclientandroidlib.entity.ContentProducer;
|
||||
import ch.boye.httpclientandroidlib.entity.EntityTemplate;
|
||||
|
||||
|
@ -91,6 +58,34 @@ public class Server11RepositorySession extends RepositorySession {
|
|||
// {}, newlines, but we get to skip one record overhead.
|
||||
private static final int PER_BATCH_OVERHEAD = 5 - PER_RECORD_OVERHEAD;
|
||||
|
||||
/**
|
||||
* Return the X-Weave-Timestamp header from <code>response</code>, or the
|
||||
* current time if it is missing.
|
||||
* <p>
|
||||
* <b>Warning:</b> this can cause the timestamp of <code>response</code> to
|
||||
* cross domains (from server clock to local clock), which could cause records
|
||||
* to be skipped on account of clock drift. This should never happen, because
|
||||
* <i>every</i> server response should have a well-formed X-Weave-Timestamp
|
||||
* header.
|
||||
*
|
||||
* @param response
|
||||
* The <code>SyncStorageResponse</code> to interrogate.
|
||||
* @return Normalized timestamp in milliseconds.
|
||||
*/
|
||||
public static long getNormalizedTimestamp(SyncStorageResponse response) {
|
||||
long normalizedTimestamp = -1;
|
||||
try {
|
||||
normalizedTimestamp = response.normalizedWeaveTimestamp();
|
||||
} catch (NumberFormatException e) {
|
||||
Logger.warn(LOG_TAG, "Malformed X-Weave-Timestamp header received.", e);
|
||||
}
|
||||
if (-1 == normalizedTimestamp) {
|
||||
Logger.warn(LOG_TAG, "Computing stand-in timestamp from local clock. Clock drift could cause records to be skipped.");
|
||||
normalizedTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
return normalizedTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HTTP request delegate callbacks into fetch callbacks within the
|
||||
* context of this RepositorySession.
|
||||
|
@ -118,29 +113,18 @@ public class Server11RepositorySession extends RepositorySession {
|
|||
|
||||
@Override
|
||||
public void handleRequestSuccess(SyncStorageResponse response) {
|
||||
Log.i(LOG_TAG, "Fetch done.");
|
||||
Logger.debug(LOG_TAG, "Fetch done.");
|
||||
|
||||
long normalizedTimestamp = -1;
|
||||
try {
|
||||
normalizedTimestamp = response.normalizedWeaveTimestamp();
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w(LOG_TAG, "Malformed X-Weave-Timestamp header received.", e);
|
||||
}
|
||||
if (-1 == normalizedTimestamp) {
|
||||
Log.w(LOG_TAG, "Computing stand-in timestamp from local clock. Clock drift could cause records to be skipped.");
|
||||
normalizedTimestamp = new Date().getTime();
|
||||
}
|
||||
|
||||
Log.d(LOG_TAG, "Fetch completed. Timestamp is " + normalizedTimestamp);
|
||||
final long ts = normalizedTimestamp;
|
||||
final long normalizedTimestamp = getNormalizedTimestamp(response);
|
||||
Logger.debug(LOG_TAG, "Fetch completed. Timestamp is " + normalizedTimestamp);
|
||||
|
||||
// When we're done processing other events, finish.
|
||||
workTracker.delayWorkItem(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(LOG_TAG, "Delayed onFetchCompleted running.");
|
||||
Logger.debug(LOG_TAG, "Delayed onFetchCompleted running.");
|
||||
// TODO: verify number of returned records.
|
||||
delegate.onFetchCompleted(ts);
|
||||
delegate.onFetchCompleted(normalizedTimestamp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -153,12 +137,12 @@ public class Server11RepositorySession extends RepositorySession {
|
|||
|
||||
@Override
|
||||
public void handleRequestError(final Exception ex) {
|
||||
Log.i(LOG_TAG, "Got request error.", ex);
|
||||
Logger.warn(LOG_TAG, "Got request error.", ex);
|
||||
// When we're done processing other events, finish.
|
||||
workTracker.delayWorkItem(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(LOG_TAG, "Running onFetchFailed.");
|
||||
Logger.debug(LOG_TAG, "Running onFetchFailed.");
|
||||
delegate.onFetchFailed(ex, null);
|
||||
}
|
||||
});
|
||||
|
@ -170,7 +154,7 @@ public class Server11RepositorySession extends RepositorySession {
|
|||
try {
|
||||
delegate.onFetchedRecord(record);
|
||||
} catch (Exception ex) {
|
||||
Log.i(LOG_TAG, "Got exception calling onFetchedRecord with WBO.", ex);
|
||||
Logger.warn(LOG_TAG, "Got exception calling onFetchedRecord with WBO.", ex);
|
||||
// TODO: handle this better.
|
||||
throw new RuntimeException(ex);
|
||||
} finally {
|
||||
|
@ -359,7 +343,7 @@ public class Server11RepositorySession extends RepositorySession {
|
|||
public RecordUploadRunnable(RepositorySessionStoreDelegate storeDelegate,
|
||||
ArrayList<byte[]> outgoing,
|
||||
long byteCount) {
|
||||
Log.i(LOG_TAG, "Preparing RecordUploadRunnable for " +
|
||||
Logger.debug(LOG_TAG, "Preparing RecordUploadRunnable for " +
|
||||
outgoing.size() + " records (" +
|
||||
byteCount + " bytes).");
|
||||
this.outgoing = outgoing;
|
||||
|
@ -378,38 +362,48 @@ public class Server11RepositorySession extends RepositorySession {
|
|||
|
||||
@Override
|
||||
public void handleRequestSuccess(SyncStorageResponse response) {
|
||||
Log.i(LOG_TAG, "POST of " + outgoing.size() + " records done.");
|
||||
Logger.debug(LOG_TAG, "POST of " + outgoing.size() + " records done.");
|
||||
|
||||
ExtendedJSONObject body;
|
||||
try {
|
||||
body = response.jsonObjectBody();
|
||||
body = response.jsonObjectBody(); // jsonObjectBody() throws or returns non-null.
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Got exception parsing POST success body.", e);
|
||||
Logger.error(LOG_TAG, "Got exception parsing POST success body.", e);
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
long modified = body.getTimestamp("modified");
|
||||
Log.i(LOG_TAG, "POST request success. Modified timestamp: " + modified);
|
||||
|
||||
// Be defensive when logging timestamp.
|
||||
if (body.containsKey("modified")) {
|
||||
Long modified = body.getTimestamp("modified");
|
||||
if (modified != null) {
|
||||
Logger.debug(LOG_TAG, "POST request success. Modified timestamp: " + modified.longValue());
|
||||
} else {
|
||||
Logger.warn(LOG_TAG, "POST success body contains malformed 'modified': " + body.toJSONString());
|
||||
}
|
||||
} else {
|
||||
Logger.warn(LOG_TAG, "POST success body does not contain key 'modified': " + body.toJSONString());
|
||||
}
|
||||
|
||||
try {
|
||||
JSONArray success = body.getArray("success");
|
||||
ExtendedJSONObject failed = body.getObject("failed");
|
||||
if ((success != null) &&
|
||||
(success.size() > 0)) {
|
||||
Log.d(LOG_TAG, "Successful records: " + success.toString());
|
||||
Logger.debug(LOG_TAG, "Successful records: " + success.toString());
|
||||
// TODO: how do we notify without the whole record?
|
||||
|
||||
long ts = response.normalizedWeaveTimestamp();
|
||||
Log.d(LOG_TAG, "Passing back upload X-Weave-Timestamp: " + ts);
|
||||
bumpUploadTimestamp(ts);
|
||||
long normalizedTimestamp = getNormalizedTimestamp(response);
|
||||
Logger.debug(LOG_TAG, "Passing back upload X-Weave-Timestamp: " + normalizedTimestamp);
|
||||
bumpUploadTimestamp(normalizedTimestamp);
|
||||
}
|
||||
if ((failed != null) &&
|
||||
(failed.object.size() > 0)) {
|
||||
Log.d(LOG_TAG, "Failed records: " + failed.object.toString());
|
||||
Logger.debug(LOG_TAG, "Failed records: " + failed.object.toString());
|
||||
// TODO: notify.
|
||||
}
|
||||
} catch (UnexpectedJSONException e) {
|
||||
Log.e(LOG_TAG, "Got exception processing success/failed in POST success body.", e);
|
||||
Logger.error(LOG_TAG, "Got exception processing success/failed in POST success body.", e);
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
@ -424,7 +418,7 @@ public class Server11RepositorySession extends RepositorySession {
|
|||
|
||||
@Override
|
||||
public void handleRequestError(final Exception ex) {
|
||||
Log.i(LOG_TAG, "Got request error: " + ex, ex);
|
||||
Logger.warn(LOG_TAG, "Got request error: " + ex, ex);
|
||||
delegate.onRecordStoreFailed(ex);
|
||||
}
|
||||
|
||||
|
@ -477,7 +471,7 @@ public class Server11RepositorySession extends RepositorySession {
|
|||
public void run() {
|
||||
if (outgoing == null ||
|
||||
outgoing.size() == 0) {
|
||||
Log.i(LOG_TAG, "No items: RecordUploadRunnable returning immediately.");
|
||||
Logger.debug(LOG_TAG, "No items: RecordUploadRunnable returning immediately.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче