blob: 6030c9f85cf0d12023400d4f4a9532e4ec79a010 [file] [log] [blame]
/*
* 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<String, String> otherTokenValues) {
return new String[0];
}
public Map<String, String>[] listTokenValues(String[] tokens, Map<String, Object> 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];
}
@Override
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);
}
public String getVariable(String value) {
return AbstractResolver.this.getSettings().getVariable(value);
}
}
}