Ignore duplicate classpath entries, and share classpaths generated from the same URLClassLoader.
This will reduce memory and processing requirements for build systems where many classpath entries
get duplicated.
Patch by: jat, amitmanjhi (pair programmed)
Review by: rjrjr
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4592 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 f963fb0..50a1985 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
@@ -42,9 +42,9 @@
/**
* The normal implementation of {@link ResourceOracle}.
*
- * TODO: this incorporates a quick-and-dirty fix for issue 3078 -- a proper
- * fix that considers module inheritance order before classpath order should
- * be implemented.
+ * TODO: this incorporates a quick-and-dirty fix for issue 3078 -- a proper fix
+ * that considers module inheritance order before classpath order should be
+ * implemented.
*/
public class ResourceOracleImpl implements ResourceOracle {
@@ -130,6 +130,8 @@
}
}
+ private static final Map<ClassLoader, List<ClassPathEntry>> classPathCache = new HashMap<ClassLoader, List<ClassPathEntry>>();
+
public static ClassPathEntry createEntryForUrl(TreeLogger logger, URL url)
throws URISyntaxException, IOException {
if (url.getProtocol().equals("file")) {
@@ -164,11 +166,16 @@
private static void addAllClassPathEntries(TreeLogger logger,
ClassLoader classLoader, List<ClassPathEntry> classPath) {
+ Set<URL> seenEntries = new HashSet<URL>();
for (; classLoader != null; classLoader = classLoader.getParent()) {
if (classLoader instanceof URLClassLoader) {
URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
URL[] urls = urlClassLoader.getURLs();
for (URL url : urls) {
+ if (seenEntries.contains(url)) {
+ continue;
+ }
+ seenEntries.add(url);
Throwable caught;
try {
ClassPathEntry entry = createEntryForUrl(logger, url);
@@ -177,7 +184,8 @@
}
continue;
} catch (AccessControlException e) {
- logger.log(TreeLogger.DEBUG, "Skipping URL due to access restrictions: " + url);
+ logger.log(TreeLogger.DEBUG,
+ "Skipping URL due to access restrictions: " + url);
continue;
} catch (URISyntaxException e) {
caught = e;
@@ -191,10 +199,14 @@
}
}
- private static List<ClassPathEntry> getAllClassPathEntries(TreeLogger logger,
- ClassLoader classLoader) {
- ArrayList<ClassPathEntry> classPath = new ArrayList<ClassPathEntry>();
- addAllClassPathEntries(logger, classLoader, classPath);
+ private static synchronized List<ClassPathEntry> getAllClassPathEntries(
+ TreeLogger logger, ClassLoader classLoader) {
+ List<ClassPathEntry> classPath = classPathCache.get(classLoader);
+ if (classPath == null) {
+ classPath = new ArrayList<ClassPathEntry>();
+ addAllClassPathEntries(logger, classLoader, classPath);
+ classPathCache.put(classLoader, classPath);
+ }
return classPath;
}
@@ -340,6 +352,11 @@
this.pathPrefixSet = pathPrefixSet;
}
+ // @VisibleForTesting
+ List<ClassPathEntry> getClassPath() {
+ return classPath;
+ }
+
private Map<String, Resource> rerootResourcePaths(
Map<String, AbstractResource> newInternalMap) {
Map<String, Resource> externalMap;
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java b/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java
index 489e571..c72d651 100644
--- a/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java
+++ b/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java
@@ -152,8 +152,7 @@
}
protected File findJarFile(String name) throws URISyntaxException {
- ClassLoader classLoader = getClass().getClassLoader();
- URL url = classLoader.getResource(name);
+ URL url = findJarUrl(name);
assertNotNull(
"Expecting on the classpath: "
+ name
@@ -164,6 +163,15 @@
return file;
}
+ /**
+ * @param name
+ * @return
+ */
+ protected URL findJarUrl(String name) {
+ ClassLoader classLoader = getClass().getClassLoader();
+ return classLoader.getResource(name);
+ }
+
protected Resource findResourceWithPath(Set<AbstractResource> resources,
String path) {
for (Resource r : resources) {
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 46e166d..8449d7d 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
@@ -23,7 +23,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.net.MalformedURLException;
import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -183,6 +186,32 @@
testReadingResource(cpe1dir, cpe2dir);
}
+ /**
+ * Verify that duplicate entries are removed from the classpath, and that
+ * multiple ResourceOracleImpls created from the same classloader return the
+ * same list of ClassPathEntries.
+ *
+ * @throws MalformedURLException
+ */
+ public void testRemoveDuplicates() throws MalformedURLException {
+ TreeLogger logger = createTestTreeLogger();
+ URL cpe1 = findJarUrl("com/google/gwt/dev/resource/impl/testdata/cpe1.jar");
+ URL cpe2 = findJarUrl("com/google/gwt/dev/resource/impl/testdata/cpe2.jar");
+ URLClassLoader classLoader = new URLClassLoader(new URL[] {
+ cpe1, cpe2, cpe2, cpe1, cpe2,}, null);
+ ResourceOracleImpl oracle = new ResourceOracleImpl(logger, classLoader);
+ List<ClassPathEntry> classPath = oracle.getClassPath();
+ assertEquals(2, classPath.size());
+ assertJarEntry(classPath.get(0), "cpe1.jar");
+ assertJarEntry(classPath.get(1), "cpe2.jar");
+ oracle = new ResourceOracleImpl(logger, classLoader);
+ List<ClassPathEntry> classPath2 = oracle.getClassPath();
+ assertEquals(2, classPath2.size());
+ for (int i = 0; i < 2; ++i) {
+ assertSame(classPath.get(i), classPath2.get(i));
+ }
+ }
+
public void testResourceAddition() throws IOException, URISyntaxException {
ClassPathEntry cpe1mock = getClassPathEntry1AsMock();
ClassPathEntry cpe1jar = getClassPathEntry1AsJar();
@@ -255,6 +284,18 @@
}
/**
+ * @param classPathEntry
+ * @param string
+ */
+ private void assertJarEntry(ClassPathEntry classPathEntry, String expected) {
+ assertTrue("Should be instance of ZipFileClassPathEntry",
+ classPathEntry instanceof ZipFileClassPathEntry);
+ ZipFileClassPathEntry zipCPE = (ZipFileClassPathEntry) classPathEntry;
+ String jar = zipCPE.getLocation();
+ assertTrue("URL should contain " + expected, jar.contains(expected));
+ }
+
+ /**
* Creates an array of class path entries, setting up each one with a
* well-known set of client prefixes.
*/