/** * 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; }
@Override public void handleRequestSuccess(SyncStorageResponse response) { Logger.trace(LOG_TAG, "POST of " + outgoing.size() + " records done."); ExtendedJSONObject body; try { body = response.jsonObjectBody(); // jsonObjectBody() throws or returns non-null. } catch (Exception e) { Logger.error(LOG_TAG, "Got exception parsing POST success body.", e); this.handleRequestError(e); return; } // Be defensive when logging timestamp. if (body.containsKey("modified")) { Long modified = body.getTimestamp("modified"); if (modified != null) { Logger.trace( 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"); if ((success != null) && (success.size() > 0)) { Logger.trace(LOG_TAG, "Successful records: " + success.toString()); for (Object o : success) { try { delegate.onRecordStoreSucceeded((String) o); } catch (ClassCastException e) { Logger.error(LOG_TAG, "Got exception parsing POST success guid.", e); // Not much to be done. } } long normalizedTimestamp = getNormalizedTimestamp(response); Logger.trace(LOG_TAG, "Passing back upload X-Weave-Timestamp: " + normalizedTimestamp); bumpUploadTimestamp(normalizedTimestamp); } success = null; // Want to GC this ASAP. ExtendedJSONObject failed = body.getObject("failed"); if ((failed != null) && (failed.object.size() > 0)) { Logger.debug(LOG_TAG, "Failed records: " + failed.object.toString()); Exception ex = new Server11RecordPostFailedException(); for (String guid : failed.keySet()) { delegate.onRecordStoreFailed(ex, guid); } } failed = null; // Want to GC this ASAP. } catch (UnexpectedJSONException e) { Logger.error(LOG_TAG, "Got exception processing success/failed in POST success body.", e); // TODO return; } Logger.debug(LOG_TAG, "POST of " + outgoing.size() + " records handled."); }