/* * 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.plugins.resolver; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Date; import java.util.Map; import org.apache.ivy.core.IvyContext; import org.apache.ivy.core.LogOptions; import org.apache.ivy.core.RelativeUrlResolver; import org.apache.ivy.core.cache.ArtifactOrigin; import org.apache.ivy.core.cache.CacheDownloadOptions; import org.apache.ivy.core.cache.CacheMetadataOptions; import org.apache.ivy.core.cache.DownloadListener; import org.apache.ivy.core.cache.RepositoryCacheManager; import org.apache.ivy.core.cache.ResolutionCacheManager; import org.apache.ivy.core.event.EventManager; import org.apache.ivy.core.event.download.EndArtifactDownloadEvent; import org.apache.ivy.core.event.download.NeedArtifactEvent; import org.apache.ivy.core.event.download.StartArtifactDownloadEvent; import org.apache.ivy.core.module.descriptor.Artifact; import org.apache.ivy.core.module.descriptor.DependencyDescriptor; import org.apache.ivy.core.module.descriptor.ModuleDescriptor; import org.apache.ivy.core.module.id.ModuleId; import org.apache.ivy.core.module.id.ModuleRevisionId; import org.apache.ivy.core.module.status.StatusManager; import org.apache.ivy.core.report.ArtifactDownloadReport; import org.apache.ivy.core.report.DownloadReport; import org.apache.ivy.core.report.DownloadStatus; import org.apache.ivy.core.report.MetadataArtifactDownloadReport; import org.apache.ivy.core.resolve.DownloadOptions; import org.apache.ivy.core.resolve.IvyNode; import org.apache.ivy.core.resolve.ResolveData; import org.apache.ivy.core.resolve.ResolveOptions; import org.apache.ivy.core.resolve.ResolvedModuleRevision; import org.apache.ivy.core.search.ModuleEntry; import org.apache.ivy.core.search.OrganisationEntry; import org.apache.ivy.core.search.RevisionEntry; import org.apache.ivy.core.settings.Validatable; import org.apache.ivy.plugins.conflict.ConflictManager; import org.apache.ivy.plugins.latest.ArtifactInfo; import org.apache.ivy.plugins.latest.LatestStrategy; import org.apache.ivy.plugins.matcher.PatternMatcher; import org.apache.ivy.plugins.namespace.NameSpaceHelper; import org.apache.ivy.plugins.namespace.Namespace; import org.apache.ivy.plugins.parser.ParserSettings; import org.apache.ivy.plugins.resolver.ChainResolver.ResolvedModuleRevisionArtifactInfo; import org.apache.ivy.plugins.resolver.util.HasLatestStrategy; import org.apache.ivy.plugins.resolver.util.ResolvedResource; import org.apache.ivy.util.Checks; import org.apache.ivy.util.Message; /** * This abstract resolver only provides handling for resolver name */ public abstract class AbstractResolver implements DependencyResolver, HasLatestStrategy, Validatable { /** * True if parsed ivy files should be validated against xsd, false if they should not, null if * default behavior should be used */ private Boolean validate = null; private String name; private ResolverSettings settings; private EventManager eventManager = null; // may remain null /** * The latest strategy to use to find latest among several artifacts */ private LatestStrategy latestStrategy; private String latestStrategyName; /** * The namespace to which this resolver belongs */ private Namespace namespace; private String namespaceName; private String cacheManagerName; private RepositoryCacheManager repositoryCacheManager; // used to store default values for nested cache private String changingMatcherName; private String changingPattern; private Boolean checkmodified; public ResolverSettings getSettings() { return settings; } public ParserSettings getParserSettings() { return new ResolverParserSettings(); } public void setSettings(ResolverSettings ivy) { settings = ivy; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * this method should remove sensitive information from a location to be displayed in a log * * @param name * location * @return location with sensitive data replaced by stars */ public String hidePassword(String name) { return name; } protected boolean doValidate(ResolveData data) { if (validate != null) { return validate.booleanValue(); } else { return data.isValidate(); } } public boolean isValidate() { return validate == null ? true : validate.booleanValue(); } public void setValidate(boolean validate) { this.validate = Boolean.valueOf(validate); } protected void checkInterrupted() { IvyContext.getContext().getIvy().checkInterrupted(); } public void reportFailure() { Message.verbose("no failure report implemented by " + getName()); } public void reportFailure(Artifact art) { Message.verbose("no failure report implemented by " + getName()); } public String[] listTokenValues(String token, Map otherTokenValues) { return new String[0]; } public Map[] listTokenValues(String[] tokens, Map criteria) { return new Map[0]; } public OrganisationEntry[] listOrganisations() { return new OrganisationEntry[0]; } public ModuleEntry[] listModules(OrganisationEntry org) { return new ModuleEntry[0]; } public RevisionEntry[] listRevisions(ModuleEntry module) { return new RevisionEntry[0]; } public String toString() { return getName(); } public void dumpSettings() { Message.verbose("\t" + getName() + " [" + getTypeName() + "]"); Message.debug("\t\tcache: " + cacheManagerName); } public String getTypeName() { return getClass().getName(); } /** * Default implementation downloads the artifact without taking advantage of its location */ public ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options) { DownloadReport r = download(new Artifact[] {artifact.getArtifact()}, options); return r.getArtifactReport(artifact.getArtifact()); } public boolean exists(Artifact artifact) { return locate(artifact) != null; } /** * Default implementation actually download the artifact Subclasses should overwrite this to * avoid the download */ public ArtifactOrigin locate(Artifact artifact) { DownloadReport dr = download(new Artifact[] {artifact}, new DownloadOptions()); if (dr == null) { /* * according to IVY-831, it seems that this actually happen sometime, while the contract * of DependencyResolver says that it should never return null */ throw new IllegalStateException("null download report returned by " + getName() + " (" + getClass().getName() + ")" + " when trying to download " + artifact); } ArtifactDownloadReport adr = dr.getArtifactReport(artifact); return adr.getDownloadStatus() == DownloadStatus.FAILED ? null : adr.getArtifactOrigin(); } public LatestStrategy getLatestStrategy() { if (latestStrategy == null) { initLatestStrategyFromSettings(); } return latestStrategy; } private void initLatestStrategyFromSettings() { if (getSettings() != null) { if (latestStrategyName != null && !"default".equals(latestStrategyName)) { latestStrategy = getSettings().getLatestStrategy(latestStrategyName); if (latestStrategy == null) { throw new IllegalStateException("unknown latest strategy '" + latestStrategyName + "'"); } } else { latestStrategy = getSettings().getDefaultLatestStrategy(); Message.debug(getName() + ": no latest strategy defined: using default"); } } else { throw new IllegalStateException("no ivy instance found: " + "impossible to get a latest strategy without ivy instance"); } } public void setLatestStrategy(LatestStrategy latestStrategy) { this.latestStrategy = latestStrategy; } public void setLatest(String strategyName) { latestStrategyName = strategyName; } public String getLatest() { if (latestStrategyName == null) { latestStrategyName = "default"; } return latestStrategyName; } public Namespace getNamespace() { if (namespace == null) { initNamespaceFromSettings(); } return namespace; } private void initNamespaceFromSettings() { if (getSettings() != null) { if (namespaceName != null) { namespace = getSettings().getNamespace(namespaceName); if (namespace == null) { throw new IllegalStateException("unknown namespace '" + namespaceName + "'"); } } else { namespace = getSettings().getSystemNamespace(); Message.debug(getName() + ": no namespace defined: using system"); } } else { Message.verbose(getName() + ": no namespace defined nor ivy instance: using system namespace"); namespace = Namespace.SYSTEM_NAMESPACE; } } public void setNamespace(String namespaceName) { this.namespaceName = namespaceName; } // Namespace conversion methods protected ModuleDescriptor toSystem(ModuleDescriptor md) { return NameSpaceHelper.toSystem(md, getNamespace()); } protected Artifact fromSystem(Artifact artifact) { return NameSpaceHelper.transform(artifact, getNamespace().getFromSystemTransformer()); } protected Artifact toSystem(Artifact artifact) { return NameSpaceHelper.transform(artifact, getNamespace().getToSystemTransformer()); } protected MetadataArtifactDownloadReport toSystem(MetadataArtifactDownloadReport report) { return NameSpaceHelper.transform(report, getNamespace().getToSystemTransformer()); } protected ResolvedModuleRevision toSystem(ResolvedModuleRevision rmr) { return NameSpaceHelper.toSystem(rmr, getNamespace()); } protected ModuleRevisionId toSystem(ModuleRevisionId resolvedMrid) { return getNamespace().getToSystemTransformer().transform(resolvedMrid); } protected DependencyDescriptor fromSystem(DependencyDescriptor dd) { return NameSpaceHelper.transform(dd, getNamespace().getFromSystemTransformer(), true); } protected DependencyDescriptor toSystem(DependencyDescriptor dd) { return NameSpaceHelper.transform(dd, getNamespace().getToSystemTransformer(), true); } protected IvyNode getSystemNode(ResolveData data, ModuleRevisionId resolvedMrid) { return data.getNode(toSystem(resolvedMrid)); } protected ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd, ResolveData data) { return findModuleInCache(dd, data, false); } protected ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd, ResolveData data, boolean anyResolver) { ResolvedModuleRevision rmr = getRepositoryCacheManager().findModuleInCache(dd, dd.getDependencyRevisionId(), getCacheOptions(data), anyResolver ? null : getName()); if (rmr == null) { return null; } if (data.getReport() != null && data.isBlacklisted(data.getReport().getConfiguration(), rmr.getId())) { Message.verbose("\t" + getName() + ": found revision in cache: " + rmr.getId() + " for " + dd + ", but it is blacklisted"); return null; } return rmr; } public void setChangingMatcher(String changingMatcherName) { this.changingMatcherName = changingMatcherName; } protected String getChangingMatcherName() { return changingMatcherName; } public void setChangingPattern(String changingPattern) { this.changingPattern = changingPattern; } protected String getChangingPattern() { return changingPattern; } public void setCheckmodified(boolean check) { checkmodified = Boolean.valueOf(check); } public RepositoryCacheManager getRepositoryCacheManager() { if (repositoryCacheManager == null) { initRepositoryCacheManagerFromSettings(); } return repositoryCacheManager; } private void initRepositoryCacheManagerFromSettings() { if (cacheManagerName == null) { repositoryCacheManager = settings.getDefaultRepositoryCacheManager(); if (repositoryCacheManager == null) { throw new IllegalStateException( "no default cache manager defined with current settings"); } } else { repositoryCacheManager = settings.getRepositoryCacheManager(cacheManagerName); if (repositoryCacheManager == null) { throw new IllegalStateException("unknown cache manager '" + cacheManagerName + "'. Available caches are " + Arrays.asList(settings.getRepositoryCacheManagers())); } } } public void setRepositoryCacheManager(RepositoryCacheManager repositoryCacheManager) { this.cacheManagerName = repositoryCacheManager.getName(); this.repositoryCacheManager = repositoryCacheManager; } public void setCache(String cacheName) { cacheManagerName = cacheName; } public void setEventManager(EventManager eventManager) { this.eventManager = eventManager; } public EventManager getEventManager() { return eventManager; } public void validate() { initRepositoryCacheManagerFromSettings(); initNamespaceFromSettings(); initLatestStrategyFromSettings(); } protected CacheMetadataOptions getCacheOptions(ResolveData data) { return (CacheMetadataOptions) new CacheMetadataOptions() .setChangingMatcherName(getChangingMatcherName()) .setChangingPattern(getChangingPattern()) .setCheckTTL(!data.getOptions().isUseCacheOnly()) .setCheckmodified( data.getOptions().isUseCacheOnly() ? Boolean.FALSE : checkmodified) .setValidate(doValidate(data)).setNamespace(getNamespace()) .setForce(data.getOptions().isRefresh()) .setListener(getDownloadListener(getDownloadOptions(data.getOptions()))); } protected CacheDownloadOptions getCacheDownloadOptions(DownloadOptions options) { CacheDownloadOptions cacheDownloadOptions = new CacheDownloadOptions(); cacheDownloadOptions.setListener(getDownloadListener(options)); return cacheDownloadOptions; } protected DownloadOptions getDownloadOptions(ResolveOptions options) { return (DownloadOptions) new DownloadOptions().setLog(options.getLog()); } public void abortPublishTransaction() throws IOException { /* Default implementation is a no-op */ } public void commitPublishTransaction() throws IOException { /* Default implementation is a no-op */ } public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException { /* Default implementation is a no-op */ } private DownloadListener getDownloadListener(final DownloadOptions options) { return new DownloadListener() { public void needArtifact(RepositoryCacheManager cache, Artifact artifact) { if (eventManager != null) { eventManager .fireIvyEvent(new NeedArtifactEvent(AbstractResolver.this, artifact)); } } public void startArtifactDownload(RepositoryCacheManager cache, ResolvedResource rres, Artifact artifact, ArtifactOrigin origin) { if (artifact.isMetadata() || LogOptions.LOG_QUIET.equals(options.getLog())) { Message.verbose("downloading " + rres.getResource() + " ..."); } else { Message.info("downloading " + rres.getResource() + " ..."); } if (eventManager != null) { eventManager.fireIvyEvent(new StartArtifactDownloadEvent(AbstractResolver.this, artifact, origin)); } } public void endArtifactDownload(RepositoryCacheManager cache, Artifact artifact, ArtifactDownloadReport adr, File archiveFile) { if (eventManager != null) { eventManager.fireIvyEvent(new EndArtifactDownloadEvent(AbstractResolver.this, artifact, adr, archiveFile)); } } }; } /** * Returns true if rmr1 is after rmr2, using the latest strategy to determine which is the * latest * * @param rmr1 * @param rmr2 * @return */ protected boolean isAfter(ResolvedModuleRevision rmr1, ResolvedModuleRevision rmr2, Date date) { ArtifactInfo[] ais = new ArtifactInfo[] {new ResolvedModuleRevisionArtifactInfo(rmr1), new ResolvedModuleRevisionArtifactInfo(rmr2)}; return getLatestStrategy().findLatest(ais, date) == ais[0]; } protected ResolvedModuleRevision checkLatest(DependencyDescriptor dd, ResolvedModuleRevision newModuleFound, ResolveData data) { Checks.checkNotNull(dd, "dd"); Checks.checkNotNull(data, "data"); // check if latest is asked and compare to return the most recent ResolvedModuleRevision previousModuleFound = data.getCurrentResolvedModuleRevision(); String newModuleDesc = describe(newModuleFound); Message.debug("\tchecking " + newModuleDesc + " against " + describe(previousModuleFound)); if (previousModuleFound == null) { Message.debug("\tmodule revision kept as first found: " + newModuleDesc); saveModuleRevisionIfNeeded(dd, newModuleFound); return newModuleFound; } else if (isAfter(newModuleFound, previousModuleFound, data.getDate())) { Message.debug("\tmodule revision kept as younger: " + newModuleDesc); saveModuleRevisionIfNeeded(dd, newModuleFound); return newModuleFound; } else if (!newModuleFound.getDescriptor().isDefault() && previousModuleFound.getDescriptor().isDefault()) { Message.debug("\tmodule revision kept as better (not default): " + newModuleDesc); saveModuleRevisionIfNeeded(dd, newModuleFound); return newModuleFound; } else { Message.debug("\tmodule revision discarded as older: " + newModuleDesc); return previousModuleFound; } } protected void saveModuleRevisionIfNeeded(DependencyDescriptor dd, ResolvedModuleRevision newModuleFound) { if (newModuleFound != null && getSettings().getVersionMatcher().isDynamic(dd.getDependencyRevisionId())) { getRepositoryCacheManager().saveResolvedRevision(dd.getDependencyRevisionId(), newModuleFound.getId().getRevision()); } } private String describe(ResolvedModuleRevision rmr) { if (rmr == null) { return "[none]"; } return rmr.getId() + (rmr.getDescriptor().isDefault() ? "[default]" : "") + " from " + rmr.getResolver().getName(); } private class ResolverParserSettings implements ParserSettings { public ConflictManager getConflictManager(String name) { return AbstractResolver.this.getSettings().getConflictManager(name); } public Namespace getContextNamespace() { return AbstractResolver.this.getNamespace(); } public String getDefaultBranch(ModuleId moduleId) { return AbstractResolver.this.getSettings().getDefaultBranch(moduleId); } public PatternMatcher getMatcher(String matcherName) { return AbstractResolver.this.getSettings().getMatcher(matcherName); } public Namespace getNamespace(String namespace) { return AbstractResolver.this.getSettings().getNamespace(namespace); } public RelativeUrlResolver getRelativeUrlResolver() { return AbstractResolver.this.getSettings().getRelativeUrlResolver(); } public ResolutionCacheManager getResolutionCacheManager() { return AbstractResolver.this.getSettings().getResolutionCacheManager(); } public DependencyResolver getResolver(ModuleRevisionId mRevId) { return AbstractResolver.this.getSettings().getResolver(mRevId); } public StatusManager getStatusManager() { return AbstractResolver.this.getSettings().getStatusManager(); } public File resolveFile(String filename) { return AbstractResolver.this.getSettings().resolveFile(filename); } public Map substitute(Map strings) { return AbstractResolver.this.getSettings().substitute(strings); } public String substitute(String value) { return AbstractResolver.this.getSettings().substitute(value); } } }