You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ant.apache.org by hi...@apache.org on 2015/09/06 14:15:13 UTC

ant-ivy git commit: IVY-1430 : dynamic revisions are not cached per resolver

Repository: ant-ivy
Updated Branches:
  refs/heads/master 12d3f2818 -> 5240c8825


IVY-1430 : dynamic revisions are not cached per resolver

Thanks to Stephen Haberman


Project: http://git-wip-us.apache.org/repos/asf/ant-ivy/repo
Commit: http://git-wip-us.apache.org/repos/asf/ant-ivy/commit/5240c882
Tree: http://git-wip-us.apache.org/repos/asf/ant-ivy/tree/5240c882
Diff: http://git-wip-us.apache.org/repos/asf/ant-ivy/diff/5240c882

Branch: refs/heads/master
Commit: 5240c8825896997f0845e354a51b1ac725dfe06b
Parents: 12d3f28
Author: Nicolas Lalevée <ni...@hibnet.org>
Authored: Sun Sep 6 14:14:58 2015 +0200
Committer: Nicolas Lalevée <ni...@hibnet.org>
Committed: Sun Sep 6 14:14:58 2015 +0200

----------------------------------------------------------------------
 doc/release-notes.html                          |  1 +
 .../cache/DefaultRepositoryCacheManager.java    | 41 +++++++++-
 .../ivy/core/cache/RepositoryCacheManager.java  | 15 ++++
 .../ivy/plugins/resolver/AbstractResolver.java  | 10 +--
 .../DefaultRepositoryCacheManagerTest.java      | 80 +++++++++++++++++++-
 5 files changed, 134 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/5240c882/doc/release-notes.html
----------------------------------------------------------------------
diff --git a/doc/release-notes.html b/doc/release-notes.html
index 63c4fcf..12624af 100644
--- a/doc/release-notes.html
+++ b/doc/release-notes.html
@@ -60,6 +60,7 @@ List of changes since Ivy 2.4.0:
 - FIX: ArrayIndexOutOfBoundsException when using a p2 repository for dependencies (IVY-1504)
 - FIX: fixdeps remove transitive 'kept' dependencies
 - FIX: PomModuleDescriptorParser should parse licenses from parent POM (IVY-1526) (Thanks to Jaikiran Pai)
+- FIX: dynamic revisions are not cached per resolver (IVY-1430) (Thanks to Stephen Haberman)
 
 - IMPROVEMENT: Optimization: limit the revision numbers scanned if revision prefix is specified (Thanks to Ernestas Vaiciukevi&#269;ius)
 

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/5240c882/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java b/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java
index a950c0b..f6b2206 100644
--- a/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java
+++ b/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java
@@ -667,6 +667,17 @@ public class DefaultRepositoryCacheManager implements RepositoryCacheManager, Iv
             getDataFilePattern(), mRevId)), "ivy cached data file for " + mRevId);
     }
 
