Bug 878670 - Version profile information cache file. r=nalexander
authorRichard Newman <rnewman@mozilla.com>
Mon, 03 Jun 2013 10:07:43 -0700
changeset 134126 76629dcc62991228708181a74c1214e0ed2d97dc
parent 133736 198e38876f7e06d49be862710f76881f79abf1d7
child 134127 a31f790f06814d1faa6289577292ace90a2a5c0b
push id29067
push userryanvm@gmail.com
push dateWed, 05 Jun 2013 20:37:20 +0000
treeherdermozilla-inbound@72fbfb2f8e51 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander
bugs878670
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 878670 - Version profile information cache file. r=nalexander
mobile/android/base/background/healthreport/ProfileInformationCache.java
--- a/mobile/android/base/background/healthreport/ProfileInformationCache.java
+++ b/mobile/android/base/background/healthreport/ProfileInformationCache.java
@@ -21,16 +21,17 @@ import org.mozilla.gecko.background.heal
  * There are some parts of the FHR environment that can't be readily computed
  * without a running Gecko -- add-ons, for example. In order to make this
  * information available without launching Gecko, we persist it on Fennec
  * startup. This class is the notepad in which we write.
  */
 public class ProfileInformationCache implements ProfileInformationProvider {
   private static final String LOG_TAG = "GeckoProfileInfo";
   private static final String CACHE_FILE = "profile_info_cache.json";
+  public static final int FORMAT_VERSION = 1;
 
   protected boolean initialized = false;
   protected boolean needsWrite = false;
 
   protected final File file;
 
   private volatile boolean blocklistEnabled = true;
   private volatile boolean telemetryEnabled = false;
@@ -46,33 +47,73 @@ public class ProfileInformationCache imp
   public synchronized void beginInitialization() {
     initialized = false;
     needsWrite = true;
   }
 
   public JSONObject toJSON() {
     JSONObject object = new JSONObject();
     try {
+      object.put("version", FORMAT_VERSION);
       object.put("blocklist", blocklistEnabled);
       object.put("telemetry", telemetryEnabled);
       object.put("profileCreated", profileCreationTime);
       object.put("addons", addons);
     } catch (JSONException e) {
       // There isn't much we can do about this.
       // Let's just quietly muffle.
       return null;
     }
     return object;
   }
 
-  private void fromJSON(JSONObject object) throws JSONException {
-    blocklistEnabled = object.getBoolean("blocklist");
-    telemetryEnabled = object.getBoolean("telemetry");
-    profileCreationTime = object.getLong("profileCreated");
-    addons = object.optJSONObject("addons");
+  /**
+   * Attempt to restore this object from a JSON blob.
+   *
+   * @return false if there's a version mismatch or an error, true on success.
+   */
+  private boolean fromJSON(JSONObject object) throws JSONException {
+    int version = object.optInt("version", 1);
+    switch (version) {
+    case FORMAT_VERSION:
+      blocklistEnabled = object.getBoolean("blocklist");
+      telemetryEnabled = object.getBoolean("telemetry");
+      profileCreationTime = object.getLong("profileCreated");
+      addons = object.optJSONObject("addons");
+      return true;
+    default:
+      Logger.warn(LOG_TAG, "Unable to restore from version " + version + " PIC file: expecting " + FORMAT_VERSION);
+      return false;
+    }
+  }
+
+  protected JSONObject readFromFile() throws FileNotFoundException, JSONException {
+    Scanner scanner = null;
+    try {
+      scanner = new Scanner(file, "UTF-8");
+      final String contents = scanner.useDelimiter("\\A").next();
+      return new JSONObject(contents);
+    } finally {
+      if (scanner != null) {
+        scanner.close();
+      }
+    }
+  }
+
+  protected void writeToFile(JSONObject object) throws IOException {
+    Logger.debug(LOG_TAG, "Writing profile information.");
+    Logger.pii(LOG_TAG, "Writing to file: " + file.getAbsolutePath());
+    FileOutputStream stream = new FileOutputStream(file);
+    OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8"));
+    try {
+      writer.append(object.toString());
+      needsWrite = false;
+    } finally {
+      writer.close();
+    }
   }
 
   /**
    * Call this <b>on a background thread</b> when you're done adding things.
    * @throws IOException if there was a problem serializing or writing the cache to disk.
    */
   public synchronized void completeInitialization() throws IOException {
     initialized = true;
@@ -81,26 +122,17 @@ public class ProfileInformationCache imp
       return;
     }
 
     JSONObject object = toJSON();
     if (object == null) {
       throw new IOException("Couldn't serialize JSON.");
     }
 
-    Logger.debug(LOG_TAG, "Writing profile information.");
-    Logger.pii(LOG_TAG, "Writing to file: " + file.getAbsolutePath());
-    FileOutputStream stream = new FileOutputStream(file);
-    OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8"));
-    try {
-      writer.append(object.toString());
-      needsWrite = false;
-    } finally {
-      writer.close();
-    }
+    writeToFile(object);
   }
 
   /**
    * Call this if you're interested in reading.
    *
    * You should be doing so on a background thread.
    *
    * @return true if this object was initialized correctly.
@@ -113,32 +145,28 @@ public class ProfileInformationCache imp
     if (!file.exists()) {
       return false;
     }
 
     // One-liner for file reading in Java. So sorry.
     Logger.info(LOG_TAG, "Restoring ProfileInformationCache from file.");
     Logger.pii(LOG_TAG, "Restoring from file: " + file.getAbsolutePath());
 
-    Scanner scanner = null;
     try {
-      scanner = new Scanner(file, "UTF-8");
-      final String contents = scanner.useDelimiter("\\A").next();
-      fromJSON(new JSONObject(contents));
+      if (!fromJSON(readFromFile())) {
+        // No need to blow away the file; the caller can eventually overwrite it.
+        return false;
+      }
       initialized = true;
       needsWrite = false;
       return true;
     } catch (FileNotFoundException e) {
       return false;
     } catch (JSONException e) {
       return false;
-    } finally {
-      if (scanner != null) {
-        scanner.close();
-      }
     }
   }
 
   private void ensureInitialized() {
     if (!initialized) {
       throw new IllegalStateException("Not initialized.");
     }
   }