0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: */
0018: package org.apache.ivy.core.cache;
0019:
0020: import java.io.File;
0021: import java.io.IOException;
0022: import java.net.MalformedURLException;
0023: import java.net.URL;
0024: import java.text.ParseException;
0025: import java.util.Date;
0026: import java.util.Map;
0027: import java.util.regex.Pattern;
0028:
0029: import org.apache.ivy.core.IvyPatternHelper;
0030: import org.apache.ivy.core.module.descriptor.Artifact;
0031: import org.apache.ivy.core.module.descriptor.DefaultArtifact;
0032: import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
0033: import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
0034: import org.apache.ivy.core.module.id.ModuleRevisionId;
0035: import org.apache.ivy.core.module.id.ModuleRules;
0036: import org.apache.ivy.core.report.ArtifactDownloadReport;
0037: import org.apache.ivy.core.report.DownloadStatus;
0038: import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
0039: import org.apache.ivy.core.resolve.ResolvedModuleRevision;
0040: import org.apache.ivy.core.settings.IvySettings;
0041: import org.apache.ivy.plugins.IvySettingsAware;
0042: import org.apache.ivy.plugins.lock.LockStrategy;
0043: import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
0044: import org.apache.ivy.plugins.matcher.MapMatcher;
0045: import org.apache.ivy.plugins.matcher.Matcher;
0046: import org.apache.ivy.plugins.matcher.NoMatcher;
0047: import org.apache.ivy.plugins.matcher.PatternMatcher;
0048: import org.apache.ivy.plugins.namespace.NameSpaceHelper;
0049: import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
0050: import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
0051: import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
0052: import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
0053: import org.apache.ivy.plugins.repository.ResourceDownloader;
0054: import org.apache.ivy.plugins.repository.ResourceHelper;
0055: import org.apache.ivy.plugins.resolver.DependencyResolver;
0056: import org.apache.ivy.plugins.resolver.util.ResolvedResource;
0057: import org.apache.ivy.util.FileUtil;
0058: import org.apache.ivy.util.Message;
0059: import org.apache.ivy.util.PropertiesFile;
0060:
0061: public class DefaultRepositoryCacheManager implements
0062: RepositoryCacheManager, IvySettingsAware {
0063: private static final String DEFAULT_ARTIFACT_PATTERN = "[organisation]/[module]/[type]s/[artifact]-[revision](.[ext])";
0064:
0065: private static final String DEFAULT_DATA_FILE_PATTERN = "[organisation]/[module]/ivydata-[revision].properties";
0066:
0067: private static final String DEFAULT_IVY_PATTERN = "[organisation]/[module]/ivy-[revision].xml";
0068:
0069: private IvySettings settings;
0070:
0071: private File basedir;
0072:
0073: private LockStrategy lockStrategy;
0074:
0075: private String name;
0076:
0077: private String ivyPattern;
0078:
0079: private String dataFilePattern = DEFAULT_DATA_FILE_PATTERN;
0080:
0081: private String artifactPattern;
0082:
0083: private String lockStrategyName;
0084:
0085: private String changingPattern;
0086:
0087: private String changingMatcherName = PatternMatcher.EXACT_OR_REGEXP;
0088:
0089: private Boolean checkmodified;
0090:
0091: private Boolean useOrigin;
0092:
0093: private ModuleRules/*<Long>*/ttlRules = new ModuleRules();
0094:
0095: private Long defaultTTL = null;
0096:
0097: public DefaultRepositoryCacheManager() {
0098: }
0099:
0100: public DefaultRepositoryCacheManager(String name,
0101: IvySettings settings, File basedir) {
0102: setName(name);
0103: setSettings(settings);
0104: setBasedir(basedir);
0105: }
0106:
0107: public IvySettings getSettings() {
0108: return settings;
0109: }
0110:
0111: public void setSettings(IvySettings settings) {
0112: this .settings = settings;
0113: }
0114:
0115: public File getIvyFileInCache(ModuleRevisionId mrid) {
0116: String file = IvyPatternHelper.substitute(getIvyPattern(),
0117: DefaultArtifact.newIvyArtifact(mrid, null));
0118: return new File(getRepositoryCacheRoot(), file);
0119: }
0120:
0121: public String getIvyPattern() {
0122: if (ivyPattern == null) {
0123: if (settings != null) {
0124: ivyPattern = settings.getDefaultCacheIvyPattern();
0125: }
0126: if (ivyPattern == null) {
0127: ivyPattern = DEFAULT_IVY_PATTERN;
0128: }
0129: }
0130: return ivyPattern;
0131: }
0132:
0133: public String getArtifactPattern() {
0134: if (artifactPattern == null) {
0135: if (settings != null) {
0136: artifactPattern = settings
0137: .getDefaultCacheArtifactPattern();
0138: }
0139: if (artifactPattern == null) {
0140: artifactPattern = DEFAULT_ARTIFACT_PATTERN;
0141: }
0142: }
0143: return artifactPattern;
0144: }
0145:
0146: public void setArtifactPattern(String artifactPattern) {
0147: this .artifactPattern = artifactPattern;
0148: }
0149:
0150: public File getBasedir() {
0151: if (basedir == null) {
0152: basedir = settings.getDefaultRepositoryCacheBasedir();
0153: }
0154: return basedir;
0155: }
0156:
0157: public void setBasedir(File cache) {
0158: this .basedir = cache;
0159: }
0160:
0161: public long getDefaultTTL() {
0162: if (defaultTTL == null) {
0163: defaultTTL = new Long(parseDuration(settings
0164: .getVariable("ivy.cache.ttl.default")));
0165: }
0166: return defaultTTL.longValue();
0167: }
0168:
0169: public void setDefaultTTL(long defaultTTL) {
0170: this .defaultTTL = new Long(defaultTTL);
0171: }
0172:
0173: public void setDefaultTTL(String defaultTTL) {
0174: this .defaultTTL = new Long(parseDuration(defaultTTL));
0175: }
0176:
0177: public String getDataFilePattern() {
0178: return dataFilePattern;
0179: }
0180:
0181: public void setDataFilePattern(String dataFilePattern) {
0182: this .dataFilePattern = dataFilePattern;
0183: }
0184:
0185: public void setIvyPattern(String ivyPattern) {
0186: this .ivyPattern = ivyPattern;
0187: }
0188:
0189: public String getName() {
0190: return name;
0191: }
0192:
0193: public void setName(String name) {
0194: this .name = name;
0195: }
0196:
0197: public String getChangingMatcherName() {
0198: return changingMatcherName;
0199: }
0200:
0201: public void setChangingMatcher(String changingMatcherName) {
0202: this .changingMatcherName = changingMatcherName;
0203: }
0204:
0205: public String getChangingPattern() {
0206: return changingPattern;
0207: }
0208:
0209: public void setChangingPattern(String changingPattern) {
0210: this .changingPattern = changingPattern;
0211: }
0212:
0213: public void addTTL(Map attributes, PatternMatcher matcher,
0214: long duration) {
0215: ttlRules.defineRule(new MapMatcher(attributes, matcher),
0216: new Long(duration));
0217: }
0218:
0219: public void addConfiguredTtl(Map/*<String,String>*/attributes) {
0220: String duration = (String) attributes.remove("duration");
0221: if (duration == null) {
0222: throw new IllegalArgumentException(
0223: "'duration' attribute is mandatory for ttl");
0224: }
0225: String matcher = (String) attributes.remove("matcher");
0226: addTTL(attributes,
0227: matcher == null ? ExactPatternMatcher.INSTANCE
0228: : settings.getMatcher(matcher),
0229: parseDuration(duration));
0230: }
0231:
0232: private static final Pattern DURATION_PATTERN = Pattern
0233: .compile("(?:(\\d+)d)? ?(?:(\\d+)h)? ?(?:(\\d+)m)? ?(?:(\\d+)s)? ?(?:(\\d+)ms)?");
0234:
0235: private static final int MILLIS_IN_SECONDS = 1000;
0236: private static final int MILLIS_IN_MINUTES = 60 * MILLIS_IN_SECONDS;
0237: private static final int MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTES;
0238: private static final int MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR;
0239:
0240: private long parseDuration(String duration) {
0241: if (duration == null) {
0242: return 0;
0243: }
0244: java.util.regex.Matcher m = DURATION_PATTERN.matcher(duration);
0245: if (m.matches()) {
0246: //CheckStyle:MagicNumber| OFF
0247: int days = getGroupIntValue(m, 1);
0248: int hours = getGroupIntValue(m, 2);
0249: int minutes = getGroupIntValue(m, 3);
0250: int seconds = getGroupIntValue(m, 4);
0251: int millis = getGroupIntValue(m, 5);
0252: //CheckStyle:MagicNumber| ON
0253:
0254: return days * MILLIS_IN_DAY + hours * MILLIS_IN_HOUR
0255: + minutes * MILLIS_IN_MINUTES + seconds
0256: * MILLIS_IN_SECONDS + millis;
0257: } else {
0258: throw new IllegalArgumentException("invalid duration '"
0259: + duration + "': it must match "
0260: + DURATION_PATTERN.pattern());
0261: }
0262: }
0263:
0264: private int getGroupIntValue(java.util.regex.Matcher m,
0265: int groupNumber) {
0266: String g = m.group(groupNumber);
0267: return g == null || g.length() == 0 ? 0 : Integer.parseInt(g);
0268: }
0269:
0270: /**
0271: * True if this cache should check lastmodified date to know if ivy files are up to date.
0272: *
0273: * @return
0274: */
0275: public boolean isCheckmodified() {
0276: if (checkmodified == null) {
0277: if (getSettings() != null) {
0278: String check = getSettings().getVariable(
0279: "ivy.resolver.default.check.modified");
0280: return check != null ? Boolean.valueOf(check)
0281: .booleanValue() : false;
0282: } else {
0283: return false;
0284: }
0285: } else {
0286: return checkmodified.booleanValue();
0287: }
0288: }
0289:
0290: public void setCheckmodified(boolean check) {
0291: checkmodified = Boolean.valueOf(check);
0292: }
0293:
0294: /**
0295: * True if this cache should use artifacts original location when possible, false if they should
0296: * be copied to cache.
0297: */
0298: public boolean isUseOrigin() {
0299: if (useOrigin == null) {
0300: if (getSettings() != null) {
0301: return getSettings().isDefaultUseOrigin();
0302: } else {
0303: return false;
0304: }
0305: } else {
0306: return useOrigin.booleanValue();
0307: }
0308: }
0309:
0310: public void setUseOrigin(boolean b) {
0311: useOrigin = Boolean.valueOf(b);
0312: }
0313:
0314: /**
0315: * Returns a File object pointing to where the artifact can be found on the local file system.
0316: * This is usually in the cache, but it can be directly in the repository if it is local and if
0317: * the resolve has been done with useOrigin = true
0318: */
0319: public File getArchiveFileInCache(Artifact artifact) {
0320: ArtifactOrigin origin = getSavedArtifactOrigin(artifact);
0321: return getArchiveFileInCache(artifact, origin);
0322: }
0323:
0324: /**
0325: * Returns a File object pointing to where the artifact can be found on the local file system.
0326: * This is usually in the cache, but it can be directly in the repository if it is local and if
0327: * the resolve has been done with useOrigin = true
0328: */
0329: public File getArchiveFileInCache(Artifact artifact,
0330: ArtifactOrigin origin) {
0331: File archive = new File(getRepositoryCacheRoot(),
0332: getArchivePathInCache(artifact, origin));
0333: if (!archive.exists() && origin != null
0334: && origin != ArtifactOrigin.UNKNOWN && origin.isLocal()) {
0335: File original = new File(origin.getLocation());
0336: if (original.exists()) {
0337: return original;
0338: }
0339: }
0340: return archive;
0341: }
0342:
0343: /**
0344: * Returns a File object pointing to where the artifact can be found on the local file system,
0345: * using or not the original location depending on the availability of origin information
0346: * provided as parameter and the setting of useOrigin. If useOrigin is false, this method will
0347: * always return the file in the cache.
0348: */
0349: private File getArchiveFileInCache(Artifact artifact,
0350: ArtifactOrigin origin, boolean useOrigin) {
0351: if (useOrigin && origin != null
0352: && origin != ArtifactOrigin.UNKNOWN && origin.isLocal()) {
0353: return new File(origin.getLocation());
0354: } else {
0355: return new File(getRepositoryCacheRoot(),
0356: getArchivePathInCache(artifact, origin));
0357: }
0358: }
0359:
0360: public String getArchivePathInCache(Artifact artifact) {
0361: return IvyPatternHelper.substitute(getArtifactPattern(),
0362: artifact);
0363: }
0364:
0365: public String getArchivePathInCache(Artifact artifact,
0366: ArtifactOrigin origin) {
0367: return IvyPatternHelper.substitute(getArtifactPattern(),
0368: artifact, origin);
0369: }
0370:
0371: /**
0372: * Saves the information of which resolver was used to resolve a md, so that this info can be
0373: * retrieve later (even after a jvm restart) by getSavedResolverName(ModuleDescriptor md)
0374: *
0375: * @param md
0376: * the module descriptor resolved
0377: * @param name
0378: * resolver name
0379: */
0380: private void saveResolver(ModuleDescriptor md, String name) {
0381: // should always be called with a lock on module metadata artifact
0382: PropertiesFile cdf = getCachedDataFile(md);
0383: cdf.setProperty("resolver", name);
0384: cdf.save();
0385: }
0386:
0387: /**
0388: * Saves the information of which resolver was used to resolve a md, so that this info can be
0389: * retrieve later (even after a jvm restart) by getSavedArtResolverName(ModuleDescriptor md)
0390: *
0391: * @param md
0392: * the module descriptor resolved
0393: * @param name
0394: * artifact resolver name
0395: */
0396: public void saveResolvers(ModuleDescriptor md,
0397: String metadataResolverName, String artifactResolverName) {
0398: ModuleRevisionId mrid = md.getResolvedModuleRevisionId();
0399: if (!lockMetadataArtifact(mrid)) {
0400: Message.error("impossible to acquire lock for " + mrid);
0401: return;
0402: }
0403: try {
0404: PropertiesFile cdf = getCachedDataFile(md);
0405: cdf.setProperty("resolver", metadataResolverName);
0406: cdf.setProperty("artifact.resolver", artifactResolverName);
0407: cdf.save();
0408: } finally {
0409: unlockMetadataArtifact(mrid);
0410: }
0411: }
0412:
0413: private String getSavedResolverName(ModuleDescriptor md) {
0414: // should always be called with a lock on module metadata artifact
0415: PropertiesFile cdf = getCachedDataFile(md);
0416: return cdf.getProperty("resolver");
0417: }
0418:
0419: private String getSavedArtResolverName(ModuleDescriptor md) {
0420: // should always be called with a lock on module metadata artifact
0421: PropertiesFile cdf = getCachedDataFile(md);
0422: return cdf.getProperty("artifact.resolver");
0423: }
0424:
0425: void saveArtifactOrigin(Artifact artifact, ArtifactOrigin origin) {
0426: // should always be called with a lock on module metadata artifact
0427: PropertiesFile cdf = getCachedDataFile(artifact
0428: .getModuleRevisionId());
0429: cdf.setProperty(getIsLocalKey(artifact), String.valueOf(origin
0430: .isLocal()));
0431: cdf.setProperty(getLocationKey(artifact), origin.getLocation());
0432: cdf.save();
0433: }
0434:
0435: private void removeSavedArtifactOrigin(Artifact artifact) {
0436: // should always be called with a lock on module metadata artifact
0437: PropertiesFile cdf = getCachedDataFile(artifact
0438: .getModuleRevisionId());
0439: cdf.remove(getLocationKey(artifact));
0440: cdf.remove(getIsLocalKey(artifact));
0441: cdf.save();
0442: }
0443:
0444: public ArtifactOrigin getSavedArtifactOrigin(Artifact artifact) {
0445: ModuleRevisionId mrid = artifact.getModuleRevisionId();
0446: if (!lockMetadataArtifact(mrid)) {
0447: Message.error("impossible to acquire lock for " + mrid);
0448: return ArtifactOrigin.UNKNOWN;
0449: }
0450: try {
0451: PropertiesFile cdf = getCachedDataFile(artifact
0452: .getModuleRevisionId());
0453: String location = cdf.getProperty(getLocationKey(artifact));
0454: String local = cdf.getProperty(getIsLocalKey(artifact));
0455: boolean isLocal = Boolean.valueOf(local).booleanValue();
0456:
0457: if (location == null) {
0458: // origin has not been specified, return null
0459: return ArtifactOrigin.UNKNOWN;
0460: }
0461:
0462: return new ArtifactOrigin(isLocal, location);
0463: } finally {
0464: unlockMetadataArtifact(mrid);
0465: }
0466: }
0467:
0468: /**
0469: * Creates the unique prefix key that will reference the artifact within the properties.
0470: *
0471: * @param artifact
0472: * the artifact to create the unique key from. Cannot be null.
0473: * @return the unique prefix key as a string.
0474: */
0475: private String getPrefixKey(Artifact artifact) {
0476: // use the hashcode as a uuid for the artifact (fingers crossed)
0477: int hashCode = artifact.getId().hashCode();
0478: // use just some visual cue
0479: return "artifact:" + artifact.getName() + "#"
0480: + artifact.getType() + "#" + artifact.getExt() + "#"
0481: + hashCode;
0482: }
0483:
0484: /**
0485: * Returns the key used to identify the location of the artifact.
0486: *
0487: * @param artifact
0488: * the artifact to generate the key from. Cannot be null.
0489: * @return the key to be used to reference the artifact location.
0490: */
0491: private String getLocationKey(Artifact artifact) {
0492: String prefix = getPrefixKey(artifact);
0493: return prefix + ".location";
0494: }
0495:
0496: /**
0497: * Returns the key used to identify if the artifact is local.
0498: *
0499: * @param artifact
0500: * the artifact to generate the key from. Cannot be null.
0501: * @return the key to be used to reference the artifact location.
0502: */
0503: private String getIsLocalKey(Artifact artifact) {
0504: String prefix = getPrefixKey(artifact);
0505: return prefix + ".is-local";
0506: }
0507:
0508: private PropertiesFile getCachedDataFile(ModuleDescriptor md) {
0509: return getCachedDataFile(md.getResolvedModuleRevisionId());
0510: }
0511:
0512: private PropertiesFile getCachedDataFile(ModuleRevisionId mRevId) {
0513: return new PropertiesFile(new File(getRepositoryCacheRoot(),
0514: IvyPatternHelper.substitute(getDataFilePattern(),
0515: mRevId)), "ivy cached data file for " + mRevId);
0516: }
0517:
0518: public ResolvedModuleRevision findModuleInCache(
0519: DependencyDescriptor dd, CacheMetadataOptions options,
0520: String expectedResolver) {
0521: ModuleRevisionId mrid = dd.getDependencyRevisionId();
0522: if (isCheckmodified(dd, options)) {
0523: Message.verbose("don't use cache for " + mrid
0524: + ": checkModified=true");
0525: return null;
0526: }
0527: if (isChanging(dd, options)) {
0528: Message.verbose("don't use cache for " + mrid
0529: + ": changing=true");
0530: return null;
0531: }
0532: return doFindModuleInCache(mrid, options, expectedResolver);
0533: }
0534:
0535: private ResolvedModuleRevision doFindModuleInCache(
0536: ModuleRevisionId mrid, CacheMetadataOptions options,
0537: String expectedResolver) {
0538: if (!lockMetadataArtifact(mrid)) {
0539: Message.error("impossible to acquire lock for " + mrid);
0540: return null;
0541: }
0542: try {
0543: if (settings.getVersionMatcher().isDynamic(mrid)) {
0544: String resolvedRevision = getResolvedRevision(mrid,
0545: options);
0546: if (resolvedRevision != null) {
0547: Message
0548: .verbose("found resolved revision in cache: "
0549: + mrid + " => " + resolvedRevision);
0550: mrid = ModuleRevisionId.newInstance(mrid,
0551: resolvedRevision);
0552: } else {
0553: return null;
0554: }
0555: }
0556:
0557: File ivyFile = getIvyFileInCache(mrid);
0558: if (ivyFile.exists()) {
0559: // found in cache !
0560: try {
0561: ModuleDescriptor depMD = XmlModuleDescriptorParser
0562: .getInstance().parseDescriptor(settings,
0563: ivyFile.toURL(),
0564: options.isValidate());
0565: String resolverName = getSavedResolverName(depMD);
0566: String artResolverName = getSavedArtResolverName(depMD);
0567: DependencyResolver resolver = settings
0568: .getResolver(resolverName);
0569: if (resolver == null) {
0570: Message
0571: .debug("\tresolver not found: "
0572: + resolverName
0573: + " => trying to use the one configured for "
0574: + mrid);
0575: resolver = settings.getResolver(depMD
0576: .getResolvedModuleRevisionId());
0577: if (resolver != null) {
0578: Message
0579: .debug("\tconfigured resolver found for "
0580: + depMD
0581: .getResolvedModuleRevisionId()
0582: + ": "
0583: + resolver.getName()
0584: + ": saving this data");
0585: saveResolver(depMD, resolver.getName());
0586: }
0587: }
0588: DependencyResolver artResolver = settings
0589: .getResolver(artResolverName);
0590: if (artResolver == null) {
0591: artResolver = resolver;
0592: }
0593: if (resolver != null) {
0594: Message.debug("\tfound ivy file in cache for "
0595: + mrid + " (resolved by "
0596: + resolver.getName() + "): " + ivyFile);
0597: if (expectedResolver == null
0598: || expectedResolver.equals(resolver
0599: .getName())) {
0600: MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(
0601: depMD.getMetadataArtifact());
0602: madr.setDownloadStatus(DownloadStatus.NO);
0603: madr.setSearched(false);
0604: madr.setLocalFile(ivyFile);
0605: madr.setSize(ivyFile.length());
0606: madr
0607: .setArtifactOrigin(getSavedArtifactOrigin(depMD
0608: .getMetadataArtifact()));
0609: return new ResolvedModuleRevision(resolver,
0610: artResolver, depMD, madr);
0611: } else {
0612: Message
0613: .debug("found module in cache but with a different resolver: "
0614: + "discarding: "
0615: + mrid
0616: + "; expected resolver="
0617: + expectedResolver
0618: + "; resolver="
0619: + resolver.getName());
0620: }
0621: } else {
0622: Message.debug("\tresolver not found: "
0623: + resolverName
0624: + " => cannot use cached ivy file for "
0625: + mrid);
0626: }
0627: } catch (Exception e) {
0628: // will try with resolver
0629: Message
0630: .debug("\tproblem while parsing cached ivy file for: "
0631: + mrid + ": " + e.getMessage());
0632: }
0633: } else {
0634: Message.debug("\tno ivy file in cache for " + mrid
0635: + ": tried " + ivyFile);
0636: }
0637: } finally {
0638: unlockMetadataArtifact(mrid);
0639: }
0640: return null;
0641: }
0642:
0643: private String getResolvedRevision(ModuleRevisionId mrid,
0644: CacheMetadataOptions options) {
0645: if (!lockMetadataArtifact(mrid)) {
0646: Message.error("impossible to acquire lock for " + mrid);
0647: return null;
0648: }
0649: try {
0650: String resolvedRevision = null;
0651: if (options.isForce()) {
0652: Message
0653: .verbose("refresh mode: no check for cached resolved revision for "
0654: + mrid);
0655: return null;
0656: }
0657: PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
0658: String expiration = cachedResolvedRevision
0659: .getProperty("expiration.time");
0660: if (expiration == null) {
0661: Message.verbose("no cached resolved revision for "
0662: + mrid);
0663: return null;
0664: }
0665: if (System.currentTimeMillis() > Long.parseLong(expiration)) {
0666: Message.verbose("cached resolved revision expired for "
0667: + mrid);
0668: return null;
0669: }
0670: resolvedRevision = cachedResolvedRevision
0671: .getProperty("resolved.revision");
0672: if (resolvedRevision == null) {
0673: Message
0674: .verbose("no cached resolved revision value for "
0675: + mrid);
0676: return null;
0677: }
0678: return resolvedRevision;
0679: } finally {
0680: unlockMetadataArtifact(mrid);
0681: }
0682: }
0683:
0684: private void saveResolvedRevision(ModuleRevisionId mrid,
0685: String revision) {
0686: if (!lockMetadataArtifact(mrid)) {
0687: Message.error("impossible to acquire lock for " + mrid);
0688: return;
0689: }
0690: try {
0691: PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
0692: cachedResolvedRevision.setProperty("expiration.time",
0693: getExpiration(mrid));
0694: cachedResolvedRevision.setProperty("resolved.revision",
0695: revision);
0696: cachedResolvedRevision.save();
0697: } finally {
0698: unlockMetadataArtifact(mrid);
0699: }
0700: }
0701:
0702: private String getExpiration(ModuleRevisionId mrid) {
0703: return String
0704: .valueOf(System.currentTimeMillis() + getTTL(mrid));
0705: }
0706:
0707: public long getTTL(ModuleRevisionId mrid) {
0708: Long ttl = (Long) ttlRules.getRule(mrid);
0709: return ttl == null ? getDefaultTTL() : ttl.longValue();
0710: }
0711:
0712: public String toString() {
0713: return name;
0714: }
0715:
0716: public File getRepositoryCacheRoot() {
0717: return getBasedir();
0718: }
0719:
0720: public LockStrategy getLockStrategy() {
0721: if (lockStrategy == null) {
0722: if (lockStrategyName != null) {
0723: lockStrategy = settings
0724: .getLockStrategy(lockStrategyName);
0725: } else {
0726: lockStrategy = settings.getDefaultLockStrategy();
0727: }
0728: }
0729: return lockStrategy;
0730: }
0731:
0732: public void setLockStrategy(LockStrategy lockStrategy) {
0733: this .lockStrategy = lockStrategy;
0734: }
0735:
0736: public void setLockStrategy(String lockStrategyName) {
0737: this .lockStrategyName = lockStrategyName;
0738: }
0739:
0740: public ArtifactDownloadReport download(Artifact artifact,
0741: ArtifactResourceResolver resourceResolver,
0742: ResourceDownloader resourceDownloader,
0743: CacheDownloadOptions options) {
0744: final ArtifactDownloadReport adr = new ArtifactDownloadReport(
0745: artifact);
0746: boolean useOrigin = isUseOrigin();
0747:
0748: // TODO: see if we could lock on the artifact to download only, instead of the module
0749: // metadata artifact. We'd need to store artifact origin and is local in artifact specific
0750: // file to do so, or lock the metadata artifact only to update artifact origin, which would
0751: // mean acquiring nested locks, which can be a dangerous thing
0752: ModuleRevisionId mrid = artifact.getModuleRevisionId();
0753: if (!lockMetadataArtifact(mrid)) {
0754: adr.setDownloadStatus(DownloadStatus.FAILED);
0755: adr
0756: .setDownloadDetails("impossible to get lock for "
0757: + mrid);
0758: return adr;
0759: }
0760: try {
0761: DownloadListener listener = options.getListener();
0762: if (listener != null) {
0763: listener.needArtifact(this , artifact);
0764: }
0765: ArtifactOrigin origin = getSavedArtifactOrigin(artifact);
0766: // if we can use origin file, we just ask ivy for the file in cache, and it will
0767: // return the original one if possible. If we are not in useOrigin mode, we use the
0768: // getArchivePath method which always return a path in the actual cache
0769: File archiveFile = getArchiveFileInCache(artifact, origin,
0770: useOrigin);
0771:
0772: if (archiveFile.exists() && !options.isForce()) {
0773: adr.setDownloadStatus(DownloadStatus.NO);
0774: adr.setSize(archiveFile.length());
0775: adr.setArtifactOrigin(origin);
0776: adr.setLocalFile(archiveFile);
0777: } else {
0778: long start = System.currentTimeMillis();
0779: try {
0780: ResolvedResource artifactRef = resourceResolver
0781: .resolve(artifact);
0782: if (artifactRef != null) {
0783: origin = new ArtifactOrigin(artifactRef
0784: .getResource().isLocal(), artifactRef
0785: .getResource().getName());
0786: if (useOrigin
0787: && artifactRef.getResource().isLocal()) {
0788: saveArtifactOrigin(artifact, origin);
0789: archiveFile = getArchiveFileInCache(
0790: artifact, origin);
0791: adr.setDownloadStatus(DownloadStatus.NO);
0792: adr.setSize(archiveFile.length());
0793: adr.setArtifactOrigin(origin);
0794: adr.setLocalFile(archiveFile);
0795: } else {
0796: // refresh archive file now that we better now its origin
0797: archiveFile = getArchiveFileInCache(
0798: artifact, origin, useOrigin);
0799: if (ResourceHelper.equals(artifactRef
0800: .getResource(), archiveFile)) {
0801: throw new IllegalStateException(
0802: "invalid settings for '"
0803: + resourceResolver
0804: + "': pointing repository to ivy cache is forbidden !");
0805: }
0806: if (listener != null) {
0807: listener.startArtifactDownload(this ,
0808: artifactRef, artifact, origin);
0809: }
0810:
0811: resourceDownloader.download(artifact,
0812: artifactRef.getResource(),
0813: archiveFile);
0814: adr.setSize(archiveFile.length());
0815: saveArtifactOrigin(artifact, origin);
0816: adr.setDownloadTimeMillis(System
0817: .currentTimeMillis()
0818: - start);
0819: adr
0820: .setDownloadStatus(DownloadStatus.SUCCESSFUL);
0821: adr.setArtifactOrigin(origin);
0822: adr.setLocalFile(archiveFile);
0823: }
0824: } else {
0825: adr.setDownloadStatus(DownloadStatus.FAILED);
0826: adr
0827: .setDownloadDetails(ArtifactDownloadReport.MISSING_ARTIFACT);
0828: adr.setDownloadTimeMillis(System
0829: .currentTimeMillis()
0830: - start);
0831: }
0832: } catch (Exception ex) {
0833: adr.setDownloadStatus(DownloadStatus.FAILED);
0834: adr.setDownloadDetails(ex.getMessage());
0835: adr.setDownloadTimeMillis(System
0836: .currentTimeMillis()
0837: - start);
0838: }
0839: }
0840: if (listener != null) {
0841: listener.endArtifactDownload(this , artifact, adr,
0842: archiveFile);
0843: }
0844: return adr;
0845: } finally {
0846: unlockMetadataArtifact(mrid);
0847: }
0848: }
0849:
0850: public void originalToCachedModuleDescriptor(
0851: DependencyResolver resolver,
0852: ResolvedResource orginalMetadataRef,
0853: Artifact requestedMetadataArtifact,
0854: ResolvedModuleRevision rmr, ModuleDescriptorWriter writer) {
0855: ModuleDescriptor md = rmr.getDescriptor();
0856: Artifact originalMetadataArtifact = getOriginalMetadataArtifact(requestedMetadataArtifact);
0857: File mdFileInCache = getIvyFileInCache(md
0858: .getResolvedModuleRevisionId());
0859:
0860: ModuleRevisionId mrid = requestedMetadataArtifact
0861: .getModuleRevisionId();
0862: if (!lockMetadataArtifact(mrid)) {
0863: Message.warn("impossible to acquire lock for: " + mrid);
0864: return;
0865: }
0866: try {
0867: File originalFileInCache = getArchiveFileInCache(originalMetadataArtifact);
0868: writer.write(orginalMetadataRef, md, originalFileInCache,
0869: mdFileInCache);
0870:
0871: saveResolvers(md, resolver.getName(), resolver.getName());
0872:
0873: if (getSettings().getVersionMatcher().isDynamic(
0874: md.getModuleRevisionId())
0875: && getTTL(md.getModuleRevisionId()) > 0) {
0876: saveResolvedRevision(md.getModuleRevisionId(), rmr
0877: .getId().getRevision());
0878: }
0879:
0880: if (!md.isDefault()) {
0881: rmr.getReport().setOriginalLocalFile(
0882: originalFileInCache);
0883: }
0884: rmr.getReport().setLocalFile(mdFileInCache);
0885: } catch (RuntimeException e) {
0886: throw e;
0887: } catch (Exception e) {
0888: Message.warn("impossible to put metadata file in cache: "
0889: + (orginalMetadataRef == null ? String.valueOf(md
0890: .getResolvedModuleRevisionId()) : String
0891: .valueOf(orginalMetadataRef)) + ". "
0892: + e.getClass().getName() + ": " + e.getMessage());
0893: } finally {
0894: unlockMetadataArtifact(mrid);
0895: }
0896: }
0897:
0898: public ResolvedModuleRevision cacheModuleDescriptor(
0899: DependencyResolver resolver, final ResolvedResource mdRef,
0900: DependencyDescriptor dd, Artifact moduleArtifact,
0901: ResourceDownloader downloader, CacheMetadataOptions options)
0902: throws ParseException {
0903: ModuleDescriptorParser parser = ModuleDescriptorParserRegistry
0904: .getInstance().getParser(mdRef.getResource());
0905: Date cachedPublicationDate = null;
0906: ArtifactDownloadReport report;
0907: ModuleRevisionId mrid = moduleArtifact.getModuleRevisionId();
0908: Artifact originalMetadataArtifact = getOriginalMetadataArtifact(moduleArtifact);
0909: if (!lockMetadataArtifact(mrid)) {
0910: Message.error("impossible to acquire lock for " + mrid);
0911: return null;
0912: }
0913: try {
0914: // now let's see if we can find it in cache and if it is up to date
0915: ResolvedModuleRevision rmr = doFindModuleInCache(mrid,
0916: options, null);
0917: if (rmr != null) {
0918: if (rmr.getDescriptor().isDefault()
0919: && rmr.getResolver() != resolver) {
0920: Message
0921: .verbose("\t"
0922: + getName()
0923: + ": found revision in cache: "
0924: + mrid
0925: + " (resolved by "
0926: + rmr.getResolver().getName()
0927: + "): but it's a default one, maybe we can find a better one");
0928: } else {
0929: if (!isCheckmodified(dd, options)
0930: && !isChanging(dd, options)) {
0931: Message.verbose("\t" + getName()
0932: + ": revision in cache: " + mrid);
0933: rmr.getReport().setSearched(true);
0934: return rmr;
0935: }
0936: long repLastModified = mdRef.getLastModified();
0937: long cacheLastModified = rmr.getDescriptor()
0938: .getLastModified();
0939: if (!rmr.getDescriptor().isDefault()
0940: && repLastModified <= cacheLastModified) {
0941: Message.verbose("\t" + getName()
0942: + ": revision in cache (not updated): "
0943: + mrid);
0944: rmr.getReport().setSearched(true);
0945: return rmr;
0946: } else {
0947: Message
0948: .verbose("\t"
0949: + getName()
0950: + ": revision in cache is not up to date: "
0951: + mrid);
0952: if (isChanging(dd, options)) {
0953: // ivy file has been updated, we should see if it has a new publication
0954: // date to see if a new download is required (in case the dependency is
0955: // a changing one)
0956: cachedPublicationDate = rmr.getDescriptor()
0957: .getResolvedPublicationDate();
0958: }
0959: }
0960: }
0961: }
0962:
0963: // now download module descriptor and parse it
0964: report = download(originalMetadataArtifact,
0965: new ArtifactResourceResolver() {
0966: public ResolvedResource resolve(
0967: Artifact artifact) {
0968: return mdRef;
0969: }
0970: }, downloader, new CacheDownloadOptions()
0971: .setListener(options.getListener())
0972: .setForce(true));
0973: Message.verbose("\t" + report);
0974:
0975: if (report.getDownloadStatus() == DownloadStatus.FAILED) {
0976: Message
0977: .warn("problem while downloading module descriptor: "
0978: + mdRef.getResource()
0979: + ": "
0980: + report.getDownloadDetails()
0981: + " ("
0982: + report.getDownloadTimeMillis()
0983: + "ms)");
0984: return null;
0985: }
0986:
0987: URL cachedMDURL = null;
0988: try {
0989: cachedMDURL = report.getLocalFile().toURL();
0990: } catch (MalformedURLException ex) {
0991: Message
0992: .warn("malformed url exception for original in cache file: "
0993: + report.getLocalFile()
0994: + ": "
0995: + ex.getMessage());
0996: return null;
0997: }
0998: try {
0999: ModuleDescriptor md = parser.parseDescriptor(settings,
1000: cachedMDURL, mdRef.getResource(), options
1001: .isValidate());
1002: if (md == null) {
1003: throw new IllegalStateException(
1004: "module descriptor parser returned a null module descriptor, "
1005: + "which is not allowed. "
1006: + "parser=" + parser
1007: + "; parser class="
1008: + parser.getClass().getName()
1009: + "; module descriptor resource="
1010: + mdRef.getResource());
1011: }
1012: Message.debug("\t" + getName()
1013: + ": parsed downloaded md file for " + mrid
1014: + "; parsed=" + md.getModuleRevisionId());
1015:
1016: // check if we should delete old artifacts
1017: boolean deleteOldArtifacts = false;
1018: if (cachedPublicationDate != null
1019: && !cachedPublicationDate.equals(md
1020: .getResolvedPublicationDate())) {
1021: // artifacts have changed, they should be downloaded again
1022: Message.verbose(mrid
1023: + " has changed: deleting old artifacts");
1024: deleteOldArtifacts = true;
1025: }
1026: if (deleteOldArtifacts) {
1027: String[] confs = md.getConfigurationsNames();
1028: for (int i = 0; i < confs.length; i++) {
1029: Artifact[] arts = md.getArtifacts(confs[i]);
1030: for (int j = 0; j < arts.length; j++) {
1031: Artifact transformedArtifact = NameSpaceHelper
1032: .transform(arts[j], options
1033: .getNamespace()
1034: .getToSystemTransformer());
1035: ArtifactOrigin origin = getSavedArtifactOrigin(transformedArtifact);
1036: File artFile = getArchiveFileInCache(
1037: transformedArtifact, origin, false);
1038: if (artFile.exists()) {
1039: Message.debug("deleting " + artFile);
1040: artFile.delete();
1041: }
1042: removeSavedArtifactOrigin(transformedArtifact);
1043: }
1044: }
1045: } else if (isChanging(dd, options)) {
1046: Message
1047: .verbose(mrid
1048: + " is changing, but has not changed: will trust cached artifacts if any");
1049: }
1050:
1051: MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(
1052: md.getMetadataArtifact());
1053: madr.setSearched(true);
1054: madr.setDownloadStatus(report.getDownloadStatus());
1055: madr.setDownloadDetails(report.getDownloadDetails());
1056: madr.setArtifactOrigin(report.getArtifactOrigin());
1057: madr.setDownloadTimeMillis(report
1058: .getDownloadTimeMillis());
1059: madr.setOriginalLocalFile(report.getLocalFile());
1060: madr.setSize(report.getSize());
1061: saveArtifactOrigin(md.getMetadataArtifact(), report
1062: .getArtifactOrigin());
1063:
1064: return new ResolvedModuleRevision(resolver, resolver,
1065: md, madr);
1066: } catch (IOException ex) {
1067: Message.warn("io problem while parsing ivy file: "
1068: + mdRef.getResource() + ": " + ex.getMessage());
1069: return null;
1070: }
1071: } finally {
1072: unlockMetadataArtifact(mrid);
1073: }
1074:
1075: }
1076:
1077: // lock used to lock all metadata related information access
1078: private boolean lockMetadataArtifact(ModuleRevisionId mrid) {
1079: Artifact artifact = getDefaultMetadataArtifact(mrid);
1080: try {
1081: // we need to provide an artifact origin to be sure we do not end up in a stack overflow
1082: // if the cache pattern is using original name, and the substitution thus trying to get
1083: // the saved artifact origin value which in turns calls this method
1084: return getLockStrategy().lockArtifact(
1085: artifact,
1086: getArchiveFileInCache(artifact,
1087: getDefaultMetadataArtifactOrigin(mrid)));
1088: } catch (InterruptedException e) {
1089: Thread.currentThread().interrupt(); // reset interrupt status
1090: throw new RuntimeException("operation interrupted");
1091: }
1092: }
1093:
1094: private void unlockMetadataArtifact(ModuleRevisionId mrid) {
1095: Artifact artifact = getDefaultMetadataArtifact(mrid);
1096: getLockStrategy().unlockArtifact(
1097: artifact,
1098: getArchiveFileInCache(artifact,
1099: getDefaultMetadataArtifactOrigin(mrid)));
1100: }
1101:
1102: private ArtifactOrigin getDefaultMetadataArtifactOrigin(
1103: ModuleRevisionId mrid) {
1104: // it's important to say the origin is not local to make sure it won't ever be used for
1105: // anything else than original token
1106: return new ArtifactOrigin(false, getIvyFileInCache(mrid)
1107: .getPath());
1108: }
1109:
1110: private Artifact getDefaultMetadataArtifact(ModuleRevisionId mrid) {
1111: return new DefaultArtifact(mrid, new Date(), "metadata",
1112: "metadata", "ivy", true);
1113: }
1114:
1115: // not used any more, but maybe useful for finer grain locking when downloading artifacts
1116: // private boolean lockArtifact(Artifact artifact) {
1117: // try {
1118: // return getLockStrategy().lockArtifact(artifact,
1119: // getArchiveFileInCache(artifact, null));
1120: // } catch (InterruptedException e) {
1121: // Thread.currentThread().interrupt(); // reset interrupt status
1122: // throw new RuntimeException("operation interrupted");
1123: // }
1124: // }
1125: //
1126: // private void unlockArtifact(Artifact artifact) {
1127: // getLockStrategy().unlockArtifact(artifact, getArchiveFileInCache(artifact, null));
1128: // }
1129:
1130: public Artifact getOriginalMetadataArtifact(Artifact moduleArtifact) {
1131: return DefaultArtifact.cloneWithAnotherName(moduleArtifact,
1132: moduleArtifact.getName() + ".original");
1133: }
1134:
1135: private boolean isChanging(DependencyDescriptor dd,
1136: CacheMetadataOptions options) {
1137: return dd.isChanging()
1138: || getChangingMatcher(options).matches(
1139: dd.getDependencyRevisionId().getRevision());
1140: }
1141:
1142: private Matcher getChangingMatcher(CacheMetadataOptions options) {
1143: String changingPattern = options.getChangingPattern() != null ? options
1144: .getChangingPattern()
1145: : this .changingPattern;
1146: if (changingPattern == null) {
1147: return NoMatcher.INSTANCE;
1148: }
1149: String changingMatcherName = options.getChangingMatcherName() != null ? options
1150: .getChangingMatcherName()
1151: : this .changingMatcherName;
1152: PatternMatcher matcher = settings
1153: .getMatcher(changingMatcherName);
1154: if (matcher == null) {
1155: throw new IllegalStateException("unknown matcher '"
1156: + changingMatcherName
1157: + "'. It is set as changing matcher in " + this );
1158: }
1159: return matcher.getMatcher(changingPattern);
1160: }
1161:
1162: private boolean isCheckmodified(DependencyDescriptor dd,
1163: CacheMetadataOptions options) {
1164: if (options.isCheckmodified() != null) {
1165: return options.isCheckmodified().booleanValue();
1166: }
1167: return isCheckmodified();
1168: }
1169:
1170: public void clean() {
1171: FileUtil.forceDelete(getBasedir());
1172: }
1173:
1174: public void dumpSettings() {
1175: Message.verbose("\t" + getName());
1176: Message.debug("\t\tivyPattern: " + getIvyPattern());
1177: Message.debug("\t\tartifactPattern: " + getArtifactPattern());
1178: Message.debug("\t\tlockingStrategy: "
1179: + getLockStrategy().getName());
1180: Message.debug("\t\tchangingPattern: " + getChangingPattern());
1181: Message.debug("\t\tchangingMatcher: "
1182: + getChangingMatcherName());
1183: }
1184:
1185: }
|