Skip to content

Commit

Permalink
Intercept artifact resolution, report the artifact during its POM pro…
Browse files Browse the repository at this point in the history
…cessing.
  • Loading branch information
sdedic committed Feb 15, 2024
1 parent 2638220 commit aaeabe1
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,15 @@
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.impl.VersionResolver;
import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
import org.eclipse.aether.util.repository.DefaultProxySelector;
import org.netbeans.modules.maven.embedder.impl.NbVersionResolver2;

/**
* Handle for the embedded Maven system, used to parse POMs and more.
Expand All @@ -123,6 +125,7 @@ public final class MavenEmbedder {
private final SettingsBuilder settingsBuilder;
private final EmbedderConfiguration embedderConfiguration;
private final SettingsDecrypter settingsDecrypter;
private final NbVersionResolver2 versionResolver;
private long settingsTimestamp;
private static final Object lastLocalRepositoryLock = new Object();
private static URI lastLocalRepository;
Expand All @@ -137,6 +140,13 @@ public final class MavenEmbedder {
this.settingsBuilder = plexus.lookup(SettingsBuilder.class);
this.populator = plexus.lookup(MavenExecutionRequestPopulator.class);
settingsDecrypter = plexus.lookup(SettingsDecrypter.class);

VersionResolver vr = plexus.lookup(VersionResolver.class);
if (vr instanceof NbVersionResolver2) {
versionResolver = (NbVersionResolver2)vr;
} else {
versionResolver = null;
}
}

public PlexusContainer getPlexus() {
Expand Down Expand Up @@ -235,7 +245,7 @@ public MavenExecutionResult readProjectWithDependencies(MavenExecutionRequest re

public MavenExecutionResult readProjectWithDependencies(MavenExecutionRequest req, boolean useWorkspaceResolution) {
if (useWorkspaceResolution) {
req.setWorkspaceReader(new NbWorkspaceReader());
req.setWorkspaceReader(new NbWorkspaceReader(versionResolver));
}
File pomFile = req.getPom();
MavenExecutionResult result = new DefaultMavenExecutionResult();
Expand All @@ -258,7 +268,7 @@ public MavenExecutionResult readProjectWithDependencies(MavenExecutionRequest re

public List<MavenExecutionResult> readProjectsWithDependencies(MavenExecutionRequest req, List<File> poms, boolean useWorkspaceResolution) {
if (useWorkspaceResolution) {
req.setWorkspaceReader(new NbWorkspaceReader());
req.setWorkspaceReader(new NbWorkspaceReader(versionResolver));
}
// File pomFile = req.getPom();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@

import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.Scopes;
import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.plugin.internal.PluginDependenciesResolver;
import org.eclipse.aether.impl.ArtifactDescriptorReader;
import org.eclipse.aether.impl.VersionRangeResolver;
import org.eclipse.aether.impl.VersionResolver;
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
Expand All @@ -48,6 +52,12 @@ public void configure(Binder binder) {
//exxperimental only.
// binder.bind(InheritanceAssembler.class).to(NbInheritanceAssembler.class);
binder.bind(ModelBuilder.class).to(NBModelBuilder.class);

// This allows to capture origin for version and artifact queries, so that ArtifactFixer can determine
// if a pom was really requested, or some other artifact's classifier/type was
binder.bind(VersionResolver.class).to(NbVersionResolver2.class).in(Scopes.SINGLETON);
binder.bind(VersionRangeResolver.class).to(NbVersionResolver2.class).in(Scopes.SINGLETON);
binder.bind(ArtifactDescriptorReader.class).to(NbVersionResolver2.class).in(Scopes.SINGLETON);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* 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.netbeans.modules.maven.embedder.impl;

import javax.inject.Inject;
import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader;
import org.apache.maven.repository.internal.DefaultVersionRangeResolver;
import org.apache.maven.repository.internal.DefaultVersionResolver;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.impl.ArtifactDescriptorReader;
import org.eclipse.aether.impl.VersionRangeResolver;
import org.eclipse.aether.impl.VersionResolver;
import org.eclipse.aether.resolution.ArtifactDescriptorException;
import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.VersionRangeRequest;
import org.eclipse.aether.resolution.VersionRangeResolutionException;
import org.eclipse.aether.resolution.VersionRangeResult;
import org.eclipse.aether.resolution.VersionRequest;
import org.eclipse.aether.resolution.VersionResolutionException;
import org.eclipse.aether.resolution.VersionResult;

/**
* This resolver intercepts requests to resolve artifact versions and artifact descriptor. The NbWorkspaceReader may
* then discover which artifact is actually missing and pass that information to ArtifactFixer.
* <p>
* Maven attempts to resolve the artifact and its POM - as another artifact, but the knowledge that the POM is actually
* implied by the real artifact is lost in Maven's DependencyResolver. This interceptor saves the info so that
* the Fixer has some context to report missing project dependencies.
*
* @author sdedic
*/
public final class NbVersionResolver2 implements VersionResolver, VersionRangeResolver, ArtifactDescriptorReader {
private static final ThreadLocal<Artifact> resolvingArtifact = new ThreadLocal<>();
private static final ThreadLocal<Artifact> resolvingPom = new ThreadLocal<>();

private final DefaultVersionResolver resolverDelegate;
private final DefaultVersionRangeResolver rangeResolverDelegate;
private final DefaultArtifactDescriptorReader descriptorReader;

@Inject
public NbVersionResolver2(DefaultVersionResolver r, DefaultVersionRangeResolver r2, DefaultArtifactDescriptorReader reader) {
this.resolverDelegate = r;
this.rangeResolverDelegate = r2;
this.descriptorReader = reader;
reader.setVersionResolver(this);
reader.setVersionRangeResolver(this);
}

@Override
public ArtifactDescriptorResult readArtifactDescriptor(RepositorySystemSession rss, ArtifactDescriptorRequest adr) throws ArtifactDescriptorException {
Artifact save = resolvingPom.get();
try {
resolvingPom.set(adr.getArtifact());
return descriptorReader.readArtifactDescriptor(rss, adr);
} finally {
resolvingPom.set(save);
}
}

@Override
public VersionResult resolveVersion(RepositorySystemSession session, VersionRequest request) throws VersionResolutionException {
Artifact a = request.getArtifact();
Artifact rq = null;
// TODO: also can record the artifact SCOPE. It is not present directly, but
// one can traverse through request.getTrace(), seeking for e.g. CollectStep that contains Dependency, which have a scope. Leaving for
// future improvement.
if (request.getTrace().getData() instanceof ArtifactDescriptorRequest) {
rq = ((ArtifactDescriptorRequest)request.getTrace().getData()).getArtifact();
if (rq.getArtifactId().equals(a.getArtifactId()) && rq.getGroupId().equals(a.getGroupId()) && rq.getVersion().equals(a.getVersion())) {
// replace POM artifacts with their original ones
a = rq;
}
} else if ("pom".equals(a.getExtension()) &&
(request.getTrace().getData() instanceof ArtifactRequest) && request.getTrace().getParent() != null &&
(request.getTrace().getParent().getData() instanceof ArtifactDescriptorRequest)) {
rq = ((ArtifactDescriptorRequest)request.getTrace().getParent().getData()).getArtifact();
}
if (rq != null &&
rq.getArtifactId().equals(a.getArtifactId()) && rq.getGroupId().equals(a.getGroupId()) && rq.getVersion().equals(a.getVersion())) {
// replace POM artifacts with their original ones
a = rq;
}
Artifact save = resolvingArtifact.get();
resolvingArtifact.set(a);
try {
return resolverDelegate.resolveVersion(session, request);
} finally {
resolvingArtifact.set(save);
}
}

@Override
public VersionRangeResult resolveVersionRange(RepositorySystemSession rss, VersionRangeRequest vrr) throws VersionRangeResolutionException {
Artifact a = vrr.getArtifact();
if (vrr.getTrace().getData() instanceof ArtifactDescriptorRequest) {
Artifact rq = ((ArtifactDescriptorRequest)vrr.getTrace().getData()).getArtifact();
if (rq.getArtifactId().equals(a.getArtifactId()) && rq.getGroupId().equals(a.getGroupId()) && rq.getVersion().equals(a.getVersion())) {
// replace POM artifacts with their original ones
a = rq;
}
}
Artifact save = resolvingArtifact.get();
resolvingArtifact.set(a);
try {
return rangeResolverDelegate.resolveVersionRange(rss, vrr);
} finally {
resolvingArtifact.set(save);
}
}

public Artifact getResolvingArtifact() {
Artifact pomOrigin = resolvingPom.get();
Artifact resolving = resolvingArtifact.get();
if (resolving == null) {
if (pomOrigin != null) {
resolving = pomOrigin;
}
} else if (pomOrigin != null) {
if (resolving.getGroupId().equals(pomOrigin.getGroupId()) &&
resolving.getArtifactId().equals(pomOrigin.getArtifactId()) &&
resolving.getVersion().equals(pomOrigin.getVersion())) {
resolving = pomOrigin;
}
}
return resolving;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.netbeans.modules.maven.embedder.ArtifactFixer;
import org.openide.util.Lookup;
import org.eclipse.aether.repository.WorkspaceReader;
Expand All @@ -33,12 +36,14 @@
*/
public class NbWorkspaceReader implements WorkspaceReader {
private final WorkspaceRepository repo;
private final NbVersionResolver2 resolver;
private final Collection<? extends ArtifactFixer> fixers;
boolean silence = false;

public NbWorkspaceReader() {
public NbWorkspaceReader(NbVersionResolver2 resolver) {
repo = new WorkspaceRepository("ide", getClass());
fixers = Lookup.getDefault().lookupAll(ArtifactFixer.class);
this.resolver = resolver;
}

@Override
Expand All @@ -51,7 +56,26 @@ public File findArtifact(org.eclipse.aether.artifact.Artifact artifact) {
if (silence) {
return null;
}

if (resolver != null) {
org.eclipse.aether.artifact.Artifact a = resolver.getResolvingArtifact();
if (a != null &&
(!Objects.equals(a.getClassifier(), artifact.getClassifier()) ||
!Objects.equals(a.getExtension(), artifact.getExtension()) ||
!Objects.equals(a.getVersion(), artifact.getVersion()))) {
// POM is requested, but it is not required by the project itself, but implied
// as metadata of some really requested artifact. Add the classifier/extension
// of the true artifact as attributes, so that ArtifactFixer may read it.

// TODO: improve interface to ArtifactFixer, so the info can be provided in a
// more sane way.
Map<String, String> m = new HashMap<>(artifact.getProperties());
m.put("nbResolvingArtifact.group", artifact.getGroupId());
m.put("nbResolvingArtifact.id", artifact.getArtifactId());
m.put("nbResolvingArtifact.classifier", a.getClassifier());
m.put("nbResolvingArtifact.extension", a.getExtension());
artifact = artifact.setProperties(m);
}
}
for (ArtifactFixer fixer : fixers) {
File f = fixer.resolve(artifact);
if (f != null) {
Expand All @@ -67,6 +91,21 @@ public List<String> findVersions(org.eclipse.aether.artifact.Artifact artifact)
return Collections.emptyList();
}
//this is important for snapshots, without it the SNAPSHOT will be attempted to be resolved to time-based snapshot version
if (resolver != null) {
org.eclipse.aether.artifact.Artifact a = resolver.getResolvingArtifact();
if (a != null &&
(!Objects.equals(a.getClassifier(), artifact.getClassifier()) ||
!Objects.equals(a.getExtension(), artifact.getExtension()) ||
!Objects.equals(a.getVersion(), artifact.getVersion()))) {
// see note in findArtifact
Map<String, String> m = new HashMap<>(artifact.getProperties());
m.put("nbResolvingArtifact.group", artifact.getGroupId());
m.put("nbResolvingArtifact.id", artifact.getArtifactId());
m.put("nbResolvingArtifact.classifier", a.getClassifier());
m.put("nbResolvingArtifact.extension", a.getExtension());
artifact = artifact.setProperties(m);
}
}
for (ArtifactFixer fixer : fixers) {
File f = fixer.resolve(artifact);
if (f != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public static MavenProject loadOriginalMavenProjectInternal(MavenEmbedder projec
res = NbArtifactFixer.collectFallbackArtifacts(() -> projectEmbedder.readProjectWithDependencies(req, true), (c) ->
c.forEach(a -> {
// artifact fixer only fakes POMs.
fakes.add(projectEmbedder.createArtifactWithClassifier(a.getGroupId(), a.getArtifactId(), a.getVersion(), "pom", a.getClassifier())); // NOI18N
fakes.add(projectEmbedder.createArtifactWithClassifier(a.getGroupId(), a.getArtifactId(), a.getVersion(), a.getExtension(), a.getClassifier())); // NOI18N
}
));
newproject = res.getProject();
Expand Down

0 comments on commit aaeabe1

Please sign in to comment.