2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
18
package org.apache.ivy.core.cache;
21
import java.io.IOException;
22
import java.text.ParseException;
23
import java.util.Date;
25
import java.util.regex.Pattern;
27
import org.apache.ivy.core.IvyPatternHelper;
28
import org.apache.ivy.core.module.descriptor.Artifact;
29
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
30
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
31
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
32
import org.apache.ivy.core.module.id.ModuleRevisionId;
33
import org.apache.ivy.core.module.id.ModuleRules;
34
import org.apache.ivy.core.report.ArtifactDownloadReport;
35
import org.apache.ivy.core.report.DownloadStatus;
36
import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
37
import org.apache.ivy.core.resolve.ResolvedModuleRevision;
38
import org.apache.ivy.core.settings.IvySettings;
39
import org.apache.ivy.plugins.IvySettingsAware;
40
import org.apache.ivy.plugins.lock.LockStrategy;
41
import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
42
import org.apache.ivy.plugins.matcher.MapMatcher;
43
import org.apache.ivy.plugins.matcher.Matcher;
44
import org.apache.ivy.plugins.matcher.NoMatcher;
45
import org.apache.ivy.plugins.matcher.PatternMatcher;
46
import org.apache.ivy.plugins.namespace.NameSpaceHelper;
47
import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
48
import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
49
import org.apache.ivy.plugins.parser.ParserSettings;
50
import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
51
import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
52
import org.apache.ivy.plugins.repository.ResourceDownloader;
53
import org.apache.ivy.plugins.repository.ResourceHelper;
54
import org.apache.ivy.plugins.resolver.DependencyResolver;
55
import org.apache.ivy.plugins.resolver.util.ResolvedResource;
56
import org.apache.ivy.util.Checks;
57
import org.apache.ivy.util.FileUtil;
58
import org.apache.ivy.util.Message;
59
import org.apache.ivy.util.PropertiesFile;
61
public class DefaultRepositoryCacheManager implements RepositoryCacheManager, IvySettingsAware {
62
private static final String DEFAULT_ARTIFACT_PATTERN =
63
"[organisation]/[module](/[branch])/[type]s/[artifact]-[revision](-[classifier])(.[ext])";
65
private static final String DEFAULT_DATA_FILE_PATTERN =
66
"[organisation]/[module](/[branch])/ivydata-[revision].properties";
68
private static final String DEFAULT_IVY_PATTERN =
69
"[organisation]/[module](/[branch])/ivy-[revision].xml";
71
private static final int DEFAULT_MEMORY_CACHE_SIZE = 150;
73
private IvySettings settings;
77
private LockStrategy lockStrategy;
81
private String ivyPattern;
83
private String dataFilePattern = DEFAULT_DATA_FILE_PATTERN;
85
private String artifactPattern;
87
private String lockStrategyName;
89
private String changingPattern;
91
private String changingMatcherName = PatternMatcher.EXACT_OR_REGEXP;
93
private Boolean checkmodified;
95
private Boolean useOrigin;
97
private ModuleRules/*<Long>*/ ttlRules = new ModuleRules();
99
private Long defaultTTL = null;
101
private ModuleDescriptorMemoryCache memoryModuleDescrCache;
103
public DefaultRepositoryCacheManager() {
106
public DefaultRepositoryCacheManager(String name, IvySettings settings, File basedir) {
108
setSettings(settings);
112
public IvySettings getSettings() {
116
public void setSettings(IvySettings settings) {
117
this.settings = settings;
120
public File getIvyFileInCache(ModuleRevisionId mrid) {
121
String file = IvyPatternHelper.substitute(getIvyPattern(), DefaultArtifact
122
.newIvyArtifact(mrid, null));
123
return new File(getRepositoryCacheRoot(), file);
126
public String getIvyPattern() {
127
if (ivyPattern == null) {
128
if (settings != null) {
129
ivyPattern = settings.getDefaultCacheIvyPattern();
131
if (ivyPattern == null) {
132
ivyPattern = DEFAULT_IVY_PATTERN;
138
public String getArtifactPattern() {
139
if (artifactPattern == null) {
140
if (settings != null) {
141
artifactPattern = settings.getDefaultCacheArtifactPattern();
143
if (artifactPattern == null) {
144
artifactPattern = DEFAULT_ARTIFACT_PATTERN;
147
return artifactPattern;
150
public void setArtifactPattern(String artifactPattern) {
151
CacheUtil.checkCachePattern(artifactPattern);
152
this.artifactPattern = artifactPattern;
155
public File getBasedir() {
156
if (basedir == null) {
157
basedir = settings.getDefaultRepositoryCacheBasedir();
162
public void setBasedir(File cache) {
163
this.basedir = cache;
166
public long getDefaultTTL() {
167
if (defaultTTL == null) {
168
defaultTTL = new Long(parseDuration(settings.getVariable("ivy.cache.ttl.default")));
170
return defaultTTL.longValue();
173
public void setDefaultTTL(long defaultTTL) {
174
this.defaultTTL = new Long(defaultTTL);
177
public void setDefaultTTL(String defaultTTL) {
178
this.defaultTTL = new Long(parseDuration(defaultTTL));
181
public String getDataFilePattern() {
182
return dataFilePattern;
185
public void setDataFilePattern(String dataFilePattern) {
186
CacheUtil.checkCachePattern(dataFilePattern);
187
this.dataFilePattern = dataFilePattern;
190
public void setIvyPattern(String ivyPattern) {
191
CacheUtil.checkCachePattern(ivyPattern);
192
this.ivyPattern = ivyPattern;
195
public String getName() {
199
public void setName(String name) {
203
public String getChangingMatcherName() {
204
return changingMatcherName;
207
public void setChangingMatcher(String changingMatcherName) {
208
this.changingMatcherName = changingMatcherName;
211
public String getChangingPattern() {
212
return changingPattern;
215
public void setChangingPattern(String changingPattern) {
216
this.changingPattern = changingPattern;
219
public void addTTL(Map attributes, PatternMatcher matcher, long duration) {
220
ttlRules.defineRule(new MapMatcher(attributes, matcher), new Long(duration));
223
public void addConfiguredTtl(Map/*<String,String>*/ attributes) {
224
String duration = (String) attributes.remove("duration");
225
if (duration == null) {
226
throw new IllegalArgumentException("'duration' attribute is mandatory for ttl");
228
String matcher = (String) attributes.remove("matcher");
231
matcher == null ? ExactPatternMatcher.INSTANCE : settings.getMatcher(matcher),
232
parseDuration(duration));
235
public void setMemorySize(int size) {
236
memoryModuleDescrCache = new ModuleDescriptorMemoryCache(size);
239
public ModuleDescriptorMemoryCache getMemoryCache() {
240
if (memoryModuleDescrCache == null) {
241
memoryModuleDescrCache = new ModuleDescriptorMemoryCache(DEFAULT_MEMORY_CACHE_SIZE);
243
return memoryModuleDescrCache;
247
private static final Pattern DURATION_PATTERN
248
= Pattern.compile("(?:(\\d+)d)? ?(?:(\\d+)h)? ?(?:(\\d+)m)? ?(?:(\\d+)s)? ?(?:(\\d+)ms)?");
250
private static final int MILLIS_IN_SECONDS = 1000;
251
private static final int MILLIS_IN_MINUTES = 60 * MILLIS_IN_SECONDS;
252
private static final int MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTES;
253
private static final int MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR;
255
private long parseDuration(String duration) {
256
if (duration == null) {
259
java.util.regex.Matcher m = DURATION_PATTERN.matcher(duration);
261
//CheckStyle:MagicNumber| OFF
262
int days = getGroupIntValue(m, 1);
263
int hours = getGroupIntValue(m, 2);
264
int minutes = getGroupIntValue(m, 3);
265
int seconds = getGroupIntValue(m, 4);
266
int millis = getGroupIntValue(m, 5);
267
//CheckStyle:MagicNumber| ON
269
return days * MILLIS_IN_DAY
270
+ hours * MILLIS_IN_HOUR
271
+ minutes * MILLIS_IN_MINUTES
272
+ seconds * MILLIS_IN_SECONDS
275
throw new IllegalArgumentException("invalid duration '"
276
+ duration + "': it must match " + DURATION_PATTERN.pattern());
280
private int getGroupIntValue(java.util.regex.Matcher m, int groupNumber) {
281
String g = m.group(groupNumber);
282
return g == null || g.length() == 0 ? 0 : Integer.parseInt(g);
286
* True if this cache should check lastmodified date to know if ivy files are up to date.
290
public boolean isCheckmodified() {
291
if (checkmodified == null) {
292
if (getSettings() != null) {
293
String check = getSettings().getVariable("ivy.resolver.default.check.modified");
294
return check != null ? Boolean.valueOf(check).booleanValue() : false;
299
return checkmodified.booleanValue();
303
public void setCheckmodified(boolean check) {
304
checkmodified = Boolean.valueOf(check);
308
* True if this cache should use artifacts original location when possible, false if they should
309
* be copied to cache.
311
public boolean isUseOrigin() {
312
if (useOrigin == null) {
313
if (getSettings() != null) {
314
return getSettings().isDefaultUseOrigin();
319
return useOrigin.booleanValue();
323
public void setUseOrigin(boolean b) {
324
useOrigin = Boolean.valueOf(b);
328
* Returns a File object pointing to where the artifact can be found on the local file system.
329
* This is usually in the cache, but it can be directly in the repository if it is local and if
330
* the resolve has been done with useOrigin = true
332
public File getArchiveFileInCache(Artifact artifact) {
333
ArtifactOrigin origin = getSavedArtifactOrigin(artifact);
334
return getArchiveFileInCache(artifact, origin);
338
* Returns a File object pointing to where the artifact can be found on the local file system.
339
* This is usually in the cache, but it can be directly in the repository if it is local and if
340
* the resolve has been done with useOrigin = true
342
public File getArchiveFileInCache(Artifact artifact, ArtifactOrigin origin) {
343
File archive = new File(getRepositoryCacheRoot(), getArchivePathInCache(artifact, origin));
344
if (!archive.exists()
345
&& !ArtifactOrigin.isUnknown(origin) && origin.isLocal()) {
346
File original = Checks.checkAbsolute(
347
origin.getLocation(), artifact + " origin location");
348
if (original.exists()) {
356
* Returns a File object pointing to where the artifact can be found on the local file system,
357
* using or not the original location depending on the availability of origin information
358
* provided as parameter and the setting of useOrigin. If useOrigin is false, this method will
359
* always return the file in the cache.
361
private File getArchiveFileInCache(
362
Artifact artifact, ArtifactOrigin origin, boolean useOrigin) {
363
if (useOrigin && !ArtifactOrigin.isUnknown(origin) && origin.isLocal()) {
364
return Checks.checkAbsolute(origin.getLocation(), artifact + " origin location");
366
return new File(getRepositoryCacheRoot(), getArchivePathInCache(artifact, origin));
370
public String getArchivePathInCache(Artifact artifact) {
371
return IvyPatternHelper.substitute(getArtifactPattern(), artifact);
374
public String getArchivePathInCache(Artifact artifact, ArtifactOrigin origin) {
375
if (isOriginalMetadataArtifact(artifact)) {
376
return IvyPatternHelper.substitute(getIvyPattern() + ".original", artifact, origin);
378
return IvyPatternHelper.substitute(getArtifactPattern(), artifact, origin);
383
* Saves the information of which resolver was used to resolve a md, so that this info can be
384
* retrieve later (even after a jvm restart) by getSavedResolverName(ModuleDescriptor md)
387
* the module descriptor resolved
391
private void saveResolver(ModuleDescriptor md, String name) {
392
// should always be called with a lock on module metadata artifact
393
PropertiesFile cdf = getCachedDataFile(md);
394
cdf.setProperty("resolver", name);
399
* Saves the information of which resolver was used to resolve a md, so that this info can be
400
* retrieve later (even after a jvm restart) by getSavedArtResolverName(ModuleDescriptor md)
403
* the module descriptor resolved
405
* artifact resolver name
407
public void saveResolvers(
408
ModuleDescriptor md, String metadataResolverName, String artifactResolverName) {
409
ModuleRevisionId mrid = md.getResolvedModuleRevisionId();
410
if (!lockMetadataArtifact(mrid)) {
411
Message.error("impossible to acquire lock for " + mrid);
415
PropertiesFile cdf = getCachedDataFile(md);
416
cdf.setProperty("resolver", metadataResolverName);
417
cdf.setProperty("artifact.resolver", artifactResolverName);
420
unlockMetadataArtifact(mrid);
424
private String getSavedResolverName(ModuleDescriptor md) {
425
// should always be called with a lock on module metadata artifact
426
PropertiesFile cdf = getCachedDataFile(md);
427
return cdf.getProperty("resolver");
430
private String getSavedArtResolverName(ModuleDescriptor md) {
431
// should always be called with a lock on module metadata artifact
432
PropertiesFile cdf = getCachedDataFile(md);
433
return cdf.getProperty("artifact.resolver");
436
void saveArtifactOrigin(Artifact artifact, ArtifactOrigin origin) {
437
// should always be called with a lock on module metadata artifact
438
PropertiesFile cdf = getCachedDataFile(artifact.getModuleRevisionId());
439
cdf.setProperty(getIsLocalKey(artifact), String.valueOf(origin.isLocal()));
440
cdf.setProperty(getLocationKey(artifact), origin.getLocation());
444
private void removeSavedArtifactOrigin(Artifact artifact) {
445
// should always be called with a lock on module metadata artifact
446
PropertiesFile cdf = getCachedDataFile(artifact.getModuleRevisionId());
447
cdf.remove(getLocationKey(artifact));
448
cdf.remove(getIsLocalKey(artifact));
452
public ArtifactOrigin getSavedArtifactOrigin(Artifact artifact) {
453
ModuleRevisionId mrid = artifact.getModuleRevisionId();
454
if (!lockMetadataArtifact(mrid)) {
455
Message.error("impossible to acquire lock for " + mrid);
456
return ArtifactOrigin.unkwnown(artifact);
459
PropertiesFile cdf = getCachedDataFile(artifact.getModuleRevisionId());
460
String location = cdf.getProperty(getLocationKey(artifact));
461
String local = cdf.getProperty(getIsLocalKey(artifact));
462
boolean isLocal = Boolean.valueOf(local).booleanValue();
464
if (location == null) {
465
// origin has not been specified, return null
466
return ArtifactOrigin.unkwnown(artifact);
469
return new ArtifactOrigin(artifact, isLocal, location);
471
unlockMetadataArtifact(mrid);
476
* Creates the unique prefix key that will reference the artifact within the properties.
479
* the artifact to create the unique key from. Cannot be null.
480
* @return the unique prefix key as a string.
482
private String getPrefixKey(Artifact artifact) {
483
// use the hashcode as a uuid for the artifact (fingers crossed)
484
int hashCode = artifact.getId().hashCode();
485
// use just some visual cue
486
return "artifact:" + artifact.getName() + "#" + artifact.getType() + "#"
487
+ artifact.getExt() + "#" + hashCode;
491
* Returns the key used to identify the location of the artifact.
494
* the artifact to generate the key from. Cannot be null.
495
* @return the key to be used to reference the artifact location.
497
private String getLocationKey(Artifact artifact) {
498
String prefix = getPrefixKey(artifact);
499
return prefix + ".location";
503
* Returns the key used to identify if the artifact is local.
506
* the artifact to generate the key from. Cannot be null.
507
* @return the key to be used to reference the artifact location.
509
private String getIsLocalKey(Artifact artifact) {
510
String prefix = getPrefixKey(artifact);
511
return prefix + ".is-local";
514
private PropertiesFile getCachedDataFile(ModuleDescriptor md) {
515
return getCachedDataFile(md.getResolvedModuleRevisionId());
518
private PropertiesFile getCachedDataFile(ModuleRevisionId mRevId) {
519
return new PropertiesFile(new File(getRepositoryCacheRoot(),
520
IvyPatternHelper.substitute(
521
getDataFilePattern(), mRevId)), "ivy cached data file for " + mRevId);
524
public ResolvedModuleRevision findModuleInCache(
525
DependencyDescriptor dd, ModuleRevisionId requestedRevisionId,
526
CacheMetadataOptions options, String expectedResolver) {
527
ModuleRevisionId mrid = requestedRevisionId;
528
if (isCheckmodified(dd, requestedRevisionId, options)) {
529
Message.verbose("don't use cache for " + mrid + ": checkModified=true");
532
if (isChanging(dd, requestedRevisionId, options)) {
533
Message.verbose("don't use cache for " + mrid + ": changing=true");
536
return doFindModuleInCache(mrid, options, expectedResolver);
539
private ResolvedModuleRevision doFindModuleInCache(
540
ModuleRevisionId mrid, CacheMetadataOptions options, String expectedResolver) {
541
if (!lockMetadataArtifact(mrid)) {
542
Message.error("impossible to acquire lock for " + mrid);
546
if (settings.getVersionMatcher().isDynamic(mrid)) {
547
String resolvedRevision = getResolvedRevision(mrid, options);
548
if (resolvedRevision != null) {
549
Message.verbose("found resolved revision in cache: "
550
+ mrid + " => " + resolvedRevision);
551
mrid = ModuleRevisionId.newInstance(mrid, resolvedRevision);
557
File ivyFile = getIvyFileInCache(mrid);
558
if (ivyFile.exists()) {
561
XmlModuleDescriptorParser parser = XmlModuleDescriptorParser.getInstance();
562
ModuleDescriptor depMD = getMdFromCache(parser, options, ivyFile);
563
String resolverName = getSavedResolverName(depMD);
564
String artResolverName = getSavedArtResolverName(depMD);
565
DependencyResolver resolver = settings.getResolver(resolverName);
566
if (resolver == null) {
567
Message.debug("\tresolver not found: " + resolverName
568
+ " => trying to use the one configured for " + mrid);
569
resolver = settings.getResolver(depMD.getResolvedModuleRevisionId());
570
if (resolver != null) {
571
Message.debug("\tconfigured resolver found for "
572
+ depMD.getResolvedModuleRevisionId() + ": "
573
+ resolver.getName() + ": saving this data");
574
saveResolver(depMD, resolver.getName());
577
DependencyResolver artResolver = settings.getResolver(artResolverName);
578
if (artResolver == null) {
579
artResolver = resolver;
581
if (resolver != null) {
582
Message.debug("\tfound ivy file in cache for " + mrid + " (resolved by "
583
+ resolver.getName() + "): " + ivyFile);
584
if (expectedResolver == null
585
|| expectedResolver.equals(resolver.getName())) {
586
MetadataArtifactDownloadReport madr
587
= new MetadataArtifactDownloadReport(
588
depMD.getMetadataArtifact());
589
madr.setDownloadStatus(DownloadStatus.NO);
590
madr.setSearched(false);
591
madr.setLocalFile(ivyFile);
592
madr.setSize(ivyFile.length());
593
madr.setArtifactOrigin(
594
getSavedArtifactOrigin(depMD.getMetadataArtifact()));
595
return new ResolvedModuleRevision(
596
resolver, artResolver, depMD, madr);
599
"found module in cache but with a different resolver: "
600
+ "discarding: " + mrid
601
+ "; expected resolver=" + expectedResolver
602
+ "; resolver=" + resolver.getName());
605
Message.debug("\tresolver not found: " + resolverName
606
+ " => cannot use cached ivy file for " + mrid);
608
} catch (Exception e) {
609
// will try with resolver
610
Message.debug("\tproblem while parsing cached ivy file for: " + mrid + ": "
614
Message.debug("\tno ivy file in cache for " + mrid + ": tried " + ivyFile);
617
unlockMetadataArtifact(mrid);
623
private class MyModuleDescriptorProvider implements ModuleDescriptorProvider {
625
private final ModuleDescriptorParser mdParser;
627
public MyModuleDescriptorProvider(ModuleDescriptorParser mdParser) {
628
this.mdParser = mdParser;
631
public ModuleDescriptor provideModule(ParserSettings ivySettings,
632
File descriptorURL, boolean validate) throws ParseException, IOException {
633
return mdParser.parseDescriptor(ivySettings, descriptorURL.toURI().toURL(), validate);
637
private ModuleDescriptor getMdFromCache(XmlModuleDescriptorParser mdParser,
638
CacheMetadataOptions options, File ivyFile)
639
throws ParseException, IOException {
640
ModuleDescriptorMemoryCache cache = getMemoryCache();
641
ModuleDescriptorProvider mdProvider = new MyModuleDescriptorProvider(mdParser);
642
return cache.get(ivyFile, settings, options.isValidate(), mdProvider);
645
private ModuleDescriptor getStaledMd(ModuleDescriptorParser mdParser,
646
CacheMetadataOptions options, File ivyFile)
647
throws ParseException, IOException {
648
ModuleDescriptorMemoryCache cache = getMemoryCache();
649
ModuleDescriptorProvider mdProvider = new MyModuleDescriptorProvider(mdParser);
650
return cache.getStale(ivyFile, settings, options.isValidate(), mdProvider);
654
private String getResolvedRevision(ModuleRevisionId mrid, CacheMetadataOptions options) {
655
if (!lockMetadataArtifact(mrid)) {
656
Message.error("impossible to acquire lock for " + mrid);
660
String resolvedRevision = null;
661
if (options.isForce()) {
662
Message.verbose("refresh mode: no check for cached resolved revision for " + mrid);
665
PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
666
String expiration = cachedResolvedRevision.getProperty("expiration.time");
667
if (expiration == null) {
668
Message.verbose("no cached resolved revision for " + mrid);
671
if (System.currentTimeMillis() > Long.parseLong(expiration)) {
672
Message.verbose("cached resolved revision expired for " + mrid);
675
resolvedRevision = cachedResolvedRevision.getProperty("resolved.revision");
676
if (resolvedRevision == null) {
677
Message.verbose("no cached resolved revision value for " + mrid);
680
return resolvedRevision;
682
unlockMetadataArtifact(mrid);
686
private void saveResolvedRevision(ModuleRevisionId mrid, String revision) {
687
if (!lockMetadataArtifact(mrid)) {
688
Message.error("impossible to acquire lock for " + mrid);
692
PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
693
cachedResolvedRevision.setProperty("expiration.time", getExpiration(mrid));
694
cachedResolvedRevision.setProperty("resolved.revision", revision);
695
cachedResolvedRevision.save();
697
unlockMetadataArtifact(mrid);
701
private String getExpiration(ModuleRevisionId mrid) {
702
return String.valueOf(System.currentTimeMillis() + getTTL(mrid));
705
public long getTTL(ModuleRevisionId mrid) {
706
Long ttl = (Long) ttlRules.getRule(mrid);
707
return ttl == null ? getDefaultTTL() : ttl.longValue();
710
public String toString() {
714
public File getRepositoryCacheRoot() {
718
public LockStrategy getLockStrategy() {
719
if (lockStrategy == null) {
720
if (lockStrategyName != null) {
721
lockStrategy = settings.getLockStrategy(lockStrategyName);
723
lockStrategy = settings.getDefaultLockStrategy();
729
public void setLockStrategy(LockStrategy lockStrategy) {
730
this.lockStrategy = lockStrategy;
733
public void setLockStrategy(String lockStrategyName) {
734
this.lockStrategyName = lockStrategyName;
737
public ArtifactDownloadReport download(
739
ArtifactResourceResolver resourceResolver,
740
ResourceDownloader resourceDownloader,
741
CacheDownloadOptions options) {
742
final ArtifactDownloadReport adr = new ArtifactDownloadReport(artifact);
743
boolean useOrigin = isUseOrigin();
745
// TODO: see if we could lock on the artifact to download only, instead of the module
746
// metadata artifact. We'd need to store artifact origin and is local in artifact specific
747
// file to do so, or lock the metadata artifact only to update artifact origin, which would
748
// mean acquiring nested locks, which can be a dangerous thing
749
ModuleRevisionId mrid = artifact.getModuleRevisionId();
750
if (!lockMetadataArtifact(mrid)) {
751
adr.setDownloadStatus(DownloadStatus.FAILED);
752
adr.setDownloadDetails("impossible to get lock for " + mrid);
756
DownloadListener listener = options.getListener();
757
if (listener != null) {
758
listener.needArtifact(this, artifact);
760
ArtifactOrigin origin = getSavedArtifactOrigin(artifact);
761
// if we can use origin file, we just ask ivy for the file in cache, and it will
762
// return the original one if possible. If we are not in useOrigin mode, we use the
763
// getArchivePath method which always return a path in the actual cache
764
File archiveFile = getArchiveFileInCache(artifact, origin, useOrigin);
766
if (archiveFile.exists() && !options.isForce()) {
767
adr.setDownloadStatus(DownloadStatus.NO);
768
adr.setSize(archiveFile.length());
769
adr.setArtifactOrigin(origin);
770
adr.setLocalFile(archiveFile);
772
long start = System.currentTimeMillis();
774
ResolvedResource artifactRef = resourceResolver.resolve(artifact);
775
if (artifactRef != null) {
776
origin = new ArtifactOrigin(
778
artifactRef.getResource().isLocal(),
779
artifactRef.getResource().getName());
780
if (useOrigin && artifactRef.getResource().isLocal()) {
781
saveArtifactOrigin(artifact, origin);
782
archiveFile = getArchiveFileInCache(artifact, origin);
783
adr.setDownloadStatus(DownloadStatus.NO);
784
adr.setSize(archiveFile.length());
785
adr.setArtifactOrigin(origin);
786
adr.setLocalFile(archiveFile);
788
// refresh archive file now that we better now its origin
789
archiveFile = getArchiveFileInCache(artifact, origin, useOrigin);
790
if (ResourceHelper.equals(artifactRef.getResource(), archiveFile)) {
791
throw new IllegalStateException("invalid settings for '"
793
+ "': pointing repository to ivy cache is forbidden !");
795
if (listener != null) {
796
listener.startArtifactDownload(this, artifactRef, artifact, origin);
799
resourceDownloader.download(
800
artifact, artifactRef.getResource(), archiveFile);
801
adr.setSize(archiveFile.length());
802
saveArtifactOrigin(artifact, origin);
803
adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
804
adr.setDownloadStatus(DownloadStatus.SUCCESSFUL);
805
adr.setArtifactOrigin(origin);
806
adr.setLocalFile(archiveFile);
809
adr.setDownloadStatus(DownloadStatus.FAILED);
810
adr.setDownloadDetails(ArtifactDownloadReport.MISSING_ARTIFACT);
811
adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
813
} catch (Exception ex) {
814
adr.setDownloadStatus(DownloadStatus.FAILED);
815
adr.setDownloadDetails(ex.getMessage());
816
adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
819
if (listener != null) {
820
listener.endArtifactDownload(this, artifact, adr, archiveFile);
824
unlockMetadataArtifact(mrid);
828
public void originalToCachedModuleDescriptor(
829
DependencyResolver resolver, ResolvedResource orginalMetadataRef,
830
Artifact requestedMetadataArtifact,
831
ResolvedModuleRevision rmr, ModuleDescriptorWriter writer) {
832
ModuleDescriptor md = rmr.getDescriptor();
833
Artifact originalMetadataArtifact = getOriginalMetadataArtifact(requestedMetadataArtifact);
834
File mdFileInCache = getIvyFileInCache(md.getResolvedModuleRevisionId());
836
ModuleRevisionId mrid = requestedMetadataArtifact.getModuleRevisionId();
837
if (!lockMetadataArtifact(mrid)) {
838
Message.warn("impossible to acquire lock for: " + mrid);
842
File originalFileInCache = getArchiveFileInCache(originalMetadataArtifact);
843
writer.write(orginalMetadataRef, md,
847
saveResolvers(md, resolver.getName(), resolver.getName());
849
if (getSettings().getVersionMatcher().isDynamic(md.getModuleRevisionId())
850
&& getTTL(md.getModuleRevisionId()) > 0) {
851
saveResolvedRevision(md.getModuleRevisionId(), rmr.getId().getRevision());
854
if (!md.isDefault()) {
855
rmr.getReport().setOriginalLocalFile(originalFileInCache);
857
rmr.getReport().setLocalFile(mdFileInCache);
858
} catch (RuntimeException e) {
860
} catch (Exception e) {
861
Message.warn("impossible to put metadata file in cache: "
862
+ (orginalMetadataRef == null
863
? String.valueOf(md.getResolvedModuleRevisionId())
864
: String.valueOf(orginalMetadataRef))
865
+ ". " + e.getClass().getName() + ": " + e.getMessage());
867
unlockMetadataArtifact(mrid);
871
public ResolvedModuleRevision cacheModuleDescriptor(
872
DependencyResolver resolver, final ResolvedResource mdRef, DependencyDescriptor dd,
873
Artifact moduleArtifact, ResourceDownloader downloader, CacheMetadataOptions options)
874
throws ParseException {
875
ModuleDescriptorParser parser = ModuleDescriptorParserRegistry
876
.getInstance().getParser(mdRef.getResource());
877
Date cachedPublicationDate = null;
878
ArtifactDownloadReport report;
879
ModuleRevisionId mrid = moduleArtifact.getModuleRevisionId();
880
Artifact originalMetadataArtifact = getOriginalMetadataArtifact(moduleArtifact);
881
if (!lockMetadataArtifact(mrid)) {
882
Message.error("impossible to acquire lock for " + mrid);
886
// now let's see if we can find it in cache and if it is up to date
887
ResolvedModuleRevision rmr = doFindModuleInCache(mrid, options, null);
889
if (rmr.getDescriptor().isDefault() && rmr.getResolver() != resolver) {
890
Message.verbose("\t" + getName() + ": found revision in cache: " + mrid
891
+ " (resolved by " + rmr.getResolver().getName()
892
+ "): but it's a default one, maybe we can find a better one");
894
if (!isCheckmodified(dd, mrid, options) && !isChanging(dd, mrid, options)) {
895
Message.verbose("\t" + getName() + ": revision in cache: " + mrid);
896
rmr.getReport().setSearched(true);
899
long repLastModified = mdRef.getLastModified();
900
long cacheLastModified = rmr.getDescriptor().getLastModified();
901
if (!rmr.getDescriptor().isDefault() && repLastModified <= cacheLastModified) {
902
Message.verbose("\t" + getName() + ": revision in cache (not updated): "
904
rmr.getReport().setSearched(true);
907
Message.verbose("\t" + getName() + ": revision in cache is not up to date: "
909
if (isChanging(dd, mrid, options)) {
910
// ivy file has been updated, we should see if it has a new publication
911
// date to see if a new download is required (in case the dependency is
913
cachedPublicationDate =
914
rmr.getDescriptor().getResolvedPublicationDate();
920
// now download module descriptor and parse it
922
originalMetadataArtifact,
923
new ArtifactResourceResolver() {
924
public ResolvedResource resolve(Artifact artifact) {
928
new CacheDownloadOptions().setListener(options.getListener()).setForce(true));
929
Message.verbose("\t" + report);
931
if (report.getDownloadStatus() == DownloadStatus.FAILED) {
932
Message.warn("problem while downloading module descriptor: " + mdRef.getResource()
933
+ ": " + report.getDownloadDetails()
934
+ " (" + report.getDownloadTimeMillis() + "ms)");
939
ModuleDescriptor md = getStaledMd(parser, options, report.getLocalFile());
941
throw new IllegalStateException(
942
"module descriptor parser returned a null module descriptor, "
943
+ "which is not allowed. "
945
+ "; parser class=" + parser.getClass().getName()
946
+ "; module descriptor resource=" + mdRef.getResource());
948
Message.debug("\t" + getName() + ": parsed downloaded md file for " + mrid
949
+ "; parsed=" + md.getModuleRevisionId());
951
// check if we should delete old artifacts
952
boolean deleteOldArtifacts = false;
953
if (cachedPublicationDate != null
954
&& !cachedPublicationDate.equals(md.getResolvedPublicationDate())) {
955
// artifacts have changed, they should be downloaded again
956
Message.verbose(mrid + " has changed: deleting old artifacts");
957
deleteOldArtifacts = true;
959
if (deleteOldArtifacts) {
960
String[] confs = md.getConfigurationsNames();
961
for (int i = 0; i < confs.length; i++) {
962
Artifact[] arts = md.getArtifacts(confs[i]);
963
for (int j = 0; j < arts.length; j++) {
964
Artifact transformedArtifact = NameSpaceHelper.transform(
965
arts[j], options.getNamespace().getToSystemTransformer());
966
ArtifactOrigin origin = getSavedArtifactOrigin(
967
transformedArtifact);
968
File artFile = getArchiveFileInCache(
969
transformedArtifact, origin, false);
970
if (artFile.exists()) {
971
Message.debug("deleting " + artFile);
974
removeSavedArtifactOrigin(transformedArtifact);
977
} else if (isChanging(dd, mrid, options)) {
979
+ " is changing, but has not changed: will trust cached artifacts if any");
982
MetadataArtifactDownloadReport madr
983
= new MetadataArtifactDownloadReport(md.getMetadataArtifact());
984
madr.setSearched(true);
985
madr.setDownloadStatus(report.getDownloadStatus());
986
madr.setDownloadDetails(report.getDownloadDetails());
987
madr.setArtifactOrigin(report.getArtifactOrigin());
988
madr.setDownloadTimeMillis(report.getDownloadTimeMillis());
989
madr.setOriginalLocalFile(report.getLocalFile());
990
madr.setSize(report.getSize());
991
saveArtifactOrigin(md.getMetadataArtifact(), report.getArtifactOrigin());
993
return new ResolvedModuleRevision(resolver, resolver, md, madr);
994
} catch (IOException ex) {
995
Message.warn("io problem while parsing ivy file: " + mdRef.getResource() + ": "
1000
unlockMetadataArtifact(mrid);
1005
// lock used to lock all metadata related information access
1006
private boolean lockMetadataArtifact(ModuleRevisionId mrid) {
1007
Artifact artifact = getDefaultMetadataArtifact(mrid);
1009
// we need to provide an artifact origin to be sure we do not end up in a stack overflow
1010
// if the cache pattern is using original name, and the substitution thus trying to get
1011
// the saved artifact origin value which in turns calls this method
1012
return getLockStrategy().lockArtifact(artifact,
1013
getArchiveFileInCache(artifact, getDefaultMetadataArtifactOrigin(mrid)));
1014
} catch (InterruptedException e) {
1015
Thread.currentThread().interrupt(); // reset interrupt status
1016
throw new RuntimeException("operation interrupted");
1020
private void unlockMetadataArtifact(ModuleRevisionId mrid) {
1021
Artifact artifact = getDefaultMetadataArtifact(mrid);
1022
getLockStrategy().unlockArtifact(artifact,
1023
getArchiveFileInCache(artifact, getDefaultMetadataArtifactOrigin(mrid)));
1027
private ArtifactOrigin getDefaultMetadataArtifactOrigin(ModuleRevisionId mrid) {
1028
// it's important to say the origin is not local to make sure it won't ever be used for
1029
// anything else than original token
1030
return new ArtifactOrigin(
1031
DefaultArtifact.newIvyArtifact(mrid, null), false, getIvyFileInCache(mrid).getPath());
1034
private Artifact getDefaultMetadataArtifact(ModuleRevisionId mrid) {
1035
return new DefaultArtifact(mrid, new Date(), "metadata", "metadata", "ivy", true);
1038
// not used any more, but maybe useful for finer grain locking when downloading artifacts
1039
// private boolean lockArtifact(Artifact artifact) {
1041
// return getLockStrategy().lockArtifact(artifact,
1042
// getArchiveFileInCache(artifact, null));
1043
// } catch (InterruptedException e) {
1044
// Thread.currentThread().interrupt(); // reset interrupt status
1045
// throw new RuntimeException("operation interrupted");
1049
// private void unlockArtifact(Artifact artifact) {
1050
// getLockStrategy().unlockArtifact(artifact, getArchiveFileInCache(artifact, null));
1053
public Artifact getOriginalMetadataArtifact(Artifact moduleArtifact) {
1054
return DefaultArtifact.cloneWithAnotherType(
1055
moduleArtifact, moduleArtifact.getType() + ".original");
1059
private boolean isOriginalMetadataArtifact(Artifact artifact) {
1060
return artifact.isMetadata()
1061
&& artifact.getType().endsWith(".original");
1064
private boolean isChanging(
1065
DependencyDescriptor dd, ModuleRevisionId requestedRevisionId,
1066
CacheMetadataOptions options) {
1067
return dd.isChanging()
1068
|| getChangingMatcher(options).matches(requestedRevisionId.getRevision());
1071
private Matcher getChangingMatcher(CacheMetadataOptions options) {
1072
String changingPattern = options.getChangingPattern() != null
1073
? options.getChangingPattern() : this.changingPattern;
1074
if (changingPattern == null) {
1075
return NoMatcher.INSTANCE;
1077
String changingMatcherName = options.getChangingMatcherName() != null
1078
? options.getChangingMatcherName() : this.changingMatcherName;
1079
PatternMatcher matcher = settings.getMatcher(changingMatcherName);
1080
if (matcher == null) {
1081
throw new IllegalStateException("unknown matcher '" + changingMatcherName
1082
+ "'. It is set as changing matcher in " + this);
1084
return matcher.getMatcher(changingPattern);
1087
private boolean isCheckmodified(
1088
DependencyDescriptor dd, ModuleRevisionId requestedRevisionId,
1089
CacheMetadataOptions options) {
1090
if (options.isCheckmodified() != null) {
1091
return options.isCheckmodified().booleanValue();
1093
return isCheckmodified();
1096
public void clean() {
1097
FileUtil.forceDelete(getBasedir());
1100
public void dumpSettings() {
1101
Message.verbose("\t" + getName());
1102
Message.debug("\t\tivyPattern: " + getIvyPattern());
1103
Message.debug("\t\tartifactPattern: " + getArtifactPattern());
1104
Message.debug("\t\tlockingStrategy: " + getLockStrategy().getName());
1105
Message.debug("\t\tchangingPattern: " + getChangingPattern());
1106
Message.debug("\t\tchangingMatcher: " + getChangingMatcherName());