blob: eff08333f01bc1811bf1462de7c02943534589be [file] [log] [blame]
/*
* 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.project;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.ProjectCycleException;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.api.Session;
import org.apache.maven.api.feature.Features;
import org.apache.maven.api.model.*;
import org.apache.maven.api.services.MavenException;
import org.apache.maven.api.services.ModelBuilder;
import org.apache.maven.api.services.ModelBuilderException;
import org.apache.maven.api.services.ModelBuilderRequest;
import org.apache.maven.api.services.ModelBuilderResult;
import org.apache.maven.api.services.ModelProblem;
import org.apache.maven.api.services.ModelResolver;
import org.apache.maven.api.services.ModelResolverException;
import org.apache.maven.api.services.ModelSource;
import org.apache.maven.api.services.ModelTransformerContext;
import org.apache.maven.api.services.ModelTransformerContextBuilder;
import org.apache.maven.api.services.Source;
import org.apache.maven.api.services.model.ModelBuildingListener;
import org.apache.maven.api.services.model.ModelProcessor;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.InvalidArtifactRTException;
import org.apache.maven.artifact.InvalidRepositoryException;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.bridge.MavenRepositorySystem;
import org.apache.maven.internal.impl.InternalSession;
import org.apache.maven.model.building.ArtifactModelSource;
import org.apache.maven.model.building.DefaultModelProblem;
import org.apache.maven.model.building.FileModelSource;
import org.apache.maven.model.building.ModelSource2;
import org.apache.maven.model.building.ModelSource3;
import org.apache.maven.model.resolution.UnresolvableModelException;
import org.apache.maven.model.root.RootLocator;
import org.apache.maven.repository.internal.ArtifactDescriptorUtils;
import org.apache.maven.repository.internal.ModelCacheFactory;
import org.apache.maven.utils.Os;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.RequestTrace;
import org.eclipse.aether.impl.RemoteRepositoryManager;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.WorkspaceRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DefaultProjectBuilder
*/
@Named
@Singleton
public class DefaultProjectBuilder implements ProjectBuilder {
public static final String BUILDER_PARALLELISM = "maven.projectBuilder.parallelism";
public static final int DEFAULT_BUILDER_PARALLELISM = Runtime.getRuntime().availableProcessors() / 2 + 1;
private final Logger logger = LoggerFactory.getLogger(getClass());
private final ModelBuilder modelBuilder;
private final ModelProcessor modelProcessor;
private final ProjectBuildingHelper projectBuildingHelper;
private final MavenRepositorySystem repositorySystem;
private final org.eclipse.aether.RepositorySystem repoSystem;
private final RemoteRepositoryManager repositoryManager;
private final ProjectDependenciesResolver dependencyResolver;
private final ModelCacheFactory modelCacheFactory;
private final RootLocator rootLocator;
@SuppressWarnings("checkstyle:ParameterNumber")
@Inject
public DefaultProjectBuilder(
ModelBuilder modelBuilder,
ModelProcessor modelProcessor,
ProjectBuildingHelper projectBuildingHelper,
MavenRepositorySystem repositorySystem,
RepositorySystem repoSystem,
RemoteRepositoryManager repositoryManager,
ProjectDependenciesResolver dependencyResolver,
ModelCacheFactory modelCacheFactory,
RootLocator rootLocator) {
this.modelBuilder = modelBuilder;
this.modelProcessor = modelProcessor;
this.projectBuildingHelper = projectBuildingHelper;
this.repositorySystem = repositorySystem;
this.repoSystem = repoSystem;
this.repositoryManager = repositoryManager;
this.dependencyResolver = dependencyResolver;
this.modelCacheFactory = modelCacheFactory;
this.rootLocator = rootLocator;
}
// ----------------------------------------------------------------------
// MavenProjectBuilder Implementation
// ----------------------------------------------------------------------
@Override
public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest request) throws ProjectBuildingException {
try (BuildSession bs = new BuildSession(request, false)) {
Path path = pomFile.toPath();
return bs.build(path, ModelSource.fromPath(path));
}
}
@Deprecated
@Override
public ProjectBuildingResult build(
org.apache.maven.model.building.ModelSource modelSource, ProjectBuildingRequest request)
throws ProjectBuildingException {
return build(toSource(modelSource), request);
}
@Deprecated
private ModelSource toSource(org.apache.maven.model.building.ModelSource modelSource) {
if (modelSource instanceof FileModelSource fms) {
return ModelSource.fromPath(fms.getPath());
} else if (modelSource instanceof ArtifactModelSource ams) {
return ModelSource.fromPath(ams.getPath(), ams.toString());
} else {
return new ModelSource() {
@Override
public ModelSource resolve(ModelLocator modelLocator, String relative) {
if (modelSource instanceof ModelSource3 ms) {
return toSource(ms.getRelatedSource(
new org.apache.maven.model.locator.ModelLocator() {
@Override
public File locatePom(File projectDirectory) {
return null;
}
@Override
public Path locatePom(Path projectDirectory) {
return null;
}
@Override
public Path locateExistingPom(Path project) {
return modelLocator.locateExistingPom(project);
}
},
relative));
}
return null;
}
@Override
public Path getPath() {
return null;
}
@Override
public InputStream openStream() throws IOException {
return modelSource.getInputStream();
}
@Override
public String getLocation() {
return modelSource.getLocation();
}
@Override
public Source resolve(String relative) {
if (modelSource instanceof ModelSource2 ms) {
return toSource(ms.getRelatedSource(relative));
} else {
return null;
}
}
};
}
}
@Override
public ProjectBuildingResult build(ModelSource modelSource, ProjectBuildingRequest request)
throws ProjectBuildingException {
try (BuildSession bs = new BuildSession(request, false)) {
return bs.build(null, modelSource);
}
}
@Override
public ProjectBuildingResult build(Artifact artifact, ProjectBuildingRequest request)
throws ProjectBuildingException {
return build(artifact, false, request);
}
@Override
public ProjectBuildingResult build(Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request)
throws ProjectBuildingException {
try (BuildSession bs = new BuildSession(request, false)) {
return bs.build(artifact, allowStubModel);
}
}
@Override
public List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive, ProjectBuildingRequest request)
throws ProjectBuildingException {
try (BuildSession bs = new BuildSession(request, true)) {
return bs.build(pomFiles, recursive);
}
}
static class InterimResult {
File pomFile;
ModelBuilderRequest request;
ModelBuilderResult result;
MavenProject project;
boolean root;
List<InterimResult> modules = Collections.emptyList();
ProjectBuildingResult projectBuildingResult;
InterimResult(
File pomFile,
ModelBuilderRequest request,
ModelBuilderResult result,
MavenProject project,
boolean root) {
this.pomFile = pomFile;
this.request = request;
this.result = result;
this.project = project;
this.root = root;
}
InterimResult(ModelBuilderRequest request, ProjectBuildingResult projectBuildingResult) {
this.request = request;
this.projectBuildingResult = projectBuildingResult;
this.pomFile = projectBuildingResult.getPomFile();
this.project = projectBuildingResult.getProject();
}
}
class BuildSession implements AutoCloseable {
private final ProjectBuildingRequest request;
private final RepositorySystemSession session;
private final List<RemoteRepository> repositories;
private final ReactorModelPool modelPool;
private final ConcurrentMap<String, Object> parentCache;
private final ModelTransformerContextBuilder transformerContextBuilder;
private final ExecutorService executor;
BuildSession(ProjectBuildingRequest request, boolean localProjects) {
this.request = request;
this.session =
RepositoryUtils.overlay(request.getLocalRepository(), request.getRepositorySession(), repoSystem);
InternalSession.from(session);
this.repositories = RepositoryUtils.toRepos(request.getRemoteRepositories());
this.executor = createExecutor(getParallelism(request));
if (localProjects) {
this.modelPool = new ReactorModelPool();
this.transformerContextBuilder = modelBuilder.newTransformerContextBuilder();
} else {
this.modelPool = null;
this.transformerContextBuilder = null;
}
this.parentCache = new ConcurrentHashMap<>();
}
ExecutorService createExecutor(int parallelism) {
//
// We need an executor that will not block.
// We can't use work stealing, as we are building a graph
// and this could lead to cycles where a thread waits for
// a task to finish, then execute another one which waits
// for the initial task...
// In order to work around that problem, we override the
// invokeAll method, so that whenever the method is called,
// the pool core size will be incremented before submitting
// all the tasks, then the thread will block waiting for all
// those subtasks to finish.
// This ensures the number of running workers is no more than
// the defined parallism, while making sure the pool will not
// be exhausted
//
return new ThreadPoolExecutor(
parallelism, Integer.MAX_VALUE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()) {
final AtomicInteger parked = new AtomicInteger();
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
setCorePoolSize(parallelism + parked.incrementAndGet());
try {
return super.invokeAll(tasks);
} finally {
setCorePoolSize(parallelism + parked.decrementAndGet());
}
}
};
}
@Override
public void close() {
this.executor.shutdownNow();
}
private int getParallelism(ProjectBuildingRequest request) {
int parallelism = DEFAULT_BUILDER_PARALLELISM;
try {
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()));
}
ProjectBuildingResult build(Path pomFile, ModelSource modelSource) throws ProjectBuildingException {
ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
try {
MavenProject project = request.getProject();
List<ModelProblem> modelProblems = null;
Throwable error = null;
if (project == null) {
project = new MavenProject();
project.setFile(pomFile != null ? pomFile.toFile() : null);
ModelBuildingListener listener =
new DefaultModelBuildingListener(project, projectBuildingHelper, this.request);
ModelBuilderRequest.ModelBuilderRequestBuilder builder = getModelBuildingRequest();
ModelBuilderRequest request = builder.projectBuild(modelPool != null)
.source(modelSource)
.locationTracking(true)
.listener(listener)
.build();
if (pomFile != null) {
project.setRootDirectory(rootLocator.findRoot(pomFile.getParent()));
}
ModelBuilderResult result;
try {
result = modelBuilder.build(request);
} catch (ModelBuilderException e) {
result = e.getResult();
if (result == null || result.getEffectiveModel() == null) {
throw new ProjectBuildingException(
e.getModelId(), e.getMessage(), pomFile != null ? pomFile.toFile() : null, e);
}
// validation error, continue project building and delay failing to help IDEs
error = e;
}
modelProblems = result.getProblems();
initProject(project, Collections.emptyMap(), result);
} else if (request.isResolveDependencies()) {
projectBuildingHelper.selectProjectRealm(project);
}
DependencyResolutionResult resolutionResult = null;
if (request.isResolveDependencies()) {
resolutionResult = resolveDependencies(project);
}
ProjectBuildingResult result =
new DefaultProjectBuildingResult(project, convert(modelProblems), resolutionResult);
if (error != null) {
ProjectBuildingException e = new ProjectBuildingException(List.of(result));
e.initCause(error);
throw e;
}
return result;
} finally {
Thread.currentThread().setContextClassLoader(oldContextClassLoader);
}
}
ProjectBuildingResult build(Artifact artifact, boolean allowStubModel) throws ProjectBuildingException {
org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact(artifact);
pomArtifact = ArtifactDescriptorUtils.toPomArtifact(pomArtifact);
boolean localProject;
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);
}
Path pomFile = pomArtifact.getPath();
if (!artifact.isResolved() && "pom".equals(artifact.getType())) {
artifact.selectVersion(pomArtifact.getVersion());
artifact.setFile(pomFile.toFile());
artifact.setResolved(true);
}
if (localProject) {
return build(pomFile, ModelSource.fromPath(pomFile));
} else {
return build(
null,
ModelSource.fromPath(
pomFile,
artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion()));
}
}
List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive) throws ProjectBuildingException {
List<ProjectBuildingResult> results = doBuild(pomFiles, recursive);
if (results.stream()
.flatMap(r -> r.getProblems().stream())
.anyMatch(p -> p.getSeverity() != org.apache.maven.model.building.ModelProblem.Severity.WARNING)) {
org.apache.maven.model.building.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);
}
return results;
}
List<ProjectBuildingResult> doBuild(List<File> pomFiles, boolean recursive) {
Map<File, MavenProject> projectIndex = new ConcurrentHashMap<>(256);
// phase 1: get file Models from the reactor.
List<InterimResult> interimResults = build(projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive);
ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
try {
// Phase 2: get effective models from the reactor
List<ProjectBuildingResult> results = build(projectIndex, interimResults);
if (Features.buildConsumer(request.getUserProperties())) {
request.getRepositorySession()
.getData()
.set(ModelTransformerContext.KEY, transformerContextBuilder.build());
}
return results;
} finally {
Thread.currentThread().setContextClassLoader(oldContextClassLoader);
}
}
@SuppressWarnings("checkstyle:parameternumber")
private List<InterimResult> build(
Map<File, MavenProject> projectIndex,
List<File> pomFiles,
Set<File> aggregatorFiles,
boolean root,
boolean recursive) {
List<Callable<InterimResult>> tasks = pomFiles.stream()
.map(pomFile -> ((Callable<InterimResult>)
() -> build(projectIndex, pomFile, concat(aggregatorFiles, pomFile), root, recursive)))
.collect(Collectors.toList());
try {
List<Future<InterimResult>> futures = executor.invokeAll(tasks);
List<InterimResult> list = new ArrayList<>();
for (Future<InterimResult> future : futures) {
InterimResult interimResult = future.get();
list.add(interimResult);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private <T> Set<T> concat(Set<T> set, T elem) {
Set<T> newSet = new HashSet<>(set);
newSet.add(elem);
return newSet;
}
@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);
project.setRootDirectory(
rootLocator.findRoot(pomFile.getParentFile().toPath()));
DefaultModelBuildingListener listener =
new DefaultModelBuildingListener(project, projectBuildingHelper, request);
ModelBuilderRequest modelBuildingRequest = getModelBuildingRequest()
.source(ModelSource.fromPath(pomFile.toPath()))
.projectBuild(true)
.twoPhaseBuilding(true)
.locationTracking(true)
.listener(listener)
.build();
ModelBuilderResult result;
try {
result = modelBuilder.build(modelBuildingRequest);
} catch (ModelBuilderException e) {
result = e.getResult();
if (result == null || result.getFileModel() == null) {
return new InterimResult(
modelBuildingRequest,
new DefaultProjectBuildingResult(e.getModelId(), pomFile, convert(e.getProblems())));
}
// validation error, continue project building and delay failing to help IDEs
// result.getProblems().addAll(e.getProblems()) ?
}
Model model = result.getActivatedFileModel();
modelPool.put(model.getPomFile(), model);
InterimResult interimResult = new InterimResult(pomFile, modelBuildingRequest, result, project, isRoot);
if (recursive) {
File basedir = pomFile.getParentFile();
List<File> moduleFiles = new ArrayList<>();
for (String module : model.getModules()) {
if (module == null || module.isEmpty()) {
continue;
}
module = module.replace('\\', File.separatorChar).replace('/', File.separatorChar);
Path modulePath = modelProcessor.locateExistingPom(new File(basedir, module).toPath());
File moduleFile = modulePath != null ? modulePath.toFile() : null;
if (moduleFile == null) {
ModelProblem problem = new org.apache.maven.internal.impl.model.DefaultModelProblem(
"Child module " + moduleFile + " of " + pomFile + " does not exist",
ModelProblem.Severity.ERROR,
ModelProblem.Version.BASE,
model,
-1,
-1,
null);
result.getProblems().add(problem);
continue;
}
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());
}
if (aggregatorFiles.contains(moduleFile)) {
StringBuilder buffer = new StringBuilder(256);
for (File aggregatorFile : aggregatorFiles) {
buffer.append(aggregatorFile).append(" -> ");
}
buffer.append(moduleFile);
ModelProblem problem = new org.apache.maven.internal.impl.model.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;
}
moduleFiles.add(moduleFile);
}
if (!moduleFiles.isEmpty()) {
interimResult.modules = build(projectIndex, moduleFiles, aggregatorFiles, false, recursive);
}
}
projectIndex.put(pomFile, project);
return interimResult;
}
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.
List<ProjectBuildingResult> results = new ArrayList<>();
boolean failure = false;
for (InterimResult r : interimResults) {
DefaultProjectBuildingResult res;
try {
Model model = modelBuilder.buildRawModel(r.request);
res = new DefaultProjectBuildingResult(
model.getId(),
model.getPomFile() != null ? model.getPomFile().toFile() : null,
null);
} catch (ModelBuilderException e) {
failure = true;
res = new DefaultProjectBuildingResult(
e.getModelId(),
r.request.getSource().getPath() != null
? r.request.getSource().getPath().toFile()
: null,
convert(e.getProblems()));
}
results.add(res);
}
if (failure) {
return results;
}
List<Callable<List<ProjectBuildingResult>>> callables = interimResults.stream()
.map(interimResult ->
(Callable<List<ProjectBuildingResult>>) () -> doBuild(projectIndex, interimResult))
.collect(Collectors.toList());
try {
List<Future<List<ProjectBuildingResult>>> futures = executor.invokeAll(callables);
return futures.stream()
.map(listFuture -> {
try {
return listFuture.get();
} catch (InterruptedException e) {
uncheckedThrow(e);
return null;
} catch (ExecutionException e) {
uncheckedThrow(e.getCause());
return null;
}
})
.flatMap(List::stream)
.collect(Collectors.toList());
} catch (InterruptedException e) {
uncheckedThrow(e);
return null;
}
}
private List<ProjectBuildingResult> doBuild(Map<File, MavenProject> projectIndex, InterimResult interimResult) {
if (interimResult.projectBuildingResult != null) {
return Collections.singletonList(interimResult.projectBuildingResult);
}
MavenProject project = interimResult.project;
try {
ModelBuilderResult result = modelBuilder.build(ModelBuilderRequest.builder(interimResult.request)
.interimResult(interimResult.result)
.build());
// 2nd pass of initialization: resolve and build parent if necessary
List<org.apache.maven.model.building.ModelProblem> problems = convert(result.getProblems());
try {
initProject(project, projectIndex, result);
} catch (InvalidArtifactRTException iarte) {
problems.add(new DefaultModelProblem(
null,
org.apache.maven.model.building.ModelProblem.Severity.ERROR,
null,
new org.apache.maven.model.Model(result.getEffectiveModel()),
-1,
-1,
iarte));
}
List<ProjectBuildingResult> results = build(projectIndex, interimResult.modules);
project.setExecutionRoot(interimResult.root);
project.setCollectedProjects(
results.stream().map(ProjectBuildingResult::getProject).collect(Collectors.toList()));
DependencyResolutionResult resolutionResult = null;
if (request.isResolveDependencies()) {
resolutionResult = resolveDependencies(project);
}
results.add(new DefaultProjectBuildingResult(project, problems, resolutionResult));
return results;
} catch (ModelBuilderException e) {
DefaultProjectBuildingResult result;
if (project == null || interimResult.result.getEffectiveModel() == null) {
result = new DefaultProjectBuildingResult(
e.getModelId(), interimResult.pomFile, convert(e.getProblems()));
} else {
project.setModel(new org.apache.maven.model.Model(interimResult.result.getEffectiveModel()));
result = new DefaultProjectBuildingResult(project, convert(e.getProblems()), null);
}
return Collections.singletonList(result);
}
}
private List<org.apache.maven.model.building.ModelProblem> convert(List<ModelProblem> problems) {
if (problems == null) {
return null;
}
return problems.stream()
.map(p -> (org.apache.maven.model.building.ModelProblem) new DefaultModelProblem(
p.getMessage(),
org.apache.maven.model.building.ModelProblem.Severity.valueOf(
p.getSeverity().name()),
org.apache.maven.model.building.ModelProblem.Version.valueOf(
p.getVersion().name()),
p.getSource(),
p.getLineNumber(),
p.getColumnNumber(),
p.getModelId(),
p.getException()))
.toList();
}
@SuppressWarnings({"checkstyle:methodlength", "deprecation"})
private void initProject(MavenProject project, Map<File, MavenProject> projects, ModelBuilderResult result) {
project.setModel(new org.apache.maven.model.Model(result.getEffectiveModel()));
project.setOriginalModel(new org.apache.maven.model.Model(result.getFileModel()));
initParent(project, projects, result);
Artifact projectArtifact = repositorySystem.createArtifact(
project.getGroupId(), project.getArtifactId(), project.getVersion(), null, project.getPackaging());
project.setArtifact(projectArtifact);
// only set those on 2nd phase, ignore on 1st pass
if (project.getFile() != null) {
Build build = project.getBuild().getDelegate();
project.addScriptSourceRoot(build.getScriptSourceDirectory());
project.addCompileSourceRoot(build.getSourceDirectory());
project.addTestCompileSourceRoot(build.getTestSourceDirectory());
}
project.setActiveProfiles(Stream.concat(
result.getActivePomProfiles(result.getModelIds().get(0)).stream(),
result.getActiveExternalProfiles().stream())
.map(org.apache.maven.model.Profile::new)
.toList());
project.setInjectedProfileIds("external", getProfileIds(result.getActiveExternalProfiles()));
for (String modelId : result.getModelIds()) {
project.setInjectedProfileIds(modelId, getProfileIds(result.getActivePomProfiles(modelId)));
}
//
// All the parts that were taken out of MavenProject for Maven 4.0.0
//
project.setProjectBuildingRequest(request);
// pluginArtifacts
Set<Artifact> pluginArtifacts = new HashSet<>();
for (Plugin plugin : project.getModel().getDelegate().getBuild().getPlugins()) {
Artifact artifact = repositorySystem.createPluginArtifact(new org.apache.maven.model.Plugin(plugin));
if (artifact != null) {
pluginArtifacts.add(artifact);
}
}
project.setPluginArtifacts(pluginArtifacts);
// reportArtifacts
Set<Artifact> reportArtifacts = new HashSet<>();
for (ReportPlugin report :
project.getModel().getDelegate().getReporting().getPlugins()) {
Plugin pp = Plugin.newBuilder()
.groupId(report.getGroupId())
.artifactId(report.getArtifactId())
.version(report.getVersion())
.build();
Artifact artifact = repositorySystem.createPluginArtifact(new org.apache.maven.model.Plugin(pp));
if (artifact != null) {
reportArtifacts.add(artifact);
}
}
project.setReportArtifacts(reportArtifacts);
// extensionArtifacts
Set<Artifact> extensionArtifacts = new HashSet<>();
List<Extension> extensions =
project.getModel().getDelegate().getBuild().getExtensions();
if (extensions != null) {
for (Extension ext : extensions) {
String version;
if (ext.getVersion() == null || ext.getVersion().isEmpty()) {
version = "RELEASE";
} else {
version = ext.getVersion();
}
Artifact artifact = repositorySystem.createArtifact(
ext.getGroupId(), ext.getArtifactId(), version, null, "jar");
if (artifact != null) {
extensionArtifacts.add(artifact);
}
}
}
project.setExtensionArtifacts(extensionArtifacts);
// managedVersionMap
Map<String, Artifact> map = Collections.emptyMap();
final DependencyManagement dependencyManagement =
project.getModel().getDelegate().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(new org.apache.maven.model.Dependency(d));
if (artifact != null) {
tmp.put(d.getManagementKey(), artifact);
}
}
return Collections.unmodifiableMap(tmp);
});
}
project.setManagedVersionMap(map);
// release artifact repository
if (project.getDistributionManagement() != null
&& project.getDistributionManagement().getRepository() != null) {
try {
DeploymentRepository r = project.getModel()
.getDelegate()
.getDistributionManagement()
.getRepository();
if (r.getId() != null
&& !r.getId().isEmpty()
&& r.getUrl() != null
&& !r.getUrl().isEmpty()) {
ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(
new org.apache.maven.model.DeploymentRepository(r));
repositorySystem.injectProxy(request.getRepositorySession(), List.of(repo));
repositorySystem.injectAuthentication(request.getRepositorySession(), List.of(repo));
project.setReleaseArtifactRepository(repo);
}
} catch (InvalidRepositoryException e) {
throw new IllegalStateException(
"Failed to create release distribution repository for " + project.getId(), e);
}
}
// snapshot artifact repository
if (project.getDistributionManagement() != null
&& project.getDistributionManagement().getSnapshotRepository() != null) {
try {
DeploymentRepository r = project.getModel()
.getDelegate()
.getDistributionManagement()
.getSnapshotRepository();
if (r.getId() != null
&& !r.getId().isEmpty()
&& r.getUrl() != null
&& !r.getUrl().isEmpty()) {
ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(
new org.apache.maven.model.DeploymentRepository(r));
repositorySystem.injectProxy(request.getRepositorySession(), List.of(repo));
repositorySystem.injectAuthentication(request.getRepositorySession(), List.of(repo));
project.setSnapshotArtifactRepository(repo);
}
} catch (InvalidRepositoryException e) {
throw new IllegalStateException(
"Failed to create snapshot distribution repository for " + project.getId(), e);
}
}
}
private void initParent(MavenProject project, Map<File, MavenProject> projects, ModelBuilderResult result) {
Model parentModel = result.getModelIds().size() > 1
&& !result.getModelIds().get(1).isEmpty()
? result.getRawModel(result.getModelIds().get(1)).orElse(null)
: 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);
Path parentPomFile =
result.getRawModel(parentModelId).map(Model::getPomFile).orElse(null);
MavenProject parent = parentPomFile != null ? projects.get(parentPomFile.toFile()) : 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.toFile());
try {
parent = build(parentPomFile, ModelSource.fromPath(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());
}
}
}
}
project.setParent(parent);
if (project.getParentFile() == null && parent != null) {
project.setParentFile(parent.getFile());
}
}
}
private ModelBuilderRequest.ModelBuilderRequestBuilder getModelBuildingRequest() {
ModelBuilderRequest.ModelBuilderRequestBuilder modelBuildingRequest = ModelBuilderRequest.builder();
RequestTrace trace = RequestTrace.newChild(null, request).newChild(modelBuildingRequest);
ProjectModelResolver pmr = new ProjectModelResolver(
session,
trace,
repoSystem,
repositoryManager,
repositories,
request.getRepositoryMerging(),
modelPool,
parentCache);
ModelResolver resolver = new ModelResolverWrapper(pmr);
modelBuildingRequest.session(InternalSession.from(session));
modelBuildingRequest.validationLevel(request.getValidationLevel());
modelBuildingRequest.processPlugins(request.isProcessPlugins());
modelBuildingRequest.profiles(
request.getProfiles() != null
? request.getProfiles().stream()
.map(org.apache.maven.model.Profile::getDelegate)
.toList()
: null);
modelBuildingRequest.activeProfileIds(request.getActiveProfileIds());
modelBuildingRequest.inactiveProfileIds(request.getInactiveProfileIds());
modelBuildingRequest.systemProperties(toMap(request.getSystemProperties()));
modelBuildingRequest.userProperties(toMap(request.getUserProperties()));
// bv4: modelBuildingRequest.setBuildStartTime(request.getBuildStartTime());
modelBuildingRequest.modelResolver(resolver);
modelBuildingRequest.transformerContextBuilder(transformerContextBuilder);
/* TODO: bv4
InternalMavenSession session =
(InternalMavenSession) this.session.getData().get(InternalMavenSession.class);
if (session != null) {
try {
modelBuildingRequest.setRootDirectory(session.getRootDirectory());
} catch (IllegalStateException e) {
// can happen if root directory cannot be found, just ignore
}
}
*/
return modelBuildingRequest;
}
private DependencyResolutionResult resolveDependencies(MavenProject project) {
DependencyResolutionResult resolutionResult;
try {
DefaultDependencyResolutionRequest resolution =
new DefaultDependencyResolutionRequest(project, session);
resolutionResult = dependencyResolver.resolve(resolution);
} catch (DependencyResolutionException e) {
resolutionResult = e.getResult();
}
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(
lrm.getRepository().getBasePath().resolve(path).toFile());
}
}
}
project.setResolvedArtifacts(artifacts);
project.setArtifacts(artifacts);
return resolutionResult;
}
}
private List<String> getProfileIds(List<Profile> profiles) {
return profiles.stream().map(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 ModelSource() {
@Override
public ModelSource resolve(ModelLocator modelLocator, String relative) {
return null;
}
@Override
public Path getPath() {
return null;
}
@Override
public InputStream openStream() throws IOException {
return new ByteArrayInputStream(buffer.toString().getBytes(StandardCharsets.UTF_8));
}
@Override
public String getLocation() {
return artifact.getId();
}
@Override
public Source resolve(String relative) {
return null;
}
};
}
private static String inheritedGroupId(final ModelBuilderResult result, final int modelIndex) {
String groupId = null;
final String modelId = result.getModelIds().get(modelIndex);
if (!modelId.isEmpty()) {
final Model model = result.getRawModel(modelId).orElseThrow();
groupId = model.getGroupId() != null ? model.getGroupId() : inheritedGroupId(result, modelIndex + 1);
}
return groupId;
}
private static String inheritedVersion(final ModelBuilderResult result, final int modelIndex) {
String version = null;
final String modelId = result.getModelIds().get(modelIndex);
if (!modelId.isEmpty()) {
version = result.getRawModel(modelId).map(Model::getVersion).orElse(null);
if (version == null) {
version = inheritedVersion(result, modelIndex + 1);
}
}
return version;
}
private static Map<String, String> toMap(Properties properties) {
if (properties != null && !properties.isEmpty()) {
return properties.entrySet().stream()
.collect(Collectors.toMap(e -> String.valueOf(e.getKey()), e -> String.valueOf(e.getValue())));
} else {
return null;
}
}
@SuppressWarnings("unchecked")
static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
throw (T) t; // rely on vacuous cast
}
static class LazyMap<K, V> extends AbstractMap<K, V> {
private final Supplier<Map<K, V>> supplier;
private volatile Map<K, V> delegate;
LazyMap(Supplier<Map<K, V>> supplier) {
this.supplier = supplier;
}
@Override
public Set<Entry<K, V>> entrySet() {
if (delegate == null) {
synchronized (this) {
if (delegate == null) {
delegate = supplier.get();
}
}
}
return delegate.entrySet();
}
}
protected class ModelResolverWrapper implements ModelResolver {
protected final org.apache.maven.model.resolution.ModelResolver resolver;
protected ModelResolverWrapper(org.apache.maven.model.resolution.ModelResolver resolver) {
this.resolver = resolver;
}
@Override
public ModelSource resolveModel(Session session, String groupId, String artifactId, String version)
throws ModelResolverException {
try {
return toSource(resolver.resolveModel(groupId, artifactId, version));
} catch (UnresolvableModelException e) {
throw new ModelResolverException(e.getMessage(), e.getGroupId(), e.getArtifactId(), e.getVersion(), e);
}
}
@Override
public ModelSource resolveModel(Session session, Parent parent, AtomicReference<Parent> modified)
throws ModelResolverException {
try {
org.apache.maven.model.Parent p = new org.apache.maven.model.Parent(parent);
ModelSource source = toSource(resolver.resolveModel(p));
if (p.getDelegate() != parent) {
modified.set(p.getDelegate());
}
return source;
} catch (UnresolvableModelException e) {
throw new ModelResolverException(e.getMessage(), e.getGroupId(), e.getArtifactId(), e.getVersion(), e);
}
}
@Override
public ModelSource resolveModel(Session session, Dependency dependency, AtomicReference<Dependency> modified)
throws ModelResolverException {
try {
org.apache.maven.model.Dependency d = new org.apache.maven.model.Dependency(dependency);
ModelSource source = toSource(resolver.resolveModel(d));
if (d.getDelegate() != dependency) {
modified.set(d.getDelegate());
}
return source;
} catch (UnresolvableModelException e) {
throw new ModelResolverException(e.getMessage(), e.getGroupId(), e.getArtifactId(), e.getVersion(), e);
}
}
@Override
public void addRepository(Session session, Repository repository) throws ModelResolverException {
addRepository(session, repository, false);
}
@Override
public void addRepository(Session session, Repository repository, boolean replace)
throws ModelResolverException {
try {
resolver.addRepository(new org.apache.maven.model.Repository(repository), replace);
} catch (org.apache.maven.model.resolution.InvalidRepositoryException e) {
throw new MavenException(e);
}
}
@Override
public ModelResolver newCopy() {
return new ModelResolverWrapper(resolver.newCopy());
}
}
}