Add a buffered input stream when reading persistent unit cache
Make -Dgwt.persistentunitcachedir override the default cache dir if set.
Fail fast if we can't reate a file in the cache directory
Better test to see if directory exists before trying to start the cache.
Added a few more unit tests

Review at http://gwt-code-reviews.appspot.com/1386807

Review by: scottb@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9904 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java b/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java
index 6a7a408..38cb2ac 100644
--- a/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java
+++ b/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java
@@ -22,6 +22,7 @@
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
 import com.google.gwt.util.tools.Utility;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.EOFException;
 import java.io.File;
@@ -43,13 +44,13 @@
  * A class that manages a persistent cache of {@link CompilationUnit} instances.
  * Writes out {@CompilationUnit} instances to a cache in a
  * background thread.
- * 
+ * <p>
  * The persistent cache is implemented as a directory of log files with a date
  * timestamp. A new log file gets created each time a new PersistentUnitCache is
  * instantiated, (once per invocation of the compiler or DevMode). The design is
  * intended to support only a single PersistentUnitCache instance in the
  * compiler at a time.
- * 
+ * <p>
  * As new units are compiled, the cache data is appended to a log. This allows
  * Java serialization to efficiently store references. The next time the cache
  * is started, all logs are replayed and loaded into the cache in chronological
@@ -59,7 +60,8 @@
  * {@link PersistentUnitCache#CACHE_FILE_THRESHOLD} , the cache files are
  * consolidated back into a single file.
  * 
- * System Properties:
+ * <p>
+ * System Properties (see {@link UnitCacheFactory}).
  * 
  * <ul>
  * <li>gwt.persistentunitcache : enables the persistent cache (eventually will
@@ -67,6 +69,7 @@
  * <li>gwt.persistentunitcachedir=<dir>: sets or overrides the cache directory</li>
  * </ul>
  * 
+ * <p>
  * Known Issues:
  * 
  * <ul>
@@ -292,7 +295,7 @@
     logger.log(TreeLogger.TRACE, "Persistent unit cache dir set to: "
         + this.cacheDirectory.getAbsolutePath());
 
-    if (!cacheDirectory.exists() && !cacheDirectory.mkdir()) {
+    if (!cacheDirectory.isDirectory() && !cacheDirectory.mkdir()) {
       logger.log(TreeLogger.ERROR, "Unable to initialize cache. Couldn't create directory "
           + cacheDirectory.getAbsolutePath() + ".");
       throw new UnableToCompleteException();
@@ -308,7 +311,9 @@
     try {
       currentCacheFile.createNewFile();
     } catch (IOException ex) {
-      // ignore, we'll try again later in the writer thread.
+      logger.log(TreeLogger.ERROR, "Unable to create new cache log file "
+          + currentCacheFile.getAbsolutePath() + ".", ex);
+      throw new UnableToCompleteException();
     }
 
     unitCacheMapLoader = new UnitCacheMapLoader(logger);
@@ -444,17 +449,19 @@
         File[] files = getCacheFiles();
         for (File cacheFile : files) {
           FileInputStream fis = null;
+          BufferedInputStream bis = null;
           ObjectInputStream inputStream = null;
           if (cacheFile.equals(currentCacheFile)) {
             continue;
           }
           try {
             fis = new FileInputStream(cacheFile);
+            bis = new BufferedInputStream(fis);
             /*
              * It is possible for the next call to throw an exception, leaving
              * inputStream null and fis still live.
              */
-            inputStream = new ObjectInputStream(fis);
+            inputStream = new ObjectInputStream(bis);
             while (true) {
               CompilationUnit unit = (CompilationUnit) inputStream.readObject();
               if (unit == null) {
@@ -481,6 +488,7 @@
                 + cacheFile.getAbsolutePath(), ex);
           } finally {
             Utility.close(inputStream);
+            Utility.close(bis);
             Utility.close(fis);
             logger.log(TreeLogger.TRACE, cacheFile.getName() + ": Load complete");
           }
diff --git a/dev/core/src/com/google/gwt/dev/javac/UnitCacheFactory.java b/dev/core/src/com/google/gwt/dev/javac/UnitCacheFactory.java
index 0bb1790..8083790 100644
--- a/dev/core/src/com/google/gwt/dev/javac/UnitCacheFactory.java
+++ b/dev/core/src/com/google/gwt/dev/javac/UnitCacheFactory.java
@@ -45,15 +45,13 @@
     assert logger != null;
     if (instance == null) {
       if (usePersistent) {
-        if (cacheDir == null) {
-          String dirProp = "gwt.persistentunitcachedir";
-          String propertyCacheDir = System.getProperty(dirProp);
-          if (propertyCacheDir != null) {
-            cacheDir = new File(propertyCacheDir);
-          } else {
-            throw new IllegalArgumentException("Expected " + dirProp
-                + " system property to be set.");
-          }
+        String dirProp = "gwt.persistentunitcachedir";
+        String propertyCacheDir = System.getProperty(dirProp);
+        if (propertyCacheDir != null) {
+          cacheDir = new File(propertyCacheDir);
+        } else if (cacheDir == null) {
+          throw new IllegalArgumentException("Expected " + dirProp
+              + " system property to be set.");
         }
         instance = new PersistentUnitCache(logger, cacheDir);
       } else {
diff --git a/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java b/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java
index a736710..d34ce1b 100644
--- a/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java
@@ -38,6 +38,31 @@
     lastCacheDir = null;
   }
 
+  public void testBadDir() {
+    TreeLogger logger = TreeLogger.NULL;
+    File badDir = new File("sHoUlDnOtExi57");
+    try {
+      new PersistentUnitCache(logger, badDir);
+      fail("Expected an exception to be thrown");
+    } catch (UnableToCompleteException expected) {
+    }
+  }
+
+  /**
+   * Test if a file already exists with the name we want to put the
+   * cache dir in.
+   */
+  public void testFileInTheWay() throws IOException {
+    TreeLogger logger = TreeLogger.NULL;
+    File fileInTheWay = File.createTempFile("PersistentUnitTest-inTheWay", "");
+    fileInTheWay.deleteOnExit();
+    try {
+      new PersistentUnitCache(logger, fileInTheWay);
+      fail("Expected an exception to be thrown");
+    } catch (UnableToCompleteException expected) {
+    }
+  }
+
   public void testPersistentCache() throws IOException, InterruptedException,
       UnableToCompleteException {
     TreeLogger logger = TreeLogger.NULL;
@@ -170,20 +195,6 @@
     assertNumCacheFiles(unitCacheDir, 1);
   }
 
-  public void testBadDir() {
-    TreeLogger logger = TreeLogger.NULL;
-    File badDir = new File("sHoUlDnOtExi57");
-    boolean caught = false;
-    try {
-      PersistentUnitCache puc = new PersistentUnitCache(logger, badDir);
-    } catch (UnableToCompleteException ex) {
-      // expected
-      caught = true;
-    } finally {
-      assertTrue("Did not catch UnableToCompleteException", caught);
-    }
-  }
-
   private void assertNumCacheFiles(File unitCacheDir, int expected) {
     assertEquals(expected, unitCacheDir.list().length);
   }