+    /**
+     * A resolver-specific ivydata file, only used for caching dynamic revisions, e.g.
+     * integration-repo.
+     */
+    private PropertiesFile getCachedDataFile(String resolverName, ModuleRevisionId mRevId) {
+        // we append ".${resolverName} onto the end of the regular ivydata location
+        return new PropertiesFile(new File(getRepositoryCacheRoot(),
+                IvyPatternHelper.substitute(getDataFilePattern(), mRevId) + "." + resolverName),
+                "ivy cached data file for " + mRevId);
+    }
+
     public ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd,
             ModuleRevisionId requestedRevisionId, CacheMetadataOptions options,
             String expectedResolver) {
@@ -693,7 +704,7 @@ public class DefaultRepositoryCacheManager implements RepositoryCacheManager, Iv
 
         try {
             if (settings.getVersionMatcher().isDynamic(mrid)) {
-                String resolvedRevision = getResolvedRevision(mrid, options);
+                String resolvedRevision = getResolvedRevision(expectedResolver, mrid, options);
                 if (resolvedRevision != null) {
                     Message.verbose("found resolved revision in cache: " + mrid + " => "
                             + resolvedRevision);
@@ -832,7 +843,11 @@ public class DefaultRepositoryCacheManager implements RepositoryCacheManager, Iv
         return cache.getStale(ivyFile, settings, options.isValidate(), mdProvider);
     }
 
-    private String getResolvedRevision(ModuleRevisionId mrid, CacheMetadataOptions options) {
+    /**
+     * Called by doFindModuleInCache to lookup the dynamic {@code mrid} in the ivycache's ivydata
+     * file.
+     */
+    private String getResolvedRevision(String expectedResolver, ModuleRevisionId mrid, CacheMetadataOptions options) {
         if (!lockMetadataArtifact(mrid)) {
             Message.error("impossible to acquire lock for " + mrid);
             return null;
@@ -843,7 +858,13 @@ public class DefaultRepositoryCacheManager implements RepositoryCacheManager, Iv
                 Message.verbose("refresh mode: no check for cached resolved revision for " + mrid);
                 return null;
             }
-            PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
+            // If a resolver is asking for its specific dynamic revision, avoid looking at a different one
+            PropertiesFile cachedResolvedRevision;
+            if (expectedResolver != null) {
+                cachedResolvedRevision = getCachedDataFile(expectedResolver, mrid);
+            } else {
+                cachedResolvedRevision = getCachedDataFile(mrid);
+            }
             resolvedRevision = cachedResolvedRevision.getProperty("resolved.revision");
             if (resolvedRevision == null) {
                 Message.verbose(getName() + ": no cached resolved revision for " + mrid);
@@ -873,15 +894,27 @@ public class DefaultRepositoryCacheManager implements RepositoryCacheManager, Iv
     }
 
     public void saveResolvedRevision(ModuleRevisionId mrid, String revision) {
+        saveResolvedRevision(null, mrid, revision);
+    }
+
+    public void saveResolvedRevision(String resolverName, ModuleRevisionId mrid, String revision) {
         if (!lockMetadataArtifact(mrid)) {
             Message.error("impossible to acquire lock for " + mrid);
             return;
         }
         try {
-            PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
+            PropertiesFile cachedResolvedRevision;
+            if (resolverName == null) {
+                cachedResolvedRevision = getCachedDataFile(mrid);
+            } else {
+                cachedResolvedRevision = getCachedDataFile(resolverName, mrid);
+            }
             cachedResolvedRevision.setProperty("resolved.time",
                 String.valueOf(System.currentTimeMillis()));
             cachedResolvedRevision.setProperty("resolved.revision", revision);
+            if (resolverName != null) {
+                cachedResolvedRevision.setProperty("resolver", resolverName);
+            }
             cachedResolvedRevision.save();
         } finally {
             unlockMetadataArtifact(mrid);

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/5240c882/src/java/org/apache/ivy/core/cache/RepositoryCacheManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/core/cache/RepositoryCacheManager.java b/src/java/org/apache/ivy/core/cache/RepositoryCacheManager.java
index f6c5c24..a5effa1 100644
--- a/src/java/org/apache/ivy/core/cache/RepositoryCacheManager.java
+++ b/src/java/org/apache/ivy/core/cache/RepositoryCacheManager.java
@@ -188,7 +188,22 @@ public interface RepositoryCacheManager {
      *            the dynamic module revision id
      * @param revision
      *            the resolved revision
+     * @deprecated See {@link #saveResolvedRevision(String, ModuleRevisionId, String)} which
+     *             prevents cache + * thrashing when multiple resolvers store the same dynamicMrid
      */
     public void saveResolvedRevision(ModuleRevisionId dynamicMrid, String revision);
 
+    /**
+     * Caches a dynamic revision constraint resolution for a specific resolver.
+     * 
+     * @param resolverName
+     *            the resolver in which this dynamic revision was resolved
+     * @param dynamicMrid
+     *            the dynamic module revision id
+     * @param revision
+     *            the resolved revision
+     */
+    public void saveResolvedRevision(String resolverName, ModuleRevisionId dynamicMrid,
+            String revision);
+
 }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/5240c882/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java b/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
index 6030c9f..ef422d4 100644
--- a/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
@@ -513,22 +513,22 @@ public abstract class AbstractResolver implements DependencyResolver, HasLatestS
         Checks.checkNotNull(dd, "dd");
         Checks.checkNotNull(data, "data");
 
+        // always cache dynamic mrids because we can store per-resolver values
+        saveModuleRevisionIfNeeded(dd, newModuleFound);
+
         // check if latest is asked and compare to return the most recent
         ResolvedModuleRevision previousModuleFound = data.getCurrentResolvedModuleRevision();
         String newModuleDesc = describe(newModuleFound);
         Message.debug("\tchecking " + newModuleDesc + " against " + describe(previousModuleFound));
         if (previousModuleFound == null) {
             Message.debug("\tmodule revision kept as first found: " + newModuleDesc);
-            saveModuleRevisionIfNeeded(dd, newModuleFound);
             return newModuleFound;
         } else if (isAfter(newModuleFound, previousModuleFound, data.getDate())) {
             Message.debug("\tmodule revision kept as younger: " + newModuleDesc);
-            saveModuleRevisionIfNeeded(dd, newModuleFound);
             return newModuleFound;
         } else if (!newModuleFound.getDescriptor().isDefault()
                 && previousModuleFound.getDescriptor().isDefault()) {
             Message.debug("\tmodule revision kept as better (not default): " + newModuleDesc);
-            saveModuleRevisionIfNeeded(dd, newModuleFound);
             return newModuleFound;
         } else {
             Message.debug("\tmodule revision discarded as older: " + newModuleDesc);
@@ -540,8 +540,8 @@ public abstract class AbstractResolver implements DependencyResolver, HasLatestS
             ResolvedModuleRevision newModuleFound) {
         if (newModuleFound != null
                 && getSettings().getVersionMatcher().isDynamic(dd.getDependencyRevisionId())) {
-            getRepositoryCacheManager().saveResolvedRevision(dd.getDependencyRevisionId(),
-                newModuleFound.getId().getRevision());
+            getRepositoryCacheManager().saveResolvedRevision(getName(),
+                dd.getDependencyRevisionId(), newModuleFound.getId().getRevision());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/5240c882/test/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManagerTest.java
----------------------------------------------------------------------
diff --git a/test/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManagerTest.java b/test/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManagerTest.java
index 0e1b147..61ac601 100644
--- a/test/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManagerTest.java
+++ b/test/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManagerTest.java
@@ -18,16 +18,33 @@
 package org.apache.ivy.core.cache;
 
 import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.ParseException;
 import java.util.Date;
 
 import junit.framework.TestCase;
 
 import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
 import org.apache.ivy.core.module.descriptor.Artifact;
 import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.core.module.id.ModuleId;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
 import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.ivy.plugins.repository.BasicResource;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.ResourceDownloader;
+import org.apache.ivy.plugins.resolver.MockResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.DefaultMessageLogger;
+import org.apache.ivy.util.Message;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.taskdefs.Delete;
 
@@ -35,16 +52,19 @@ import org.apache.tools.ant.taskdefs.Delete;
  * @see DefaultResolutionCacheManager
  */
 public class DefaultRepositoryCacheManagerTest extends TestCase {
+    
     private DefaultRepositoryCacheManager cacheManager;
-
     private Artifact artifact;
-
     private ArtifactOrigin origin;
+    private Ivy ivy;
 
     protected void setUp() throws Exception {
         File f = File.createTempFile("ivycache", ".dir");
-        Ivy ivy = new Ivy();
+        ivy = new Ivy();
         ivy.configureDefault();
+        ivy.getLoggerEngine().setDefaultLogger(new DefaultMessageLogger(Message.MSG_DEBUG));
+        IvyContext.pushNewContext().setIvy(ivy);
+        
         IvySettings settings = ivy.getSettings();
         f.delete(); // we want to use the file as a directory, so we delete the file itself
         cacheManager = new DefaultRepositoryCacheManager();
@@ -62,6 +82,7 @@ public class DefaultRepositoryCacheManagerTest extends TestCase {
     }
 
     protected void tearDown() throws Exception {
+        IvyContext.popContext();
         Delete del = new Delete();
         del.setProject(new Project());
         del.setDir(cacheManager.getRepositoryCacheRoot());
@@ -106,7 +127,58 @@ public class DefaultRepositoryCacheManagerTest extends TestCase {
         assertTrue(ArtifactOrigin.isUnknown(found));
     }
 
-    protected Artifact createArtifact(String org, String module, String rev, String name,
+    public void testLatestIntegrationIsCachedPerResolver() throws Exception {
+        // given a module org#module
+        ModuleId mi = new ModuleId("org", "module");
+
+        // and a latest.integration mrid/dd
+        ModuleRevisionId mridLatest = new ModuleRevisionId(mi, "trunk", "latest.integration");
+        DependencyDescriptor ddLatest = new DefaultDependencyDescriptor(mridLatest,  false);
+
+        // and some random options
+        CacheMetadataOptions options = new CacheMetadataOptions().setCheckTTL(false);
+
+        // setup resolver1 to download the static content so we can call cacheModuleDescriptor
+        MockResolver resolver1 = new MockResolver();
+        resolver1.setName("resolver1");
+        resolver1.setSettings(ivy.getSettings());
+        ivy.getSettings().addResolver(resolver1);
+        ResourceDownloader downloader = new ResourceDownloader() {
+            public void download(Artifact artifact, Resource resource, File dest)
+                    throws IOException {
+                String content = "<ivy-module version=\"2.0\"><info organisation=\"org\" module=\"module\" status=\"integration\" revision=\"1.1\" branch=\"trunk\"/></ivy-module>";
+                dest.getParentFile().mkdirs();
+                FileOutputStream out = new FileOutputStream(dest);
+                PrintWriter pw = new PrintWriter(out);
+                pw.write(content);
+                pw.flush();
+                out.close();
+            }
+        };
+        ModuleDescriptorWriter writer = new ModuleDescriptorWriter() {
+            public void write(ResolvedResource originalMdResource, ModuleDescriptor md, File src, File dest) throws IOException, ParseException {
+                XmlModuleDescriptorWriter.write(md, dest);
+            }
+        };
+
+        // latest.integration will resolve to 1.1 in resolver1
+        ModuleRevisionId mrid11 = new ModuleRevisionId(mi, "trunk", "1.1");
+        DependencyDescriptor dd11 = new DefaultDependencyDescriptor(mrid11,  false);
+        DefaultArtifact artifact11 = new DefaultArtifact(mrid11, new Date(), "module-1.1.ivy", "ivy", "ivy", true);
+        BasicResource resource11 = new BasicResource("/module-1-1.ivy", true, 1, 0, true);
+        ResolvedResource mdRef11 = new ResolvedResource(resource11, "1.1");
+
+        // tell the cache about 1.1
+        ResolvedModuleRevision rmr11 = cacheManager.cacheModuleDescriptor(resolver1, mdRef11, dd11, artifact11, downloader, options);
+        cacheManager.originalToCachedModuleDescriptor(resolver1, mdRef11, artifact11, rmr11, writer);
+        // and use the new overload that passes in resolver name
+        cacheManager.saveResolvedRevision("resolver1", mridLatest, "1.1");
+
+        ResolvedModuleRevision rmrFromCache = cacheManager.findModuleInCache(ddLatest, mridLatest, options, "resolver1");
+        assertEquals(rmr11, rmrFromCache);
+    }
+
+    protected static DefaultArtifact createArtifact(String org, String module, String rev, String name,
             String type, String ext) {
         ModuleId mid = new ModuleId(org, module);
         ModuleRevisionId mrid = new ModuleRevisionId(mid, rev);