Pre-initialize ResourceOracleImpl's classpaths in parallel with other startup
tasks.

http://gwt-code-reviews.appspot.com/1265801/show

Review by: conroy@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9512 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/DevMode.java b/dev/core/src/com/google/gwt/dev/DevMode.java
index 8bcdcca..4bc6b14 100644
--- a/dev/core/src/com/google/gwt/dev/DevMode.java
+++ b/dev/core/src/com/google/gwt/dev/DevMode.java
@@ -23,6 +23,7 @@
 import com.google.gwt.core.ext.linker.EmittedArtifact.Visibility;
 import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
 import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.resource.impl.ResourceOracleImpl;
 import com.google.gwt.dev.shell.jetty.JettyLauncher;
 import com.google.gwt.dev.ui.RestartServerCallback;
 import com.google.gwt.dev.ui.RestartServerEvent;
@@ -411,6 +412,21 @@
   }
 
   @Override
+  protected boolean doStartup() {
+    // Background scan the classpath to warm the cache.
+    Thread scanThread = new Thread(new Runnable() {
+      public void run() {
+        ResourceOracleImpl.preload(getTopLogger());
+      }
+    });
+    scanThread.setDaemon(true);
+    scanThread.setPriority((Thread.MIN_PRIORITY + Thread.NORM_PRIORITY) / 2);
+    scanThread.start();
+
+    return super.doStartup();
+  }
+  
+  @Override
   protected int doStartUpServer() {
     // Create the war directory if it doesn't exist
     File warDir = options.getWarDir();
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
index 31b5b44..d6dcb5b 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
@@ -172,6 +172,30 @@
   }
 
   /**
+   * Preinitializes the classpath from the thread default {@link ClassLoader}.
+   */
+  public static void preload(TreeLogger logger) {
+    preload(logger, Thread.currentThread().getContextClassLoader());
+  }
+
+  /**
+   * Preinitializes the classpath for a given {@link ClassLoader}.
+   */
+  public static void preload(TreeLogger logger, ClassLoader classLoader) {
+    Event resourceOracle = SpeedTracerLogger.start(
+        CompilerEventType.RESOURCE_ORACLE, "phase", "preload");
+    List<ClassPathEntry> entries = getAllClassPathEntries(logger, classLoader);
+    for (ClassPathEntry entry : entries) {
+      // We only handle pre-indexing jars, the file system could change.
+      if (entry instanceof ZipFileClassPathEntry) {
+        ZipFileClassPathEntry zpe = (ZipFileClassPathEntry) entry;
+        zpe.index(logger);
+      }
+    }
+    resourceOracle.end();
+  }
+
+  /**
    * Rescans the associated paths to recompute the available resources.
    * 
    * TODO(conroy,scottb): This synchronization could be improved upon to allow
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ZipFileClassPathEntry.java b/dev/core/src/com/google/gwt/dev/resource/impl/ZipFileClassPathEntry.java
index 5194594..be2fc8f 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/ZipFileClassPathEntry.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/ZipFileClassPathEntry.java
@@ -90,11 +90,7 @@
   @Override
   public Map<AbstractResource, PathPrefix> findApplicableResources(
       TreeLogger logger, PathPrefixSet pathPrefixSet) {
-    // Never re-index.
-    if (allZipFileResources == null) {
-      allZipFileResources = buildIndex(logger);
-    }
-
+    index(logger);
     ZipFileSnapshot snapshot = cachedSnapshots.get(pathPrefixSet);
     if (snapshot == null || snapshot.prefixSetSize != pathPrefixSet.getSize()) {
       snapshot = new ZipFileSnapshot(pathPrefixSet.getSize(),
@@ -113,6 +109,13 @@
     return zipFile;
   }
 
+  synchronized void index(TreeLogger logger) {
+    // Never re-index.
+    if (allZipFileResources == null) {
+      allZipFileResources = buildIndex(logger);
+    }
+  }
+
   private Set<ZipFileResource> buildIndex(TreeLogger logger) {
     logger = Messages.BUILDING_INDEX.branch(logger, zipFile.getName(), null);