Cache parent artifact resolution

This commit is contained in:
Guillaume Nodet 2024-04-02 15:07:01 +02:00
parent 97518b5b29
commit 8418fb3960
5 changed files with 72 additions and 2 deletions

View File

@ -304,6 +304,7 @@ under the License.
<!-- END default constructor on Plexus/JSR 330 components --> <!-- END default constructor on Plexus/JSR 330 components -->
<!-- system property with that name no longer evaluated --> <!-- system property with that name no longer evaluated -->
<exclude>org.apache.maven.project.DefaultProjectBuilder#DISABLE_GLOBAL_MODEL_CACHE_SYSTEM_PROPERTY</exclude> <exclude>org.apache.maven.project.DefaultProjectBuilder#DISABLE_GLOBAL_MODEL_CACHE_SYSTEM_PROPERTY</exclude>
<exclude>org.apache.maven.project.ProjectModelResolver#ProjectModelResolver(org.eclipse.aether.RepositorySystemSession,org.eclipse.aether.RequestTrace,org.eclipse.aether.RepositorySystem,org.eclipse.aether.impl.RemoteRepositoryManager,java.util.List,org.apache.maven.project.ProjectBuildingRequest$RepositoryMerging,org.apache.maven.project.ReactorModelPool):CONSTRUCTOR_REMOVED</exclude>
<!-- was only a workaround for Plexus Container, hopefully never used by anyone else --> <!-- was only a workaround for Plexus Container, hopefully never used by anyone else -->
<exclude>org.apache.maven.plugin.DefaultBuildPluginManager#setMojoExecutionListeners(java.util.List)</exclude> <exclude>org.apache.maven.plugin.DefaultBuildPluginManager#setMojoExecutionListeners(java.util.List)</exclude>
<!-- could have never been used due to usage of non-export class (CoreExportsProvider) --> <!-- could have never been used due to usage of non-export class (CoreExportsProvider) -->

View File

@ -208,6 +208,7 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
remoteRepositoryManager, remoteRepositoryManager,
project.getRemoteProjectRepositories(), project.getRemoteProjectRepositories(),
ProjectBuildingRequest.RepositoryMerging.POM_DOMINANT, ProjectBuildingRequest.RepositoryMerging.POM_DOMINANT,
null,
null)); null));
request.setTransformerContextBuilder(modelBuilder.newTransformerContextBuilder()); request.setTransformerContextBuilder(modelBuilder.newTransformerContextBuilder());
request.setSystemProperties(toProperties(session.getSystemProperties())); request.setSystemProperties(toProperties(session.getSystemProperties()));

View File

