/**
  * 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.");
    }