package org.jboss.shrinkwrap.resolver.impl.maven;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.maven.model.Model;
import org.apache.maven.model.Profile;
import org.apache.maven.model.Repository;
import org.apache.maven.model.building.DefaultModelBuilderFactory;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.model.building.ModelBuildingException;
import org.apache.maven.model.building.ModelBuildingResult;
import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.model.profile.ProfileActivationContext;
import org.apache.maven.model.profile.ProfileSelector;
import org.apache.maven.settings.Mirror;
import org.apache.maven.settings.Server;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.DependencyCollectionException;
import org.eclipse.aether.collection.DependencySelector;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RemoteRepository.Builder;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.VersionRangeRequest;
import org.eclipse.aether.resolution.VersionRangeResolutionException;
import org.eclipse.aether.resolution.VersionRangeResult;
import org.eclipse.aether.util.graph.selector.AndDependencySelector;
import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
import org.eclipse.aether.util.graph.selector.OptionalDependencySelector;
import org.eclipse.aether.util.graph.selector.ScopeDependencySelector;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
import org.jboss.shrinkwrap.resolver.api.InvalidConfigurationFileException;
import org.jboss.shrinkwrap.resolver.api.NoResolvedResultException;
import org.jboss.shrinkwrap.resolver.api.ResolutionException;
import org.jboss.shrinkwrap.resolver.api.VersionResolutionException;
import org.jboss.shrinkwrap.resolver.api.maven.MavenResolvedArtifact;
import org.jboss.shrinkwrap.resolver.api.maven.MavenVersionRangeResult;
import org.jboss.shrinkwrap.resolver.api.maven.MavenWorkingSession;
import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenCoordinate;
import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependency;
import org.jboss.shrinkwrap.resolver.api.maven.pom.ParsedPomFile;
import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenRemoteRepository;
import org.jboss.shrinkwrap.resolver.api.maven.strategy.MavenResolutionStrategy;
import org.jboss.shrinkwrap.resolver.api.maven.strategy.TransitiveExclusionPolicy;
import org.jboss.shrinkwrap.resolver.impl.maven.convert.MavenConverter;
import org.jboss.shrinkwrap.resolver.impl.maven.internal.MavenModelResolver;
import org.jboss.shrinkwrap.resolver.impl.maven.internal.SettingsXmlProfileSelector;
import org.jboss.shrinkwrap.resolver.impl.maven.logging.LogModelProblemCollector;
import org.jboss.shrinkwrap.resolver.impl.maven.pom.ParsedPomFileImpl;
public class MavenWorkingSessionImpl extends ConfigurableMavenWorkingSessionImpl {
private static final Logger log = Logger.getLogger(MavenWorkingSessionImpl.class.getName());
private final Set<MavenDependency> dependencyManagement;
private final List<MavenDependency> dependencies;
private final Set<MavenDependency> declaredDependencies;
private static final String MAVEN_CENTRAL_NAME = "central";
private static final RemoteRepository MAVEN_CENTRAL = new RemoteRepository.Builder(MAVEN_CENTRAL_NAME, "default",
"https://repo1.maven.org/maven2").build();
private Model model;
private final List<RemoteRepository> remoteRepositories;
private final List<RemoteRepository> additionalRemoteRepositories;
private boolean useMavenCentralRepository = true;
public MavenWorkingSessionImpl() {
super();
this.remoteRepositories = new ArrayList<RemoteRepository>();
this.additionalRemoteRepositories = new ArrayList<RemoteRepository>();
this.dependencies = new ArrayList<MavenDependency>();
this.dependencyManagement = new LinkedHashSet<MavenDependency>();
this.declaredDependencies = new LinkedHashSet<MavenDependency>();
}
@Override
public Set<MavenDependency> getDependencyManagement() {
return dependencyManagement;
}
@Override
public List<MavenDependency> getDependenciesForResolution() {
return dependencies;
}
@Override
public Set<MavenDependency> getDeclaredDependencies() {
return declaredDependencies;
}
@Override
public MavenWorkingSession loadPomFromFile(File pomFile, String... profiles) throws InvalidConfigurationFileException {
loadPomFromFile(pomFile, null, profiles);
return this;
}
public MavenWorkingSession loadPomFromFile(File pomFile, Properties userProperties, String... profiles)
throws InvalidConfigurationFileException {
final DefaultModelBuildingRequest request = new DefaultModelBuildingRequest()
.setSystemProperties(SecurityActions.getProperties()).setProfiles(this.getSettingsDefinedProfiles())
.setPomFile(pomFile).setActiveProfileIds(SettingsXmlProfileSelector.explicitlyActivatedProfiles(profiles))
.setInactiveProfileIds(SettingsXmlProfileSelector.explicitlyDisabledProfiles(profiles));
if (userProperties != null){
request.setUserProperties(userProperties);
}
ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();
ModelBuildingResult result;
try {
request.setModelResolver(new MavenModelResolver(getSystem(), getSession(), getRemoteRepositories()));
result = builder.build(request);
}
catch (ModelBuildingException e) {
String pomPath = request.getPomFile().getAbsolutePath();
StringBuilder sb = new StringBuilder("Found ").append(e.getProblems().size())
.append(" problems while building POM model from ").append(pomPath).append("\n");
int counter = 1;
for (ModelProblem problem : e.getProblems()) {
sb.append(counter++).append("/ ").append(problem).append("\n");
}
throw new InvalidConfigurationFileException(sb.toString());
}
Model model = result.getEffectiveModel();
this.model = model;
for (Repository repository : model.getRepositories()) {
remoteRepositories.add(MavenConverter.asRemoteRepository(repository));
}
return this;
}
@Override
public Collection<MavenResolvedArtifact> resolveDependencies(final MavenResolutionStrategy strategy)
throws ResolutionException {
final List<MavenDependency> depsForResolution = Collections.unmodifiableList(new ArrayList<MavenDependency>(
this.getDependenciesForResolution()));
final List<MavenDependency> depManagement = new ArrayList<MavenDependency>(this.getDependencyManagement());
final List<RemoteRepository> repos = this.getRemoteRepositories();
final CollectRequest request = new CollectRequest(MavenConverter.asDependencies(depsForResolution,
getSession().getArtifactTypeRegistry()),
MavenConverter.asDependencies(depManagement, getSession().getArtifactTypeRegistry()), repos);
Collection<ArtifactResult> results = Collections.emptyList();
final Set<DependencySelector> dependencySelectors = new LinkedHashSet<DependencySelector>(3);
final TransitiveExclusionPolicy transitiveExclusionPolicy = strategy.getTransitiveExclusionPolicy();
final ScopeType[] filteredScopes = transitiveExclusionPolicy.getFilteredScopes();
final int numFilteredScopes = filteredScopes.length;
final String[] filteredScopeStrings = new String[numFilteredScopes];
for (int i = 0; i < numFilteredScopes; i++) {
filteredScopeStrings[i] = filteredScopes[i].toString();
}
if (numFilteredScopes > 0) {
dependencySelectors.add(new ScopeDependencySelector(filteredScopeStrings));
}
if (!transitiveExclusionPolicy.allowOptional()) {
dependencySelectors.add(new OptionalDependencySelector());
}
dependencySelectors.add(new ExclusionDependencySelector());
final DependencySelector dependencySelector = new AndDependencySelector(dependencySelectors);
getSession().setDependencySelector(dependencySelector);
try {
results = getSystem().resolveDependencies(getSession(), this, request,
strategy.getResolutionFilters());
} catch (DependencyResolutionException e) {
throw wrapException(e);
}
final Collection<MavenResolvedArtifact> resolvedArtifacts = new ArrayList<MavenResolvedArtifact>(results.size());
for (final ArtifactResult result : results) {
resolvedArtifacts.add(MavenResolvedArtifactImpl.fromArtifactResult(result));
}
this.getDependenciesForResolution().clear();
return PostResolutionFilterApplicator.postFilter(resolvedArtifacts);
}
@Override
public MavenVersionRangeResult resolveVersionRange(final MavenCoordinate coordinate) throws VersionResolutionException {
final Artifact artifact = MavenConverter.asArtifact(coordinate, getSession().getArtifactTypeRegistry());
final VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, this.getRemoteRepositories(), null);
try {
final VersionRangeResult versionRangeResult = getSystem().resolveVersionRange(getSession(), versionRangeRequest);
if (!versionRangeResult.getVersions().isEmpty()) {
return new MavenVersionRangeResultImpl(artifact, versionRangeResult);
}
final List<Exception> exceptions = versionRangeResult.getExceptions();
if (exceptions.isEmpty()) {
return new MavenVersionRangeResultImpl(artifact, versionRangeResult);
} else {
StringBuilder builder = new StringBuilder("Version range request failed with ")
.append(exceptions.size()).append(" exceptions.").append("\n");
int counter = 1;
for (final Exception exception : exceptions) {
log.log(Level.SEVERE, "Version range request failed", exception);
builder.append(counter++).append("/ ").append(exception.getLocalizedMessage()).append("\n");
}
throw new VersionResolutionException(builder.toString());
}
} catch (VersionRangeResolutionException vrre) {
throw new VersionResolutionException("Version range request failed", vrre);
}
}
@Override
public ParsedPomFile getParsedPomFile() {
return new ParsedPomFileImpl(model, getSession().getArtifactTypeRegistry());
}
@Override
public void disableMavenCentral() {
log.log(Level.FINEST, "Disabling Maven Central");
this.useMavenCentralRepository = false;
}
@Override
public void addRemoteRepo(MavenRemoteRepository repository) {
Builder builder = new Builder(repository.getId(), repository.getType(), repository.getUrl());
builder.setPolicy(new RepositoryPolicy(true, repository.getUpdatePolicy() == null ? null : repository
.getUpdatePolicy().apiValue(), repository.getChecksumPolicy() == null ? null : repository
.getChecksumPolicy().apiValue()));
for (RemoteRepository r : this.additionalRemoteRepositories) {
if (r.getId().equals(repository.getId())) {
this.additionalRemoteRepositories.remove(r);
}
}
this.additionalRemoteRepositories.add(builder.build());
}
private List<RemoteRepository> getRemoteRepositories() throws IllegalStateException {
if (isOffline()) {
log.log(Level.FINE, "No remote repositories will be available, working in offline mode");
return Collections.emptyList();
}
Set<RemoteRepository> enhancedRepos = new LinkedHashSet<RemoteRepository>();
enhancedRepos.addAll(additionalRemoteRepositories);
ProfileSelector selector = new SettingsXmlProfileSelector();
LogModelProblemCollector problems = new LogModelProblemCollector();
List<Profile> activeProfiles = selector.getActiveProfiles(MavenConverter.asProfiles(getSettings().getProfiles()),
new ProfileActivationContext() {
@Override
public Map<String, String> getUserProperties() {
return Collections.emptyMap();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Map<String, String> getSystemProperties() {
return new HashMap<String, String>((Map) SecurityActions.getProperties());
}
@Override
public File getProjectDirectory() {
return new File(SecurityActions.getProperty("user.dir"));
}
@Override
public Map<String, String> getProjectProperties() {
return Collections.emptyMap();
}
@Override
public List<String> getInactiveProfileIds() {
return Collections.emptyList();
}
@Override
public List<String> getActiveProfileIds() {
return getSettings().getActiveProfiles();
}
}, problems);
if (problems.hasSevereFailures()) {
throw new IllegalStateException("Unable to get active profiles from Maven settings.");
}
for (Profile p : activeProfiles) {
for (Repository repository : p.getRepositories()) {
RemoteRepository repo = MavenConverter.asRemoteRepository(repository);
if (!isIdIncluded(additionalRemoteRepositories, repo)) {
enhancedRepos.add(repo);
}
}
}
for (RemoteRepository repo : remoteRepositories) {
if (!isIdIncluded(additionalRemoteRepositories, repo)) {
enhancedRepos.add(repo);
}
}
if (useMavenCentralRepository) {
if (!isIdIncluded(additionalRemoteRepositories, MAVEN_CENTRAL)) {
enhancedRepos.add(MAVEN_CENTRAL);
}
} else {
List<RemoteRepository> reposToRemove = new ArrayList<RemoteRepository>();
for (final RemoteRepository repo : enhancedRepos) {
final String repoUrl = repo.getUrl();
if ((repoUrl.contains("maven.org") || repoUrl.contains("apache.org"))
&& repo.getId().equalsIgnoreCase(MAVEN_CENTRAL_NAME)) {
if (!isIdIncluded(additionalRemoteRepositories, MAVEN_CENTRAL)) {
reposToRemove.add(repo);
}
}
}
enhancedRepos.removeAll(reposToRemove);
}
DefaultMirrorSelector dms = new DefaultMirrorSelector();
for (Mirror mirror : getSettings().getMirrors()) {
dms.add(mirror.getId(), mirror.getUrl(), mirror.getLayout(), false, mirror.getMirrorOf(),
mirror.getMirrorOfLayouts());
}
Set<RemoteRepository> mirroredRepos = new LinkedHashSet<RemoteRepository>();
for (RemoteRepository repository : enhancedRepos) {
RemoteRepository mirror = dms.getMirror(repository);
if (mirror != null) {
mirroredRepos.add(mirror);
} else {
mirroredRepos.add(repository);
}
}
final Set<RemoteRepository> authorizedRepos = new LinkedHashSet<RemoteRepository>();
for (RemoteRepository remoteRepository : mirroredRepos) {
final RemoteRepository.Builder builder = new RemoteRepository.Builder(remoteRepository);
Server server = getSettings().getServer(remoteRepository.getId());
if (server != null) {
final AuthenticationBuilder authenticationBuilder = new AuthenticationBuilder()
.addUsername(server.getUsername())
.addPassword(server.getPassword())
.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
builder.setAuthentication(authenticationBuilder.build());
}
authorizedRepos.add(builder.build());
}
if (log.isLoggable(Level.FINER)) {
for (RemoteRepository repository : authorizedRepos) {
log.finer("Repository " + repository.getUrl() + " have been made available for artifact resolution");
}
}
return new ArrayList<RemoteRepository>(authorizedRepos);
}
private static boolean isIdIncluded(Collection<RemoteRepository> repositories, RemoteRepository candidate) {
for (RemoteRepository r : repositories) {
if (r.getId().equals(candidate.getId())) {
return true;
}
}
return false;
}
private List<Profile> getSettingsDefinedProfiles() {
return MavenConverter.asProfiles(getSettings().getProfiles());
}
private static ResolutionException wrapException(DependencyResolutionException e) {
Throwable cause = (Throwable) e;
Throwable nextCause = null;
while ((nextCause = cause.getCause()) != null) {
cause = nextCause;
}
if (cause instanceof ArtifactResolutionException) {
throw new NoResolvedResultException("Unable to get artifact from the repository due to: "
+ e.getMessage() + ", caused by: " + cause.getMessage());
} else if (cause instanceof DependencyCollectionException) {
throw new NoResolvedResultException(
"Unable to collect dependency tree for given dependencies due to: "
+ e.getMessage() + ", caused by: " + cause.getMessage(), e);
}
throw new NoResolvedResultException(
"Unable to collect/resolve dependency tree for a resolution due to: "
+ e.getMessage() + ", caused by: " + cause.getMessage(), e);
}
}