@ -206,6 +206,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
private final RepositorySystemSession session; private final RepositorySystemSession session;
private final List<RemoteRepository> repositories; private final List<RemoteRepository> repositories;
private final ReactorModelPool modelPool; private final ReactorModelPool modelPool;
private final ConcurrentMap<String, Object> parentCache;
private final TransformerContextBuilder transformerContextBuilder; private final TransformerContextBuilder transformerContextBuilder;
private final ExecutorService executor; private final ExecutorService executor;
@ -222,6 +223,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
this.modelPool = null; this.modelPool = null;
this.transformerContextBuilder = null; this.transformerContextBuilder = null;
} }
this.parentCache = new ConcurrentHashMap<>();
} }
ExecutorService createExecutor(int parallelism) { ExecutorService createExecutor(int parallelism) {
@ -893,7 +895,8 @@ public class DefaultProjectBuilder implements ProjectBuilder {
repositoryManager, repositoryManager,
repositories, repositories,
request.getRepositoryMerging(), request.getRepositoryMerging(),
modelPool); modelPool,
parentCache);
modelBuildingRequest.setValidationLevel(request.getValidationLevel()); modelBuildingRequest.setValidationLevel(request.getValidationLevel());
modelBuildingRequest.setProcessPlugins(request.isProcessPlugins()); modelBuildingRequest.setProcessPlugins(request.isProcessPlugins());

View File

@ -23,7 +23,11 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.apache.maven.api.model.Dependency; import org.apache.maven.api.model.Dependency;
@ -57,6 +61,8 @@ import org.eclipse.aether.resolution.VersionRangeResult;
*/ */
public class ProjectModelResolver implements ModelResolver { public class ProjectModelResolver implements ModelResolver {
private static final int MAX_CAP = 0x7fff;
private final RepositorySystemSession session; private final RepositorySystemSession session;
private final RequestTrace trace; private final RequestTrace trace;
@ -79,6 +85,8 @@ public class ProjectModelResolver implements ModelResolver {
private final ProjectBuildingRequest.RepositoryMerging repositoryMerging; private final ProjectBuildingRequest.RepositoryMerging repositoryMerging;
private final Map<String, ForkJoinTask<Result>> parentCache;
public ProjectModelResolver( public ProjectModelResolver(
RepositorySystemSession session, RepositorySystemSession session,
RequestTrace trace, RequestTrace trace,
@ -86,7 +94,8 @@ public class ProjectModelResolver implements ModelResolver {
RemoteRepositoryManager remoteRepositoryManager, RemoteRepositoryManager remoteRepositoryManager,
List<RemoteRepository> repositories, List<RemoteRepository> repositories,
ProjectBuildingRequest.RepositoryMerging repositoryMerging, ProjectBuildingRequest.RepositoryMerging repositoryMerging,
ReactorModelPool modelPool) { ReactorModelPool modelPool,
Map<String, Object> parentCache) {
this.session = session; this.session = session;
this.trace = trace; this.trace = trace;
this.resolver = resolver; this.resolver = resolver;
@ -98,6 +107,7 @@ public class ProjectModelResolver implements ModelResolver {
this.repositoryMerging = repositoryMerging; this.repositoryMerging = repositoryMerging;
this.repositoryIds = new HashSet<>(); this.repositoryIds = new HashSet<>();
this.modelPool = modelPool; this.modelPool = modelPool;
this.parentCache = parentCache != null ? (Map) parentCache : new ConcurrentHashMap<>();
} }
private ProjectModelResolver(ProjectModelResolver original) { private ProjectModelResolver(ProjectModelResolver original) {
@ -111,6 +121,7 @@ public class ProjectModelResolver implements ModelResolver {
this.repositoryMerging = original.repositoryMerging; this.repositoryMerging = original.repositoryMerging;
this.repositoryIds = new HashSet<>(original.repositoryIds); this.repositoryIds = new HashSet<>(original.repositoryIds);
this.modelPool = original.modelPool; this.modelPool = original.modelPool;
this.parentCache = original.parentCache;
} }
public void addRepository(Repository repository) throws InvalidRepositoryException { public void addRepository(Repository repository) throws InvalidRepositoryException {
@ -171,9 +182,62 @@ public class ProjectModelResolver implements ModelResolver {
return new ArtifactModelSource(pomArtifact.getFile(), groupId, artifactId, version); return new ArtifactModelSource(pomArtifact.getFile(), groupId, artifactId, version);
} }
record Result(ModelSource source, Parent parent, Exception e) {}
private ForkJoinPool pool = new ForkJoinPool(MAX_CAP);
@Override @Override
public ModelSource resolveModel(final Parent parent, AtomicReference<Parent> modified) public ModelSource resolveModel(final Parent parent, AtomicReference<Parent> modified)
throws UnresolvableModelException { throws UnresolvableModelException {
Result result;
try {
ForkJoinTask<Result> future = parentCache.computeIfAbsent(parent.getId(), id -> new ForkJoinTask<>() {
Result result;
@Override
public Result getRawResult() {
return result;
}
@Override
protected void setRawResult(Result result) {
this.result = result;
}
@Override
protected boolean exec() {
try {
AtomicReference<Parent> modified = new AtomicReference<>();
ModelSource source = doResolveModel(parent, modified);
result = new Result(source, modified.get(), null);
} catch (Exception e) {
result = new Result(null, null, e);
}
return true;
}
});
pool.execute(future);
result = future.get();
} catch (Exception e) {
throw new UnresolvableModelException(e, parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
}
if (result.e != null) {
uncheckedThrow(result.e);
return null;
} else {
if (result.parent != null && modified != null) {
modified.set(result.parent);
}
return result.source;
}
}
static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
throw (T) t; // rely on vacuous cast
}
private ModelSource doResolveModel(Parent parent, AtomicReference<Parent> modified)
throws UnresolvableModelException {
try { try {
final Artifact artifact = final Artifact artifact =
new DefaultArtifact(parent.getGroupId(), parent.getArtifactId(), "", "pom", parent.getVersion()); new DefaultArtifact(parent.getGroupId(), parent.getArtifactId(), "", "pom", parent.getVersion());

View File

@ -200,6 +200,7 @@ class ProjectModelResolverTest extends AbstractMavenProjectTestCase {
getContainer().lookup(RemoteRepositoryManager.class), getContainer().lookup(RemoteRepositoryManager.class),
this.getRemoteRepositories(), this.getRemoteRepositories(),
ProjectBuildingRequest.RepositoryMerging.REQUEST_DOMINANT, ProjectBuildingRequest.RepositoryMerging.REQUEST_DOMINANT,
null,
null); null);
} }