incubator-easyant-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jlboud...@apache.org
Subject svn commit: r1398103 - in /incubator/easyant/core/trunk/src/main/java/org/apache: easyant/core/ easyant/core/parser/ easyant/tasks/ ivy/ant/ ivy/core/ ivy/core/cache/
Date Sun, 14 Oct 2012 17:13:05 GMT
Author: jlboudart
Date: Sun Oct 14 17:13:04 2012
New Revision: 1398103

URL: http://svn.apache.org/viewvc?rev=1398103&view=rev
Log:
Fix issues with EasyantXmlParser when resolving stuff from parent in cache

Added:
    incubator/easyant/core/trunk/src/main/java/org/apache/ivy/core/
    incubator/easyant/core/trunk/src/main/java/org/apache/ivy/core/cache/
    incubator/easyant/core/trunk/src/main/java/org/apache/ivy/core/cache/EasyAntRepositoryCacheManager.java
Modified:
    incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/EasyAntEngine.java
    incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/parser/DefaultEasyAntXmlModuleDescriptorParser.java
    incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/AbstractEasyAntTask.java
    incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/LoadModule.java
    incubator/easyant/core/trunk/src/main/java/org/apache/ivy/ant/EasyAntPluginBridge.java

Modified: incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/EasyAntEngine.java
URL: http://svn.apache.org/viewvc/incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/EasyAntEngine.java?rev=1398103&r1=1398102&r2=1398103&view=diff
==============================================================================
--- incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/EasyAntEngine.java (original)
+++ incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/EasyAntEngine.java Sun Oct 14 17:13:04 2012
@@ -39,8 +39,10 @@ import org.apache.easyant.core.services.
 import org.apache.easyant.tasks.ConfigureBuildScopedRepository;
 import org.apache.easyant.tasks.Import;
 import org.apache.easyant.tasks.LoadModule;
+import org.apache.ivy.Ivy;
 import org.apache.ivy.ant.IvyAntSettings;
 import org.apache.ivy.ant.IvyConfigure;
+import org.apache.ivy.core.cache.EasyAntRepositoryCacheManager;
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.BuildListener;
 import org.apache.tools.ant.BuildLogger;
@@ -130,7 +132,15 @@ public class EasyAntEngine {
 
         configureEasyAntOfflineRepository(project);
 
-        return IvyInstanceHelper.getEasyAntIvyAntSettings(project);
+        IvyAntSettings easyantIvySettings = IvyInstanceHelper.getEasyAntIvyAntSettings(project);
+
+        Ivy easyantIvyInstance = easyantIvySettings.getConfiguredIvyInstance(easyantIvyConfigure);
+
+        EasyAntRepositoryCacheManager cacheManager = new EasyAntRepositoryCacheManager("default-easyant-cache",
+                easyantIvyInstance.getSettings(), easyantIvyInstance.getSettings().getDefaultCache());
+        easyantIvyInstance.getSettings().setDefaultRepositoryCacheManager(cacheManager);
+
+        return easyantIvySettings;
     }
 
     private static Method getLocalURL;

Modified: incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/parser/DefaultEasyAntXmlModuleDescriptorParser.java
URL: http://svn.apache.org/viewvc/incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/parser/DefaultEasyAntXmlModuleDescriptorParser.java?rev=1398103&r1=1398102&r2=1398103&view=diff
==============================================================================
--- incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/parser/DefaultEasyAntXmlModuleDescriptorParser.java (original)
+++ incubator/easyant/core/trunk/src/main/java/org/apache/easyant/core/parser/DefaultEasyAntXmlModuleDescriptorParser.java Sun Oct 14 17:13:04 2012
@@ -62,11 +62,17 @@ import org.xml.sax.SAXException;
 public class DefaultEasyAntXmlModuleDescriptorParser extends XmlModuleDescriptorParser implements
         EasyAntModuleDescriptorParser {
 
+    private static final DefaultEasyAntXmlModuleDescriptorParser INSTANCE = new DefaultEasyAntXmlModuleDescriptorParser();
+
+    public static DefaultEasyAntXmlModuleDescriptorParser getInstance() {
+        return INSTANCE;
+    }
+
     static final String[] PLUGIN_REGULAR_ATTRIBUTES = new String[] { "organisation", "org", "module", "revision",
             "rev", "mrid", "conf", "mode", "as", "inheritable", "inherit-scope" };
 
     public boolean accept(Resource res) {
-        return res.getName().endsWith(".ivy");
+        return res.getName().endsWith(".ivy") || res.getName().endsWith(".xml");
     }
 
     protected Parser newParser(ParserSettings ivySettings) {
@@ -89,7 +95,6 @@ public class DefaultEasyAntXmlModuleDesc
         parser.setInput(xmlURL);
         parser.parse();
         easyAntModuleDescriptor = parser.getEasyAntModuleDescriptor();
-
         return parser.getModuleDescriptor();
     }
 

Modified: incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/AbstractEasyAntTask.java
URL: http://svn.apache.org/viewvc/incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/AbstractEasyAntTask.java?rev=1398103&r1=1398102&r2=1398103&view=diff
==============================================================================
--- incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/AbstractEasyAntTask.java (original)
+++ incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/AbstractEasyAntTask.java Sun Oct 14 17:13:04 2012
@@ -47,6 +47,10 @@ public class AbstractEasyAntTask extends
         return getEasyAntIvyAntSettings().getConfiguredIvyInstance(this);
     }
 
