You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2023/11/07 08:20:57 UTC
(maven) branch master updated (4ddc4bf406 -> 7fcdd32e87)
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
from 4ddc4bf406 Use 4.0.99 as as intermediate model to keep 4.1.0 for the Maven 4.x model (#1297)
new 54ca01c779 [MNG-7615] Use lambas to access ModelCache
new 53ff1690de [MNG-7615] Add some methods for supporting Maven 4 api to ProfileInjector / ProfileSelector
new 2f6ec159fe [MNG-7615] Add a cycle detection to the transformer context
new 7fcdd32e87 [MNG-7615] Multithreaded project builder
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../maven/internal/impl/DefaultProjectBuilder.java | 12 +-
.../maven/project/DefaultProjectBuilder.java | 1278 ++++++++++----------
.../ConsumerPomArtifactTransformerTest.java | 4 +-
maven-model-builder/pom.xml | 5 +
.../building/BuildModelSourceTransformer.java | 11 +-
.../maven/model/building/DefaultModelBuilder.java | 494 ++++----
.../model/building/DefaultTransformerContext.java | 4 +-
.../building/DefaultTransformerContextBuilder.java | 176 +++
.../org/apache/maven/model/building}/Graph.java | 29 +-
.../apache/maven/model/building/ModelCache.java | 17 +
.../maven/model/building/TransformerContext.java | 14 +-
.../model/profile/DefaultProfileInjector.java | 55 +-
.../model/profile/DefaultProfileSelector.java | 12 +
.../maven/model/profile/ProfileInjector.java | 43 +
.../maven/model/profile/ProfileSelector.java | 21 +
.../building/BuildModelSourceTransformerTest.java | 4 +-
.../repository/internal/DefaultModelCache.java | 28 +-
17 files changed, 1233 insertions(+), 974 deletions(-)
create mode 100644 maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java
copy {maven-core/src/main/java/org/apache/maven/project => maven-model-builder/src/main/java/org/apache/maven/model/building}/Graph.java (84%)
(maven) 01/04: [MNG-7615] Use lambas to access ModelCache
Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
commit 54ca01c7790517cc6977412f84e3dff98ac029a1
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Fri Dec 2 09:56:40 2022 +0100
[MNG-7615] Use lambas to access ModelCache
---
maven-model-builder/pom.xml | 2 +
.../maven/model/building/DefaultModelBuilder.java | 153 +++++++++++----------
.../apache/maven/model/building/ModelCache.java | 17 +++
.../repository/internal/DefaultModelCache.java | 28 +++-
4 files changed, 125 insertions(+), 75 deletions(-)
diff --git a/maven-model-builder/pom.xml b/maven-model-builder/pom.xml
index e7a6fe7aa9..f315f64b75 100644
--- a/maven-model-builder/pom.xml
+++ b/maven-model-builder/pom.xml
@@ -142,6 +142,8 @@ under the License.
<exclude>org.apache.maven.model.building.ModelCache#put(org.apache.maven.building.Source,java.lang.String,java.lang.Object):METHOD_NEW_DEFAULT</exclude>
<exclude>org.apache.maven.model.building.ModelCache#put(org.apache.maven.building.Source,org.apache.maven.model.building.ModelCacheTag,java.lang.Object):METHOD_NEW_DEFAULT</exclude>
<exclude>org.apache.maven.model.building.ModelCache#put(java.lang.String,java.lang.String,java.lang.String,org.apache.maven.model.building.ModelCacheTag,java.lang.Object):METHOD_NEW_DEFAULT</exclude>
+ <exclude>org.apache.maven.model.building.ModelCache#computeIfAbsent(java.lang.String,java.lang.String,java.lang.String,org.apache.maven.model.building.ModelCacheTag,java.util.function.Supplier):METHOD_NEW_DEFAULT</exclude>
+ <exclude>org.apache.maven.model.building.ModelCache#computeIfAbsent(org.apache.maven.building.Source,org.apache.maven.model.building.ModelCacheTag,java.util.function.Supplier):METHOD_NEW_DEFAULT</exclude>
<exclude>org.apache.maven.model.composition.DependencyManagementImporter#importManagement(org.apache.maven.model.Model,java.util.List,org.apache.maven.model.building.ModelBuildingRequest,org.apache.maven.model.building.ModelProblemCollector):METHOD_REMOVED</exclude>
<exclude>org.apache.maven.model.composition.DefaultDependencyManagementImporter#importManagement(org.apache.maven.model.Model,java.util.List,org.apache.maven.model.building.ModelBuildingRequest,org.apache.maven.model.building.ModelProblemCollector):METHOD_REMOVED</exclude>
<exclude>org.apache.maven.model.inheritance.DefaultInheritanceAssembler</exclude>
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
index c8ba3ca319..f3399b748d 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
@@ -38,7 +38,11 @@ import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
+import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ForkJoinTask;
+import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.maven.api.feature.Features;
@@ -1007,12 +1011,11 @@ public class DefaultModelBuilder implements ModelBuilder {
private Model readFileModel(ModelBuildingRequest request, DefaultModelProblemCollector problems)
throws ModelBuildingException {
ModelSource modelSource = request.getModelSource();
- org.apache.maven.api.model.Model model = fromCache(request.getModelCache(), modelSource, ModelCacheTag.FILE);
- if (model == null) {
- model = doReadFileModel(modelSource, request, problems);
-
- intoCache(request.getModelCache(), modelSource, ModelCacheTag.FILE, model);
- }
+ org.apache.maven.api.model.Model model = cache(
+ request.getModelCache(),
+ modelSource,
+ ModelCacheTag.FILE,
+ () -> doReadFileModel(modelSource, request, problems));
if (modelSource instanceof FileModelSource) {
if (request.getTransformerContextBuilder() instanceof DefaultTransformerContextBuilder) {
@@ -1129,45 +1132,41 @@ public class DefaultModelBuilder implements ModelBuilder {
throws ModelBuildingException {
ModelSource modelSource = request.getModelSource();
- ModelData cachedData = fromCache(request.getModelCache(), modelSource, ModelCacheTag.RAW);
- if (cachedData != null) {
- return cachedData.getModel();
- }
+ ModelData cachedData = cache(request.getModelCache(), modelSource, ModelCacheTag.RAW, () -> {
+ Model rawModel;
+ if (Features.buildConsumer(request.getUserProperties()) && modelSource instanceof FileModelSource) {
+ rawModel = readFileModel(request, problems);
+ File pomFile = ((FileModelSource) modelSource).getFile();
- Model rawModel;
- if (Features.buildConsumer(request.getUserProperties()) && modelSource instanceof FileModelSource) {
- rawModel = readFileModel(request, problems);
- File pomFile = ((FileModelSource) modelSource).getFile();
-
- try {
- if (request.getTransformerContextBuilder() != null) {
- TransformerContext context =
- request.getTransformerContextBuilder().initialize(request, problems);
- transformer.transform(pomFile.toPath(), context, rawModel);
+ try {
+ if (request.getTransformerContextBuilder() != null) {
+ TransformerContext context =
+ request.getTransformerContextBuilder().initialize(request, problems);
+ transformer.transform(pomFile.toPath(), context, rawModel);
+ }
+ } catch (TransformerException e) {
+ problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V40).setException(e));
}
- } catch (TransformerException e) {
- problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V40).setException(e));
+ } else if (request.getFileModel() == null) {
+ rawModel = readFileModel(request, problems);
+ } else {
+ rawModel = request.getFileModel().clone();
}
- } else if (request.getFileModel() == null) {
- rawModel = readFileModel(request, problems);
- } else {
- rawModel = request.getFileModel().clone();
- }
- modelValidator.validateRawModel(rawModel, request, problems);
+ modelValidator.validateRawModel(rawModel, request, problems);
- if (hasFatalErrors(problems)) {
- throw problems.newModelBuildingException();
- }
-
- String groupId = getGroupId(rawModel);
- String artifactId = rawModel.getArtifactId();
- String version = getVersion(rawModel);
+ if (hasFatalErrors(problems)) {
+ throw problems.newModelBuildingException();
+ }
- ModelData modelData = new ModelData(modelSource, rawModel, groupId, artifactId, version);
- intoCache(request.getModelCache(), modelSource, ModelCacheTag.RAW, modelData);
+ String groupId = getGroupId(rawModel);
+ String artifactId = rawModel.getArtifactId();
+ String version = getVersion(rawModel);
- return rawModel;
+ ModelData modelData = new ModelData(modelSource, rawModel, groupId, artifactId, version);
+ return modelData;
+ });
+ return cachedData != null ? cachedData.getModel() : null;
}
private String getGroupId(Model model) {
@@ -1708,15 +1707,11 @@ public class DefaultModelBuilder implements ModelBuilder {
}
org.apache.maven.api.model.DependencyManagement importMgmt =
- fromCache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT);
- if (importMgmt == null) {
- DependencyManagement importMgmtV3 = doLoadDependencyManagement(
- model, request, problems, dependency, groupId, artifactId, version, importIds);
- if (importMgmtV3 != null) {
- importMgmt = importMgmtV3.getDelegate();
- intoCache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMgmt);
- }
- }
+ cache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, () -> {
+ DependencyManagement importMgmtV3 = doLoadDependencyManagement(
+ model, request, problems, dependency, groupId, artifactId, version, importIds);
+ return importMgmtV3 != null ? importMgmtV3.getDelegate() : null;
+ });
// [MNG-5600] Dependency management import should support exclusions.
List<Exclusion> exclusions = dependency.getDelegate().getExclusions();
@@ -1823,32 +1818,50 @@ public class DefaultModelBuilder implements ModelBuilder {
return importMgmt;
}
- private <T> void intoCache(
- ModelCache modelCache, String groupId, String artifactId, String version, ModelCacheTag<T> tag, T data) {
- if (modelCache != null) {
- modelCache.put(groupId, artifactId, version, tag, data);
- }
- }
-
- private <T> void intoCache(ModelCache modelCache, Source source, ModelCacheTag<T> tag, T data) {
- if (modelCache != null) {
- modelCache.put(source, tag, data);
- }
- }
-
- private static <T> T fromCache(
- ModelCache modelCache, String groupId, String artifactId, String version, ModelCacheTag<T> tag) {
- if (modelCache != null) {
- return modelCache.get(groupId, artifactId, version, tag);
+ private static <T> T cache(
+ ModelCache cache,
+ String groupId,
+ String artifactId,
+ String version,
+ ModelCacheTag<T> tag,
+ Callable<T> supplier) {
+ return doWithCache(cache, supplier, s -> cache.computeIfAbsent(groupId, artifactId, version, tag, s));
+ }
+
+ private static <T> T cache(ModelCache cache, Source source, ModelCacheTag<T> tag, Callable<T> supplier) {
+ return doWithCache(cache, supplier, s -> cache.computeIfAbsent(source, tag, s));
+ }
+
+ private static <T> T doWithCache(
+ ModelCache cache, Callable<T> supplier, Function<Supplier<Supplier<T>>, T> asyncSupplierConsumer) {
+ if (cache != null) {
+ return asyncSupplierConsumer.apply(() -> {
+ ForkJoinTask<T> task = ForkJoinTask.adapt(supplier);
+ task.fork();
+ return () -> {
+ task.quietlyJoin();
+ if (task.isCompletedAbnormally()) {
+ Throwable e = task.getException();
+ while (e instanceof RuntimeException && e.getCause() != null) {
+ e = e.getCause();
+ }
+ uncheckedThrow(e);
+ }
+ return task.getRawResult();
+ };
+ });
+ } else {
+ try {
+ return supplier.call();
+ } catch (Exception e) {
+ uncheckedThrow(e);
+ return null;
+ }
}
- return null;
}
- private static <T> T fromCache(ModelCache modelCache, Source source, ModelCacheTag<T> tag) {
- if (modelCache != null) {
- return modelCache.get(source, tag);
- }
- return null;
+ static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
+ throw (T) t; // rely on vacuous cast
}
private void fireEvent(
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCache.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCache.java
index 63fd48372d..6f9cba374e 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCache.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCache.java
@@ -18,6 +18,8 @@
*/
package org.apache.maven.model.building;
+import java.util.function.Supplier;
+
import org.apache.maven.building.Source;
/**
@@ -127,4 +129,19 @@ public interface ModelCache {
Object obj = get(groupId, artifactId, version, tag.getName());
return (obj != null) ? tag.fromCache(tag.getType().cast(obj)) : null;
}
+
+ default <T> T computeIfAbsent(
+ String groupId, String artifactId, String version, ModelCacheTag<T> tag, Supplier<Supplier<T>> data) {
+ Object obj = computeIfAbsent(groupId, artifactId, version, tag.getName(), (Supplier) data);
+ return (obj != null) ? tag.fromCache(tag.getType().cast(obj)) : null;
+ }
+
+ default <T> T computeIfAbsent(Source path, ModelCacheTag<T> tag, Supplier<Supplier<T>> data) {
+ Object obj = computeIfAbsent(path, tag.getName(), (Supplier) data);
+ return (obj != null) ? tag.fromCache(tag.getType().cast(obj)) : null;
+ }
+
+ Object computeIfAbsent(String groupId, String artifactId, String version, String tag, Supplier<Supplier<?>> data);
+
+ Object computeIfAbsent(Source path, String tag, Supplier<Supplier<?>> data);
}
diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java
index c503487e76..7b2831e921 100644
--- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java
+++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java
@@ -21,6 +21,7 @@ package org.apache.maven.repository.internal;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
import org.apache.maven.building.Source;
import org.apache.maven.model.building.ModelCache;
@@ -34,10 +35,10 @@ public class DefaultModelCache implements ModelCache {
private static final String KEY = DefaultModelCache.class.getName();
- private final Map<Object, Object> cache;
+ private final Map<Object, Supplier<?>> cache;
public static ModelCache newInstance(RepositorySystemSession session) {
- Map<Object, Object> cache;
+ Map<Object, Supplier<?>> cache;
if (session.getCache() == null) {
cache = new ConcurrentHashMap<>();
} else {
@@ -50,7 +51,7 @@ public class DefaultModelCache implements ModelCache {
return new DefaultModelCache(cache);
}
- private DefaultModelCache(Map<Object, Object> cache) {
+ private DefaultModelCache(Map<Object, Supplier<?>> cache) {
this.cache = cache;
}
@@ -73,11 +74,28 @@ public class DefaultModelCache implements ModelCache {
}
protected Object get(Object key) {
- return cache.get(key);
+ Supplier<?> s = cache.get(key);
+ return s != null ? s.get() : null;
}
protected void put(Object key, Object data) {
- cache.put(key, data);
+ cache.put(key, () -> data);
+ }
+
+ @Override
+ public Object computeIfAbsent(
+ String groupId, String artifactId, String version, String tag, Supplier<Supplier<?>> data) {
+ return computeIfAbsent(new GavCacheKey(groupId, artifactId, version, tag), data);
+ }
+
+ @Override
+ public Object computeIfAbsent(Source path, String tag, Supplier<Supplier<?>> data) {
+ return computeIfAbsent(new SourceCacheKey(path, tag), data);
+ }
+
+ protected Object computeIfAbsent(Object key, Supplier<Supplier<?>> data) {
+ Supplier<?> s = cache.computeIfAbsent(key, k -> data.get());
+ return s != null ? s.get() : null;
}
static class GavCacheKey {
(maven) 03/04: [MNG-7615] Add a cycle detection to the transformer context
Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
commit 2f6ec159fe49c6ebc769da2b604f4d3af4037267
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Wed Sep 20 20:48:50 2023 +0200
[MNG-7615] Add a cycle detection to the transformer context
---
.../ConsumerPomArtifactTransformerTest.java | 4 +-
.../building/BuildModelSourceTransformer.java | 11 +-
.../maven/model/building/DefaultModelBuilder.java | 137 +---------------
.../model/building/DefaultTransformerContext.java | 4 +-
.../building/DefaultTransformerContextBuilder.java | 178 +++++++++++++++++++++
.../maven/model/building/TransformerContext.java | 14 +-
.../building/BuildModelSourceTransformerTest.java | 4 +-
7 files changed, 201 insertions(+), 151 deletions(-)
diff --git a/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java b/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
index 0c177964dd..0d5ca973c3 100644
--- a/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
+++ b/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
@@ -88,12 +88,12 @@ class ConsumerPomArtifactTransformerTest {
}
@Override
- public Model getRawModel(String groupId, String artifactId) throws IllegalStateException {
+ public Model getRawModel(Path from, String groupId, String artifactId) throws IllegalStateException {
throw new UnsupportedOperationException();
}
@Override
- public Model getRawModel(Path p) {
+ public Model getRawModel(Path from, Path p) {
throw new UnsupportedOperationException();
}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java
index f0a8cb79da..cc1b90c362 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java
@@ -71,7 +71,7 @@ class BuildModelSourceTransformer implements ModelSourceTransformer {
String path = Optional.ofNullable(parent.getRelativePath()).orElse("..");
if (version == null && !path.isEmpty()) {
Optional<RelativeProject> resolvedParent = resolveRelativePath(
- pomFile.getParent(), context, Paths.get(path), parent.getGroupId(), parent.getArtifactId());
+ pomFile, context, Paths.get(path), parent.getGroupId(), parent.getArtifactId());
resolvedParent.ifPresent(relativeProject -> parent.setVersion(relativeProject.getVersion()));
}
}
@@ -99,7 +99,8 @@ class BuildModelSourceTransformer implements ModelSourceTransformer {
void handleReactorDependencies(TransformerContext context, Model model) {
for (Dependency dep : model.getDependencies()) {
if (dep.getVersion() == null) {
- Model depModel = context.getRawModel(dep.getGroupId(), dep.getArtifactId());
+ Model depModel =
+ context.getRawModel(model.getDelegate().getPomFile(), dep.getGroupId(), dep.getArtifactId());
if (depModel != null) {
String v = depModel.getVersion();
if (v == null && depModel.getParent() != null) {
@@ -124,8 +125,8 @@ class BuildModelSourceTransformer implements ModelSourceTransformer {
}
protected Optional<RelativeProject> resolveRelativePath(
- Path projectPath, TransformerContext context, Path relativePath, String groupId, String artifactId) {
- Path pomPath = projectPath.resolve(relativePath).normalize();
+ Path pomFile, TransformerContext context, Path relativePath, String groupId, String artifactId) {
+ Path pomPath = pomFile.resolveSibling(relativePath).normalize();
if (Files.isDirectory(pomPath)) {
pomPath = context.locate(pomPath);
}
@@ -134,7 +135,7 @@ class BuildModelSourceTransformer implements ModelSourceTransformer {
return Optional.empty();
}
- Optional<RelativeProject> mappedProject = Optional.ofNullable(context.getRawModel(pomPath.normalize()))
+ Optional<RelativeProject> mappedProject = Optional.ofNullable(context.getRawModel(pomFile, pomPath.normalize()))
.map(BuildModelSourceTransformer::toRelativeProject);
if (mappedProject.isPresent()) {
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
index f3399b748d..77cb05f89e 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
@@ -25,21 +25,16 @@ import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
-import java.util.Set;
import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinTask;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -660,7 +655,7 @@ public class DefaultModelBuilder implements ModelBuilder {
@Override
public DefaultTransformerContextBuilder newTransformerContextBuilder() {
- return new DefaultTransformerContextBuilder();
+ return new DefaultTransformerContextBuilder(this);
}
@Override
@@ -1021,7 +1016,7 @@ public class DefaultModelBuilder implements ModelBuilder {
if (request.getTransformerContextBuilder() instanceof DefaultTransformerContextBuilder) {
DefaultTransformerContextBuilder contextBuilder =
(DefaultTransformerContextBuilder) request.getTransformerContextBuilder();
- contextBuilder.putSource(getGroupId(model), model.getArtifactId(), modelSource);
+ contextBuilder.putSource(getGroupId(model), model.getArtifactId(), (FileModelSource) modelSource);
}
}
@@ -1128,7 +1123,7 @@ public class DefaultModelBuilder implements ModelBuilder {
return model;
}
- private Model readRawModel(ModelBuildingRequest request, DefaultModelProblemCollector problems)
+ Model readRawModel(ModelBuildingRequest request, DefaultModelProblemCollector problems)
throws ModelBuildingException {
ModelSource modelSource = request.getModelSource();
@@ -1169,7 +1164,7 @@ public class DefaultModelBuilder implements ModelBuilder {
return cachedData != null ? cachedData.getModel() : null;
}
- private String getGroupId(Model model) {
+ String getGroupId(Model model) {
return getGroupId(model.getDelegate());
}
@@ -1906,127 +1901,7 @@ public class DefaultModelBuilder implements ModelBuilder {
}
}
- /**
- * Builds up the transformer context.
- * After the buildplan is ready, the build()-method returns the immutable context useful during distribution.
- * This is an inner class, as it must be able to call readRawModel()
- *
- * @since 4.0.0
- */
- private class DefaultTransformerContextBuilder implements TransformerContextBuilder {
- private final DefaultTransformerContext context = new DefaultTransformerContext(modelProcessor);
-
- private final Map<DefaultTransformerContext.GAKey, Set<Source>> mappedSources = new ConcurrentHashMap<>(64);
-
- /**
- * If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt
- *
- * @param request
- * @param collector
- * @return
- */
- @Override
- public TransformerContext initialize(ModelBuildingRequest request, ModelProblemCollector collector) {
- // We must assume the TransformerContext was created using this.newTransformerContextBuilder()
- DefaultModelProblemCollector problems = (DefaultModelProblemCollector) collector;
- return new TransformerContext() {
- @Override
- public Path locate(Path path) {
- File file = modelProcessor.locateExistingPom(path.toFile());
- return file != null ? file.toPath() : null;
- }
-
- @Override
- public String getUserProperty(String key) {
- return context.userProperties.computeIfAbsent(
- key, k -> request.getUserProperties().getProperty(key));
- }
-
- @Override
- public Model getRawModel(String gId, String aId) {
- return context.modelByGA
- .computeIfAbsent(
- new DefaultTransformerContext.GAKey(gId, aId),
- k -> new DefaultTransformerContext.Holder())
- .computeIfAbsent(() -> findRawModel(gId, aId));
- }
-
- @Override
- public Model getRawModel(Path path) {
- return context.modelByPath
- .computeIfAbsent(path, k -> new DefaultTransformerContext.Holder())
- .computeIfAbsent(() -> findRawModel(path));
- }
-
- private Model findRawModel(String groupId, String artifactId) {
- Source source = getSource(groupId, artifactId);
- if (source != null) {
- try {
- ModelBuildingRequest gaBuildingRequest =
- new DefaultModelBuildingRequest(request).setModelSource((ModelSource) source);
- Model model = readRawModel(gaBuildingRequest, problems);
- if (source instanceof FileModelSource) {
- Path path = ((FileModelSource) source).getFile().toPath();
- context.modelByPath
- .computeIfAbsent(path, k -> new DefaultTransformerContext.Holder())
- .computeIfAbsent(() -> model);
- }
- return model;
- } catch (ModelBuildingException e) {
- // gathered with problem collector
- }
- }
- return null;
- }
-
- private Model findRawModel(Path p) {
- if (!Files.isRegularFile(p)) {
- throw new IllegalArgumentException("Not a regular file: " + p);
- }
-
- DefaultModelBuildingRequest req = new DefaultModelBuildingRequest(request)
- .setPomFile(p.toFile())
- .setModelSource(new FileModelSource(p.toFile()));
-
- try {
- Model model = readRawModel(req, problems);
- DefaultTransformerContext.GAKey key =
- new DefaultTransformerContext.GAKey(getGroupId(model), model.getArtifactId());
- context.modelByGA
- .computeIfAbsent(key, k -> new DefaultTransformerContext.Holder())
- .computeIfAbsent(() -> model);
- return model;
- } catch (ModelBuildingException e) {
- // gathered with problem collector
- }
- return null;
- }
- };
- }
-
- @Override
- public TransformerContext build() {
- return context;
- }
-
- public Source getSource(String groupId, String artifactId) {
- Set<Source> sources = mappedSources.get(new DefaultTransformerContext.GAKey(groupId, artifactId));
- if (sources == null) {
- return null;
- }
- return sources.stream()
- .reduce((a, b) -> {
- throw new IllegalStateException(String.format(
- "No unique Source for %s:%s: %s and %s",
- groupId, artifactId, a.getLocation(), b.getLocation()));
- })
- .orElse(null);
- }
-
- public void putSource(String groupId, String artifactId, Source source) {
- mappedSources
- .computeIfAbsent(new DefaultTransformerContext.GAKey(groupId, artifactId), k -> new HashSet<>())
- .add(source);
- }
+ ModelProcessor getModelProcessor() {
+ return modelProcessor;
}
}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java
index c0ac5c38c3..462278445f 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java
@@ -95,12 +95,12 @@ class DefaultTransformerContext implements TransformerContext {
}
@Override
- public Model getRawModel(Path p) {
+ public Model getRawModel(Path from, Path p) {
return Holder.deref(modelByPath.get(p));
}
@Override
- public Model getRawModel(String groupId, String artifactId) {
+ public Model getRawModel(Path from, String groupId, String artifactId) {
return Holder.deref(modelByGA.get(new GAKey(groupId, artifactId)));
}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java
new file mode 100644
index 0000000000..a2dce0e491
--- /dev/null
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java
@@ -0,0 +1,178 @@
+/*
+ * 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.maven.model.building;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.maven.model.Model;
+import org.apache.maven.model.building.DefaultTransformerContext.GAKey;
+import org.apache.maven.model.building.DefaultTransformerContext.Holder;
+import org.codehaus.plexus.util.dag.CycleDetectedException;
+import org.codehaus.plexus.util.dag.DAG;
+
+/**
+ * Builds up the transformer context.
+ * After the buildplan is ready, the build()-method returns the immutable context useful during distribution.
+ * This is an inner class, as it must be able to call readRawModel()
+ *
+ * @since 4.0.0
+ */
+class DefaultTransformerContextBuilder implements TransformerContextBuilder {
+ private final DAG dag = new DAG();
+ private final DefaultModelBuilder defaultModelBuilder;
+ private final DefaultTransformerContext context;
+
+ private final Map<String, Set<FileModelSource>> mappedSources = new ConcurrentHashMap<>(64);
+
+ DefaultTransformerContextBuilder(DefaultModelBuilder defaultModelBuilder) {
+ this.defaultModelBuilder = defaultModelBuilder;
+ this.context = new DefaultTransformerContext(defaultModelBuilder.getModelProcessor());
+ }
+
+ /**
+ * If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt
+ */
+ @Override
+ public TransformerContext initialize(ModelBuildingRequest request, ModelProblemCollector collector) {
+ // We must assume the TransformerContext was created using this.newTransformerContextBuilder()
+ DefaultModelProblemCollector problems = (DefaultModelProblemCollector) collector;
+ return new TransformerContext() {
+ @Override
+ public Path locate(Path path) {
+ return context.locate(path);
+ }
+
+ @Override
+ public String getUserProperty(String key) {
+ return context.userProperties.computeIfAbsent(
+ key, k -> request.getUserProperties().getProperty(key));
+ }
+
+ @Override
+ public Model getRawModel(Path from, String gId, String aId) {
+ Model model = findRawModel(from, gId, aId);
+ if (model != null) {
+ context.modelByGA.put(new GAKey(gId, aId), new Holder(model));
+ context.modelByPath.put(model.getPomFile().toPath(), new Holder(model));
+ }
+ return model;
+ }
+
+ @Override
+ public Model getRawModel(Path from, Path path) {
+ Model model = findRawModel(from, path);
+ if (model != null) {
+ String groupId = defaultModelBuilder.getGroupId(model);
+ context.modelByGA.put(
+ new DefaultTransformerContext.GAKey(groupId, model.getArtifactId()), new Holder(model));
+ context.modelByPath.put(path, new Holder(model));
+ }
+ return model;
+ }
+
+ private Model findRawModel(Path from, String groupId, String artifactId) {
+ FileModelSource source = getSource(groupId, artifactId);
+ if (source != null) {
+ if (!addEdge(from, source.getFile().toPath(), problems)) {
+ return null;
+ }
+ try {
+ ModelBuildingRequest gaBuildingRequest =
+ new DefaultModelBuildingRequest(request).setModelSource(source);
+ return defaultModelBuilder.readRawModel(gaBuildingRequest, problems);
+ } catch (ModelBuildingException e) {
+ // gathered with problem collector
+ }
+ }
+ return null;
+ }
+
+ private Model findRawModel(Path from, Path p) {
+ if (!Files.isRegularFile(p)) {
+ throw new IllegalArgumentException("Not a regular file: " + p);
+ }
+
+ if (!addEdge(from, p, problems)) {
+ return null;
+ }
+
+ DefaultModelBuildingRequest req = new DefaultModelBuildingRequest(request)
+ .setPomFile(p.toFile())
+ .setModelSource(new FileModelSource(p.toFile()));
+
+ try {
+ return defaultModelBuilder.readRawModel(req, problems);
+ } catch (ModelBuildingException e) {
+ // gathered with problem collector
+ }
+ return null;
+ }
+ };
+ }
+
+ private boolean addEdge(Path from, Path p, DefaultModelProblemCollector problems) {
+ try {
+ synchronized (dag) {
+ dag.addEdge(from.toString(), p.toString());
+ }
+ return true;
+ } catch (CycleDetectedException e) {
+ problems.add(new DefaultModelProblem(
+ "Cycle detected between models at " + from + " and " + p,
+ ModelProblem.Severity.FATAL,
+ null,
+ null,
+ 0,
+ 0,
+ null,
+ e));
+ return false;
+ }
+ }
+
+ @Override
+ public TransformerContext build() {
+ return context;
+ }
+
+ public FileModelSource getSource(String groupId, String artifactId) {
+ Set<FileModelSource> sources = mappedSources.get(groupId + ":" + artifactId);
+ if (sources == null) {
+ return null;
+ }
+ return sources.stream()
+ .reduce((a, b) -> {
+ throw new IllegalStateException(String.format(
+ "No unique Source for %s:%s: %s and %s",
+ groupId, artifactId, a.getLocation(), b.getLocation()));
+ })
+ .orElse(null);
+ }
+
+ public void putSource(String groupId, String artifactId, FileModelSource source) {
+ mappedSources
+ .computeIfAbsent(groupId + ":" + artifactId, k -> new HashSet<>())
+ .add(source);
+ }
+}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java
index 2f9f26f2d0..7eb4fea6e1 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java
@@ -36,35 +36,31 @@ public interface TransformerContext {
/**
* Get the value of the Maven user property.
- *
- * @param key
- * @return
*/
String getUserProperty(String key);
/**
* Get the model based on the path when resolving the parent based on relativePath.
*
+ * @param from the requiring model
* @param pomFile the path to the pomFile
* @return the model, otherwise {@code null}
*/
- Model getRawModel(Path pomFile);
+ Model getRawModel(Path from, Path pomFile);
/**
* Get the model from the reactor based on the groupId and artifactId when resolving reactor dependencies.
*
- * @param groupId the groupId
+ * @param from the requiring model
+ * @param groupId the groupId
* @param artifactId the artifactId
* @return the model, otherwise {@code null}
* @throws IllegalStateException if multiple versions of the same GA are part of the reactor
*/
- Model getRawModel(String groupId, String artifactId);
+ Model getRawModel(Path from, String groupId, String artifactId);
/**
* Locate the POM file inside the given directory.
- *
- * @param path
- * @return
*/
Path locate(Path path);
}
diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/building/BuildModelSourceTransformerTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/building/BuildModelSourceTransformerTest.java
index 2d9072a243..7784cb4cce 100644
--- a/maven-model-builder/src/test/java/org/apache/maven/model/building/BuildModelSourceTransformerTest.java
+++ b/maven-model-builder/src/test/java/org/apache/maven/model/building/BuildModelSourceTransformerTest.java
@@ -60,7 +60,7 @@ public class BuildModelSourceTransformerTest {
.artifactId("ARTIFACTID")
.version("1.0-SNAPSHOT")
.build());
- Mockito.when(context.getRawModel(root.resolve("pom.xml"))).thenReturn(parent);
+ Mockito.when(context.getRawModel(pomFile, root.resolve("pom.xml"))).thenReturn(parent);
Mockito.when(context.locate(root)).thenReturn(root.resolve("pom.xml"));
Model initial = new Model(org.apache.maven.api.model.Model.newBuilder()
.parent(org.apache.maven.api.model.Parent.newBuilder()
@@ -86,7 +86,7 @@ public class BuildModelSourceTransformerTest {
.artifactId("ARTIFACTID")
.version("1.0-SNAPSHOT")
.build());
- Mockito.when(context.getRawModel("GROUPID", "ARTIFACTID")).thenReturn(dep);
+ Mockito.when(context.getRawModel(pomFile, "GROUPID", "ARTIFACTID")).thenReturn(dep);
Model initial = new Model(org.apache.maven.api.model.Model.newBuilder()
.dependencies(Collections.singleton(org.apache.maven.api.model.Dependency.newBuilder()
.groupId("GROUPID")
(maven) 04/04: [MNG-7615] Multithreaded project builder
Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
commit 7fcdd32e8739b4686e6cb9a09a58f8423680a8e4
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Wed Sep 20 22:58:10 2023 +0200
[MNG-7615] Multithreaded project builder
---
.../maven/internal/impl/DefaultProjectBuilder.java | 12 +-
.../maven/project/DefaultProjectBuilder.java | 1278 ++++++++++----------
.../maven/model/building/DefaultModelBuilder.java | 318 ++---
.../building/DefaultTransformerContextBuilder.java | 8 +-
.../org/apache/maven/model/building/Graph.java | 150 +++
5 files changed, 969 insertions(+), 797 deletions(-)
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java
index cd41925c9a..fc45b79192 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java
@@ -150,10 +150,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
public String getLocation() {
StringBuilder buffer = new StringBuilder(256);
- if (getSource().length() > 0) {
- if (buffer.length() > 0) {
- buffer.append(", ");
- }
+ if (!getSource().isEmpty()) {
buffer.append(getSource());
}
@@ -205,13 +202,6 @@ public class DefaultProjectBuilder implements ProjectBuilder {
public Node getRoot() {
return session.getNode(r.getDependencyGraph());
}
-
- // @Override
- // public List<ArtifactResolverResult>
- // getArtifactResults()
- // {
- // return Collections.emptyList();
- // }
});
}
};
diff --git a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
index 341dee9261..4008ca50b4 100644
--- a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
+++ b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
@@ -25,8 +25,13 @@ import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
+import org.apache.maven.ProjectCycleException;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.api.feature.Features;
import org.apache.maven.artifact.Artifact;
@@ -45,6 +50,7 @@ import org.apache.maven.model.Plugin;
import org.apache.maven.model.Profile;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.building.ArtifactModelSource;
+import org.apache.maven.model.building.DefaultModelBuilder;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.building.DefaultModelProblem;
import org.apache.maven.model.building.FileModelSource;
@@ -81,6 +87,8 @@ import org.slf4j.LoggerFactory;
@Named
@Singleton
public class DefaultProjectBuilder implements ProjectBuilder {
+ public static final String BUILDER_PARALLELISM = "maven.projectBuilder.parallelism";
+ public static final int DEFAULT_BUILDER_PARALLELISM = 4;
private final Logger logger = LoggerFactory.getLogger(getClass());
private final ModelBuilder modelBuilder;
@@ -122,486 +130,452 @@ public class DefaultProjectBuilder implements ProjectBuilder {
@Override
public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest request) throws ProjectBuildingException {
- InternalConfig config = new InternalConfig(request, null, modelBuilder.newTransformerContextBuilder());
- return build(pomFile, new FileModelSource(pomFile), config);
+ return new BuildSession(request, false).build(pomFile, new FileModelSource(pomFile));
}
@Override
public ProjectBuildingResult build(ModelSource modelSource, ProjectBuildingRequest request)
throws ProjectBuildingException {
- return build(null, modelSource, new InternalConfig(request, null, null));
+ return new BuildSession(request, false).build(null, modelSource);
}
- private ProjectBuildingResult build(File pomFile, ModelSource modelSource, InternalConfig config)
+ @Override
+ public ProjectBuildingResult build(Artifact artifact, ProjectBuildingRequest request)
throws ProjectBuildingException {
- ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
-
- try {
- ProjectBuildingRequest projectBuildingRequest = config.request;
-
- MavenProject project = projectBuildingRequest.getProject();
-
- List<ModelProblem> modelProblems = null;
- Throwable error = null;
-
- if (project == null) {
- ModelBuildingRequest request = getModelBuildingRequest(config);
-
- project = new MavenProject();
- project.setFile(pomFile);
+ return build(artifact, false, request);
+ }
- DefaultModelBuildingListener listener =
- new DefaultModelBuildingListener(project, projectBuildingHelper, projectBuildingRequest);
+ @Override
+ public ProjectBuildingResult build(Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request)
+ throws ProjectBuildingException {
+ return new BuildSession(request, false).build(artifact, allowStubModel);
+ }
- request.setModelBuildingListener(listener);
+ @Override
+ public List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive, ProjectBuildingRequest request)
+ throws ProjectBuildingException {
+ return new BuildSession(request, true).build(pomFiles, recursive);
+ }
- request.setPomFile(pomFile);
- request.setModelSource(modelSource);
- request.setLocationTracking(true);
+ static class InterimResult {
- if (pomFile != null) {
- project.setRootDirectory(
- rootLocator.findRoot(pomFile.getParentFile().toPath()));
- }
+ File pomFile;
- ModelBuildingResult result;
- try {
- result = modelBuilder.build(request);
- } catch (ModelBuildingException e) {
- result = e.getResult();
- if (result == null || result.getEffectiveModel() == null) {
- throw new ProjectBuildingException(e.getModelId(), e.getMessage(), pomFile, e);
- }
- // validation error, continue project building and delay failing to help IDEs
- error = e;
- }
+ ModelBuildingRequest request;
- modelProblems = result.getProblems();
+ ModelBuildingResult result;
- initProject(project, Collections.emptyMap(), true, result, new HashMap<>(), projectBuildingRequest);
- } else if (projectBuildingRequest.isResolveDependencies()) {
- projectBuildingHelper.selectProjectRealm(project);
- }
+ MavenProject project;
- DependencyResolutionResult resolutionResult = null;
+ boolean root;
- if (projectBuildingRequest.isResolveDependencies()) {
- resolutionResult = resolveDependencies(project, config.session);
- }
+ List<InterimResult> modules = Collections.emptyList();
- ProjectBuildingResult result = new DefaultProjectBuildingResult(project, modelProblems, resolutionResult);
+ ProjectBuildingResult projectBuildingResult;
- if (error != null) {
- ProjectBuildingException e = new ProjectBuildingException(Arrays.asList(result));
- e.initCause(error);
- throw e;
- }
+ InterimResult(
+ File pomFile,
+ ModelBuildingRequest request,
+ ModelBuildingResult result,
+ MavenProject project,
+ boolean root) {
+ this.pomFile = pomFile;
+ this.request = request;
+ this.result = result;
+ this.project = project;
+ this.root = root;
+ }
- return result;
- } finally {
- Thread.currentThread().setContextClassLoader(oldContextClassLoader);
+ InterimResult(ModelBuildingRequest request, ProjectBuildingResult projectBuildingResult) {
+ this.request = request;
+ this.projectBuildingResult = projectBuildingResult;
+ this.pomFile = projectBuildingResult.getPomFile();
+ this.project = projectBuildingResult.getProject();
}
}
- private DependencyResolutionResult resolveDependencies(MavenProject project, RepositorySystemSession session) {
- DependencyResolutionResult resolutionResult;
-
- try {
- DefaultDependencyResolutionRequest resolution = new DefaultDependencyResolutionRequest(project, session);
- resolutionResult = dependencyResolver.resolve(resolution);
- } catch (DependencyResolutionException e) {
- resolutionResult = e.getResult();
- }
+ class BuildSession {
+ private final ProjectBuildingRequest request;
+ private final RepositorySystemSession session;
+ private final List<RemoteRepository> repositories;
+ private final ReactorModelPool modelPool;
+ private final TransformerContextBuilder transformerContextBuilder;
+ private final ForkJoinPool forkJoinPool;
- Set<Artifact> artifacts = new LinkedHashSet<>();
- if (resolutionResult.getDependencyGraph() != null) {
- RepositoryUtils.toArtifacts(
- artifacts,
- resolutionResult.getDependencyGraph().getChildren(),
- Collections.singletonList(project.getArtifact().getId()),
- null);
-
- // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
- LocalRepositoryManager lrm = session.getLocalRepositoryManager();
- for (Artifact artifact : artifacts) {
- if (!artifact.isResolved()) {
- String path = lrm.getPathForLocalArtifact(RepositoryUtils.toArtifact(artifact));
- artifact.setFile(new File(lrm.getRepository().getBasedir(), path));
- }
+ BuildSession(ProjectBuildingRequest request, boolean localProjects) {
+ this.request = request;
+ this.session =
+ RepositoryUtils.overlay(request.getLocalRepository(), request.getRepositorySession(), repoSystem);
+ this.repositories = RepositoryUtils.toRepos(request.getRemoteRepositories());
+ if (localProjects) {
+ this.modelPool = new ReactorModelPool();
+ this.transformerContextBuilder = modelBuilder.newTransformerContextBuilder();
+ this.forkJoinPool = new ForkJoinPool(getParallelism(request));
+ } else {
+ this.modelPool = null;
+ this.transformerContextBuilder = null;
+ this.forkJoinPool = null;
}
}
- project.setResolvedArtifacts(artifacts);
- project.setArtifacts(artifacts);
-
- return resolutionResult;
- }
-
- private List<String> getProfileIds(List<org.apache.maven.model.Profile> profiles) {
- return profiles.stream().map(org.apache.maven.model.Profile::getId).collect(Collectors.toList());
- }
- private ModelBuildingRequest getModelBuildingRequest(InternalConfig config) {
- ProjectBuildingRequest configuration = config.request;
-
- ModelBuildingRequest request = new DefaultModelBuildingRequest();
-
- RequestTrace trace = RequestTrace.newChild(null, configuration).newChild(request);
-
- ModelResolver resolver = new ProjectModelResolver(
- config.session,
- trace,
- repoSystem,
- repositoryManager,
- config.repositories,
- configuration.getRepositoryMerging(),
- config.modelPool);
-
- request.setValidationLevel(configuration.getValidationLevel());
- request.setProcessPlugins(configuration.isProcessPlugins());
- request.setProfiles(configuration.getProfiles());
- request.setActiveProfileIds(configuration.getActiveProfileIds());
- request.setInactiveProfileIds(configuration.getInactiveProfileIds());
- request.setSystemProperties(configuration.getSystemProperties());
- request.setUserProperties(configuration.getUserProperties());
- request.setBuildStartTime(configuration.getBuildStartTime());
- request.setModelResolver(resolver);
- // this is a hint that we want to build 1 file, so don't cache. See MNG-7063
- if (config.modelPool != null) {
- request.setModelCache(modelCacheFactory.createCache(config.session));
- }
- request.setTransformerContextBuilder(config.transformerContextBuilder);
- DefaultSession session = (DefaultSession) config.session.getData().get(DefaultSession.class);
- if (session != null) {
+ private int getParallelism(ProjectBuildingRequest request) {
+ int parallelism = DEFAULT_BUILDER_PARALLELISM;
try {
- request.setRootDirectory(session.getRootDirectory());
- } catch (IllegalStateException e) {
- // can happen if root directory cannot be found, just ignore
+ String str = request.getUserProperties().getProperty(BUILDER_PARALLELISM);
+ if (str == null) {
+ str = request.getSystemProperties().getProperty(BUILDER_PARALLELISM);
+ }
+ if (str != null) {
+ parallelism = Integer.parseInt(str);
+ }
+ } catch (Exception e) {
+ // ignore
}
+ return Math.max(1, Math.min(parallelism, Runtime.getRuntime().availableProcessors()));
}
- return request;
- }
+ ProjectBuildingResult build(File pomFile, ModelSource modelSource) throws ProjectBuildingException {
+ ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
- @Override
- public ProjectBuildingResult build(Artifact artifact, ProjectBuildingRequest request)
- throws ProjectBuildingException {
- return build(artifact, false, request);
- }
+ try {
+ MavenProject project = request.getProject();
- @Override
- public ProjectBuildingResult build(Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request)
- throws ProjectBuildingException {
- org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact(artifact);
- pomArtifact = ArtifactDescriptorUtils.toPomArtifact(pomArtifact);
+ List<ModelProblem> modelProblems = null;
+ Throwable error = null;
- InternalConfig config = new InternalConfig(request, null, null);
+ if (project == null) {
+ ModelBuildingRequest request = getModelBuildingRequest();
- boolean localProject;
+ project = new MavenProject();
+ project.setFile(pomFile);
- try {
- ArtifactRequest pomRequest = new ArtifactRequest();
- pomRequest.setArtifact(pomArtifact);
- pomRequest.setRepositories(config.repositories);
- ArtifactResult pomResult = repoSystem.resolveArtifact(config.session, pomRequest);
+ DefaultModelBuildingListener listener =
+ new DefaultModelBuildingListener(project, projectBuildingHelper, this.request);
+ request.setModelBuildingListener(listener);
- pomArtifact = pomResult.getArtifact();
- localProject = pomResult.getRepository() instanceof WorkspaceRepository;
- } catch (org.eclipse.aether.resolution.ArtifactResolutionException e) {
- if (e.getResults().get(0).isMissing() && allowStubModel) {
- return build(null, createStubModelSource(artifact), config);
- }
- throw new ProjectBuildingException(
- artifact.getId(), "Error resolving project artifact: " + e.getMessage(), e);
- }
+ request.setPomFile(pomFile);
+ request.setModelSource(modelSource);
+ request.setLocationTracking(true);
- File pomFile = pomArtifact.getFile();
+ if (pomFile != null) {
+ project.setRootDirectory(
+ rootLocator.findRoot(pomFile.getParentFile().toPath()));
+ }
- if ("pom".equals(artifact.getType())) {
- artifact.selectVersion(pomArtifact.getVersion());
- artifact.setFile(pomFile);
- artifact.setResolved(true);
- }
+ ModelBuildingResult result;
+ try {
+ result = modelBuilder.build(request);
+ } catch (ModelBuildingException e) {
+ result = e.getResult();
+ if (result == null || result.getEffectiveModel() == null) {
+ throw new ProjectBuildingException(e.getModelId(), e.getMessage(), pomFile, e);
+ }
+ // validation error, continue project building and delay failing to help IDEs
+ error = e;
+ }
- if (localProject) {
- return build(pomFile, new FileModelSource(pomFile), config);
- } else {
- return build(
- null,
- new ArtifactModelSource(
- pomFile, artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()),
- config);
- }
- }
+ modelProblems = result.getProblems();
- private ModelSource createStubModelSource(Artifact artifact) {
- StringBuilder buffer = new StringBuilder(1024);
+ initProject(project, Collections.emptyMap(), result);
+ } else if (request.isResolveDependencies()) {
+ projectBuildingHelper.selectProjectRealm(project);
+ }
- buffer.append("<?xml version='1.0'?>");
- buffer.append("<project>");
- buffer.append("<modelVersion>4.0.0</modelVersion>");
- buffer.append("<groupId>").append(artifact.getGroupId()).append("</groupId>");
- buffer.append("<artifactId>").append(artifact.getArtifactId()).append("</artifactId>");
- buffer.append("<version>").append(artifact.getBaseVersion()).append("</version>");
- buffer.append("<packaging>").append(artifact.getType()).append("</packaging>");
- buffer.append("</project>");
+ DependencyResolutionResult resolutionResult = null;
- return new StringModelSource(buffer.toString(), artifact.getId());
- }
+ if (request.isResolveDependencies()) {
+ resolutionResult = resolveDependencies(project);
+ }
- @Override
- public List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive, ProjectBuildingRequest request)
- throws ProjectBuildingException {
- List<ProjectBuildingResult> results = new ArrayList<>();
+ ProjectBuildingResult result =
+ new DefaultProjectBuildingResult(project, modelProblems, resolutionResult);
- List<InterimResult> interimResults = new ArrayList<>();
+ if (error != null) {
+ ProjectBuildingException e = new ProjectBuildingException(Arrays.asList(result));
+ e.initCause(error);
+ throw e;
+ }
- ReactorModelPool pool = new ReactorModelPool();
+ return result;
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldContextClassLoader);
+ }
+ }
- InternalConfig config = new InternalConfig(request, pool, modelBuilder.newTransformerContextBuilder());
+ ProjectBuildingResult build(Artifact artifact, boolean allowStubModel) throws ProjectBuildingException {
+ org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact(artifact);
+ pomArtifact = ArtifactDescriptorUtils.toPomArtifact(pomArtifact);
- Map<File, MavenProject> projectIndex = new HashMap<>(256);
+ boolean localProject;
- // phase 1: get file Models from the reactor.
- boolean noErrors = build(
- results, interimResults, projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive, config, pool);
+ try {
+ ArtifactRequest pomRequest = new ArtifactRequest();
+ pomRequest.setArtifact(pomArtifact);
+ pomRequest.setRepositories(repositories);
+ ArtifactResult pomResult = repoSystem.resolveArtifact(session, pomRequest);
+
+ pomArtifact = pomResult.getArtifact();
+ localProject = pomResult.getRepository() instanceof WorkspaceRepository;
+ } catch (org.eclipse.aether.resolution.ArtifactResolutionException e) {
+ if (e.getResults().get(0).isMissing() && allowStubModel) {
+ return build(null, createStubModelSource(artifact));
+ }
+ throw new ProjectBuildingException(
+ artifact.getId(), "Error resolving project artifact: " + e.getMessage(), e);
+ }
- ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
+ File pomFile = pomArtifact.getFile();
- try {
- // Phase 2: get effective models from the reactor
- noErrors = build(
- results,
- new ArrayList<>(),
- projectIndex,
- interimResults,
- request,
- new HashMap<>(),
- config.session)
- && noErrors;
- } finally {
- Thread.currentThread().setContextClassLoader(oldContextClassLoader);
- }
+ if ("pom".equals(artifact.getType())) {
+ artifact.selectVersion(pomArtifact.getVersion());
+ artifact.setFile(pomFile);
+ artifact.setResolved(true);
+ }
- if (Features.buildConsumer(request.getUserProperties())) {
- request.getRepositorySession()
- .getData()
- .set(TransformerContext.KEY, config.transformerContextBuilder.build());
+ if (localProject) {
+ return build(pomFile, new FileModelSource(pomFile));
+ } else {
+ return build(
+ null,
+ new ArtifactModelSource(
+ pomFile, artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()));
+ }
}
- if (!noErrors) {
- throw new ProjectBuildingException(results);
- }
+ List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive) throws ProjectBuildingException {
+ ForkJoinTask<List<ProjectBuildingResult>> task = forkJoinPool.submit(() -> doBuild(pomFiles, recursive));
- return results;
- }
+ // ForkJoinTask.getException rewraps the exception in a weird way
+ // which cause an additional layer of exception, so try to unwrap it
+ task.quietlyJoin();
+ if (task.isCompletedAbnormally()) {
+ Throwable e = task.getException();
+ Throwable c = e.getCause();
+ uncheckedThrow(c != null && c.getClass() == e.getClass() ? c : e);
+ }
- @SuppressWarnings("checkstyle:parameternumber")
- private boolean build(
- List<ProjectBuildingResult> results,
- List<InterimResult> interimResults,
- Map<File, MavenProject> projectIndex,
- List<File> pomFiles,
- Set<File> aggregatorFiles,
- boolean root,
- boolean recursive,
- InternalConfig config,
- ReactorModelPool pool) {
- boolean noErrors = true;
-
- for (File pomFile : pomFiles) {
- aggregatorFiles.add(pomFile);
-
- if (!build(
- results, interimResults, projectIndex, pomFile, aggregatorFiles, root, recursive, config, pool)) {
- noErrors = false;
+ List<ProjectBuildingResult> results = task.getRawResult();
+ if (results.stream()
+ .flatMap(r -> r.getProblems().stream())
+ .anyMatch(p -> p.getSeverity() != ModelProblem.Severity.WARNING)) {
+ ModelProblem cycle = results.stream()
+ .flatMap(r -> r.getProblems().stream())
+ .filter(p -> p.getException() instanceof CycleDetectedException)
+ .findAny()
+ .orElse(null);
+ if (cycle != null) {
+ throw new RuntimeException(new ProjectCycleException(
+ "The projects in the reactor contain a cyclic reference: " + cycle.getMessage(),
+ (CycleDetectedException) cycle.getException()));
+ }
+ throw new ProjectBuildingException(results);
}
- aggregatorFiles.remove(pomFile);
+ return results;
}
- return noErrors;
- }
+ List<ProjectBuildingResult> doBuild(List<File> pomFiles, boolean recursive) {
+ Map<File, MavenProject> projectIndex = new ConcurrentHashMap<>(256);
- @SuppressWarnings("checkstyle:parameternumber")
- private boolean build(
- List<ProjectBuildingResult> results,
- List<InterimResult> interimResults,
- Map<File, MavenProject> projectIndex,
- File pomFile,
- Set<File> aggregatorFiles,
- boolean isRoot,
- boolean recursive,
- InternalConfig config,
- ReactorModelPool pool) {
- boolean noErrors = true;
-
- MavenProject project = new MavenProject();
- project.setFile(pomFile);
-
- project.setRootDirectory(rootLocator.findRoot(pomFile.getParentFile().toPath()));
-
- ModelBuildingRequest request = getModelBuildingRequest(config)
- .setPomFile(pomFile)
- .setTwoPhaseBuilding(true)
- .setLocationTracking(true);
-
- DefaultModelBuildingListener listener =
- new DefaultModelBuildingListener(project, projectBuildingHelper, config.request);
- request.setModelBuildingListener(listener);
+ // phase 1: get file Models from the reactor.
+ List<InterimResult> interimResults = build(projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive);
- ModelBuildingResult result;
- try {
- result = modelBuilder.build(request);
- } catch (ModelBuildingException e) {
- result = e.getResult();
- if (result == null || result.getFileModel() == null) {
- results.add(new DefaultProjectBuildingResult(e.getModelId(), pomFile, e.getProblems()));
-
- return false;
- }
- // validation error, continue project building and delay failing to help IDEs
- // result.getProblems().addAll(e.getProblems()) ?
- noErrors = false;
- }
+ ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
- Model model = request.getFileModel();
+ try {
+ // Phase 2: get effective models from the reactor
+ List<ProjectBuildingResult> results = build(projectIndex, interimResults);
- pool.put(model.getPomFile().toPath(), model);
+ if (Features.buildConsumer(request.getUserProperties())) {
+ request.getRepositorySession()
+ .getData()
+ .set(TransformerContext.KEY, transformerContextBuilder.build());
+ }
- InterimResult interimResult = new InterimResult(pomFile, request, result, listener, isRoot);
- interimResults.add(interimResult);
+ return results;
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldContextClassLoader);
+ }
+ }
- if (recursive) {
- File basedir = pomFile.getParentFile();
- List<File> moduleFiles = new ArrayList<>();
- for (String module : model.getModules()) {
- if (module == null || module.isEmpty()) {
- continue;
- }
+ @SuppressWarnings("checkstyle:parameternumber")
+ private List<InterimResult> build(
+ Map<File, MavenProject> projectIndex,
+ List<File> pomFiles,
+ Set<File> aggregatorFiles,
+ boolean root,
+ boolean recursive) {
+ List<ForkJoinTask<InterimResult>> tasks = pomFiles.stream()
+ .map(pomFile -> ForkJoinTask.adapt(
+ () -> build(projectIndex, pomFile, concat(aggregatorFiles, pomFile), root, recursive)))
+ .collect(Collectors.toList());
+
+ return ForkJoinTask.invokeAll(tasks).stream()
+ .map(ForkJoinTask::getRawResult)
+ .collect(Collectors.toList());
+ }
- module = module.replace('\\', File.separatorChar).replace('/', File.separatorChar);
+ private <T> Set<T> concat(Set<T> set, T elem) {
+ Set<T> newSet = new HashSet<>(set);
+ newSet.add(elem);
+ return newSet;
+ }
- File moduleFile = modelProcessor.locateExistingPom(new File(basedir, module));
+ @SuppressWarnings("checkstyle:parameternumber")
+ private InterimResult build(
+ Map<File, MavenProject> projectIndex,
+ File pomFile,
+ Set<File> aggregatorFiles,
+ boolean isRoot,
+ boolean recursive) {
+ MavenProject project = new MavenProject();
+ project.setFile(pomFile);
- if (moduleFile == null) {
- ModelProblem problem = new DefaultModelProblem(
- "Child module " + module + " of " + pomFile + " does not exist",
- ModelProblem.Severity.ERROR,
- ModelProblem.Version.BASE,
- model,
- -1,
- -1,
- null);
- result.getProblems().add(problem);
+ project.setRootDirectory(
+ rootLocator.findRoot(pomFile.getParentFile().toPath()));
- noErrors = false;
+ ModelBuildingRequest modelBuildingRequest = getModelBuildingRequest()
+ .setPomFile(pomFile)
+ .setTwoPhaseBuilding(true)
+ .setLocationTracking(true);
- continue;
- }
+ DefaultModelBuildingListener listener =
+ new DefaultModelBuildingListener(project, projectBuildingHelper, request);
+ modelBuildingRequest.setModelBuildingListener(listener);
- if (Os.IS_WINDOWS) {
- // we don't canonicalize on unix to avoid interfering with symlinks
- try {
- moduleFile = moduleFile.getCanonicalFile();
- } catch (IOException e) {
- moduleFile = moduleFile.getAbsoluteFile();
- }
- } else {
- moduleFile = new File(moduleFile.toURI().normalize());
+ ModelBuildingResult result;
+ try {
+ result = modelBuilder.build(modelBuildingRequest);
+ } catch (ModelBuildingException e) {
+ result = e.getResult();
+ if (result == null || result.getFileModel() == null) {
+ return new InterimResult(
+ modelBuildingRequest,
+ new DefaultProjectBuildingResult(e.getModelId(), pomFile, e.getProblems()));
}
+ // validation error, continue project building and delay failing to help IDEs
+ // result.getProblems().addAll(e.getProblems()) ?
+ }
- if (aggregatorFiles.contains(moduleFile)) {
- StringBuilder buffer = new StringBuilder(256);
- for (File aggregatorFile : aggregatorFiles) {
- buffer.append(aggregatorFile).append(" -> ");
- }
- buffer.append(moduleFile);
+ Model model = modelBuildingRequest.getFileModel();
- ModelProblem problem = new DefaultModelProblem(
- "Child module " + moduleFile + " of " + pomFile + " forms aggregation cycle " + buffer,
- ModelProblem.Severity.ERROR,
- ModelProblem.Version.BASE,
- model,
- -1,
- -1,
- null);
- result.getProblems().add(problem);
+ modelPool.put(model.getPomFile().toPath(), model);
- noErrors = false;
+ InterimResult interimResult = new InterimResult(pomFile, modelBuildingRequest, result, project, isRoot);
- continue;
- }
+ if (recursive) {
+ File basedir = pomFile.getParentFile();
+ List<File> moduleFiles = new ArrayList<>();
+ for (String module : model.getModules()) {
+ if (module == null || module.isEmpty()) {
+ continue;
+ }
- moduleFiles.add(moduleFile);
- }
+ module = module.replace('\\', File.separatorChar).replace('/', File.separatorChar);
- interimResult.modules = new ArrayList<>();
-
- if (!build(
- results,
- interimResult.modules,
- projectIndex,
- moduleFiles,
- aggregatorFiles,
- false,
- recursive,
- config,
- pool)) {
- noErrors = false;
- }
- }
+ File moduleFile = modelProcessor.locateExistingPom(new File(basedir, module));
- projectIndex.put(pomFile, project);
+ if (moduleFile == null) {
+ ModelProblem problem = new DefaultModelProblem(
+ "Child module " + moduleFile + " of " + pomFile + " does not exist",
+ ModelProblem.Severity.ERROR,
+ ModelProblem.Version.BASE,
+ model,
+ -1,
+ -1,
+ null);
+ result.getProblems().add(problem);
- return noErrors;
- }
+ continue;
+ }
- static class InterimResult {
+ if (Os.IS_WINDOWS) {
+ // we don't canonicalize on unix to avoid interfering with symlinks
+ try {
+ moduleFile = moduleFile.getCanonicalFile();
+ } catch (IOException e) {
+ moduleFile = moduleFile.getAbsoluteFile();
+ }
+ } else {
+ moduleFile = new File(moduleFile.toURI().normalize());
+ }
- File pomFile;
+ if (aggregatorFiles.contains(moduleFile)) {
+ StringBuilder buffer = new StringBuilder(256);
+ for (File aggregatorFile : aggregatorFiles) {
+ buffer.append(aggregatorFile).append(" -> ");
+ }
+ buffer.append(moduleFile);
+
+ ModelProblem problem = new DefaultModelProblem(
+ "Child module " + moduleFile + " of " + pomFile + " forms aggregation cycle " + buffer,
+ ModelProblem.Severity.ERROR,
+ ModelProblem.Version.BASE,
+ model,
+ -1,
+ -1,
+ null);
+ result.getProblems().add(problem);
+
+ continue;
+ }
- ModelBuildingRequest request;
+ moduleFiles.add(moduleFile);
+ }
- ModelBuildingResult result;
+ if (!moduleFiles.isEmpty()) {
+ interimResult.modules = build(projectIndex, moduleFiles, aggregatorFiles, false, recursive);
+ }
+ }
- DefaultModelBuildingListener listener;
+ projectIndex.put(pomFile, project);
- boolean root;
+ return interimResult;
+ }
- List<InterimResult> modules = Collections.emptyList();
+ private List<ProjectBuildingResult> build(
+ Map<File, MavenProject> projectIndex, List<InterimResult> interimResults) {
+ // The transformation may need to access dependencies raw models,
+ // which may cause some re-entrance in the build() method and can
+ // actually cause deadlocks. In order to workaround the problem,
+ // we do a first pass by reading all rawModels in order.
+ if (modelBuilder instanceof DefaultModelBuilder) {
+ List<ProjectBuildingResult> results = new ArrayList<>();
+ DefaultModelBuilder dmb = (DefaultModelBuilder) modelBuilder;
+ boolean failure = false;
+ for (InterimResult r : interimResults) {
+ DefaultProjectBuildingResult res;
+ try {
+ Model model = dmb.buildRawModel(r.request);
+ res = new DefaultProjectBuildingResult(model.getId(), model.getPomFile(), null);
+ } catch (ModelBuildingException e) {
+ failure = true;
+ res = new DefaultProjectBuildingResult(e.getModelId(), r.request.getPomFile(), e.getProblems());
+ }
+ results.add(res);
+ }
+ if (failure) {
+ return results;
+ }
+ }
- InterimResult(
- File pomFile,
- ModelBuildingRequest request,
- ModelBuildingResult result,
- DefaultModelBuildingListener listener,
- boolean root) {
- this.pomFile = pomFile;
- this.request = request;
- this.result = result;
- this.listener = listener;
- this.root = root;
+ return interimResults.parallelStream()
+ .map(interimResult -> doBuild(projectIndex, interimResult))
+ .flatMap(List::stream)
+ .collect(Collectors.toList());
}
- }
- private boolean build(
- List<ProjectBuildingResult> results,
- List<MavenProject> projects,
- Map<File, MavenProject> projectIndex,
- List<InterimResult> interimResults,
- ProjectBuildingRequest request,
- Map<File, Boolean> profilesXmls,
- RepositorySystemSession session) {
- boolean noErrors = true;
-
- for (InterimResult interimResult : interimResults) {
- MavenProject project = interimResult.listener.getProject();
+ private List<ProjectBuildingResult> doBuild(Map<File, MavenProject> projectIndex, InterimResult interimResult) {
+ if (interimResult.projectBuildingResult != null) {
+ return Collections.singletonList(interimResult.projectBuildingResult);
+ }
+ MavenProject project = interimResult.project;
try {
ModelBuildingResult result = modelBuilder.build(interimResult.request, interimResult.result);
// 2nd pass of initialization: resolve and build parent if necessary
try {
- initProject(project, projectIndex, true, result, profilesXmls, request);
+ initProject(project, projectIndex, result);
} catch (InvalidArtifactRTException iarte) {
result.getProblems()
.add(new DefaultModelProblem(
@@ -614,299 +588,332 @@ public class DefaultProjectBuilder implements ProjectBuilder {
iarte));
}
- List<MavenProject> modules = new ArrayList<>();
- noErrors = build(results, modules, projectIndex, interimResult.modules, request, profilesXmls, session)
- && noErrors;
-
- projects.addAll(modules);
- projects.add(project);
+ List<ProjectBuildingResult> results = build(projectIndex, interimResult.modules);
project.setExecutionRoot(interimResult.root);
- project.setCollectedProjects(modules);
+ project.setCollectedProjects(
+ results.stream().map(ProjectBuildingResult::getProject).collect(Collectors.toList()));
DependencyResolutionResult resolutionResult = null;
if (request.isResolveDependencies()) {
- resolutionResult = resolveDependencies(project, session);
+ resolutionResult = resolveDependencies(project);
}
results.add(new DefaultProjectBuildingResult(project, result.getProblems(), resolutionResult));
+
+ return results;
} catch (ModelBuildingException e) {
- DefaultProjectBuildingResult result = null;
+ DefaultProjectBuildingResult result;
if (project == null || interimResult.result.getEffectiveModel() == null) {
result = new DefaultProjectBuildingResult(e.getModelId(), interimResult.pomFile, e.getProblems());
} else {
project.setModel(interimResult.result.getEffectiveModel());
-
result = new DefaultProjectBuildingResult(project, e.getProblems(), null);
}
- results.add(result);
-
- noErrors = false;
+ return Collections.singletonList(result);
}
}
- return noErrors;
- }
-
- @SuppressWarnings("checkstyle:methodlength")
- private void initProject(
- MavenProject project,
- Map<File, MavenProject> projects,
- boolean buildParentIfNotExisting,
- ModelBuildingResult result,
- Map<File, Boolean> profilesXmls,
- ProjectBuildingRequest projectBuildingRequest) {
- project.setModel(result.getEffectiveModel());
- project.setOriginalModel(result.getFileModel());
-
- initParent(project, projects, buildParentIfNotExisting, result, projectBuildingRequest);
-
- Artifact projectArtifact = repositorySystem.createArtifact(
- project.getGroupId(), project.getArtifactId(), project.getVersion(), null, project.getPackaging());
- project.setArtifact(projectArtifact);
-
- if (project.getFile() != null && buildParentIfNotExisting) // only set those on 2nd phase, ignore on 1st pass
- {
- Build build = project.getBuild();
- project.addScriptSourceRoot(build.getScriptSourceDirectory());
- project.addCompileSourceRoot(build.getSourceDirectory());
- project.addTestCompileSourceRoot(build.getTestSourceDirectory());
- }
-
- List<Profile> activeProfiles = new ArrayList<>();
- activeProfiles.addAll(result.getActivePomProfiles(result.getModelIds().get(0)));
- activeProfiles.addAll(result.getActiveExternalProfiles());
- project.setActiveProfiles(activeProfiles);
+ @SuppressWarnings("checkstyle:methodlength")
+ private void initProject(MavenProject project, Map<File, MavenProject> projects, ModelBuildingResult result) {
+ project.setModel(result.getEffectiveModel());
+ project.setOriginalModel(result.getFileModel());
- project.setInjectedProfileIds("external", getProfileIds(result.getActiveExternalProfiles()));
- for (String modelId : result.getModelIds()) {
- project.setInjectedProfileIds(modelId, getProfileIds(result.getActivePomProfiles(modelId)));
- }
+ initParent(project, projects, result);
- //
- // All the parts that were taken out of MavenProject for Maven 4.0.0
- //
+ Artifact projectArtifact = repositorySystem.createArtifact(
+ project.getGroupId(), project.getArtifactId(), project.getVersion(), null, project.getPackaging());
+ project.setArtifact(projectArtifact);
- project.setProjectBuildingRequest(projectBuildingRequest);
+ // only set those on 2nd phase, ignore on 1st pass
+ if (project.getFile() != null) {
+ Build build = project.getBuild();
+ project.addScriptSourceRoot(build.getScriptSourceDirectory());
+ project.addCompileSourceRoot(build.getSourceDirectory());
+ project.addTestCompileSourceRoot(build.getTestSourceDirectory());
+ }
- // pluginArtifacts
- Set<Artifact> pluginArtifacts = new HashSet<>();
- for (Plugin plugin : project.getBuildPlugins()) {
- Artifact artifact = repositorySystem.createPluginArtifact(plugin);
+ List<Profile> activeProfiles = new ArrayList<>();
+ activeProfiles.addAll(
+ result.getActivePomProfiles(result.getModelIds().get(0)));
+ activeProfiles.addAll(result.getActiveExternalProfiles());
+ project.setActiveProfiles(activeProfiles);
- if (artifact != null) {
- pluginArtifacts.add(artifact);
+ project.setInjectedProfileIds("external", getProfileIds(result.getActiveExternalProfiles()));
+ for (String modelId : result.getModelIds()) {
+ project.setInjectedProfileIds(modelId, getProfileIds(result.getActivePomProfiles(modelId)));
}
- }
- project.setPluginArtifacts(pluginArtifacts);
- // reportArtifacts
- Set<Artifact> reportArtifacts = new HashSet<>();
- for (ReportPlugin report : project.getReportPlugins()) {
- Plugin pp = new Plugin();
- pp.setGroupId(report.getGroupId());
- pp.setArtifactId(report.getArtifactId());
- pp.setVersion(report.getVersion());
+ //
+ // All the parts that were taken out of MavenProject for Maven 4.0.0
+ //
- Artifact artifact = repositorySystem.createPluginArtifact(pp);
+ project.setProjectBuildingRequest(request);
- if (artifact != null) {
- reportArtifacts.add(artifact);
- }
- }
- project.setReportArtifacts(reportArtifacts);
-
- // extensionArtifacts
- Set<Artifact> extensionArtifacts = new HashSet<>();
- List<Extension> extensions = project.getBuildExtensions();
- if (extensions != null) {
- for (Extension ext : extensions) {
- String version;
- if (ext.getVersion() == null || ext.getVersion().isEmpty()) {
- version = "RELEASE";
- } else {
- version = ext.getVersion();
+ // pluginArtifacts
+ Set<Artifact> pluginArtifacts = new HashSet<>();
+ for (Plugin plugin : project.getBuildPlugins()) {
+ Artifact artifact = repositorySystem.createPluginArtifact(plugin);
+
+ if (artifact != null) {
+ pluginArtifacts.add(artifact);
}
+ }
+ project.setPluginArtifacts(pluginArtifacts);
- Artifact artifact =
- repositorySystem.createArtifact(ext.getGroupId(), ext.getArtifactId(), version, null, "jar");
+ // reportArtifacts
+ Set<Artifact> reportArtifacts = new HashSet<>();
+ for (ReportPlugin report : project.getReportPlugins()) {
+ Plugin pp = new Plugin();
+ pp.setGroupId(report.getGroupId());
+ pp.setArtifactId(report.getArtifactId());
+ pp.setVersion(report.getVersion());
+
+ Artifact artifact = repositorySystem.createPluginArtifact(pp);
if (artifact != null) {
- extensionArtifacts.add(artifact);
+ reportArtifacts.add(artifact);
}
}
- }
- project.setExtensionArtifacts(extensionArtifacts);
-
- // managedVersionMap
- Map<String, Artifact> map = null;
- if (repositorySystem != null) {
- final DependencyManagement dependencyManagement = project.getDependencyManagement();
- if ((dependencyManagement != null)
- && ((dependencyManagement.getDependencies()) != null)
- && (dependencyManagement.getDependencies().size() > 0)) {
- map = new AbstractMap<String, Artifact>() {
- HashMap<String, Artifact> delegate;
-
- @Override
- public Set<Entry<String, Artifact>> entrySet() {
- return Collections.unmodifiableSet(compute().entrySet());
+ project.setReportArtifacts(reportArtifacts);
+
+ // extensionArtifacts
+ Set<Artifact> extensionArtifacts = new HashSet<>();
+ List<Extension> extensions = project.getBuildExtensions();
+ if (extensions != null) {
+ for (Extension ext : extensions) {
+ String version;
+ if (ext.getVersion() == null || ext.getVersion().isEmpty()) {
+ version = "RELEASE";
+ } else {
+ version = ext.getVersion();
}
- @Override
- public Set<String> keySet() {
- return Collections.unmodifiableSet(compute().keySet());
- }
+ Artifact artifact = repositorySystem.createArtifact(
+ ext.getGroupId(), ext.getArtifactId(), version, null, "jar");
- @Override
- public Collection<Artifact> values() {
- return Collections.unmodifiableCollection(compute().values());
+ if (artifact != null) {
+ extensionArtifacts.add(artifact);
}
+ }
+ }
+ project.setExtensionArtifacts(extensionArtifacts);
- @Override
- public boolean containsValue(Object value) {
- return compute().containsValue(value);
+ // managedVersionMap
+ Map<String, Artifact> map = Collections.emptyMap();
+ final DependencyManagement dependencyManagement = project.getDependencyManagement();
+ if (dependencyManagement != null
+ && dependencyManagement.getDependencies() != null
+ && !dependencyManagement.getDependencies().isEmpty()) {
+ map = new LazyMap<>(() -> {
+ Map<String, Artifact> tmp = new HashMap<>();
+ for (Dependency d : dependencyManagement.getDependencies()) {
+ Artifact artifact = repositorySystem.createDependencyArtifact(d);
+ if (artifact != null) {
+ tmp.put(d.getManagementKey(), artifact);
+ }
}
+ return Collections.unmodifiableMap(tmp);
+ });
+ }
+ project.setManagedVersionMap(map);
- @Override
- public boolean containsKey(Object key) {
- return compute().containsKey(key);
+ // release artifact repository
+ if (project.getDistributionManagement() != null
+ && project.getDistributionManagement().getRepository() != null) {
+ try {
+ DeploymentRepository r = project.getDistributionManagement().getRepository();
+ if (r.getId() != null
+ && !r.getId().isEmpty()
+ && r.getUrl() != null
+ && !r.getUrl().isEmpty()) {
+ ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
+ repositorySystem.injectProxy(request.getRepositorySession(), Arrays.asList(repo));
+ repositorySystem.injectAuthentication(request.getRepositorySession(), Arrays.asList(repo));
+ project.setReleaseArtifactRepository(repo);
}
+ } catch (InvalidRepositoryException e) {
+ throw new IllegalStateException(
+ "Failed to create release distribution repository for " + project.getId(), e);
+ }
+ }
- @Override
- public Artifact get(Object key) {
- return compute().get(key);
+ // snapshot artifact repository
+ if (project.getDistributionManagement() != null
+ && project.getDistributionManagement().getSnapshotRepository() != null) {
+ try {
+ DeploymentRepository r = project.getDistributionManagement().getSnapshotRepository();
+ if (r.getId() != null
+ && !r.getId().isEmpty()
+ && r.getUrl() != null
+ && !r.getUrl().isEmpty()) {
+ ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
+ repositorySystem.injectProxy(request.getRepositorySession(), Arrays.asList(repo));
+ repositorySystem.injectAuthentication(request.getRepositorySession(), Arrays.asList(repo));
+ project.setSnapshotArtifactRepository(repo);
}
+ } catch (InvalidRepositoryException e) {
+ throw new IllegalStateException(
+ "Failed to create snapshot distribution repository for " + project.getId(), e);
+ }
+ }
+ }
- HashMap<String, Artifact> compute() {
- if (delegate == null) {
- delegate = new HashMap<>();
- for (Dependency d : dependencyManagement.getDependencies()) {
- Artifact artifact = repositorySystem.createDependencyArtifact(d);
-
- if (artifact != null) {
- delegate.put(d.getManagementKey(), artifact);
- }
+ private void initParent(MavenProject project, Map<File, MavenProject> projects, ModelBuildingResult result) {
+ Model parentModel = result.getModelIds().size() > 1
+ && !result.getModelIds().get(1).isEmpty()
+ ? result.getRawModel(result.getModelIds().get(1))
+ : null;
+
+ if (parentModel != null) {
+ final String parentGroupId = inheritedGroupId(result, 1);
+ final String parentVersion = inheritedVersion(result, 1);
+
+ project.setParentArtifact(repositorySystem.createProjectArtifact(
+ parentGroupId, parentModel.getArtifactId(), parentVersion));
+
+ // org.apache.maven.its.mng4834:parent:0.1
+ String parentModelId = result.getModelIds().get(1);
+ File parentPomFile = result.getRawModel(parentModelId).getPomFile();
+ MavenProject parent = parentPomFile != null ? projects.get(parentPomFile) : null;
+ if (parent == null) {
+ //
+ // At this point the DefaultModelBuildingListener has fired and it populates the
+ // remote repositories with those found in the pom.xml, along with the existing externally
+ // defined repositories.
+ //
+ request.setRemoteRepositories(project.getRemoteArtifactRepositories());
+ if (parentPomFile != null) {
+ project.setParentFile(parentPomFile);
+ try {
+ parent = build(parentPomFile, new FileModelSource(parentPomFile))
+ .getProject();
+ } catch (ProjectBuildingException e) {
+ // MNG-4488 where let invalid parents slide on by
+ if (logger.isDebugEnabled()) {
+ // Message below is checked for in the MNG-2199 core IT.
+ logger.warn("Failed to build parent project for " + project.getId(), e);
+ } else {
+ // Message below is checked for in the MNG-2199 core IT.
+ logger.warn("Failed to build parent project for " + project.getId());
+ }
+ }
+ } else {
+ Artifact parentArtifact = project.getParentArtifact();
+ try {
+ parent = build(parentArtifact, false).getProject();
+ } catch (ProjectBuildingException e) {
+ // MNG-4488 where let invalid parents slide on by
+ if (logger.isDebugEnabled()) {
+ // Message below is checked for in the MNG-2199 core IT.
+ logger.warn("Failed to build parent project for " + project.getId(), e);
+ } else {
+ // Message below is checked for in the MNG-2199 core IT.
+ logger.warn("Failed to build parent project for " + project.getId());
}
}
-
- return delegate;
}
- };
- } else {
- map = Collections.emptyMap();
+ }
+ project.setParent(parent);
+ if (project.getParentFile() == null && parent != null) {
+ project.setParentFile(parent.getFile());
+ }
}
}
- project.setManagedVersionMap(map);
- // release artifact repository
- if (project.getDistributionManagement() != null
- && project.getDistributionManagement().getRepository() != null) {
- try {
- DeploymentRepository r = project.getDistributionManagement().getRepository();
- if (r.getId() != null
- && !r.getId().isEmpty()
- && r.getUrl() != null
- && !r.getUrl().isEmpty()) {
- ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
- repositorySystem.injectProxy(projectBuildingRequest.getRepositorySession(), Arrays.asList(repo));
- repositorySystem.injectAuthentication(
- projectBuildingRequest.getRepositorySession(), Arrays.asList(repo));
- project.setReleaseArtifactRepository(repo);
+ private ModelBuildingRequest getModelBuildingRequest() {
+ ModelBuildingRequest modelBuildingRequest = new DefaultModelBuildingRequest();
+
+ RequestTrace trace = RequestTrace.newChild(null, request).newChild(modelBuildingRequest);
+
+ ModelResolver resolver = new ProjectModelResolver(
+ session,
+ trace,
+ repoSystem,
+ repositoryManager,
+ repositories,
+ request.getRepositoryMerging(),
+ modelPool);
+
+ modelBuildingRequest.setValidationLevel(request.getValidationLevel());
+ modelBuildingRequest.setProcessPlugins(request.isProcessPlugins());
+ modelBuildingRequest.setProfiles(request.getProfiles());
+ modelBuildingRequest.setActiveProfileIds(request.getActiveProfileIds());
+ modelBuildingRequest.setInactiveProfileIds(request.getInactiveProfileIds());
+ modelBuildingRequest.setSystemProperties(request.getSystemProperties());
+ modelBuildingRequest.setUserProperties(request.getUserProperties());
+ modelBuildingRequest.setBuildStartTime(request.getBuildStartTime());
+ modelBuildingRequest.setModelResolver(resolver);
+ // this is a hint that we want to build 1 file, so don't cache. See MNG-7063
+ if (modelPool != null) {
+ modelBuildingRequest.setModelCache(modelCacheFactory.createCache(session));
+ }
+ modelBuildingRequest.setTransformerContextBuilder(transformerContextBuilder);
+ DefaultSession session = (DefaultSession) this.session.getData().get(DefaultSession.class);
+ if (session != null) {
+ try {
+ modelBuildingRequest.setRootDirectory(session.getRootDirectory());
+ } catch (IllegalStateException e) {
+ // can happen if root directory cannot be found, just ignore
}
- } catch (InvalidRepositoryException e) {
- throw new IllegalStateException(
- "Failed to create release distribution repository for " + project.getId(), e);
}
+
+ return modelBuildingRequest;
}
- // snapshot artifact repository
- if (project.getDistributionManagement() != null
- && project.getDistributionManagement().getSnapshotRepository() != null) {
+ private DependencyResolutionResult resolveDependencies(MavenProject project) {
+ DependencyResolutionResult resolutionResult;
+
try {
- DeploymentRepository r = project.getDistributionManagement().getSnapshotRepository();
- if (r.getId() != null
- && !r.getId().isEmpty()
- && r.getUrl() != null
- && !r.getUrl().isEmpty()) {
- ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
- repositorySystem.injectProxy(projectBuildingRequest.getRepositorySession(), Arrays.asList(repo));
- repositorySystem.injectAuthentication(
- projectBuildingRequest.getRepositorySession(), Arrays.asList(repo));
- project.setSnapshotArtifactRepository(repo);
- }
- } catch (InvalidRepositoryException e) {
- throw new IllegalStateException(
- "Failed to create snapshot distribution repository for " + project.getId(), e);
+ DefaultDependencyResolutionRequest resolution =
+ new DefaultDependencyResolutionRequest(project, session);
+ resolutionResult = dependencyResolver.resolve(resolution);
+ } catch (DependencyResolutionException e) {
+ resolutionResult = e.getResult();
}
- }
- }
- private void initParent(
- MavenProject project,
- Map<File, MavenProject> projects,
- boolean buildParentIfNotExisting,
- ModelBuildingResult result,
- ProjectBuildingRequest projectBuildingRequest) {
- Model parentModel =
- result.getModelIds().size() > 1 && !result.getModelIds().get(1).isEmpty()
- ? result.getRawModel(result.getModelIds().get(1))
- : null;
-
- if (parentModel != null) {
- final String parentGroupId = inheritedGroupId(result, 1);
- final String parentVersion = inheritedVersion(result, 1);
-
- project.setParentArtifact(
- repositorySystem.createProjectArtifact(parentGroupId, parentModel.getArtifactId(), parentVersion));
-
- // org.apache.maven.its.mng4834:parent:0.1
- String parentModelId = result.getModelIds().get(1);
- File parentPomFile = result.getRawModel(parentModelId).getPomFile();
- MavenProject parent = projects.get(parentPomFile);
- if (parent == null && buildParentIfNotExisting) {
- //
- // At this point the DefaultModelBuildingListener has fired and it populates the
- // remote repositories with those found in the pom.xml, along with the existing externally
- // defined repositories.
- //
- projectBuildingRequest.setRemoteRepositories(project.getRemoteArtifactRepositories());
- if (parentPomFile != null) {
- project.setParentFile(parentPomFile);
- try {
- parent = build(parentPomFile, projectBuildingRequest).getProject();
- } catch (ProjectBuildingException e) {
- // MNG-4488 where let invalid parents slide on by
- if (logger.isDebugEnabled()) {
- // Message below is checked for in the MNG-2199 core IT.
- logger.warn("Failed to build parent project for " + project.getId(), e);
- } else {
- // Message below is checked for in the MNG-2199 core IT.
- logger.warn("Failed to build parent project for " + project.getId());
- }
- }
- } else {
- Artifact parentArtifact = project.getParentArtifact();
- try {
- parent = build(parentArtifact, projectBuildingRequest).getProject();
- } catch (ProjectBuildingException e) {
- // MNG-4488 where let invalid parents slide on by
- if (logger.isDebugEnabled()) {
- // Message below is checked for in the MNG-2199 core IT.
- logger.warn("Failed to build parent project for " + project.getId(), e);
- } else {
- // Message below is checked for in the MNG-2199 core IT.
- logger.warn("Failed to build parent project for " + project.getId());
- }
+ Set<Artifact> artifacts = new LinkedHashSet<>();
+ if (resolutionResult.getDependencyGraph() != null) {
+ RepositoryUtils.toArtifacts(
+ artifacts,
+ resolutionResult.getDependencyGraph().getChildren(),
+ Collections.singletonList(project.getArtifact().getId()),
+ null);
+
+ // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
+ LocalRepositoryManager lrm = session.getLocalRepositoryManager();
+ for (Artifact artifact : artifacts) {
+ if (!artifact.isResolved()) {
+ String path = lrm.getPathForLocalArtifact(RepositoryUtils.toArtifact(artifact));
+ artifact.setFile(new File(lrm.getRepository().getBasedir(), path));
}
}
}
- project.setParent(parent);
- if (project.getParentFile() == null && parent != null) {
- project.setParentFile(parent.getFile());
- }
+ project.setResolvedArtifacts(artifacts);
+ project.setArtifacts(artifacts);
+
+ return resolutionResult;
}
}
+ private List<String> getProfileIds(List<org.apache.maven.model.Profile> profiles) {
+ return profiles.stream().map(org.apache.maven.model.Profile::getId).collect(Collectors.toList());
+ }
+
+ private static ModelSource createStubModelSource(Artifact artifact) {
+ StringBuilder buffer = new StringBuilder(1024);
+
+ buffer.append("<?xml version='1.0'?>");
+ buffer.append("<project>");
+ buffer.append("<modelVersion>4.0.0</modelVersion>");
+ buffer.append("<groupId>").append(artifact.getGroupId()).append("</groupId>");
+ buffer.append("<artifactId>").append(artifact.getArtifactId()).append("</artifactId>");
+ buffer.append("<version>").append(artifact.getBaseVersion()).append("</version>");
+ buffer.append("<packaging>").append(artifact.getType()).append("</packaging>");
+ buffer.append("</project>");
+
+ return new StringModelSource(buffer, artifact.getId());
+ }
+
private static String inheritedGroupId(final ModelBuildingResult result, final int modelIndex) {
String groupId = null;
final String modelId = result.getModelIds().get(modelIndex);
@@ -933,31 +940,28 @@ public class DefaultProjectBuilder implements ProjectBuilder {
return version;
}
- /**
- * InternalConfig
- */
- class InternalConfig {
-
- private final ProjectBuildingRequest request;
-
- private final RepositorySystemSession session;
-
- private final List<RemoteRepository> repositories;
-
- private final ReactorModelPool modelPool;
+ static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
+ throw (T) t; // rely on vacuous cast
+ }
- private final TransformerContextBuilder transformerContextBuilder;
+ static class LazyMap<K, V> extends AbstractMap<K, V> {
+ private final Supplier<Map<K, V>> supplier;
+ private volatile Map<K, V> delegate;
- InternalConfig(
- ProjectBuildingRequest request,
- ReactorModelPool modelPool,
- TransformerContextBuilder transformerContextBuilder) {
- this.request = request;
- this.modelPool = modelPool;
- this.transformerContextBuilder = transformerContextBuilder;
+ LazyMap(Supplier<Map<K, V>> supplier) {
+ this.supplier = supplier;
+ }
- session = RepositoryUtils.overlay(request.getLocalRepository(), request.getRepositorySession(), repoSystem);
- repositories = RepositoryUtils.toRepos(request.getRemoteRepositories());
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ if (delegate == null) {
+ synchronized (this) {
+ if (delegate == null) {
+ delegate = supplier.get();
+ }
+ }
+ }
+ return delegate.entrySet();
}
}
}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
index 77cb05f89e..e1db4c34de 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
@@ -25,15 +25,7 @@ import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Properties;
+import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ForkJoinTask;
import java.util.function.Function;
@@ -48,7 +40,6 @@ import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.building.Source;
import org.apache.maven.model.Activation;
-import org.apache.maven.model.ActivationFile;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
@@ -58,7 +49,6 @@ import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.model.Profile;
-import org.apache.maven.model.Repository;
import org.apache.maven.model.building.ModelProblem.Severity;
import org.apache.maven.model.building.ModelProblem.Version;
import org.apache.maven.model.composition.DependencyManagementImporter;
@@ -713,7 +703,7 @@ public class DefaultModelBuilder implements ModelBuilder {
profileActivationContext.setUserProperties(profileProps);
}
- profileActivationContext.setProjectProperties(inputModel.getProperties());
+ profileActivationContext.setProjectProperties(inputModel.getDelegate().getProperties());
problems.setSource(inputModel);
List<Profile> activePomProfiles =
profileSelector.getActiveProfiles(inputModel.getProfiles(), profileActivationContext, problems);
@@ -742,6 +732,9 @@ public class DefaultModelBuilder implements ModelBuilder {
DefaultModelProblemCollector problems)
throws ModelBuildingException {
Model inputModel = readRawModel(request, problems);
+ if (problems.hasFatalErrors()) {
+ throw problems.newModelBuildingException();
+ }
problems.setRootModel(inputModel);
@@ -777,50 +770,43 @@ public class DefaultModelBuilder implements ModelBuilder {
String modelId = currentData.getId();
result.addModelId(modelId);
- Model rawModel = currentData.getModel();
- result.setRawModel(modelId, rawModel);
-
- profileActivationContext.setProjectProperties(rawModel.getProperties());
- problems.setSource(rawModel);
- List<Profile> activePomProfiles =
- profileSelector.getActiveProfiles(rawModel.getProfiles(), profileActivationContext, problems);
- result.setActivePomProfiles(modelId, activePomProfiles);
-
- Model tmpModel = rawModel.clone();
-
- problems.setSource(tmpModel);
+ Model model = currentData.getModel();
+ result.setRawModel(modelId, model);
+ problems.setSource(model);
+ org.apache.maven.api.model.Model modelv4 = model.getDelegate();
// model normalization
- tmpModel = new Model(modelNormalizer.mergeDuplicates(tmpModel.getDelegate(), request, problems));
+ modelv4 = modelNormalizer.mergeDuplicates(modelv4, request, problems);
- profileActivationContext.setProjectProperties(tmpModel.getProperties());
+ // profile activation
+ profileActivationContext.setProjectProperties(modelv4.getProperties());
- Map<String, Activation> interpolatedActivations =
- getInterpolatedActivations(rawModel, profileActivationContext, problems);
- injectProfileActivations(tmpModel, interpolatedActivations);
+ List<org.apache.maven.api.model.Profile> interpolatedProfiles =
+ interpolateActivations(modelv4.getProfiles(), profileActivationContext, problems);
// profile injection
- for (Profile activeProfile : result.getActivePomProfiles(modelId)) {
- profileInjector.injectProfile(tmpModel, activeProfile, request, problems);
- }
-
+ List<org.apache.maven.api.model.Profile> activePomProfiles =
+ profileSelector.getActiveProfilesV4(interpolatedProfiles, profileActivationContext, problems);
+ result.setActivePomProfiles(
+ modelId, activePomProfiles.stream().map(Profile::new).collect(Collectors.toList()));
+ modelv4 = profileInjector.injectProfiles(modelv4, activePomProfiles, request, problems);
if (currentData == resultData) {
for (Profile activeProfile : activeExternalProfiles) {
- profileInjector.injectProfile(tmpModel, activeProfile, request, problems);
+ modelv4 = profileInjector.injectProfile(modelv4, activeProfile.getDelegate(), request, problems);
}
- result.setEffectiveModel(tmpModel);
}
- lineage.add(tmpModel);
+ lineage.add(new Model(modelv4));
if (currentData == superData) {
break;
}
- configureResolver(request.getModelResolver(), tmpModel, problems);
+ // add repositories specified by the current model so that we can resolve the parent
+ configureResolver(request.getModelResolver(), modelv4, problems, false);
- ModelData parentData =
- readParent(currentData.getModel(), currentData.getSource(), request, result, problems);
+ // we pass a cloned model, so that resolving the parent version does not affect the returned model
+ ModelData parentData = readParent(new Model(modelv4), currentData.getSource(), request, problems);
if (parentData == null) {
currentData = superData;
@@ -840,7 +826,22 @@ public class DefaultModelBuilder implements ModelBuilder {
}
}
- problems.setSource(result.getRawModel());
+ Model tmpModel = lineage.get(0);
+
+ // inject interpolated activations
+ List<org.apache.maven.api.model.Profile> interpolated =
+ interpolateActivations(tmpModel.getDelegate().getProfiles(), profileActivationContext, problems);
+ if (interpolated != tmpModel.getDelegate().getProfiles()) {
+ tmpModel.update(tmpModel.getDelegate().withProfiles(interpolated));
+ }
+
+ // inject external profile into current model
+ tmpModel.update(profileInjector.injectProfiles(
+ tmpModel.getDelegate(),
+ activeExternalProfiles.stream().map(Profile::getDelegate).collect(Collectors.toList()),
+ request,
+ problems));
+
checkPluginVersions(lineage, request, problems);
// inheritance assembly
@@ -860,44 +861,76 @@ public class DefaultModelBuilder implements ModelBuilder {
result.setEffectiveModel(resultModel);
// Now the fully interpolated model is available: reconfigure the resolver
- configureResolver(request.getModelResolver(), resultModel, problems, true);
+ configureResolver(request.getModelResolver(), resultModel.getDelegate(), problems, true);
return resultModel;
}
- private Map<String, Activation> getInterpolatedActivations(
- Model rawModel, DefaultProfileActivationContext context, DefaultModelProblemCollector problems) {
- Map<String, Activation> interpolatedActivations = getProfileActivations(rawModel, true);
- for (Activation activation : interpolatedActivations.values()) {
- if (activation.getFile() != null) {
- replaceWithInterpolatedValue(activation.getFile(), context, problems);
+ private List<org.apache.maven.api.model.Profile> interpolateActivations(
+ List<org.apache.maven.api.model.Profile> profiles,
+ DefaultProfileActivationContext context,
+ DefaultModelProblemCollector problems) {
+ List<org.apache.maven.api.model.Profile> newProfiles = null;
+ for (int index = 0; index < profiles.size(); index++) {
+ org.apache.maven.api.model.Profile profile = profiles.get(index);
+ org.apache.maven.api.model.Activation activation = profile.getActivation();
+ if (activation != null) {
+ org.apache.maven.api.model.ActivationFile file = activation.getFile();
+ if (file != null) {
+ String oldExists = file.getExists();
+ if (isNotEmpty(oldExists)) {
+ try {
+ String newExists = interpolate(oldExists, context);
+ if (!Objects.equals(oldExists, newExists)) {
+ if (newProfiles == null) {
+ newProfiles = new ArrayList<>(profiles);
+ }
+ newProfiles.set(
+ index, profile.withActivation(activation.withFile(file.withExists(newExists))));
+ }
+ } catch (InterpolationException e) {
+ addInterpolationProblem(problems, file, oldExists, e, "exists");
+ }
+ } else {
+ String oldMissing = file.getMissing();
+ if (isNotEmpty(oldMissing)) {
+ try {
+ String newMissing = interpolate(oldMissing, context);
+ if (!Objects.equals(oldMissing, newMissing)) {
+ if (newProfiles == null) {
+ newProfiles = new ArrayList<>(profiles);
+ }
+ newProfiles.set(
+ index,
+ profile.withActivation(activation.withFile(file.withMissing(newMissing))));
+ }
+ } catch (InterpolationException e) {
+ addInterpolationProblem(problems, file, oldMissing, e, "missing");
+ }
+ }
+ }
+ }
}
}
- return interpolatedActivations;
+ return newProfiles != null ? newProfiles : profiles;
}
- private void replaceWithInterpolatedValue(
- ActivationFile activationFile, ProfileActivationContext context, DefaultModelProblemCollector problems) {
- try {
- if (isNotEmpty(activationFile.getExists())) {
- String path = activationFile.getExists();
- String absolutePath = profileActivationFilePathInterpolator.interpolate(path, context);
- activationFile.setExists(absolutePath);
- } else if (isNotEmpty(activationFile.getMissing())) {
- String path = activationFile.getMissing();
- String absolutePath = profileActivationFilePathInterpolator.interpolate(path, context);
- activationFile.setMissing(absolutePath);
- }
- } catch (InterpolationException e) {
- String path =
- isNotEmpty(activationFile.getExists()) ? activationFile.getExists() : activationFile.getMissing();
+ private static void addInterpolationProblem(
+ DefaultModelProblemCollector problems,
+ org.apache.maven.api.model.ActivationFile file,
+ String path,
+ InterpolationException e,
+ String locationKey) {
+ problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
+ .setMessage("Failed to interpolate file location " + path + ": " + e.getMessage())
+ .setLocation(Optional.ofNullable(file.getLocation(locationKey))
+ .map(InputLocation::new)
+ .orElse(null))
+ .setException(e));
+ }
- problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
- .setMessage("Failed to interpolate file location " + path + ": " + e.getMessage())
- .setLocation(
- activationFile.getLocation(isNotEmpty(activationFile.getExists()) ? "exists" : "missing"))
- .setException(e));
- }
+ private String interpolate(String path, ProfileActivationContext context) throws InterpolationException {
+ return isNotEmpty(path) ? profileActivationFilePathInterpolator.interpolate(path, context) : path;
}
private static boolean isNotEmpty(String string) {
@@ -910,6 +943,15 @@ public class DefaultModelBuilder implements ModelBuilder {
return build(request, result, new LinkedHashSet<>());
}
+ public Model buildRawModel(final ModelBuildingRequest request) throws ModelBuildingException {
+ DefaultModelProblemCollector problems = new DefaultModelProblemCollector(new DefaultModelBuildingResult());
+ Model model = readRawModel(request, problems);
+ if (hasModelErrors(problems)) {
+ throw problems.newModelBuildingException();
+ }
+ return model;
+ }
+
private ModelBuildingResult build(
final ModelBuildingRequest request, final ModelBuildingResult phaseOneResult, Collection<String> imports)
throws ModelBuildingException {
@@ -1127,41 +1169,49 @@ public class DefaultModelBuilder implements ModelBuilder {
throws ModelBuildingException {
ModelSource modelSource = request.getModelSource();
- ModelData cachedData = cache(request.getModelCache(), modelSource, ModelCacheTag.RAW, () -> {
- Model rawModel;
- if (Features.buildConsumer(request.getUserProperties()) && modelSource instanceof FileModelSource) {
- rawModel = readFileModel(request, problems);
- File pomFile = ((FileModelSource) modelSource).getFile();
+ ModelData modelData = cache(
+ request.getModelCache(),
+ modelSource,
+ ModelCacheTag.RAW,
+ () -> doReadRawModel(modelSource, request, problems));
+
+ return modelData.getModel();
+ }
- try {
- if (request.getTransformerContextBuilder() != null) {
- TransformerContext context =
- request.getTransformerContextBuilder().initialize(request, problems);
- transformer.transform(pomFile.toPath(), context, rawModel);
- }
- } catch (TransformerException e) {
- problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V40).setException(e));
+ private ModelData doReadRawModel(
+ ModelSource modelSource, ModelBuildingRequest request, DefaultModelProblemCollector problems)
+ throws ModelBuildingException {
+ Model rawModel;
+ if (Features.buildConsumer(request.getUserProperties()) && modelSource instanceof FileModelSource) {
+ rawModel = readFileModel(request, problems);
+ File pomFile = ((FileModelSource) modelSource).getFile();
+
+ try {
+ if (request.getTransformerContextBuilder() != null) {
+ TransformerContext context =
+ request.getTransformerContextBuilder().initialize(request, problems);
+ transformer.transform(pomFile.toPath(), context, rawModel);
}
- } else if (request.getFileModel() == null) {
- rawModel = readFileModel(request, problems);
- } else {
- rawModel = request.getFileModel().clone();
+ } catch (TransformerException e) {
+ problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V40).setException(e));
}
+ } else if (request.getFileModel() == null) {
+ rawModel = readFileModel(request, problems);
+ } else {
+ rawModel = request.getFileModel().clone();
+ }
- modelValidator.validateRawModel(rawModel, request, problems);
+ modelValidator.validateRawModel(rawModel, request, problems);
- if (hasFatalErrors(problems)) {
- throw problems.newModelBuildingException();
- }
+ if (hasFatalErrors(problems)) {
+ throw problems.newModelBuildingException();
+ }
- String groupId = getGroupId(rawModel);
- String artifactId = rawModel.getArtifactId();
- String version = getVersion(rawModel);
+ String groupId = getGroupId(rawModel);
+ String artifactId = rawModel.getArtifactId();
+ String version = getVersion(rawModel);
- ModelData modelData = new ModelData(modelSource, rawModel, groupId, artifactId, version);
- return modelData;
- });
- return cachedData != null ? cachedData.getModel() : null;
+ return new ModelData(modelSource, rawModel, groupId, artifactId, version);
}
String getGroupId(Model model) {
@@ -1204,31 +1254,21 @@ public class DefaultModelBuilder implements ModelBuilder {
return context;
}
- private void configureResolver(ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems) {
- configureResolver(modelResolver, model, problems, false);
- }
-
private void configureResolver(
ModelResolver modelResolver,
- Model model,
+ org.apache.maven.api.model.Model model,
DefaultModelProblemCollector problems,
boolean replaceRepositories) {
- if (modelResolver == null) {
- return;
- }
-
- problems.setSource(model);
-
- List<Repository> repositories = model.getRepositories();
-
- for (Repository repository : repositories) {
- try {
- modelResolver.addRepository(repository, replaceRepositories);
- } catch (InvalidRepositoryException e) {
- problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
- .setMessage("Invalid repository " + repository.getId() + ": " + e.getMessage())
- .setLocation(repository.getLocation(""))
- .setException(e));
+ if (modelResolver != null) {
+ for (org.apache.maven.api.model.Repository repository : model.getRepositories()) {
+ try {
+ modelResolver.addRepository(repository, replaceRepositories);
+ } catch (InvalidRepositoryException e) {
+ problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
+ .setMessage("Invalid repository " + repository.getId() + ": " + e.getMessage())
+ .setLocation(new InputLocation(repository.getLocation("")))
+ .setException(e));
+ }
}
}
}
@@ -1353,19 +1393,15 @@ public class DefaultModelBuilder implements ModelBuilder {
}
private ModelData readParent(
- Model childModel,
- Source childSource,
- ModelBuildingRequest request,
- ModelBuildingResult result,
- DefaultModelProblemCollector problems)
+ Model childModel, Source childSource, ModelBuildingRequest request, DefaultModelProblemCollector problems)
throws ModelBuildingException {
ModelData parentData = null;
Parent parent = childModel.getParent();
if (parent != null) {
- parentData = readParentLocally(childModel, childSource, request, result, problems);
+ parentData = readParentLocally(childModel, childSource, request, problems);
if (parentData == null) {
- parentData = readParentExternally(childModel, request, result, problems);
+ parentData = readParentExternally(childModel, request, problems);
}
Model parentModel = parentData.getModel();
@@ -1381,11 +1417,7 @@ public class DefaultModelBuilder implements ModelBuilder {
}
private ModelData readParentLocally(
- Model childModel,
- Source childSource,
- ModelBuildingRequest request,
- ModelBuildingResult result,
- DefaultModelProblemCollector problems)
+ Model childModel, Source childSource, ModelBuildingRequest request, DefaultModelProblemCollector problems)
throws ModelBuildingException {
final Parent parent = childModel.getParent();
final ModelSource2 candidateSource;
@@ -1524,10 +1556,7 @@ public class DefaultModelBuilder implements ModelBuilder {
}
private ModelData readParentExternally(
- Model childModel,
- ModelBuildingRequest request,
- ModelBuildingResult result,
- DefaultModelProblemCollector problems)
+ Model childModel, ModelBuildingRequest request, DefaultModelProblemCollector problems)
throws ModelBuildingException {
problems.setSource(childModel);
@@ -1701,12 +1730,14 @@ public class DefaultModelBuilder implements ModelBuilder {
return null;
}
- org.apache.maven.api.model.DependencyManagement importMgmt =
- cache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, () -> {
- DependencyManagement importMgmtV3 = doLoadDependencyManagement(
- model, request, problems, dependency, groupId, artifactId, version, importIds);
- return importMgmtV3 != null ? importMgmtV3.getDelegate() : null;
- });
+ org.apache.maven.api.model.DependencyManagement importMgmt = cache(
+ request.getModelCache(),
+ groupId,
+ artifactId,
+ version,
+ ModelCacheTag.IMPORT,
+ () -> doLoadDependencyManagement(
+ model, request, problems, dependency, groupId, artifactId, version, importIds));
// [MNG-5600] Dependency management import should support exclusions.
List<Exclusion> exclusions = dependency.getDelegate().getExclusions();
@@ -1732,7 +1763,7 @@ public class DefaultModelBuilder implements ModelBuilder {
}
@SuppressWarnings("checkstyle:parameternumber")
- private DependencyManagement doLoadDependencyManagement(
+ private org.apache.maven.api.model.DependencyManagement doLoadDependencyManagement(
Model model,
ModelBuildingRequest request,
DefaultModelProblemCollector problems,
@@ -1810,7 +1841,7 @@ public class DefaultModelBuilder implements ModelBuilder {
if (importMgmt == null) {
importMgmt = new DependencyManagement();
}
- return importMgmt;
+ return importMgmt.getDelegate();
}
private static <T> T cache(
@@ -1863,8 +1894,7 @@ public class DefaultModelBuilder implements ModelBuilder {
Model model,
ModelBuildingRequest request,
ModelProblemCollector problems,
- ModelBuildingEventCatapult catapult)
- throws ModelBuildingException {
+ ModelBuildingEventCatapult catapult) {
ModelBuildingListener listener = request.getModelBuildingListener();
if (listener != null) {
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java
index a2dce0e491..6b5de94484 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java
@@ -28,8 +28,6 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.DefaultTransformerContext.GAKey;
import org.apache.maven.model.building.DefaultTransformerContext.Holder;
-import org.codehaus.plexus.util.dag.CycleDetectedException;
-import org.codehaus.plexus.util.dag.DAG;
/**
* Builds up the transformer context.
@@ -39,7 +37,7 @@ import org.codehaus.plexus.util.dag.DAG;
* @since 4.0.0
*/
class DefaultTransformerContextBuilder implements TransformerContextBuilder {
- private final DAG dag = new DAG();
+ private final Graph dag = new Graph();
private final DefaultModelBuilder defaultModelBuilder;
private final DefaultTransformerContext context;
@@ -134,10 +132,10 @@ class DefaultTransformerContextBuilder implements TransformerContextBuilder {
private boolean addEdge(Path from, Path p, DefaultModelProblemCollector problems) {
try {
synchronized (dag) {
- dag.addEdge(from.toString(), p.toString());
+ dag.addEdge(dag.addVertex(from.toString()), dag.addVertex(p.toString()));
}
return true;
- } catch (CycleDetectedException e) {
+ } catch (Graph.CycleDetectedException e) {
problems.add(new DefaultModelProblem(
"Cycle detected between models at " + from + " and " + p,
ModelProblem.Severity.FATAL,
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/Graph.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/Graph.java
new file mode 100644
index 0000000000..1f8abee697
--- /dev/null
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/Graph.java
@@ -0,0 +1,150 @@
+/*
+ * 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.maven.model.building;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+class Graph {
+ private enum DfsState {
+ VISITING,
+ VISITED
+ }
+
+ final Map<String, Vertex> vertices = new LinkedHashMap<>();
+
+ public Vertex getVertex(String id) {
+ return vertices.get(id);
+ }
+
+ public Collection<Vertex> getVertices() {
+ return vertices.values();
+ }
+
+ Vertex addVertex(String label) {
+ return vertices.computeIfAbsent(label, Vertex::new);
+ }
+
+ void addEdge(Vertex from, Vertex to) throws CycleDetectedException {
+ from.children.add(to);
+ to.parents.add(from);
+ List<String> cycle = findCycle(to);
+ if (cycle != null) {
+ // remove edge which introduced cycle
+ removeEdge(from, to);
+ throw new CycleDetectedException(
+ "Edge between '" + from.label + "' and '" + to.label + "' introduces to cycle in the graph", cycle);
+ }
+ }
+
+ void removeEdge(Vertex from, Vertex to) {
+ from.children.remove(to);
+ to.parents.remove(from);
+ }
+
+ List<String> visitAll() {
+ return visitAll(vertices.values(), new HashMap<>(), new ArrayList<>());
+ }
+
+ List<String> findCycle(Vertex vertex) {
+ return visitCycle(Collections.singleton(vertex), new HashMap<>(), new LinkedList<>());
+ }
+
+ private static List<String> visitAll(
+ Collection<Vertex> children, Map<Vertex, DfsState> stateMap, List<String> list) {
+ for (Vertex v : children) {
+ DfsState state = stateMap.putIfAbsent(v, DfsState.VISITING);
+ if (state == null) {
+ visitAll(v.children, stateMap, list);
+ stateMap.put(v, DfsState.VISITED);
+ list.add(v.label);
+ }
+ }
+ return list;
+ }
+
+ private static List<String> visitCycle(
+ Collection<Vertex> children, Map<Vertex, DfsState> stateMap, LinkedList<String> cycle) {
+ for (Vertex v : children) {
+ DfsState state = stateMap.putIfAbsent(v, DfsState.VISITING);
+ if (state == null) {
+ cycle.addLast(v.label);
+ List<String> ret = visitCycle(v.children, stateMap, cycle);
+ if (ret != null) {
+ return ret;
+ }
+ cycle.removeLast();
+ stateMap.put(v, DfsState.VISITED);
+ } else if (state == DfsState.VISITING) {
+ // we are already visiting this vertex, this mean we have a cycle
+ int pos = cycle.lastIndexOf(v.label);
+ List<String> ret = cycle.subList(pos, cycle.size());
+ ret.add(v.label);
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ static class Vertex {
+ final String label;
+ final List<Vertex> children = new ArrayList<>();
+ final List<Vertex> parents = new ArrayList<>();
+
+ Vertex(String label) {
+ this.label = label;
+ }
+
+ String getLabel() {
+ return label;
+ }
+
+ List<Vertex> getChildren() {
+ return children;
+ }
+
+ List<Vertex> getParents() {
+ return parents;
+ }
+ }
+
+ public static class CycleDetectedException extends Exception {
+ private final List<String> cycle;
+
+ CycleDetectedException(String message, List<String> cycle) {
+ super(message);
+ this.cycle = cycle;
+ }
+
+ public List<String> getCycle() {
+ return cycle;
+ }
+
+ @Override
+ public String getMessage() {
+ return super.getMessage() + " " + String.join(" --> ", cycle);
+ }
+ }
+}
(maven) 02/04: [MNG-7615] Add some methods for supporting Maven 4 api to ProfileInjector / ProfileSelector
Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
commit 53ff1690de5f453cb7a5f3a004c46eba06ec875c
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Wed Sep 20 16:42:17 2023 +0200
[MNG-7615] Add some methods for supporting Maven 4 api to ProfileInjector / ProfileSelector
---
maven-model-builder/pom.xml | 3 ++
.../model/profile/DefaultProfileInjector.java | 55 +++++++++++++++++-----
.../model/profile/DefaultProfileSelector.java | 12 +++++
.../maven/model/profile/ProfileInjector.java | 43 +++++++++++++++++
.../maven/model/profile/ProfileSelector.java | 21 +++++++++
5 files changed, 123 insertions(+), 11 deletions(-)
diff --git a/maven-model-builder/pom.xml b/maven-model-builder/pom.xml
index f315f64b75..4ff0becbc5 100644
--- a/maven-model-builder/pom.xml
+++ b/maven-model-builder/pom.xml
@@ -167,6 +167,9 @@ under the License.
<exclude>org.apache.maven.model.profile.activation.FileProfileActivator#setProfileActivationFilePathInterpolator(org.apache.maven.model.path.ProfileActivationFilePathInterpolator):METHOD_REMOVED</exclude>
<exclude>org.apache.maven.model.profile.activation.FileProfileActivator#FileProfileActivator():CONSTRUCTOR_REMOVED</exclude>
<exclude>org.apache.maven.model.profile.DefaultProfileInjector</exclude>
+ <exclude>org.apache.maven.model.profile.ProfileInjector#injectProfile(org.apache.maven.api.model.Model,org.apache.maven.api.model.Profile,org.apache.maven.model.building.ModelBuildingRequest,org.apache.maven.model.building.ModelProblemCollector):METHOD_NEW_DEFAULT</exclude>
+ <exclude>org.apache.maven.model.profile.ProfileInjector#injectProfiles(org.apache.maven.api.model.Model,java.util.List,org.apache.maven.model.building.ModelBuildingRequest,org.apache.maven.model.building.ModelProblemCollector):METHOD_NEW_DEFAULT</exclude>
+ <exclude>org.apache.maven.model.profile.ProfileSelector#getActiveProfilesV4(java.util.Collection,org.apache.maven.model.profile.ProfileActivationContext,org.apache.maven.model.building.ModelProblemCollector):METHOD_NEW_DEFAULT</exclude>
<exclude>org.apache.maven.model.resolution.InvalidRepositoryException#getRepository():METHOD_RETURN_TYPE_CHANGED</exclude>
<exclude>org.apache.maven.model.resolution.InvalidRepositoryException#InvalidRepositoryException(java.lang.String,org.apache.maven.model.Repository,java.lang.Throwable):CONSTRUCTOR_REMOVED</exclude>
<exclude>org.apache.maven.model.resolution.InvalidRepositoryException#InvalidRepositoryException(java.lang.String,org.apache.maven.model.Repository):CONSTRUCTOR_REMOVED</exclude>
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java
index f67ca6275c..a435002837 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java
@@ -26,6 +26,8 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.maven.api.model.Build;
import org.apache.maven.api.model.BuildBase;
@@ -34,6 +36,7 @@ import org.apache.maven.api.model.ModelBase;
import org.apache.maven.api.model.Plugin;
import org.apache.maven.api.model.PluginContainer;
import org.apache.maven.api.model.PluginExecution;
+import org.apache.maven.api.model.Profile;
import org.apache.maven.api.model.ReportPlugin;
import org.apache.maven.api.model.ReportSet;
import org.apache.maven.api.model.Reporting;
@@ -50,6 +53,13 @@ import org.apache.maven.model.merge.MavenModelMerger;
@SuppressWarnings({"checkstyle:methodname"})
public class DefaultProfileInjector implements ProfileInjector {
+ private static final Map<Model, Map<List<Profile>, Model>> CACHE = Collections.synchronizedMap(new WeakHashMap<>());
+
+ // In order for the weak hash map to work correctly, we must not hold any reference to
+ // the model used as the key. So we use a dummy model as a placeholder to indicate that
+ // we want to store the model used as they key.
+ private static final Model KEY = Model.newInstance();
+
private ProfileModelMerger merger = new ProfileModelMerger();
@Override
@@ -58,19 +68,42 @@ public class DefaultProfileInjector implements ProfileInjector {
org.apache.maven.model.Profile profile,
ModelBuildingRequest request,
ModelProblemCollector problems) {
- if (profile != null) {
- Model.Builder builder = Model.newBuilder(model.getDelegate());
- merger.mergeModelBase(builder, model.getDelegate(), profile.getDelegate());
-
- if (profile.getBuild() != null) {
- Build build = model.getBuild() != null ? model.getBuild().getDelegate() : Build.newInstance();
- Build.Builder bbuilder = Build.newBuilder(build);
- merger.mergeBuildBase(bbuilder, build, profile.getBuild().getDelegate());
- builder.build(bbuilder.build());
- }
+ model.update(
+ injectProfile(model.getDelegate(), profile != null ? profile.getDelegate() : null, request, problems));
+ }
+
+ @Override
+ public Model injectProfile(
+ Model model, Profile profile, ModelBuildingRequest request, ModelProblemCollector problems) {
+ return injectProfiles(model, Collections.singletonList(profile), request, problems);
+ }
- model.update(builder.build());
+ @Override
+ public Model injectProfiles(
+ Model model, List<Profile> profiles, ModelBuildingRequest request, ModelProblemCollector problems) {
+ Model result = CACHE.computeIfAbsent(model, k -> new ConcurrentHashMap<>())
+ .computeIfAbsent(profiles, l -> doInjectProfiles(model, profiles));
+ return result == KEY ? model : result;
+ }
+
+ private Model doInjectProfiles(Model model, List<Profile> profiles) {
+ Model orgModel = model;
+ for (Profile profile : profiles) {
+ if (profile != null) {
+ Model.Builder builder = Model.newBuilder(model);
+ merger.mergeModelBase(builder, model, profile);
+
+ if (profile.getBuild() != null) {
+ Build build = model.getBuild() != null ? model.getBuild() : Build.newInstance();
+ Build.Builder bbuilder = Build.newBuilder(build);
+ merger.mergeBuildBase(bbuilder, build, profile.getBuild());
+ builder.build(bbuilder.build());
+ }
+
+ model = builder.build();
+ }
}
+ return model == orgModel ? KEY : model;
}
/**
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java
index 45959f2ed1..b2d0e9a801 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.stream.Collectors;
import org.apache.maven.model.Activation;
import org.apache.maven.model.Profile;
@@ -61,6 +62,17 @@ public class DefaultProfileSelector implements ProfileSelector {
return this;
}
+ @Override
+ public List<org.apache.maven.api.model.Profile> getActiveProfilesV4(
+ Collection<org.apache.maven.api.model.Profile> profiles,
+ ProfileActivationContext context,
+ ModelProblemCollector problems) {
+ return getActiveProfiles(profiles.stream().map(Profile::new).collect(Collectors.toList()), context, problems)
+ .stream()
+ .map(Profile::getDelegate)
+ .collect(Collectors.toList());
+ }
+
@Override
public List<Profile> getActiveProfiles(
Collection<Profile> profiles, ProfileActivationContext context, ModelProblemCollector problems) {
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java
index ed62c86221..e4ab40015b 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java
@@ -18,6 +18,8 @@
*/
package org.apache.maven.model.profile;
+import java.util.List;
+
import org.apache.maven.model.Model;
import org.apache.maven.model.Profile;
import org.apache.maven.model.building.ModelBuildingRequest;
@@ -39,4 +41,45 @@ public interface ProfileInjector {
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
*/
void injectProfile(Model model, Profile profile, ModelBuildingRequest request, ModelProblemCollector problems);
+
+ /**
+ * Merges values from the specified profile into the given model. Implementations are expected to keep the profile
+ * and model completely decoupled by injecting deep copies rather than the original objects from the profile.
+ *
+ * @param model The model into which to merge the values defined by the profile, must not be <code>null</code>.
+ * @param profile The (read-only) profile whose values should be injected, may be <code>null</code>.
+ * @param request The model building request that holds further settings, must not be {@code null}.
+ * @param problems The container used to collect problems that were encountered, must not be {@code null}.
+ */
+ default org.apache.maven.api.model.Model injectProfile(
+ org.apache.maven.api.model.Model model,
+ org.apache.maven.api.model.Profile profile,
+ ModelBuildingRequest request,
+ ModelProblemCollector problems) {
+ Model m = new Model(model);
+ injectProfile(m, profile != null ? new Profile(profile) : null, request, problems);
+ return m.getDelegate();
+ }
+
+ /**
+ * Merges values from the specified profile into the given model. Implementations are expected to keep the profile
+ * and model completely decoupled by injecting deep copies rather than the original objects from the profile.
+ *
+ * @param model The model into which to merge the values defined by the profile, must not be <code>null</code>.
+ * @param profiles The (read-only) list of profiles whose values should be injected, must not be <code>null</code>.
+ * @param request The model building request that holds further settings, must not be {@code null}.
+ * @param problems The container used to collect problems that were encountered, must not be {@code null}.
+ */
+ default org.apache.maven.api.model.Model injectProfiles(
+ org.apache.maven.api.model.Model model,
+ List<org.apache.maven.api.model.Profile> profiles,
+ ModelBuildingRequest request,
+ ModelProblemCollector problems) {
+ for (org.apache.maven.api.model.Profile profile : profiles) {
+ if (profile != null) {
+ model = injectProfile(model, profile, request, problems);
+ }
+ }
+ return model;
+ }
}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java
index f0412961c6..a87d695e5c 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java
@@ -20,6 +20,7 @@ package org.apache.maven.model.profile;
import java.util.Collection;
import java.util.List;
+import java.util.stream.Collectors;
import org.apache.maven.model.Profile;
import org.apache.maven.model.building.ModelProblemCollector;
@@ -42,4 +43,24 @@ public interface ProfileSelector {
*/
List<Profile> getActiveProfiles(
Collection<Profile> profiles, ProfileActivationContext context, ModelProblemCollector problems);
+
+ /**
+ * Determines the profiles which are active in the specified activation context. Active profiles will eventually be
+ * injected into the model.
+ *
+ * @param profiles The profiles whose activation status should be determined, must not be {@code null}.
+ * @param context The environmental context used to determine the activation status of a profile, must not be
+ * {@code null}.
+ * @param problems The container used to collect problems that were encountered, must not be {@code null}.
+ * @return The profiles that have been activated, never {@code null}.
+ */
+ default List<org.apache.maven.api.model.Profile> getActiveProfilesV4(
+ Collection<org.apache.maven.api.model.Profile> profiles,
+ ProfileActivationContext context,
+ ModelProblemCollector problems) {
+ return getActiveProfiles(profiles.stream().map(Profile::new).collect(Collectors.toList()), context, problems)
+ .stream()
+ .map(Profile::getDelegate)
+ .collect(Collectors.toList());
+ }
}