| /* |
| * 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.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.repository.url.URLResource; |
| 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 = parseDuration(settings.getVariable("ivy.cache.ttl.default")); |
| } |
| return defaultTTL; |
| } |
| |
| public void setDefaultTTL(long defaultTTL) { |
| this.defaultTTL = defaultTTL; |
| } |
| |
| public void setDefaultTTL(String defaultTTL) { |
| this.defaultTTL = 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), 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.isEmpty() ? 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) : false; |
| } else { |
| return false; |
| } |
| } else { |
| return checkmodified; |
| } |
| } |
| |
| public void setCheckmodified(boolean check) { |
| checkmodified = 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; |
| } |
| } |
| |
| public void setUseOrigin(boolean b) { |
| useOrigin = 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 |
| */ |
| 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); |
| |
| 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)); |
| } |
| |
| 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) { |
| if (isCheckmodified(dd, requestedRevisionId, options)) { |
| Message.verbose("don't use cache for " + requestedRevisionId + ": checkModified=true"); |
| return null; |
| } |
| if (isChanging(dd, requestedRevisionId, options)) { |
| Message.verbose("don't use cache for " + requestedRevisionId + ": changing=true"); |
| return null; |
| } |
| return doFindModuleInCache(requestedRevisionId, 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 { |
| ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance().getParser( |
| new URLResource(ivyFile.toURI().toURL())); |
| 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 (ParseException e) { |
| // will try with resolver |
| Message.debug("\tproblem while parsing cached ivy file for: " + mrid + ": " + e.getMessage()); |
| } catch (IOException e) { |
| // will try with resolver |
| Message.debug("\tproblem while accessing 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(ModuleDescriptorParser 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; |
| } |
| |
| 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(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()) < 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(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 (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 (String conf : confs) { |
| Artifact[] arts = md.getArtifacts(conf); |
| for (Artifact art : arts) { |
| Artifact transformedArtifact = NameSpaceHelper.transform(art, 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(); |
| } |
| 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(); |
| } |
| } |
| |
| } |
| } |