+    protected Ivy getProjectIvyInstance() {
+        return IvyInstanceHelper.getProjectIvyAntSettings(getProject()).getConfiguredIvyInstance(this);
+    }
+
     /**
      * Utilitary method to configure a task with the current one
      * 

Modified: incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/LoadModule.java
URL: http://svn.apache.org/viewvc/incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/LoadModule.java?rev=1398103&r1=1398102&r2=1398103&view=diff
==============================================================================
--- incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/LoadModule.java (original)
+++ incubator/easyant/core/trunk/src/main/java/org/apache/easyant/tasks/LoadModule.java Sun Oct 14 17:13:04 2012
@@ -39,6 +39,7 @@ import org.apache.easyant.core.parser.Ea
 import org.apache.ivy.ant.IvyAntSettings;
 import org.apache.ivy.ant.IvyConfigure;
 import org.apache.ivy.ant.IvyInfo;
+import org.apache.ivy.core.IvyContext;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
 import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
@@ -249,6 +250,7 @@ public class LoadModule extends Abstract
     }
 
     protected void loadBuildModule(File buildModule) {
+        IvyContext.pushNewContext().setIvy(getEasyAntIvyInstance());
         EasyAntModuleDescriptorParser parser = getEasyAntModuleDescriptorParser(buildModule);
         log("Loading EasyAnt module descriptor :" + parser.getClass().getName(), Project.MSG_DEBUG);
 
@@ -317,6 +319,7 @@ public class LoadModule extends Abstract
         } catch (Exception e) {
             throw new BuildException("problem while parsing Ivy module file: " + e.getMessage(), e);
         }
+        IvyContext.popContext();
     }
 
     /**
@@ -398,7 +401,6 @@ public class LoadModule extends Abstract
             // the default one
             log("Creating instance of " + DefaultEasyAntXmlModuleDescriptorParser.class.getName(), Project.MSG_DEBUG);
             parser = new DefaultEasyAntXmlModuleDescriptorParser();
-
             ModuleDescriptorParserRegistry.getInstance().addParser(parser);
             return parser;
 

Modified: incubator/easyant/core/trunk/src/main/java/org/apache/ivy/ant/EasyAntPluginBridge.java
URL: http://svn.apache.org/viewvc/incubator/easyant/core/trunk/src/main/java/org/apache/ivy/ant/EasyAntPluginBridge.java?rev=1398103&r1=1398102&r2=1398103&view=diff
==============================================================================
--- incubator/easyant/core/trunk/src/main/java/org/apache/ivy/ant/EasyAntPluginBridge.java (original)
+++ incubator/easyant/core/trunk/src/main/java/org/apache/ivy/ant/EasyAntPluginBridge.java Sun Oct 14 17:13:04 2012
@@ -1,3 +1,20 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
 package org.apache.ivy.ant;
 
 import java.util.List;
@@ -6,6 +23,9 @@ import org.apache.ivy.core.module.descri
 import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
 import org.apache.ivy.core.settings.IvySettings;
 
+/**
+ * Give access to protected fields in {@link IvyDependency} {@link IvyExclude} {@link IvyConflict}
+ */
 public class EasyAntPluginBridge {
 
     public static DefaultModuleDescriptor computeModuleDescriptor(DefaultModuleDescriptor md, IvySettings settings,

Added: incubator/easyant/core/trunk/src/main/java/org/apache/ivy/core/cache/EasyAntRepositoryCacheManager.java
URL: http://svn.apache.org/viewvc/incubator/easyant/core/trunk/src/main/java/org/apache/ivy/core/cache/EasyAntRepositoryCacheManager.java?rev=1398103&view=auto
==============================================================================
--- incubator/easyant/core/trunk/src/main/java/org/apache/ivy/core/cache/EasyAntRepositoryCacheManager.java (added)
+++ incubator/easyant/core/trunk/src/main/java/org/apache/ivy/core/cache/EasyAntRepositoryCacheManager.java Sun Oct 14 17:13:04 2012
@@ -0,0 +1,1344 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.easyant.core.parser.DefaultEasyAntXmlModuleDescriptorParser;
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.id.ModuleRules;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.IvySettingsAware;
+import org.apache.ivy.plugins.lock.LockStrategy;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.MapMatcher;
+import org.apache.ivy.plugins.matcher.Matcher;
+import org.apache.ivy.plugins.matcher.NoMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.NameSpaceHelper;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
+import org.apache.ivy.plugins.repository.Repository;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.ResourceDownloader;
+import org.apache.ivy.plugins.repository.ResourceHelper;
+import org.apache.ivy.plugins.resolver.AbstractResolver;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.HexEncoder;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.PropertiesFile;
+
+/**
+ * Fork default CacheManager as default cache resolver use {@link XmlModuleDescriptorParser} to resolve files from cache
+ * If parent module is resolved from cache with {@link XmlModuleDescriptorParser} then easyant is not able to handle
+ * inherit properties or plugins We use the same package as original {@link RepositoryCacheManager} to access
+ * {@link ModuleDescriptorMemoryCache}
+ */
+public class EasyAntRepositoryCacheManager implements RepositoryCacheManager, IvySettingsAware {
+    private static final String DEFAULT_ARTIFACT_PATTERN = "[organisation]/[module](/[branch])/[type]s/[artifact]-[revision](-[classifier])(.[ext])";
+
+    private static final String DEFAULT_DATA_FILE_PATTERN = "[organisation]/[module](/[branch])/ivydata-[revision].properties";
+
+    private static final String DEFAULT_IVY_PATTERN = "[organisation]/[module](/[branch])/ivy-[revision].xml";
+
+    private static final int DEFAULT_MEMORY_CACHE_SIZE = 150;
+
+    private static MessageDigest SHA_DIGEST;
+    static {
+        try {
+            SHA_DIGEST = MessageDigest.getInstance("SHA1");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("The SHA1 algorithm is not available in your classpath", e);
+        }
+    }
+
+    private IvySettings settings;
+
+    private File basedir;
+
+    private LockStrategy lockStrategy;
+
+    private String name;
+
+    private String ivyPattern;
+
+    private String dataFilePattern = DEFAULT_DATA_FILE_PATTERN;
+
+    private String artifactPattern;
+
+    private String lockStrategyName;
+
+    private String changingPattern;
+
+    private String changingMatcherName = PatternMatcher.EXACT_OR_REGEXP;
+
+    private Boolean checkmodified;
+
+    private Boolean useOrigin;
+
+    private ModuleRules/* <Long> */ttlRules = new ModuleRules();
+
+    private Long defaultTTL = null;
+
+    private ModuleDescriptorMemoryCache memoryModuleDescrCache;
+
+    public EasyAntRepositoryCacheManager() {
+    }
+
+    public EasyAntRepositoryCacheManager(String name, IvySettings settings, File basedir) {
+        setName(name);
+        setSettings(settings);
+        setBasedir(basedir);
+    }
+
+    public IvySettings getSettings() {
+        return settings;
+    }
+
+    public void setSettings(IvySettings settings) {
+        this.settings = settings;
+    }
+
+    public File getIvyFileInCache(ModuleRevisionId mrid) {
+        String file = IvyPatternHelper.substitute(getIvyPattern(), DefaultArtifact.newIvyArtifact(mrid, null));
+        return new File(getRepositoryCacheRoot(), file);
+    }
+
+    public String getIvyPattern() {
+        if (ivyPattern == null) {
+            if (settings != null) {
+                ivyPattern = settings.getDefaultCacheIvyPattern();
+            }
+            if (ivyPattern == null) {
+                ivyPattern = DEFAULT_IVY_PATTERN;
+            }
+        }
+        return ivyPattern;
+    }
+
+    public String getArtifactPattern() {
+        if (artifactPattern == null) {
+            if (settings != null) {
+                artifactPattern = settings.getDefaultCacheArtifactPattern();
+            }
+            if (artifactPattern == null) {
+                artifactPattern = DEFAULT_ARTIFACT_PATTERN;
+            }
+        }
+        return artifactPattern;
+    }
+
+    public void setArtifactPattern(String artifactPattern) {
+        CacheUtil.checkCachePattern(artifactPattern);
+        this.artifactPattern = artifactPattern;
+    }
+
+    public File getBasedir() {
+        if (basedir == null) {
+            basedir = settings.getDefaultRepositoryCacheBasedir();
+        }
+        return basedir;
+    }
+
+    public void setBasedir(File cache) {
+        this.basedir = cache;
+    }
+
+    public long getDefaultTTL() {
+        if (defaultTTL == null) {
+            defaultTTL = new Long(parseDuration(settings.getVariable("ivy.cache.ttl.default")));
+        }
+        return defaultTTL.longValue();
+    }
+
+    public void setDefaultTTL(long defaultTTL) {
+        this.defaultTTL = new Long(defaultTTL);
+    }
+
+    public void setDefaultTTL(String defaultTTL) {
+        this.defaultTTL = new Long(parseDuration(defaultTTL));
+    }
+
+    public String getDataFilePattern() {
+        return dataFilePattern;
+    }
+
+    public void setDataFilePattern(String dataFilePattern) {
+        CacheUtil.checkCachePattern(dataFilePattern);
+        this.dataFilePattern = dataFilePattern;
+    }
+
+    public void setIvyPattern(String ivyPattern) {
+        CacheUtil.checkCachePattern(ivyPattern);
+        this.ivyPattern = ivyPattern;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getChangingMatcherName() {
+        return changingMatcherName;
+    }
+
+    public void setChangingMatcher(String changingMatcherName) {
+        this.changingMatcherName = changingMatcherName;
+    }
+
+    public String getChangingPattern() {
+        return changingPattern;
+    }
+
+    public void setChangingPattern(String changingPattern) {
+        this.changingPattern = changingPattern;
+    }
+
+    public void addTTL(Map attributes, PatternMatcher matcher, long duration) {
+        ttlRules.defineRule(new MapMatcher(attributes, matcher), new Long(duration));
+    }
+
+    public void addConfiguredTtl(Map/* <String,String> */attributes) {
+        String duration = (String) attributes.remove("duration");
+        if (duration == null) {
+            throw new IllegalArgumentException("'duration' attribute is mandatory for ttl");
+        }
+        String matcher = (String) attributes.remove("matcher");
+        addTTL(attributes, matcher == null ? ExactPatternMatcher.INSTANCE : settings.getMatcher(matcher),
+                parseDuration(duration));
+    }
+
+    public void setMemorySize(int size) {
+        memoryModuleDescrCache = new ModuleDescriptorMemoryCache(size);
+    }
+
+    public ModuleDescriptorMemoryCache getMemoryCache() {
+        if (memoryModuleDescrCache == null) {
+            memoryModuleDescrCache = new ModuleDescriptorMemoryCache(DEFAULT_MEMORY_CACHE_SIZE);
+        }
+        return memoryModuleDescrCache;
+    }
+
+    private static final Pattern DURATION_PATTERN = Pattern
+            .compile("(?:(\\d+)d)? ?(?:(\\d+)h)? ?(?:(\\d+)m)? ?(?:(\\d+)s)? ?(?:(\\d+)ms)?");
+
+    private static final int MILLIS_IN_SECONDS = 1000;
+    private static final int MILLIS_IN_MINUTES = 60 * MILLIS_IN_SECONDS;
+    private static final int MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTES;
+    private static final int MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR;
+
+    private long parseDuration(String duration) {
+        if (duration == null) {
+            return 0;
+        }
+        if ("eternal".equals(duration)) {
+            return Long.MAX_VALUE;
+        }
+        java.util.regex.Matcher m = DURATION_PATTERN.matcher(duration);
+        if (m.matches()) {
+            // CheckStyle:MagicNumber| OFF
+            int days = getGroupIntValue(m, 1);
+            int hours = getGroupIntValue(m, 2);
+            int minutes = getGroupIntValue(m, 3);
+            int seconds = getGroupIntValue(m, 4);
+            int millis = getGroupIntValue(m, 5);
+            // CheckStyle:MagicNumber| ON
+
+            return days * MILLIS_IN_DAY + hours * MILLIS_IN_HOUR + minutes * MILLIS_IN_MINUTES + seconds
+                    * MILLIS_IN_SECONDS + millis;
+        } else {
+            throw new IllegalArgumentException("invalid duration '" + duration + "': it must match "
+                    + DURATION_PATTERN.pattern() + " or 'eternal'");
+        }
+    }
+
+    private int getGroupIntValue(java.util.regex.Matcher m, int groupNumber) {
+        String g = m.group(groupNumber);
+        return g == null || g.length() == 0 ? 0 : Integer.parseInt(g);
+    }
+
+    /**
+     * True if this cache should check lastmodified date to know if ivy files are up to date.
+     * 
+     * @return
+     */
+    public boolean isCheckmodified() {
+        if (checkmodified == null) {
+            if (getSettings() != null) {
+                String check = getSettings().getVariable("ivy.resolver.default.check.modified");
+                return check != null ? Boolean.valueOf(check).booleanValue() : false;
+            } else {
+                return false;
+            }
+        } else {
+            return checkmodified.booleanValue();
+        }
+    }
+
+    public void setCheckmodified(boolean check) {
+        checkmodified = Boolean.valueOf(check);
+    }
+
+    /**
+     * True if this cache should use artifacts original location when possible, false if they should be copied to cache.
+     */
+    public boolean isUseOrigin() {
+        if (useOrigin == null) {
+            if (getSettings() != null) {
+                return getSettings().isDefaultUseOrigin();
+            } else {
+                return false;
+            }
+        } else {
+            return useOrigin.booleanValue();
+        }
+    }
+
+    public void setUseOrigin(boolean b) {
+        useOrigin = Boolean.valueOf(b);
+    }
+
+    /**
+     * Returns a File object pointing to where the artifact can be found on the local file system. This is usually in
+     * the cache, but it can be directly in the repository if it is local and if the resolve has been done with
+     * useOrigin = true
+     */
+    public File getArchiveFileInCache(Artifact artifact) {
+        ArtifactOrigin origin = getSavedArtifactOrigin(artifact);
+        return getArchiveFileInCache(artifact, origin);
+    }
+
+    /**
+     * Returns a File object pointing to where the artifact can be found on the local file system. This is usually in
+     * the cache, but it can be directly in the repository if it is local and if the resolve has been done with
+     * useOrigin = true
+     */
+    public File getArchiveFileInCache(Artifact artifact, ArtifactOrigin origin) {
+        File archive = new File(getRepositoryCacheRoot(), getArchivePathInCache(artifact, origin));
+        if (!archive.exists() && !ArtifactOrigin.isUnknown(origin) && origin.isLocal()) {
+            File original = Checks.checkAbsolute(origin.getLocation(), artifact + " origin location");
+            if (original.exists()) {
+                return original;
+            }
+        }
+        return archive;
+    }
+
+    /**
+     * Returns a File object pointing to where the artifact can be found on the local file system, using or not the
+     * original location depending on the availability of origin information provided as parameter and the setting of
+     * useOrigin. If useOrigin is false, this method will always return the file in the cache.
+     */
+    private File getArchiveFileInCache(Artifact artifact, ArtifactOrigin origin, boolean useOrigin) {
+        if (useOrigin && !ArtifactOrigin.isUnknown(origin) && origin.isLocal()) {
+            return Checks.checkAbsolute(origin.getLocation(), artifact + " origin location");
+        } else {
+            return new File(getRepositoryCacheRoot(), getArchivePathInCache(artifact, origin));
+        }
+    }
+
+    public String getArchivePathInCache(Artifact artifact) {
+        return IvyPatternHelper.substitute(getArtifactPattern(), artifact);
+    }
+
+    public String getArchivePathInCache(Artifact artifact, ArtifactOrigin origin) {
+        if (isOriginalMetadataArtifact(artifact)) {
+            return IvyPatternHelper.substitute(getIvyPattern() + ".original", artifact, origin);
+        } else {
+            return IvyPatternHelper.substitute(getArtifactPattern(), artifact, origin);
+        }
+    }
+
+    /**
+     * Saves the information of which resolver was used to resolve a md, so that this info can be retrieve later (even
+     * after a jvm restart) by getSavedResolverName(ModuleDescriptor md)
+     * 
+     * @param md
+     *            the module descriptor resolved
+     * @param name
+     *            resolver name
+     */
+    private void saveResolver(ModuleDescriptor md, String name) {
+        // should always be called with a lock on module metadata artifact
+        PropertiesFile cdf = getCachedDataFile(md);
+        cdf.setProperty("resolver", name);
+        cdf.save();
+    }
+
+    /**
+     * Saves the information of which resolver was used to resolve a md, so that this info can be retrieve later (even
+     * after a jvm restart) by getSavedArtResolverName(ModuleDescriptor md)
+     * 
+     * @param md
+     *            the module descriptor resolved
+     * @param name
+     *            artifact resolver name
+     */
+    public void saveResolvers(ModuleDescriptor md, String metadataResolverName, String artifactResolverName) {
+        ModuleRevisionId mrid = md.getResolvedModuleRevisionId();
+        if (!lockMetadataArtifact(mrid)) {
+            Message.error("impossible to acquire lock for " + mrid);
+            return;
+        }
+        try {
+            PropertiesFile cdf = getCachedDataFile(md);
+            cdf.setProperty("resolver", metadataResolverName);
+            cdf.setProperty("artifact.resolver", artifactResolverName);
+            cdf.save();
+        } finally {
+            unlockMetadataArtifact(mrid);
+        }
+    }
+
+    private String getSavedResolverName(ModuleDescriptor md) {
+        // should always be called with a lock on module metadata artifact
+        PropertiesFile cdf = getCachedDataFile(md);
+        return cdf.getProperty("resolver");
+    }
+
+    private String getSavedArtResolverName(ModuleDescriptor md) {
+        // should always be called with a lock on module metadata artifact
+        PropertiesFile cdf = getCachedDataFile(md);
+        return cdf.getProperty("artifact.resolver");
+    }
+
+    void saveArtifactOrigin(Artifact artifact, ArtifactOrigin origin) {
+        // should always be called with a lock on module metadata artifact
+        PropertiesFile cdf = getCachedDataFile(artifact.getModuleRevisionId());
+        cdf.setProperty(getIsLocalKey(artifact), String.valueOf(origin.isLocal()));
+        cdf.setProperty(getLocationKey(artifact), origin.getLocation());
+        if (origin.getLastChecked() != null) {
+            cdf.setProperty(getLastCheckedKey(artifact), origin.getLastChecked().toString());
+        }
+        cdf.setProperty(getExistsKey(artifact), Boolean.toString(origin.isExists()));
+        cdf.save();
+    }
+
+    private void removeSavedArtifactOrigin(Artifact artifact) {
+        // should always be called with a lock on module metadata artifact
+        PropertiesFile cdf = getCachedDataFile(artifact.getModuleRevisionId());
+        cdf.remove(getLocationKey(artifact));
+        cdf.remove(getIsLocalKey(artifact));
+        cdf.remove(getLastCheckedKey(artifact));
+        cdf.save();
+    }
+
+    public ArtifactOrigin getSavedArtifactOrigin(Artifact artifact) {
+        ModuleRevisionId mrid = artifact.getModuleRevisionId();
+        if (!lockMetadataArtifact(mrid)) {
+            Message.error("impossible to acquire lock for " + mrid);
+            return ArtifactOrigin.unkwnown(artifact);
+        }
+        try {
+            PropertiesFile cdf = getCachedDataFile(artifact.getModuleRevisionId());
+            String location = cdf.getProperty(getLocationKey(artifact));
+            String local = cdf.getProperty(getIsLocalKey(artifact));
+            String lastChecked = cdf.getProperty(getLastCheckedKey(artifact));
+            String exists = cdf.getProperty(getExistsKey(artifact));
+
+            boolean isLocal = Boolean.valueOf(local).booleanValue();
+
+            if (location == null) {
+                // origin has not been specified, return null
+                return ArtifactOrigin.unkwnown(artifact);
+            }
+
+            ArtifactOrigin origin = new ArtifactOrigin(artifact, isLocal, location);
+            if (lastChecked != null) {
+                origin.setLastChecked(Long.valueOf(lastChecked));
+            }
+            if (exists != null) {
+                origin.setExist(Boolean.valueOf(exists).booleanValue());
+            }
+
+            return origin;
+        } finally {
+            unlockMetadataArtifact(mrid);
+        }
+    }
+
+    /**
+     * Creates the unique prefix key that will reference the artifact within the properties.
+     * 
+     * @param artifact
+     *            the artifact to create the unique key from. Cannot be null.
+     * @return the unique prefix key as a string.
+     */
+    private String getPrefixKey(Artifact artifact) {
+        // use the hashcode as a uuid for the artifact (fingers crossed)
+        int hashCode = artifact.getId().hashCode();
+        // use just some visual cue
+        return "artifact:" + artifact.getName() + "#" + artifact.getType() + "#" + artifact.getExt() + "#" + hashCode;
+    }
+
+    /**
+     * Returns the key used to identify the location of the artifact.
+     * 
+     * @param artifact
+     *            the artifact to generate the key from. Cannot be null.
+     * @return the key to be used to reference the artifact location.
+     */
+    private String getLocationKey(Artifact artifact) {
+        String prefix = getPrefixKey(artifact);
+        return prefix + ".location";
+    }
+
+    /**
+     * Returns the key used to identify if the artifact is local.
+     * 
+     * @param artifact
+     *            the artifact to generate the key from. Cannot be null.
+     * @return the key to be used to reference the artifact locality.
+     */
+    private String getIsLocalKey(Artifact artifact) {
+        String prefix = getPrefixKey(artifact);
+        return prefix + ".is-local";
+    }
+
+    /**
+     * Returns the key used to identify the last time the artifact was checked to be up to date.
+     * 
+     * @param artifact
+     *            the artifact to generate the key from. Cannot be null.
+     * @return the key to be used to reference the artifact's last check date.
+     */
+    private String getLastCheckedKey(Artifact artifact) {
+        String prefix = getPrefixKey(artifact);
+        return prefix + ".lastchecked";
+    }
+
+    /**
+     * Returns the key used to identify the existence of the remote artifact.
+     * 
+     * @param artifact
+     *            the artifact to generate the key from. Cannot be null.
+     * @return the key to be used to reference the existence of the artifact.
+     */
+    private String getExistsKey(Artifact artifact) {
+        String prefix = getPrefixKey(artifact);
+        return prefix + ".exists";
+    }
+
+    private PropertiesFile getCachedDataFile(ModuleDescriptor md) {
+        return getCachedDataFile(md.getResolvedModuleRevisionId());
+    }
+
+    private PropertiesFile getCachedDataFile(ModuleRevisionId mRevId) {
+        return new PropertiesFile(new File(getRepositoryCacheRoot(), IvyPatternHelper.substitute(getDataFilePattern(),
+                mRevId)), "ivy cached data file for " + mRevId);
+    }
+
+    public ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd, ModuleRevisionId requestedRevisionId,
+            CacheMetadataOptions options, String expectedResolver) {
+        ModuleRevisionId mrid = requestedRevisionId;
+        if (isCheckmodified(dd, requestedRevisionId, options)) {
+            Message.verbose("don't use cache for " + mrid + ": checkModified=true");
+            return null;
+        }
+        if (isChanging(dd, requestedRevisionId, options)) {
+            Message.verbose("don't use cache for " + mrid + ": changing=true");
+            return null;
+        }
+        return doFindModuleInCache(mrid, options, expectedResolver);
+    }
+
+    private ResolvedModuleRevision doFindModuleInCache(ModuleRevisionId mrid, CacheMetadataOptions options,
+            String expectedResolver) {
+        if (!lockMetadataArtifact(mrid)) {
+            Message.error("impossible to acquire lock for " + mrid);
+            return null;
+        }
+
+        boolean unlock = true;
+
+        try {
+            if (settings.getVersionMatcher().isDynamic(mrid)) {
+                String resolvedRevision = getResolvedRevision(mrid, options);
+                if (resolvedRevision != null) {
+                    Message.verbose("found resolved revision in cache: " + mrid + " => " + resolvedRevision);
+
+                    // we have found another module in the cache, make sure we unlock
+                    // the original module
+                    unlockMetadataArtifact(mrid);
+                    mrid = ModuleRevisionId.newInstance(mrid, resolvedRevision);
+
+                    // don't forget to request a lock on the new module!
+                    if (!lockMetadataArtifact(mrid)) {
+                        Message.error("impossible to acquire lock for " + mrid);
+
+                        // we couldn't lock the new module, so no need to unlock it
+                        unlock = false;
+                        return null;
+                    }
+                } else {
+                    return null;
+                }
+            }
+
+            File ivyFile = getIvyFileInCache(mrid);
+            if (ivyFile.exists()) {
+                // found in cache !
+                try {
+                    DefaultEasyAntXmlModuleDescriptorParser parser = DefaultEasyAntXmlModuleDescriptorParser
+                            .getInstance();
+                    ModuleDescriptor depMD = getMdFromCache(parser, options, ivyFile);
+                    String resolverName = getSavedResolverName(depMD);
+                    String artResolverName = getSavedArtResolverName(depMD);
+                    DependencyResolver resolver = settings.getResolver(resolverName);
+                    if (resolver == null) {
+                        Message.debug("\tresolver not found: " + resolverName
+                                + " => trying to use the one configured for " + mrid);
+                        resolver = settings.getResolver(depMD.getResolvedModuleRevisionId());
+                        if (resolver != null) {
+                            Message.debug("\tconfigured resolver found for " + depMD.getResolvedModuleRevisionId()
+                                    + ": " + resolver.getName() + ": saving this data");
+                            saveResolver(depMD, resolver.getName());
+                        }
+                    }
+                    DependencyResolver artResolver = settings.getResolver(artResolverName);
+                    if (artResolver == null) {
+                        artResolver = resolver;
+                    }
+                    if (resolver != null) {
+                        Message.debug("\tfound ivy file in cache for " + mrid + " (resolved by " + resolver.getName()
+                                + "): " + ivyFile);
+                        if (expectedResolver == null || expectedResolver.equals(resolver.getName())) {
+                            MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(
+                                    depMD.getMetadataArtifact());
+                            madr.setDownloadStatus(DownloadStatus.NO);
+                            madr.setSearched(false);
+                            madr.setLocalFile(ivyFile);
+                            madr.setSize(ivyFile.length());
+                            madr.setArtifactOrigin(getSavedArtifactOrigin(depMD.getMetadataArtifact()));
+                            return new ResolvedModuleRevision(resolver, artResolver, depMD, madr);
+                        } else {
+                            Message.debug("found module in cache but with a different resolver: " + "discarding: "
+                                    + mrid + "; expected resolver=" + expectedResolver + "; resolver="
+                                    + resolver.getName());
+                        }
+                    } else {
+                        Message.debug("\tresolver not found: " + resolverName + " => cannot use cached ivy file for "
+                                + mrid);
+                    }
+                } catch (Exception e) {
+                    // will try with resolver
+                    Message.debug("\tproblem while parsing cached ivy file for: " + mrid + ": " + e.getMessage());
+                }
+            } else {
+                Message.debug("\tno ivy file in cache for " + mrid + ": tried " + ivyFile);
+            }
+        } finally {
+            if (unlock) {
+                unlockMetadataArtifact(mrid);
+            }
+        }
+        return null;
+    }
+
+    private class MyModuleDescriptorProvider implements ModuleDescriptorProvider {
+
+        private final ModuleDescriptorParser mdParser;
+        private final ParserSettings settings;
+
+        public MyModuleDescriptorProvider(ModuleDescriptorParser mdParser, ParserSettings settings) {
+            this.mdParser = mdParser;
+            this.settings = settings;
+        }
+
+        public ModuleDescriptor provideModule(ParserSettings ivySettings, File descriptorURL, boolean validate)
+                throws ParseException, IOException {
+            return mdParser.parseDescriptor(settings, descriptorURL.toURI().toURL(), validate);
+        }
+    }
+
+    private ModuleDescriptor getMdFromCache(XmlModuleDescriptorParser mdParser, CacheMetadataOptions options,
+            File ivyFile) throws ParseException, IOException {
+        ModuleDescriptorMemoryCache cache = getMemoryCache();
+        ModuleDescriptorProvider mdProvider = new MyModuleDescriptorProvider(mdParser, settings);
+        return cache.get(ivyFile, settings, options.isValidate(), mdProvider);
+    }
+
+    private ModuleDescriptor getStaledMd(ModuleDescriptorParser mdParser, CacheMetadataOptions options, File ivyFile,
+            ParserSettings parserSettings) throws ParseException, IOException {
+        ModuleDescriptorMemoryCache cache = getMemoryCache();
+        ModuleDescriptorProvider mdProvider = new MyModuleDescriptorProvider(mdParser, parserSettings);
+        return cache.getStale(ivyFile, settings, options.isValidate(), mdProvider);
+    }
+
+    private String getResolvedRevision(ModuleRevisionId mrid, CacheMetadataOptions options) {
+        if (!lockMetadataArtifact(mrid)) {
+            Message.error("impossible to acquire lock for " + mrid);
+            return null;
+        }
+        try {
+            String resolvedRevision = null;
+            if (options.isForce()) {
+                Message.verbose("refresh mode: no check for cached resolved revision for " + mrid);
+                return null;
+            }
+            PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
+            resolvedRevision = cachedResolvedRevision.getProperty("resolved.revision");
+            if (resolvedRevision == null) {
+                Message.verbose(getName() + ": no cached resolved revision for " + mrid);
+                return null;
+            }
+
+            String resolvedTime = cachedResolvedRevision.getProperty("resolved.time");
+            if (resolvedTime == null) {
+                Message.verbose(getName() + ": inconsistent or old cache: no cached resolved time for " + mrid);
+                saveResolvedRevision(mrid, resolvedRevision);
+                return resolvedRevision;
+            }
+            if (options.isCheckTTL()) {
+                long expiration = Long.parseLong(resolvedTime) + getTTL(mrid);
+                if (expiration > 0 // negative expiration means that Long.MAX_VALUE has been exceeded
+                        && System.currentTimeMillis() > expiration) {
+                    Message.verbose(getName() + ": cached resolved revision expired for " + mrid);
+                    return null;
+                }
+            }
+            return resolvedRevision;
+        } finally {
+            unlockMetadataArtifact(mrid);
+        }
+    }
+
+    public void saveResolvedRevision(ModuleRevisionId mrid, String revision) {
+        if (!lockMetadataArtifact(mrid)) {
+            Message.error("impossible to acquire lock for " + mrid);
+            return;
+        }
+        try {
+            PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
+            cachedResolvedRevision.setProperty("resolved.time", String.valueOf(System.currentTimeMillis()));
+            cachedResolvedRevision.setProperty("resolved.revision", revision);
+            cachedResolvedRevision.save();
+        } finally {
+            unlockMetadataArtifact(mrid);
+        }
+    }
+
+    public long getTTL(ModuleRevisionId mrid) {
+        Long ttl = (Long) ttlRules.getRule(mrid);
+        return ttl == null ? getDefaultTTL() : ttl.longValue();
+    }
+
+    public String toString() {
+        return name;
+    }
+
+    public File getRepositoryCacheRoot() {
+        return getBasedir();
+    }
+
+    public LockStrategy getLockStrategy() {
+        if (lockStrategy == null) {
+            if (lockStrategyName != null) {
+                lockStrategy = settings.getLockStrategy(lockStrategyName);
+            } else {
+                lockStrategy = settings.getDefaultLockStrategy();
+            }
+        }
+        return lockStrategy;
+    }
+
+    public void setLockStrategy(LockStrategy lockStrategy) {
+        this.lockStrategy = lockStrategy;
+    }
+
+    public void setLockStrategy(String lockStrategyName) {
+        this.lockStrategyName = lockStrategyName;
+    }
+
+    public ArtifactDownloadReport download(Artifact artifact, ArtifactResourceResolver resourceResolver,
+            ResourceDownloader resourceDownloader, CacheDownloadOptions options) {
+        final ArtifactDownloadReport adr = new ArtifactDownloadReport(artifact);
+        boolean useOrigin = isUseOrigin();
+
+        // TODO: see if we could lock on the artifact to download only, instead of the module
+        // metadata artifact. We'd need to store artifact origin and is local in artifact specific
+        // file to do so, or lock the metadata artifact only to update artifact origin, which would
+        // mean acquiring nested locks, which can be a dangerous thing
+        ModuleRevisionId mrid = artifact.getModuleRevisionId();
+        if (!lockMetadataArtifact(mrid)) {
+            adr.setDownloadStatus(DownloadStatus.FAILED);
+            adr.setDownloadDetails("impossible to get lock for " + mrid);
+            return adr;
+        }
+        try {
+            DownloadListener listener = options.getListener();
+            if (listener != null) {
+                listener.needArtifact(this, artifact);
+            }
+            ArtifactOrigin origin = getSavedArtifactOrigin(artifact);
+            // if we can use origin file, we just ask ivy for the file in cache, and it will
+            // return the original one if possible. If we are not in useOrigin mode, we use the
+            // getArchivePath method which always return a path in the actual cache
+            File archiveFile = getArchiveFileInCache(artifact, origin, useOrigin);
+
+            if (archiveFile.exists() && !options.isForce()) {
+                adr.setDownloadStatus(DownloadStatus.NO);
+                adr.setSize(archiveFile.length());
+                adr.setArtifactOrigin(origin);
+                adr.setLocalFile(archiveFile);
+            } else {
+                long start = System.currentTimeMillis();
+                try {
+                    ResolvedResource artifactRef = resourceResolver.resolve(artifact);
+                    if (artifactRef != null) {
+                        origin = new ArtifactOrigin(artifact, artifactRef.getResource().isLocal(), artifactRef
+                                .getResource().getName());
+                        if (useOrigin && artifactRef.getResource().isLocal()) {
+                            saveArtifactOrigin(artifact, origin);
+                            archiveFile = getArchiveFileInCache(artifact, origin);
+                            adr.setDownloadStatus(DownloadStatus.NO);
+                            adr.setSize(archiveFile.length());
+                            adr.setArtifactOrigin(origin);
+                            adr.setLocalFile(archiveFile);
+                        } else {
+                            // refresh archive file now that we better now its origin
+                            archiveFile = getArchiveFileInCache(artifact, origin, useOrigin);
+                            if (ResourceHelper.equals(artifactRef.getResource(), archiveFile)) {
+                                throw new IllegalStateException("invalid settings for '" + resourceResolver
+                                        + "': pointing repository to ivy cache is forbidden !");
+                            }
+                            if (listener != null) {
+                                listener.startArtifactDownload(this, artifactRef, artifact, origin);
+                            }
+
+                            resourceDownloader.download(artifact, artifactRef.getResource(), archiveFile);
+                            adr.setSize(archiveFile.length());
+                            saveArtifactOrigin(artifact, origin);
+                            adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+                            adr.setDownloadStatus(DownloadStatus.SUCCESSFUL);
+                            adr.setArtifactOrigin(origin);
+                            adr.setLocalFile(archiveFile);
+                        }
+                    } else {
+                        adr.setDownloadStatus(DownloadStatus.FAILED);
+                        adr.setDownloadDetails(ArtifactDownloadReport.MISSING_ARTIFACT);
+                        adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+                    }
+                } catch (Exception ex) {
+                    adr.setDownloadStatus(DownloadStatus.FAILED);
+                    adr.setDownloadDetails(ex.getMessage());
+                    adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+                }
+            }
+            if (listener != null) {
+                listener.endArtifactDownload(this, artifact, adr, archiveFile);
+            }
+            return adr;
+        } finally {
+            unlockMetadataArtifact(mrid);
+        }
+    }
+
+    public ArtifactDownloadReport downloadRepositoryResource(final Resource resource, String name, String type,
+            String extension, CacheResourceOptions options, Repository repository) {
+
+        String hash = computeResourceNameHash(resource);
+        ModuleRevisionId mrid = ModuleRevisionId.newInstance("_repository_metadata_", hash, Ivy.getWorkingRevision());
+        Artifact artifact = new DefaultArtifact(mrid, null, name, type, extension);
+        final ArtifactDownloadReport adr = new ArtifactDownloadReport(artifact);
+        boolean useOrigin = isUseOrigin();
+
+        try {
+            DownloadListener listener = options.getListener();
+            if (listener != null) {
+                listener.needArtifact(this, artifact);
+            }
+            ArtifactOrigin savedOrigin = getSavedArtifactOrigin(artifact);
+            File archiveFile = getArchiveFileInCache(artifact, savedOrigin, useOrigin);
+
+            ArtifactOrigin origin = new ArtifactOrigin(artifact, resource.isLocal(), resource.getName());
+
+            if (!options.isForce()
+            // if the local file has been checked to be up to date enough recently, don't download
+                    && checkCacheUptodate(archiveFile, resource, savedOrigin, origin, options.getTtl())) {
+                if (archiveFile.exists()) {
+                    saveArtifactOrigin(artifact, origin);
+                    adr.setDownloadStatus(DownloadStatus.NO);
+                    adr.setSize(archiveFile.length());
+                    adr.setArtifactOrigin(savedOrigin);
+                    adr.setLocalFile(archiveFile);
+                } else {
+                    // we trust the cache to says that the resource doesn't exist
+                    adr.setDownloadStatus(DownloadStatus.FAILED);
+                    adr.setDownloadDetails("Remote resource is known to not exist");
+                }
+            } else {
+                long start = System.currentTimeMillis();
+                origin.setLastChecked(new Long(start));
+                try {
+                    ResolvedResource artifactRef = new ResolvedResource(resource, Ivy.getWorkingRevision());
+                    if (useOrigin && resource.isLocal()) {
+                        saveArtifactOrigin(artifact, origin);
+                        archiveFile = getArchiveFileInCache(artifact, origin);
+                        adr.setDownloadStatus(DownloadStatus.NO);
+                        adr.setSize(archiveFile.length());
+                        adr.setArtifactOrigin(origin);
+                        adr.setLocalFile(archiveFile);
+                    } else {
+                        if (listener != null) {
+                            listener.startArtifactDownload(this, artifactRef, artifact, origin);
+                        }
+
+                        // actual download
+                        if (archiveFile.exists()) {
+                            archiveFile.delete();
+                        }
+                        File part = new File(archiveFile.getAbsolutePath() + ".part");
+                        repository.get(resource.getName(), part);
+                        if (!part.renameTo(archiveFile)) {
+                            throw new IOException("impossible to move part file to definitive one: " + part + " -> "
+                                    + archiveFile);
+                        }
+
+                        adr.setSize(archiveFile.length());
+                        saveArtifactOrigin(artifact, origin);
+                        adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+                        adr.setDownloadStatus(DownloadStatus.SUCCESSFUL);
+                        adr.setArtifactOrigin(origin);
+                        adr.setLocalFile(archiveFile);
+                    }
+                } catch (Exception ex) {
+                    origin.setExist(false);
+                    saveArtifactOrigin(artifact, origin);
+                    adr.setDownloadStatus(DownloadStatus.FAILED);
+                    adr.setDownloadDetails(ex.getMessage());
+                    adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+                }
+            }
+            if (listener != null) {
+                listener.endArtifactDownload(this, artifact, adr, archiveFile);
+            }
+            return adr;
+        } finally {
+            unlockMetadataArtifact(mrid);
+        }
+    }
+
+    /**
+     * Compute a SHA1 of the resource name, encoded in base64, so we can use it as a file name.
+     * 
+     * @param resource
+     *            the resource which name will be hashed
+     * @return the hash
+     */
+    private String computeResourceNameHash(Resource resource) {
+        byte[] shaDigest;
+        try {
+            shaDigest = SHA_DIGEST.digest(resource.getName().getBytes("UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("UTF-8 not supported", e);
+        }
+        return HexEncoder.encode(shaDigest);
+    }
+
+    /**
+     * Check that a cached file can be considered up to date and thus not downloaded
+     * 
+     * @param archiveFile
+     *            the file in the cache
+     * @param resource
+     *            the remote resource to check
+     * @param savedOrigin
+     *            the saved origin which contains that last checked date
+     * @param origin
+     *            the origin in which to store the new last checked date
+     * @param ttl
+     *            the time to live to consider the cache up to date
+     * @return <code>true</code> if the cache is considered up to date
+     */
+    private boolean checkCacheUptodate(File archiveFile, Resource resource, ArtifactOrigin savedOrigin,
+            ArtifactOrigin origin, long ttl) {
+        long time = System.currentTimeMillis();
+        if (savedOrigin.getLastChecked() != null && (time - savedOrigin.getLastChecked().longValue()) < ttl) {
+            // still in the ttl period, no need to check, trust the cache
+            if (!archiveFile.exists()) {
+                // but if the local archive doesn't exist, trust the cache only if the cached origin
+                // says that the remote resource doesn't exist either
+                return !savedOrigin.isExists();
+            }
+            return true;
+        }
+        if (!archiveFile.exists()) {
+            // the the file doesn't exist in the cache, obviously not up to date
+            return false;
+        }
+        origin.setLastChecked(new Long(time));
+        // check if the local resource is up to date regarding the remote one
+        return archiveFile.lastModified() >= resource.getLastModified();
+    }
+
+    public void originalToCachedModuleDescriptor(DependencyResolver resolver, ResolvedResource orginalMetadataRef,
+            Artifact requestedMetadataArtifact, ResolvedModuleRevision rmr, ModuleDescriptorWriter writer) {
+        ModuleDescriptor md = rmr.getDescriptor();
+        Artifact originalMetadataArtifact = getOriginalMetadataArtifact(requestedMetadataArtifact);
+        File mdFileInCache = getIvyFileInCache(md.getResolvedModuleRevisionId());
+
+        ModuleRevisionId mrid = requestedMetadataArtifact.getModuleRevisionId();
+        if (!lockMetadataArtifact(mrid)) {
+            Message.warn("impossible to acquire lock for: " + mrid);
+            return;
+        }
+        try {
+            File originalFileInCache = getArchiveFileInCache(originalMetadataArtifact);
+            writer.write(orginalMetadataRef, md, originalFileInCache, mdFileInCache);
+
+            getMemoryCache().putInCache(mdFileInCache, new ParserSettingsMonitor(settings), true, md);
+            saveResolvers(md, resolver.getName(), resolver.getName());
+
+            if (!md.isDefault()) {
+                rmr.getReport().setOriginalLocalFile(originalFileInCache);
+            }
+            rmr.getReport().setLocalFile(mdFileInCache);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            Message.warn("impossible to put metadata file in cache: "
+                    + (orginalMetadataRef == null ? String.valueOf(md.getResolvedModuleRevisionId()) : String
+                            .valueOf(orginalMetadataRef)) + ". " + e.getClass().getName() + ": " + e.getMessage());
+        } finally {
+            unlockMetadataArtifact(mrid);
+        }
+    }
+
+    public ResolvedModuleRevision cacheModuleDescriptor(DependencyResolver resolver, final ResolvedResource mdRef,
+            DependencyDescriptor dd, Artifact moduleArtifact, ResourceDownloader downloader,
+            CacheMetadataOptions options) throws ParseException {
+        Date cachedPublicationDate = null;
+        ArtifactDownloadReport report;
+        ModuleRevisionId mrid = moduleArtifact.getModuleRevisionId();
+        if (!lockMetadataArtifact(mrid)) {
+            Message.error("impossible to acquire lock for " + mrid);
+            return null;
+        }
+
+        BackupResourceDownloader backupDownloader = new BackupResourceDownloader(downloader);
+
+        try {
+            if (!moduleArtifact.isMetadata()) {
+                // the descriptor we are trying to cache is a default one, not much to do
+                // just make sure the old artifacts are deleted...
+                if (isChanging(dd, mrid, options)) {
+                    long repoLastModified = mdRef.getLastModified();
+
+                    Artifact transformedArtifact = NameSpaceHelper.transform(moduleArtifact, options.getNamespace()
+                            .getToSystemTransformer());
+                    ArtifactOrigin origin = getSavedArtifactOrigin(transformedArtifact);
+                    File artFile = getArchiveFileInCache(transformedArtifact, origin, false);
+                    if (artFile.exists() && repoLastModified > artFile.lastModified()) {
+                        // artifacts have changed, they should be downloaded again
+                        Message.verbose(mrid + " has changed: deleting old artifacts");
+                        Message.debug("deleting " + artFile);
+                        if (!artFile.delete()) {
+                            Message.error("Couldn't delete outdated artifact from cache: " + artFile);
+                            return null;
+                        }
+                        removeSavedArtifactOrigin(transformedArtifact);
+                    }
+                }
+                return null;
+            }
+
+            // now let's see if we can find it in cache and if it is up to date
+            ResolvedModuleRevision rmr = doFindModuleInCache(mrid, options, null);
+            if (rmr != null) {
+                if (rmr.getDescriptor().isDefault() && rmr.getResolver() != resolver) {
+                    Message.verbose("\t" + getName() + ": found revision in cache: " + mrid + " (resolved by "
+                            + rmr.getResolver().getName() + "): but it's a default one, maybe we can find a better one");
+                } else {
+                    if (!isCheckmodified(dd, mrid, options) && !isChanging(dd, mrid, options)) {
+                        Message.verbose("\t" + getName() + ": revision in cache: " + mrid);
+                        rmr.getReport().setSearched(true);
+                        return rmr;
+                    }
+                    long repLastModified = mdRef.getLastModified();
+                    long cacheLastModified = rmr.getDescriptor().getLastModified();
+                    if (!rmr.getDescriptor().isDefault() && repLastModified <= cacheLastModified) {
+                        Message.verbose("\t" + getName() + ": revision in cache (not updated): " + mrid);
+                        rmr.getReport().setSearched(true);
+                        return rmr;
+                    } else {
+                        Message.verbose("\t" + getName() + ": revision in cache is not up to date: " + mrid);
+                        if (isChanging(dd, mrid, options)) {
+                            // ivy file has been updated, we should see if it has a new publication
+                            // date to see if a new download is required (in case the dependency is
+                            // a changing one)
+                            cachedPublicationDate = rmr.getDescriptor().getResolvedPublicationDate();
+                        }
+                    }
+                }
+            }
+
+            Artifact originalMetadataArtifact = getOriginalMetadataArtifact(moduleArtifact);
+            // now download module descriptor and parse it
+            report = download(originalMetadataArtifact, new ArtifactResourceResolver() {
+                public ResolvedResource resolve(Artifact artifact) {
+                    return mdRef;
+                }
+            }, backupDownloader, new CacheDownloadOptions().setListener(options.getListener()).setForce(true));
+            Message.verbose("\t" + report);
+
+            if (report.getDownloadStatus() == DownloadStatus.FAILED) {
+                Message.warn("problem while downloading module descriptor: " + mdRef.getResource() + ": "
+                        + report.getDownloadDetails() + " (" + report.getDownloadTimeMillis() + "ms)");
+                return null;
+            }
+
+            try {
+                ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance().getParser(
+                        mdRef.getResource());
+                ParserSettings parserSettings = settings;
+                if (resolver instanceof AbstractResolver) {
+                    parserSettings = ((AbstractResolver) resolver).getParserSettings();
+                }
+                ModuleDescriptor md = getStaledMd(parser, options, report.getLocalFile(), parserSettings);
+                if (md == null) {
+                    throw new IllegalStateException("module descriptor parser returned a null module descriptor, "
+                            + "which is not allowed. " + "parser=" + parser + "; parser class="
+                            + parser.getClass().getName() + "; module descriptor resource=" + mdRef.getResource());
+                }
+                Message.debug("\t" + getName() + ": parsed downloaded md file for " + mrid + "; parsed="
+                        + md.getModuleRevisionId());
+
+                // check if we should delete old artifacts
+                boolean deleteOldArtifacts = false;
+                if (cachedPublicationDate != null && !cachedPublicationDate.equals(md.getResolvedPublicationDate())) {
+                    // artifacts have changed, they should be downloaded again
+                    Message.verbose(mrid + " has changed: deleting old artifacts");
+                    deleteOldArtifacts = true;
+                }
+                if (deleteOldArtifacts) {
+                    String[] confs = md.getConfigurationsNames();
+                    for (int i = 0; i < confs.length; i++) {
+                        Artifact[] arts = md.getArtifacts(confs[i]);
+                        for (int j = 0; j < arts.length; j++) {
+                            Artifact transformedArtifact = NameSpaceHelper.transform(arts[j], options.getNamespace()
+                                    .getToSystemTransformer());
+                            ArtifactOrigin origin = getSavedArtifactOrigin(transformedArtifact);
+                            File artFile = getArchiveFileInCache(transformedArtifact, origin, false);
+                            if (artFile.exists()) {
+                                Message.debug("deleting " + artFile);
+                                if (!artFile.delete()) {
+                                    // Old artifacts couldn't get deleted!
+                                    // Restore the original ivy file so the next time we
+                                    // resolve the old artifacts are deleted again
+                                    backupDownloader.restore();
+                                    Message.error("Couldn't delete outdated artifact from cache: " + artFile);
+                                    return null;
+                                }
+                            }
+                            removeSavedArtifactOrigin(transformedArtifact);
+                        }
+                    }
+                } else if (isChanging(dd, mrid, options)) {
+                    Message.verbose(mrid + " is changing, but has not changed: will trust cached artifacts if any");
+                }
+
+                MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(md.getMetadataArtifact());
+                madr.setSearched(true);
+                madr.setDownloadStatus(report.getDownloadStatus());
+                madr.setDownloadDetails(report.getDownloadDetails());
+                madr.setArtifactOrigin(report.getArtifactOrigin());
+                madr.setDownloadTimeMillis(report.getDownloadTimeMillis());
+                madr.setOriginalLocalFile(report.getLocalFile());
+                madr.setSize(report.getSize());
+
+                Artifact transformedMetadataArtifact = NameSpaceHelper.transform(md.getMetadataArtifact(), options
+                        .getNamespace().getToSystemTransformer());
+                saveArtifactOrigin(transformedMetadataArtifact, report.getArtifactOrigin());
+
+                return new ResolvedModuleRevision(resolver, resolver, md, madr);
+            } catch (IOException ex) {
+                Message.warn("io problem while parsing ivy file: " + mdRef.getResource() + ": " + ex.getMessage());
+                return null;
+            }
+        } finally {
+            unlockMetadataArtifact(mrid);
+            backupDownloader.cleanUp();
+        }
+
+    }
+
+    // lock used to lock all metadata related information access
+    private boolean lockMetadataArtifact(ModuleRevisionId mrid) {
+        Artifact artifact = getDefaultMetadataArtifact(mrid);
+        try {
+            // we need to provide an artifact origin to be sure we do not end up in a stack overflow
+            // if the cache pattern is using original name, and the substitution thus trying to get
+            // the saved artifact origin value which in turns calls this method
+            return getLockStrategy().lockArtifact(artifact,
+                    getArchiveFileInCache(artifact, getDefaultMetadataArtifactOrigin(mrid)));
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt(); // reset interrupt status
+            throw new RuntimeException("operation interrupted");
+        }
+    }
+
+    private void unlockMetadataArtifact(ModuleRevisionId mrid) {
+        Artifact artifact = getDefaultMetadataArtifact(mrid);
+        getLockStrategy().unlockArtifact(artifact,
+                getArchiveFileInCache(artifact, getDefaultMetadataArtifactOrigin(mrid)));
+    }
+
+    private ArtifactOrigin getDefaultMetadataArtifactOrigin(ModuleRevisionId mrid) {
+        // it's important to say the origin is not local to make sure it won't ever be used for
+        // anything else than original token
+        return new ArtifactOrigin(DefaultArtifact.newIvyArtifact(mrid, null), false, getIvyFileInCache(mrid).getPath());
+    }
+
+    private Artifact getDefaultMetadataArtifact(ModuleRevisionId mrid) {
+        return new DefaultArtifact(mrid, new Date(), "metadata", "metadata", "ivy", true);
+    }
+
+    // not used any more, but maybe useful for finer grain locking when downloading artifacts
+    // private boolean lockArtifact(Artifact artifact) {
+    // try {
+    // return getLockStrategy().lockArtifact(artifact,
+    // getArchiveFileInCache(artifact, null));
+    // } catch (InterruptedException e) {
+    // Thread.currentThread().interrupt(); // reset interrupt status
+    // throw new RuntimeException("operation interrupted");
+    // }
+    // }
+    //
+    // private void unlockArtifact(Artifact artifact) {
+    // getLockStrategy().unlockArtifact(artifact, getArchiveFileInCache(artifact, null));
+    // }
+
+    public Artifact getOriginalMetadataArtifact(Artifact moduleArtifact) {
+        return DefaultArtifact.cloneWithAnotherType(moduleArtifact, moduleArtifact.getType() + ".original");
+    }
+
+    private boolean isOriginalMetadataArtifact(Artifact artifact) {
+        return artifact.isMetadata() && artifact.getType().endsWith(".original");
+    }
+
+    private boolean isChanging(DependencyDescriptor dd, ModuleRevisionId requestedRevisionId,
+            CacheMetadataOptions options) {
+        return dd.isChanging() || getChangingMatcher(options).matches(requestedRevisionId.getRevision());
+    }
+
+    private Matcher getChangingMatcher(CacheMetadataOptions options) {
+        String changingPattern = options.getChangingPattern() != null ? options.getChangingPattern()
+                : this.changingPattern;
+        if (changingPattern == null) {
+            return NoMatcher.INSTANCE;
+        }
+        String changingMatcherName = options.getChangingMatcherName() != null ? options.getChangingMatcherName()
+                : this.changingMatcherName;
+        PatternMatcher matcher = settings.getMatcher(changingMatcherName);
+        if (matcher == null) {
+            throw new IllegalStateException("unknown matcher '" + changingMatcherName
+                    + "'. It is set as changing matcher in " + this);
+        }
+        return matcher.getMatcher(changingPattern);
+    }
+
+    private boolean isCheckmodified(DependencyDescriptor dd, ModuleRevisionId requestedRevisionId,
+            CacheMetadataOptions options) {
+        if (options.isCheckmodified() != null) {
+            return options.isCheckmodified().booleanValue();
+        }
+        return isCheckmodified();
+    }
+
+    public void clean() {
+        FileUtil.forceDelete(getBasedir());
+    }
+
+    public void dumpSettings() {
+        Message.verbose("\t" + getName());
+        Message.debug("\t\tivyPattern: " + getIvyPattern());
+        Message.debug("\t\tartifactPattern: " + getArtifactPattern());
+        Message.debug("\t\tlockingStrategy: " + getLockStrategy().getName());
+        Message.debug("\t\tchangingPattern: " + getChangingPattern());
+        Message.debug("\t\tchangingMatcher: " + getChangingMatcherName());
+    }
+
+    /**
+     * Resource downloader which makes a copy of the previously existing file before overriding it.
+     * <p>
+     * The backup file can be restored or cleanuped later
+     */
+    private final class BackupResourceDownloader implements ResourceDownloader {
+
+        private ResourceDownloader delegate;
+        private File backup;
+        private String originalPath;
+
+        private BackupResourceDownloader(ResourceDownloader delegate) {
+            this.delegate = delegate;
+        }
+
+        public void download(Artifact artifact, Resource resource, File dest) throws IOException {
+            // keep a copy of the original file
+            if (dest.exists()) {
+                originalPath = dest.getAbsolutePath();
+                backup = new File(dest.getAbsolutePath() + ".backup");
+                FileUtil.copy(dest, backup, null, true);
+            }
+            delegate.download(artifact, resource, dest);
+        }
+
+        public void restore() throws IOException {
+            if ((backup != null) && backup.exists()) {
+                File original = new File(originalPath);
+                FileUtil.copy(backup, original, null, true);
+                backup.delete();
+            }
+        }
+
+        public void cleanUp() {
+            if ((backup != null) && backup.exists()) {
+                backup.delete();
+            }
+        }
+
+    }
+}



Mime
View raw message