[MNG-7862] The ModelLocator should always be used when locating pom.xml (#1217)

This commit is contained in:
Guillaume Nodet 2023-08-23 21:25:14 +02:00 committed by GitHub
parent 8a88c45a2d
commit f2593b97ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 153 additions and 56 deletions

View File

@ -1802,7 +1802,7 @@
]]>
</description>
<type>String</type>
<defaultValue>../pom.xml</defaultValue>
<defaultValue>..</defaultValue>
</field>
</fields>
<codeSegments>

View File

@ -18,7 +18,6 @@
*/
package org.apache.maven.project;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
@ -60,23 +59,6 @@ class ReactorModelPool {
.orElse(null);
}
/**
* Find model by path, useful when location the parent by relativePath
*
* @param path
* @return the matching model or {@code null}
* @since 4.0.0
*/
public Model get(Path path) {
final Path pomFile;
if (Files.isDirectory(path)) {
pomFile = path.resolve("pom.xml");
} else {
pomFile = path;
}
return modelsByPath.get(pomFile);
}
private String getVersion(Model model) {
String version = model.getVersion();
if (version == null && model.getParent() != null) {

View File

@ -74,12 +74,17 @@ class ConsumerPomArtifactTransformerTest {
@Override
public Model getRawModel(String groupId, String artifactId) throws IllegalStateException {
return null;
throw new UnsupportedOperationException();
}
@Override
public Model getRawModel(Path p) {
return null;
throw new UnsupportedOperationException();
}
@Override
public Path locate(Path path) {
throw new UnsupportedOperationException();
}
}
}

View File

@ -1349,23 +1349,20 @@ public class MavenCli {
alternatePomFile = commandLine.getOptionValue(CLIManager.ALTERNATE_POM_FILE);
}
File current = baseDirectory;
if (alternatePomFile != null) {
File pom = resolveFile(new File(alternatePomFile), workingDirectory);
if (pom.isDirectory()) {
if (modelProcessor != null) {
pom = modelProcessor.locatePom(pom);
} else {
pom = new File(pom, "pom.xml");
}
}
current = resolveFile(new File(alternatePomFile), workingDirectory);
}
File pom;
if (current.isDirectory() && modelProcessor != null) {
pom = modelProcessor.locatePom(current);
} else {
pom = current;
}
if (pom.isFile()) {
return pom;
} else if (modelProcessor != null) {
File pom = modelProcessor.locatePom(baseDirectory);
if (pom.isFile()) {
return pom;
}
}
return null;

View File

@ -51,6 +51,11 @@ public class DefaultBuildPomXMLFilterFactory extends BuildToRawPomXMLFilterFacto
return p -> Optional.ofNullable(context.getRawModel(p)).map(DefaultBuildPomXMLFilterFactory::toRelativeProject);
}
@Override
protected Function<Path, Path> getModelLocator() {
return context::locate;
}
@Override
protected BiFunction<String, String, String> getDependencyKeyToVersionMapper() {
return (g, a) -> Optional.ofNullable(context.getRawModel(g, a))

View File

@ -1363,7 +1363,7 @@ public class DefaultModelBuilder implements ModelBuilder {
DefaultModelProblemCollector problems)
throws ModelBuildingException {
final Parent parent = childModel.getParent();
final ModelSource candidateSource;
final ModelSource2 candidateSource;
final Model candidateModel;
final WorkspaceModelResolver resolver = request.getWorkspaceModelResolver();
if (resolver == null) {
@ -1480,7 +1480,7 @@ public class DefaultModelBuilder implements ModelBuilder {
|| rawChildModelVersion.equals("${project.parent.version}");
}
private ModelSource getParentPomFile(Model childModel, Source source) {
private ModelSource2 getParentPomFile(Model childModel, Source source) {
if (!(source instanceof ModelSource2)) {
return null;
}
@ -1491,7 +1491,11 @@ public class DefaultModelBuilder implements ModelBuilder {
return null;
}
return ((ModelSource2) source).getRelatedSource(parentPath);
if (source instanceof ModelSource3) {
return ((ModelSource3) source).getRelatedSource(modelProcessor, parentPath);
} else {
return ((ModelSource2) source).getRelatedSource(parentPath);
}
}
private ModelData readParentExternally(
@ -1866,7 +1870,7 @@ public class DefaultModelBuilder implements ModelBuilder {
* @since 4.0.0
*/
private class DefaultTransformerContextBuilder implements TransformerContextBuilder {
private final DefaultTransformerContext context = new DefaultTransformerContext();
private final DefaultTransformerContext context = new DefaultTransformerContext(modelProcessor);
private final Map<DefaultTransformerContext.GAKey, Set<Source>> mappedSources = new ConcurrentHashMap<>(64);
@ -1882,6 +1886,11 @@ public class DefaultModelBuilder implements ModelBuilder {
// We must assume the TransformerContext was created using this.newTransformerContextBuilder()
DefaultModelProblemCollector problems = (DefaultModelProblemCollector) collector;
return new TransformerContext() {
@Override
public Path locate(Path path) {
return modelProcessor.locatePom(path.toFile()).toPath();
}
@Override
public String getUserProperty(String key) {
return context.userProperties.computeIfAbsent(

View File

@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import org.apache.maven.model.Model;
import org.apache.maven.model.locator.ModelLocator;
/**
*
@ -32,6 +33,8 @@ import org.apache.maven.model.Model;
* @since 4.0.0
*/
class DefaultTransformerContext implements TransformerContext {
final ModelLocator modelLocator;
final Map<String, String> userProperties = new ConcurrentHashMap<>();
final Map<Path, Holder> modelByPath = new ConcurrentHashMap<>();
@ -77,6 +80,10 @@ class DefaultTransformerContext implements TransformerContext {
}
}
DefaultTransformerContext(ModelLocator modelLocator) {
this.modelLocator = modelLocator;
}
@Override
public String getUserProperty(String key) {
return userProperties.get(key);
@ -92,6 +99,11 @@ class DefaultTransformerContext implements TransformerContext {
return Holder.deref(modelByGA.get(new GAKey(groupId, artifactId)));
}
@Override
public Path locate(Path path) {
return modelLocator.locatePom(path.toFile()).toPath();
}
static class GAKey {
private final String groupId;
private final String artifactId;

View File

@ -22,13 +22,14 @@ import java.io.File;
import java.net.URI;
import org.apache.maven.building.FileSource;
import org.apache.maven.model.locator.ModelLocator;
/**
* Wraps an ordinary {@link File} as a model source.
*
* @author Benjamin Bentmann
*/
public class FileModelSource extends FileSource implements ModelSource2 {
public class FileModelSource extends FileSource implements ModelSource3 {
/**
* Creates a new model source backed by the specified file.
@ -51,18 +52,17 @@ public class FileModelSource extends FileSource implements ModelSource2 {
}
@Override
public ModelSource2 getRelatedSource(String relPath) {
public ModelSource3 getRelatedSource(ModelLocator locator, String relPath) {
relPath = relPath.replace('\\', File.separatorChar).replace('/', File.separatorChar);
File relatedPom = new File(getFile().getParentFile(), relPath);
if (relatedPom.isDirectory()) {
// TODO figure out how to reuse ModelLocator.locatePom(File) here
relatedPom = new File(relatedPom, "pom.xml");
if (relatedPom.isDirectory() && locator != null) {
relatedPom = locator.locatePom(relatedPom);
}
if (relatedPom.isFile() && relatedPom.canRead()) {
return new FileModelSource(new File(relatedPom.toURI().normalize()));
return new FileModelSource(relatedPom.toPath().normalize().toFile());
}
return null;

View File

@ -0,0 +1,56 @@
/*
* 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 org.apache.maven.model.locator.ModelLocator;
/**
* Enhancement to the {@link ModelSource2} to support locating POM files using the {@link ModelLocator}
* when pointing to a directory.
*/
public interface ModelSource3 extends ModelSource2 {
/**
* Returns model source identified by a path relative to this model source POM. Implementation <strong>MUST</strong>
* accept <code>relPath</code> parameter values that
* <ul>
* <li>use either / or \ file path separator</li>
* <li>have .. parent directory references</li>
* <li>point either at file or directory</li>
* </ul>
* If the given path points at a directory, the provided {@code ModelLocator} will be used
* to find the POM file, else if no locator is provided, a file named 'pom.xml' needs to be
* used by the requested model source.
*
* @param locator locator used to locate the pom file
* @param relPath path of the requested model source relative to this model source POM
* @return related model source or <code>null</code> if no such model source
*/
ModelSource3 getRelatedSource(ModelLocator locator, String relPath);
/**
* When using a ModelSource3, the method with a {@code ModelLocator} argument should
* be used instead.
*
* @deprecated use {@link #getRelatedSource(ModelLocator, String)} instead
*/
@Deprecated
default ModelSource3 getRelatedSource(String relPath) {
return getRelatedSource(null, relPath);
}
}

View File

@ -59,4 +59,6 @@ public interface TransformerContext {
* @throws IllegalStateException if multiple versions of the same GA are part of the reactor
*/
Model getRawModel(String groupId, String artifactId);
Path locate(Path path);
}

View File

@ -56,7 +56,7 @@ public class BuildToRawPomXMLFilterFactory {
}
if (getRelativePathMapper() != null) {
parser = new ParentXMLFilter(parser, getRelativePathMapper(), projectFile.getParent());
parser = new ParentXMLFilter(parser, getRelativePathMapper(), getModelLocator(), projectFile.getParent());
}
CiFriendlyXMLFilter ciFriendlyFilter = new CiFriendlyXMLFilter(parser, consume);
@ -77,6 +77,10 @@ public class BuildToRawPomXMLFilterFactory {
return null;
}
protected Function<Path, Path> getModelLocator() {
return null;
}
protected BiFunction<String, String, String> getDependencyKeyToVersionMapper() {
return null;
}

View File

@ -47,6 +47,8 @@ class ParentXMLFilter extends NodeBufferingParser {
private final Function<Path, Optional<RelativeProject>> relativePathMapper;
private final Function<Path, Path> modelLocator;
private final Path projectPath;
private static final Pattern S_FILTER = Pattern.compile("\\s+");
@ -55,9 +57,13 @@ class ParentXMLFilter extends NodeBufferingParser {
* @param relativePathMapper
*/
ParentXMLFilter(
XMLStreamReader delegate, Function<Path, Optional<RelativeProject>> relativePathMapper, Path projectPath) {
XMLStreamReader delegate,
Function<Path, Optional<RelativeProject>> relativePathMapper,
Function<Path, Path> modelLocator,
Path projectPath) {
super(delegate, "parent");
this.relativePathMapper = relativePathMapper;
this.modelLocator = modelLocator;
this.projectPath = projectPath;
}
@ -93,7 +99,7 @@ class ParentXMLFilter extends NodeBufferingParser {
} else if (event.event == END_ELEMENT && "parent".equals(event.name)) {
Optional<RelativeProject> resolvedParent;
if (!hasVersion && (!hasRelativePath || relativePath != null)) {
Path relPath = Paths.get(Objects.toString(relativePath, "../pom.xml"));
Path relPath = Paths.get(Objects.toString(relativePath, ".."));
resolvedParent = resolveRelativePath(relPath, groupId, artifactId);
} else {
resolvedParent = Optional.empty();
@ -128,9 +134,13 @@ class ParentXMLFilter extends NodeBufferingParser {
}
protected Optional<RelativeProject> resolveRelativePath(Path relativePath, String groupId, String artifactId) {
Path pomPath = projectPath.resolve(relativePath);
if (Files.isDirectory(pomPath)) {
pomPath = pomPath.resolve("pom.xml");
Path pomPath = projectPath.resolve(relativePath).normalize();
if (Files.isDirectory(pomPath) && modelLocator != null) {
pomPath = modelLocator.apply(pomPath);
}
if (pomPath == null || !Files.isRegularFile(pomPath)) {
return Optional.empty();
}
Optional<RelativeProject> mappedProject = relativePathMapper.apply(pomPath.normalize());

View File

@ -20,6 +20,8 @@ package org.apache.maven.model.transform;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
@ -32,10 +34,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
class ParentXMLFilterTest extends AbstractXMLFilterTests {
private Function<XMLStreamReader, XMLStreamReader> filterCreator;
private Path projectPath;
@BeforeEach
void reset() {
void reset() throws IOException {
filterCreator = null;
projectPath = Paths.get("target/test-classes/" + getClass().getSimpleName() + "/child");
Files.createDirectories(projectPath);
if (!Files.isRegularFile(projectPath.resolve("../pom.xml"))) {
Files.createFile(projectPath.resolve("../pom.xml"));
}
if (!Files.isRegularFile(projectPath.resolve("pom.xml"))) {
Files.createFile(projectPath.resolve("pom.xml"));
}
Path relPath = projectPath.resolve("RELATIVEPATH");
Files.createDirectories(relPath);
if (!Files.isRegularFile(relPath.resolve("pom.xml"))) {
Files.createFile(relPath.resolve("pom.xml"));
}
}
@Override
@ -47,14 +63,13 @@ class ParentXMLFilterTest extends AbstractXMLFilterTests {
protected XMLStreamReader createFilter(XMLStreamReader parser) {
return createFilter(
parser,
x -> Optional.of(new RelativeProject("GROUPID", "ARTIFACTID", "1.0.0")),
Paths.get("pom.xml").toAbsolutePath());
parser, x -> Optional.of(new RelativeProject("GROUPID", "ARTIFACTID", "1.0.0")), projectPath);
}
protected XMLStreamReader createFilter(
XMLStreamReader parser, Function<Path, Optional<RelativeProject>> pathMapper, Path projectPath) {
return new ParentXMLFilter(new FastForwardFilter(parser), pathMapper, projectPath);
Function<Path, Path> locator = p -> p.resolve("pom.xml");
return new ParentXMLFilter(new FastForwardFilter(parser), pathMapper, locator, projectPath);
}
@Test