Re-roll issue 887801. Update the embedded Tomcat server and don't let callers accidentally refresh 0 oracles.
*** Original change description ***
Optimize ResourceOracle refresh by doing multiple oracles at the same time
Review at http://gwt-code-reviews.appspot.com/887801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8813 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
index 06fd472..da9dcb8 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -340,7 +340,7 @@
NON_JAVA_RESOURCES, pathPrefix.shouldReroot()));
}
lazyResourcesOracle.setPathPrefixes(newPathPrefixes);
- lazyResourcesOracle.refresh(TreeLogger.NULL);
+ ResourceOracleImpl.refresh(TreeLogger.NULL, lazyResourcesOracle);
}
return lazyResourcesOracle;
}
@@ -402,11 +402,11 @@
+ "'");
// Refresh resource oracles.
- lazyPublicOracle.refresh(logger);
- lazySourceOracle.refresh(logger);
-
- if (lazyResourcesOracle != null) {
- lazyResourcesOracle.refresh(logger);
+ if (lazyResourcesOracle == null) {
+ ResourceOracleImpl.refresh(logger, lazyPublicOracle, lazySourceOracle);
+ } else {
+ ResourceOracleImpl.refresh(
+ logger, lazyPublicOracle, lazySourceOracle, lazyResourcesOracle);
}
moduleDefEvent.end();
}
@@ -482,13 +482,13 @@
lazyPublicOracle = new ResourceOracleImpl(branch);
lazyPublicOracle.setPathPrefixes(publicPrefixSet);
}
- lazyPublicOracle.refresh(branch);
// Create the source path.
branch = Messages.SOURCE_PATH_LOCATIONS.branch(logger, null);
lazySourceOracle = new ResourceOracleImpl(branch);
lazySourceOracle.setPathPrefixes(sourcePrefixSet);
- lazySourceOracle.refresh(branch);
+
+ ResourceOracleImpl.refresh(logger, lazyPublicOracle, lazySourceOracle);
if (lazySourceOracle.getResources().isEmpty()) {
branch.log(TreeLogger.WARN,
"No source path entries; expect subsequent failures", null);
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java b/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java
index 5ada435..850c03f 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java
@@ -17,6 +17,8 @@
import com.google.gwt.core.ext.TreeLogger;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
/**
@@ -37,6 +39,22 @@
*/
public abstract Map<AbstractResource, PathPrefix> findApplicableResources(
TreeLogger logger, PathPrefixSet pathPrefixSet);
+
+ /**
+ * Finds applicable resources for a list of pathPrefixSets, returning a
+ * distinct answer for each set.
+ *
+ * @see #findApplicableResources(TreeLogger, PathPrefixSet)
+ */
+ public List<Map<AbstractResource, PathPrefix>> findApplicableResources(
+ TreeLogger logger, List<PathPrefixSet> pathPrefixSets) {
+ List<Map<AbstractResource, PathPrefix>> results = new ArrayList<
+ Map<AbstractResource, PathPrefix>>(pathPrefixSets.size());
+ for (PathPrefixSet pathPrefixSet : pathPrefixSets) {
+ results.add(findApplicableResources(logger, pathPrefixSet));
+ }
+ return results;
+ }
/**
* Gets a URL string that describes this class path entry.
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java b/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java
index 0359a1b..315fcd9 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java
@@ -16,10 +16,13 @@
package com.google.gwt.dev.resource.impl;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.util.collect.Lists;
import com.google.gwt.dev.util.msg.Message1String;
import java.io.File;
+import java.util.ArrayList;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -28,15 +31,9 @@
public class DirectoryClassPathEntry extends ClassPathEntry {
private static class Messages {
- static final Message1String NOT_DESCENDING_INTO_DIR = new Message1String(
- TreeLogger.SPAM, "Prefix set does not include dir: $0");
-
static final Message1String DESCENDING_INTO_DIR = new Message1String(
TreeLogger.SPAM, "Descending into dir: $0");
- static final Message1String EXCLUDING_FILE = new Message1String(
- TreeLogger.DEBUG, "Filter excludes file: $0");
-
static final Message1String INCLUDING_FILE = new Message1String(
TreeLogger.DEBUG, "Including file: $0");
}
@@ -58,10 +55,23 @@
}
@Override
+ public List<Map<AbstractResource, PathPrefix>> findApplicableResources(
+ TreeLogger logger, List<PathPrefixSet> pathPrefixSets) {
+ List<Map<AbstractResource, PathPrefix>> results = new ArrayList<Map<AbstractResource, PathPrefix>>(
+ pathPrefixSets.size());
+ for (int i = 0, c = pathPrefixSets.size(); i < c; ++i) {
+ results.add(new IdentityHashMap<AbstractResource, PathPrefix>());
+ }
+ descendToFindResources(logger, pathPrefixSets, results, dir, "");
+ return results;
+ }
+
+ @Override
public Map<AbstractResource, PathPrefix> findApplicableResources(
TreeLogger logger, PathPrefixSet pathPrefixSet) {
Map<AbstractResource, PathPrefix> results = new IdentityHashMap<AbstractResource, PathPrefix>();
- descendToFindResources(logger, pathPrefixSet, results, dir, "");
+ descendToFindResources(logger, Lists.create(pathPrefixSet),
+ Lists.create(results), dir, "");
return results;
}
@@ -72,16 +82,19 @@
/**
* @param logger logs progress
- * @param resources the accumulating set of resources (each with the
+ * @param pathPrefixSets the sets of path prefixes to determine what resources
+ * are included
+ * @param results the accumulating sets of resources (each with the
* corresponding pathPrefix) found
* @param dir the file or directory to consider
* @param dirPath the abstract path name associated with 'parent', which
* explicitly does not include the classpath entry in its path
*/
private void descendToFindResources(TreeLogger logger,
- PathPrefixSet pathPrefixSet, Map<AbstractResource, PathPrefix> resources,
- File dir, String dirPath) {
+ List<PathPrefixSet> pathPrefixSets,
+ List<Map<AbstractResource, PathPrefix>> results, File dir, String dirPath) {
assert (dir.isDirectory()) : dir + " is not a directory";
+ int len = pathPrefixSets.size();
// Assert: this directory is included in the path prefix set.
@@ -90,26 +103,23 @@
String childPath = dirPath + child.getName();
if (child.isDirectory()) {
String childDirPath = childPath + "/";
- if (pathPrefixSet.includesDirectory(childDirPath)) {
- Messages.DESCENDING_INTO_DIR.log(logger, child.getAbsolutePath(),
- null);
- descendToFindResources(logger, pathPrefixSet, resources, child,
- childDirPath);
- } else {
- Messages.NOT_DESCENDING_INTO_DIR.log(logger, child.getAbsolutePath(),
- null);
+ for (int i = 0; i < len; ++i) {
+ if (pathPrefixSets.get(i).includesDirectory(childDirPath)) {
+ Messages.DESCENDING_INTO_DIR.log(logger, child.getPath(), null);
+ descendToFindResources(logger, pathPrefixSets, results, child,
+ childDirPath);
+ break;
+ }
}
} else if (child.isFile()) {
- PathPrefix prefix = null;
- if ((prefix = pathPrefixSet.includesResource(childPath)) != null) {
- Messages.INCLUDING_FILE.log(logger, childPath, null);
- FileResource r = new FileResource(this, childPath, child);
- resources.put(r, prefix);
- } else {
- Messages.EXCLUDING_FILE.log(logger, childPath, null);
+ for (int i = 0; i < len; ++i) {
+ PathPrefix prefix = null;
+ if ((prefix = pathPrefixSets.get(i).includesResource(childPath)) != null) {
+ Messages.INCLUDING_FILE.log(logger, childPath, null);
+ FileResource r = new FileResource(this, childPath, child);
+ results.get(i).put(r, prefix);
+ }
}
- } else {
- Messages.EXCLUDING_FILE.log(logger, childPath, null);
}
}
}
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 2b7e547..f62d6ba 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
@@ -40,8 +40,8 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import java.util.Map.Entry;
+import java.util.Set;
/**
* The normal implementation of {@link ResourceOracle}.
@@ -51,24 +51,11 @@
private static class Messages {
static final Message1String EXAMINING_PATH_ROOT = new Message1String(
TreeLogger.DEBUG, "Searching for resources within $0");
-
static final Message1String IGNORING_SHADOWED_RESOURCE = new Message1String(
TreeLogger.DEBUG,
"Resource '$0' is being shadowed by another resource higher in the classpath having the same name; this one will not be used");
-
- static final Message1String NEW_RESOURCE_FOUND = new Message1String(
- TreeLogger.DEBUG, "Found new resource: $0");
-
static final Message0 REFRESHING_RESOURCES = new Message0(TreeLogger.TRACE,
"Refreshing resources");
-
- static final Message1String RESOURCE_BECAME_INVALID_BECAUSE_IT_IS_STALE = new Message1String(
- TreeLogger.SPAM,
- "Resource '$0' has been modified since it was last loaded and needs to be reloaded");
-
- static final Message1String RESOURCE_BECAME_INVALID_BECAUSE_IT_MOVED = new Message1String(
- TreeLogger.DEBUG,
- "Resource '$0' was found on a different classpath entry and needs to be reloaded");
}
/**
@@ -184,6 +171,101 @@
}
}
+ /**
+ * Rescans the associated paths to recompute the available resources.
+ *
+ * @param logger status and error details are written here
+ * @param first At least one ResourceOracleImpl must be passed to refresh
+ * @param rest Callers may optionally pass several oracles
+ */
+ public static void refresh(
+ TreeLogger logger, ResourceOracleImpl first, ResourceOracleImpl... rest) {
+ int len = 1 + rest.length;
+ ResourceOracleImpl[] oracles = new ResourceOracleImpl[1 + rest.length];
+ oracles[0] = first;
+ System.arraycopy(rest, 0, oracles, 1, rest.length);
+
+ Event resourceOracle =
+ SpeedTracerLogger.start(CompilerEventType.RESOURCE_ORACLE, "phase", "refresh");
+ TreeLogger refreshBranch = Messages.REFRESHING_RESOURCES.branch(logger,
+ null);
+
+ /*
+ * Allocate fresh data structures in anticipation of needing to honor the
+ * "new identity for the collections if anything changes" guarantee. Use a
+ * LinkedHashMap because we do not want the order to change.
+ */
+ List<Map<String, ResourceData>> resourceDataMaps = new ArrayList<
+ Map<String, ResourceData>>();
+
+ List<PathPrefixSet> pathPrefixSets = new ArrayList<PathPrefixSet>();
+ for (ResourceOracleImpl oracle : oracles) {
+ if (oracle.classPath != oracles[0].classPath) {
+ throw new IllegalArgumentException();
+ }
+ resourceDataMaps.add(new LinkedHashMap<String, ResourceData>());
+ pathPrefixSets.add(oracle.pathPrefixSet);
+ }
+
+ /*
+ * Walk across path roots (i.e. classpath entries) in priority order. This
+ * is a "reverse painter's algorithm", relying on being careful never to add
+ * a resource that has already been added to the new map under construction
+ * to create the effect that resources founder earlier on the classpath take
+ * precedence.
+ *
+ * Exceptions: super has priority over non-super; and if there are two super
+ * resources with the same path, the one with the higher-priority path
+ * prefix wins.
+ */
+ for (ClassPathEntry pathRoot : oracles[0].classPath) {
+ TreeLogger branchForClassPathEntry = Messages.EXAMINING_PATH_ROOT.branch(
+ refreshBranch, pathRoot.getLocation(), null);
+
+ List<Map<AbstractResource, PathPrefix>> resourceToPrefixMaps = pathRoot.findApplicableResources(
+ branchForClassPathEntry, pathPrefixSets);
+ for (int i = 0; i < len; ++i) {
+ Map<String, ResourceData> resourceDataMap = resourceDataMaps.get(i);
+ Map<AbstractResource, PathPrefix> resourceToPrefixMap =
+ resourceToPrefixMaps.get(i);
+ for (Entry<AbstractResource, PathPrefix> entry :
+ resourceToPrefixMap.entrySet()) {
+ ResourceData newCpeData = new ResourceData(entry.getKey(),
+ entry.getValue());
+ String resourcePath = newCpeData.resource.getPath();
+ ResourceData oldCpeData = resourceDataMap.get(resourcePath);
+ // Old wins unless the new resource has higher priority.
+ if (oldCpeData == null || oldCpeData.compareTo(newCpeData) < 0) {
+ resourceDataMap.put(resourcePath, newCpeData);
+ } else {
+ Messages.IGNORING_SHADOWED_RESOURCE.log(branchForClassPathEntry,
+ resourcePath, null);
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < len; ++i) {
+ Map<String, ResourceData> resourceDataMap = resourceDataMaps.get(i);
+ Map<String, Resource> externalMap = new HashMap<String, Resource>();
+ Set<Resource> externalSet = new HashSet<Resource>();
+ for (Entry<String, ResourceData> entry : resourceDataMap.entrySet()) {
+ String path = entry.getKey();
+ ResourceData data = entry.getValue();
+ externalMap.put(path, data.resource);
+ externalSet.add(data.resource);
+ }
+
+ // Update exposed collections with new (unmodifiable) data structures.
+ oracles[i].exposedResources = Collections.unmodifiableSet(externalSet);
+ oracles[i].exposedResourceMap = Collections.unmodifiableMap(externalMap);
+ oracles[i].exposedPathNames = Collections.unmodifiableSet(
+ externalMap.keySet());
+ }
+
+ resourceOracle.end();
+ }
+
private static void addAllClassPathEntries(TreeLogger logger,
ClassLoader classLoader, List<ClassPathEntry> classPath) {
// URL is expensive in collections, so we use URI instead
@@ -240,7 +322,7 @@
return classPath;
}
- private final List<ClassPathEntry> classPath = new ArrayList<ClassPathEntry>();
+ private final List<ClassPathEntry> classPath;
private Set<String> exposedPathNames = Collections.emptySet();
@@ -252,13 +334,11 @@
/**
* Constructs a {@link ResourceOracleImpl} from a set of
- * {@link ClassPathEntry ClassPathEntries}. The passed-in list is copied, but
- * the underlying entries in the list are not. Those entries must be
- * effectively immutable except for reflecting actual changes to the
- * underlying resources.
+ * {@link ClassPathEntry ClassPathEntries}. The list is held by reference and
+ * must not be modified.
*/
public ResourceOracleImpl(List<ClassPathEntry> classPath) {
- this.classPath.addAll(classPath);
+ this.classPath = classPath;
}
/**
@@ -301,72 +381,6 @@
return exposedResources;
}
- /**
- * Rescans the associated paths to recompute the available resources.
- *
- * @param logger status and error details are written here
- */
- public void refresh(TreeLogger logger) {
- Event resourceOracle =
- SpeedTracerLogger.start(CompilerEventType.RESOURCE_ORACLE, "phase", "refresh");
- TreeLogger refreshBranch = Messages.REFRESHING_RESOURCES.branch(logger,
- null);
-
- /*
- * Allocate fresh data structures in anticipation of needing to honor the
- * "new identity for the collections if anything changes" guarantee. Use a
- * LinkedHashMap because we do not want the order to change.
- */
- Map<String, ResourceData> newInternalMap = new LinkedHashMap<String, ResourceData>();
-
- /*
- * Walk across path roots (i.e. classpath entries) in priority order. This
- * is a "reverse painter's algorithm", relying on being careful never to add
- * a resource that has already been added to the new map under construction
- * to create the effect that resources founder earlier on the classpath take
- * precedence.
- *
- * Exceptions: super has priority over non-super; and if there are two super
- * resources with the same path, the one with the higher-priority path
- * prefix wins.
- */
- for (ClassPathEntry pathRoot : classPath) {
- TreeLogger branchForClassPathEntry = Messages.EXAMINING_PATH_ROOT.branch(
- refreshBranch, pathRoot.getLocation(), null);
-
- Map<AbstractResource, PathPrefix> resourceToPrefixMap = pathRoot.findApplicableResources(
- branchForClassPathEntry, pathPrefixSet);
- for (Entry<AbstractResource, PathPrefix> entry : resourceToPrefixMap.entrySet()) {
- ResourceData newCpeData = new ResourceData(entry.getKey(),
- entry.getValue());
- String resourcePath = newCpeData.resource.getPath();
- ResourceData oldCpeData = newInternalMap.get(resourcePath);
- // Old wins unless the new resource has higher priority.
- if (oldCpeData == null || oldCpeData.compareTo(newCpeData) < 0) {
- newInternalMap.put(resourcePath, newCpeData);
- } else {
- Messages.IGNORING_SHADOWED_RESOURCE.log(branchForClassPathEntry,
- resourcePath, null);
- }
- }
- }
-
- Map<String, Resource> externalMap = new HashMap<String, Resource>();
- Set<Resource> externalSet = new HashSet<Resource>();
- for (Entry<String, ResourceData> entry : newInternalMap.entrySet()) {
- String path = entry.getKey();
- ResourceData data = entry.getValue();
- externalMap.put(path, data.resource);
- externalSet.add(data.resource);
- }
-
- // Update exposed collections with new (unmodifiable) data structures.
- exposedResources = Collections.unmodifiableSet(externalSet);
- exposedResourceMap = Collections.unmodifiableMap(externalMap);
- exposedPathNames = Collections.unmodifiableSet(externalMap.keySet());
- resourceOracle.end();
- }
-
public void setPathPrefixes(PathPrefixSet pathPrefixSet) {
this.pathPrefixSet = pathPrefixSet;
}
diff --git a/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java b/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
index cfb0a87..bc693fb 100644
--- a/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
+++ b/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
@@ -382,7 +382,7 @@
PathPrefix pathPrefix = new PathPrefix(prefix, null, true);
pathPrefixSet.add(pathPrefix);
resourceOracle.setPathPrefixes(pathPrefixSet);
- resourceOracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, resourceOracle);
Map<String, Resource> resourceMap = resourceOracle.getResourceMap();
return resourceMap;
}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java
index d623069..68bc014 100644
--- a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java
+++ b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java
@@ -66,7 +66,7 @@
pathPrefixSet.add(makeJunitPrefix());
pathPrefixSet.add(makeThisClassPrefix());
resourceOracle.setPathPrefixes(pathPrefixSet);
- resourceOracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, resourceOracle);
Map<String, Resource> resourceMap = resourceOracle.getResourceMap();
assertEquals(2, resourceMap.size());
}
@@ -76,17 +76,17 @@
pathPrefixSet.add(makeJunitPrefix());
pathPrefixSet.add(makeThisClassPrefix());
resourceOracle.setPathPrefixes(pathPrefixSet);
- resourceOracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, resourceOracle);
Map<String, Resource> resourceMap = resourceOracle.getResourceMap();
assertEquals(2, resourceMap.size());
// Plain refresh should have no effect.
- resourceOracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, resourceOracle);
assertResourcesEqual(resourceMap, resourceOracle.getResourceMap());
// Setting same path entries should have no effect.
resourceOracle.setPathPrefixes(pathPrefixSet);
- resourceOracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, resourceOracle);
assertResourcesEqual(resourceMap, resourceOracle.getResourceMap());
// Setting identical path entries should have no effect.
@@ -94,17 +94,17 @@
pathPrefixSet.add(makeJunitPrefix());
pathPrefixSet.add(makeThisClassPrefix());
resourceOracle.setPathPrefixes(pathPrefixSet);
- resourceOracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, resourceOracle);
assertResourcesEqual(resourceMap, resourceOracle.getResourceMap());
// Setting identical result should have no effect.
pathPrefixSet.add(makeJunitPrefix());
- resourceOracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, resourceOracle);
assertResourcesEqual(resourceMap, resourceOracle.getResourceMap());
// Actually change the working set.
pathPrefixSet.add(makeThisClassPrefixPlus());
- resourceOracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, resourceOracle);
assertEquals(3, resourceOracle.getResourceMap().size());
}
}
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java
index 12b257f..c2a449e 100644
--- a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java
+++ b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java
@@ -511,10 +511,6 @@
makeJavaLangPrefix());
}
- /**
- * @param classPathEntry
- * @param string
- */
private void assertJarEntry(ClassPathEntry classPathEntry, String expected) {
assertTrue("Should be instance of ZipFileClassPathEntry",
classPathEntry instanceof ZipFileClassPathEntry);
@@ -545,9 +541,9 @@
private ResourceOracleSnapshot refreshAndSnapshot(TreeLogger logger,
ResourceOracleImpl oracle) {
- oracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, oracle);
ResourceOracleSnapshot snapshot1 = new ResourceOracleSnapshot(oracle);
- oracle.refresh(logger);
+ ResourceOracleImpl.refresh(logger, oracle);
ResourceOracleSnapshot snapshot2 = new ResourceOracleSnapshot(oracle);
snapshot1.assertSameCollections(snapshot2);
return snapshot1;