Skip to content

Commit

Permalink
[MNG-8520] Add cache for model resolution during project building (#2047
Browse files Browse the repository at this point in the history
)
  • Loading branch information
gnodet authored Jan 23, 2025
1 parent e9805bb commit ab4149f
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
*/
package org.apache.maven.api.services.model;

import java.util.List;
import java.util.function.Supplier;

import org.apache.maven.api.RemoteRepository;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.ThreadSafe;
import org.apache.maven.api.services.Source;
Expand All @@ -40,7 +42,14 @@
@ThreadSafe
public interface ModelCache {

<T> T computeIfAbsent(String groupId, String artifactId, String version, String tag, Supplier<T> data);
<T> T computeIfAbsent(
List<RemoteRepository> repositories,
String groupId,
String artifactId,
String version,
String classifier,
String tag,
Supplier<T> data);

<T> T computeIfAbsent(Source path, String tag, Supplier<T> data);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -129,6 +130,7 @@ public class DefaultModelBuilder implements ModelBuilder {
private static final String FILE = "file";
private static final String IMPORT = "import";
private static final String PARENT = "parent";
private static final String MODEL = "model";

private final Logger logger = LoggerFactory.getLogger(getClass());

Expand Down Expand Up @@ -1025,7 +1027,8 @@ Model resolveAndReadParentExternally(Model childModel, DefaultProfileActivationC
modelSource = resolveReactorModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
if (modelSource == null) {
AtomicReference<Parent> modified = new AtomicReference<>();
modelSource = modelResolver.resolveModel(request.getSession(), repositories, parent, modified);
modelSource = new CachingModelResolver()
.resolveModel(request.getSession(), repositories, parent, modified);
if (modified.get() != null) {
parent = modified.get();
}
Expand Down Expand Up @@ -1605,9 +1608,11 @@ private DependencyManagement loadDependencyManagement(Dependency dependency, Col
}

Model importModel = cache(
repositories,
groupId,
artifactId,
version,
null,
IMPORT,
() -> doLoadDependencyManagement(dependency, groupId, artifactId, version, importIds));
DependencyManagement importMgmt = importModel != null ? importModel.getDependencyManagement() : null;
Expand Down Expand Up @@ -1641,8 +1646,8 @@ private Model doLoadDependencyManagement(
try {
importSource = resolveReactorModel(groupId, artifactId, version);
if (importSource == null) {
importSource = modelResolver.resolveModel(
request.getSession(), repositories, dependency, new AtomicReference<>());
importSource = new CachingModelResolver()
.resolveModel(request.getSession(), repositories, dependency, new AtomicReference<>());
}
} catch (ModelBuilderException | ModelResolverException e) {
StringBuilder buffer = new StringBuilder(256);
Expand Down Expand Up @@ -1711,8 +1716,15 @@ ModelSource resolveReactorModel(String groupId, String artifactId, String versio
return null;
}

private <T> T cache(String groupId, String artifactId, String version, String tag, Supplier<T> supplier) {
return cache.computeIfAbsent(groupId, artifactId, version, tag, supplier);
private <T> T cache(
List<RemoteRepository> repositories,
String groupId,
String artifactId,
String version,
String classifier,
String tag,
Supplier<T> supplier) {
return cache.computeIfAbsent(repositories, groupId, artifactId, version, classifier, tag, supplier);
}

private <T> T cache(Source source, String tag, Supplier<T> supplier) throws ModelBuilderException {
Expand Down Expand Up @@ -1801,6 +1813,85 @@ private String transformPath(String path, ActivationFile target, String location
}
return profiles.stream().map(new ProfileInterpolator()).toList();
}

record ModelResolverResult(ModelSource source, String resolvedVersion) {}

class CachingModelResolver implements ModelResolver {
@Override
public ModelSource resolveModel(
Session session,
List<RemoteRepository> repositories,
Parent parent,
AtomicReference<Parent> modified)
throws ModelResolverException {
ModelResolverResult result = cache.computeIfAbsent(
repositories,
parent.getGroupId(),
parent.getArtifactId(),
parent.getVersion(),
null,
MODEL,
() -> {
AtomicReference<Parent> mod = new AtomicReference<>();
ModelSource res = modelResolver.resolveModel(session, repositories, parent, mod);
return new ModelResolverResult(
res, mod.get() != null ? mod.get().getVersion() : null);
});
if (result.resolvedVersion != null && modified != null) {
modified.set(parent.withVersion(result.resolvedVersion));
}
return result.source;
}

@Override
public ModelSource resolveModel(
Session session,
List<RemoteRepository> repositories,
Dependency dependency,
AtomicReference<Dependency> modified)
throws ModelResolverException {
ModelResolverResult result = cache.computeIfAbsent(
repositories,
dependency.getGroupId(),
dependency.getArtifactId(),
dependency.getVersion(),
dependency.getClassifier(),
MODEL,
() -> {
AtomicReference<Dependency> mod = new AtomicReference<>();
ModelSource res = modelResolver.resolveModel(session, repositories, dependency, mod);
return new ModelResolverResult(
res, mod.get() != null ? mod.get().getVersion() : null);
});
if (result.resolvedVersion != null && modified != null) {
modified.set(dependency.withVersion(result.resolvedVersion));
}
return result.source;
}

@Override
public ModelSource resolveModel(
Session session,
List<RemoteRepository> repositories,
String groupId,
String artifactId,
String version,
String classifier,
Consumer<String> resolvedVersion)
throws ModelResolverException {
ModelResolverResult result =
cache.computeIfAbsent(repositories, groupId, artifactId, version, classifier, MODEL, () -> {
AtomicReference<String> mod = new AtomicReference<>();
ModelSource res = modelResolver.resolveModel(
session, repositories, groupId, artifactId, version, classifier, mod::set);
return new ModelResolverResult(res, mod.get());
});
if (result.resolvedVersion != null) {
resolvedVersion.accept(result.resolvedVersion);
}
return result.source;
}
}
}

@SuppressWarnings("deprecation")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
*/
package org.apache.maven.internal.impl.model;

import java.util.Objects;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;

import org.apache.maven.api.RemoteRepository;
import org.apache.maven.api.services.Source;
import org.apache.maven.api.services.model.ModelCache;

Expand All @@ -46,8 +47,15 @@ private DefaultModelCache(ConcurrentMap<Object, Supplier<?>> cache) {

@Override
@SuppressWarnings({"unchecked"})
public <T> T computeIfAbsent(String groupId, String artifactId, String version, String tag, Supplier<T> data) {
return (T) computeIfAbsent(new GavCacheKey(groupId, artifactId, version, tag), data);
public <T> T computeIfAbsent(
List<RemoteRepository> repositories,
String groupId,
String artifactId,
String version,
String classifier,
String tag,
Supplier<T> data) {
return (T) computeIfAbsent(new RgavCacheKey(repositories, groupId, artifactId, version, classifier, tag), data);
}

@Override
Expand All @@ -65,26 +73,19 @@ protected Object computeIfAbsent(Object key, Supplier<?> data) {
return cache.computeIfAbsent(key, k -> new CachingSupplier<>(data)).get();
}

static class GavCacheKey {
record RgavCacheKey(
List<RemoteRepository> repositories,
String groupId,
String artifactId,
String version,
String classifier,
String tag) {

private final String gav;

private final String tag;

private final int hash;

GavCacheKey(String groupId, String artifactId, String version, String tag) {
this(gav(groupId, artifactId, version), tag);
}

GavCacheKey(String gav, String tag) {
this.gav = gav;
this.tag = tag;
this.hash = Objects.hash(gav, tag);
}

private static String gav(String groupId, String artifactId, String version) {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("GavCacheKey[");
sb.append("gav='");
if (groupId != null) {
sb.append(groupId);
}
Expand All @@ -96,71 +97,28 @@ private static String gav(String groupId, String artifactId, String version) {
if (version != null) {
sb.append(version);
}
return sb.toString();
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (null == obj || !getClass().equals(obj.getClass())) {
return false;
sb.append(":");
if (classifier != null) {
sb.append(classifier);
}
GavCacheKey that = (GavCacheKey) obj;
return Objects.equals(this.gav, that.gav) && Objects.equals(this.tag, that.tag);
}

@Override
public int hashCode() {
return hash;
}

@Override
public String toString() {
return "GavCacheKey[" + "gav='" + gav + '\'' + ", tag='" + tag + '\'' + ']';
sb.append("', tag='");
sb.append(tag);
sb.append("']");
return sb.toString();
}
}

private static final class SourceCacheKey {
private final Source source;

private final String tag;

private final int hash;

SourceCacheKey(Source source, String tag) {
this.source = source;
this.tag = tag;
this.hash = Objects.hash(source, tag);
}
record SourceCacheKey(Source source, String tag) {

@Override
public String toString() {
return "SourceCacheKey[" + "location=" + source.getLocation() + ", tag=" + tag + ", path="
+ source.getPath() + ']';
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (null == obj || !getClass().equals(obj.getClass())) {
return false;
}
SourceCacheKey that = (SourceCacheKey) obj;
return Objects.equals(this.source, that.source) && Objects.equals(this.tag, that.tag);
}

@Override
public int hashCode() {
return hash;
}
}

static class CachingSupplier<T> implements Supplier<T> {
final Supplier<T> supplier;
Supplier<T> supplier;
volatile Object value;

CachingSupplier(Supplier<T> supplier) {
Expand All @@ -176,6 +134,7 @@ public T get() {
if ((v = value) == null) {
try {
v = value = supplier.get();
supplier = null;
} catch (Exception e) {
v = value = new AltRes(e);
}
Expand Down

0 comments on commit ab4149f

Please sign in to comment.