Revision: 9977
Author: [email protected]
Date: Tue Apr 12 07:43:39 2011
Log: Addresses ClassNotFoundException problems when the data
structures serialized in
the unit cache log files no longer matches due to changes in GWT.
Review at http://gwt-code-reviews.appspot.com/1412801
http://code.google.com/p/google-web-toolkit/source/detail?r=9977
Modified:
/trunk/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java
/trunk/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java
Thu Mar 31 08:40:20 2011
+++ /trunk/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java
Tue Apr 12 07:43:39 2011
@@ -266,7 +266,7 @@
*/
static final String UNIT_CACHE_PREFIX = "gwt-unitCache";
- static final String CACHE_PREFIX = UNIT_CACHE_PREFIX + "-";
+ static final String CACHE_FILE_PREFIX = UNIT_CACHE_PREFIX + "-";
/**
* If there are more than this many files in the cache, clean up the old
@@ -310,7 +310,7 @@
long timestamp = System.currentTimeMillis();
do {
currentCacheFile =
- new File(cacheDirectory, CACHE_PREFIX + String.format("%016X",
timestamp++));
+ new File(cacheDirectory, CACHE_FILE_PREFIX +
String.format("%016X", timestamp++));
} while (currentCacheFile.exists());
// this isn't 100% reliable if multiple processes are in contention
@@ -424,7 +424,7 @@
File[] files = cacheDirectory.listFiles();
List<File> cacheFiles = new ArrayList<File>();
for (File file : files) {
- if (file.getName().startsWith(CACHE_PREFIX)) {
+ if (file.getName().startsWith(CACHE_FILE_PREFIX)) {
cacheFiles.add(file);
}
}
@@ -460,6 +460,7 @@
if (cacheFile.equals(currentCacheFile)) {
continue;
}
+ boolean deleteCacheFile = false;
try {
fis = new FileInputStream(cacheFile);
bis = new BufferedInputStream(fis);
@@ -487,15 +488,21 @@
} catch (EOFException ex) {
// Go on to the next file.
} catch (IOException ex) {
- logger.log(TreeLogger.WARN, "Error reading cache file: " +
cacheFile.getAbsolutePath(),
- ex);
+ deleteCacheFile = true;
+ logger.log(TreeLogger.TRACE, "Ignoring and deleting cache log "
+ + cacheFile.getAbsolutePath() + " due to read error.", ex);
} catch (ClassNotFoundException ex) {
- logger.log(TreeLogger.ERROR, "Error deserializing
CompilationUnit in "
- + cacheFile.getAbsolutePath(), ex);
+ deleteCacheFile = true;
+ logger.log(TreeLogger.TRACE, "Ignoring and deleting cache log "
+ + cacheFile.getAbsolutePath() + " due to deserialization
error.", ex);
} finally {
Utility.close(inputStream);
Utility.close(bis);
Utility.close(fis);
+ }
+ if (deleteCacheFile) {
+ cacheFile.delete();
+ } else {
logger.log(TreeLogger.TRACE, cacheFile.getName() + ": Load
complete");
}
}
=======================================
---
/trunk/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java
Thu Mar 31 08:40:20 2011
+++
/trunk/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java
Tue Apr 12 07:43:39 2011
@@ -22,13 +22,32 @@
import junit.framework.TestCase;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
/**
* Unit test for {@link PersistentUnitCache}.
*/
public class PersistentUnitCacheTest extends TestCase {
+ private static class ThrowsClassNotFoundException implements
Serializable {
+ @SuppressWarnings("unused")
+ private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
+ throw new ClassNotFoundException();
+ }
+ }
+
+ private static class ThrowsIOException implements Serializable {
+ @SuppressWarnings("unused")
+ private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
+ throw new IOException();
+ }
+ }
+
File lastCacheDir = null;
public void tearDown() {
@@ -39,22 +58,17 @@
}
/**
- * The cache should recursively create the directories it needs.
+ * When a cache file encounters a serialization error, the logic should
assume
+ * the cache log is stale and remove it.
*/
- public void testNewDir() throws IOException, UnableToCompleteException {
- TreeLogger logger = TreeLogger.NULL;
- File baseDir = File.createTempFile("PersistentUnitTest-newDir", "");
- assertNotNull(baseDir);
- assertTrue(baseDir.exists());
- assertTrue(baseDir.delete());
- File newDir = lastCacheDir = new File(baseDir, "sHoUlDnOtExi57");
- new PersistentUnitCache(logger, newDir);
- assertTrue(newDir.isDirectory());
+ public void testClassNotFoundException() throws IOException,
UnableToCompleteException,
+ InterruptedException {
+ checkInvalidObjectInCache(new ThrowsClassNotFoundException());
}
/**
- * Test if a file already exists with the name we want to put the
- * cache dir in.
+ * 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;
@@ -68,21 +82,37 @@
} catch (UnableToCompleteException expected) {
}
}
+
+ /**
+ * If a cache file has some kind of IO exception, (this can happen with a
+ * stale cache file), then the exception should be ignored and the cache
file
+ * removed.
+ */
+ public void testIOException() throws IOException,
UnableToCompleteException, InterruptedException {
+ checkInvalidObjectInCache(new ThrowsIOException());
+ }
+
+ /**
+ * The cache should recursively create the directories it needs.
+ */
+ public void testNewDir() throws IOException, UnableToCompleteException {
+ TreeLogger logger = TreeLogger.NULL;
+ File baseDir = File.createTempFile("PersistentUnitTest-newDir", "");
+ assertNotNull(baseDir);
+ assertTrue(baseDir.exists());
+ assertTrue(baseDir.delete());
+ File newDir = lastCacheDir = new File(baseDir, "sHoUlDnOtExi57");
+ new PersistentUnitCache(logger, newDir);
+ assertTrue(newDir.isDirectory());
+ }
public void testPersistentCache() throws IOException,
InterruptedException,
UnableToCompleteException {
TreeLogger logger = TreeLogger.NULL;
- File cacheDir = null;
- lastCacheDir = cacheDir =
File.createTempFile("persistentCacheTest", "");
- assertNotNull(cacheDir);
- // Wait, this needs to be a directory, not a file.
- assertTrue(cacheDir.delete());
- // directory will get cleaned up in tearDown()
- assertTrue(cacheDir.mkdir());
-
- File unitCacheDir = new File(cacheDir,
PersistentUnitCache.UNIT_CACHE_PREFIX);
- assertNull(unitCacheDir.list());
+ File cacheDir = lastCacheDir =
File.createTempFile("persistentCacheTest", "");
+ File unitCacheDir = mkCacheDir(cacheDir);
+
PersistentUnitCache cache = new PersistentUnitCache(logger, cacheDir);
MockCompilationUnit foo1 = new
MockCompilationUnit("com.example.Foo", "Foo: source1");
@@ -204,4 +234,38 @@
private void assertNumCacheFiles(File unitCacheDir, int expected) {
assertEquals(expected, unitCacheDir.list().length);
}
-}
+
+ private void checkInvalidObjectInCache(Object toSerialize) throws
IOException,
+ FileNotFoundException, UnableToCompleteException,
InterruptedException {
+ TreeLogger logger = TreeLogger.NULL;
+ File cacheDir = lastCacheDir =
File.createTempFile("PersistentUnitTest-CNF", "");
+ File unitCacheDir = mkCacheDir(cacheDir);
+
+ /*
+ * Create a cache file that has the right filename, but the wrong kind
of
+ * object in it.
+ */
+ File errorFile = new File(unitCacheDir,
PersistentUnitCache.CACHE_FILE_PREFIX + "12345");
+ ObjectOutputStream os = new ObjectOutputStream(new
FileOutputStream(errorFile));
+ os.writeObject(toSerialize);
+ os.close();
+
+ assertNumCacheFiles(unitCacheDir, 1);
+
+ PersistentUnitCache cache = new PersistentUnitCache(logger, cacheDir);
+ cache.cleanup(logger);
+ cache.shutdown();
+
+ // The bogus file should have been removed.
+ assertNumCacheFiles(unitCacheDir, 0);
+ }
+
+ private File mkCacheDir(File cacheDir) {
+ assertNotNull(cacheDir);
+ assertTrue(cacheDir.exists());
+ cacheDir.delete();
+ File unitCacheDir = new File(cacheDir,
PersistentUnitCache.UNIT_CACHE_PREFIX);
+ unitCacheDir.mkdirs();
+ return unitCacheDir;
+ }
+}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors