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
The following commit(s) were added to refs/heads/master by this push:
new 69bc993b80 [MNG-7945] Fix profile settings being injected into
consumer POM (#1323)
69bc993b80 is described below
commit 69bc993b8089a2d3d1ddfd6c7d4f8dc6cc205995
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Nov 28 18:17:10 2023 +0100
[MNG-7945] Fix profile settings being injected into consumer POM (#1323)
---
.../ConsumerPomArtifactTransformer.java | 293 +--------------------
.../transformation/OnChangeTransformer.java | 104 --------
.../TransformationFailedException.java | 30 +++
.../transformation/TransformedArtifact.java | 142 ----------
.../transformation/impl/ConsumerPomBuilder.java | 40 +++
.../DefaultConsumerPomArtifactTransformer.java | 207 +++++++++++++++
.../impl/DefaultConsumerPomBuilder.java | 230 ++++++++++++++++
.../transformation/impl/TransformedArtifact.java | 148 +++++++++++
.../impl/TransformedArtifactHandler.java | 66 +++++
.../lifecycle/internal/LifecycleModuleBuilder.java | 2 +-
.../maven/AbstractCoreMavenComponentTestCase.java | 7 +-
.../transformation/AbstractRepositoryTestCase.java | 78 ++++++
.../ConsumerPomArtifactTransformerTest.java | 19 +-
.../impl/ConsumerPomBuilderTest.java | 68 +++++
.../apache/maven/project/ProjectBuilderTest.java | 3 +-
.../src/test/resources/consumer/simple/pom.xml | 32 +++
.../consumer/simple/simple-parent/pom.xml | 51 ++++
.../simple/simple-parent/simple-testutils/pom.xml | 27 ++
.../simple/simple-parent/simple-weather/pom.xml | 38 +++
.../simple/simple-parent/simple-webapp/pom.xml | 47 ++++
.../simple/simple-parent/utils-parent/pom.xml | 27 ++
.../test/resources/consumer/trivial/child/pom.xml | 18 ++
.../src/test/resources/consumer/trivial/pom.xml | 47 ++++
.../pom.xml} | 0
.../maven/model/building/DefaultModelBuilder.java | 2 +-
.../building/DefaultTransformerContextBuilder.java | 50 ++++
26 files changed, 1228 insertions(+), 548 deletions(-)
diff --git
a/maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java
b/maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java
index 383fd64908..ef2901a652 100644
---
a/maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java
+++
b/maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java
@@ -18,45 +18,10 @@
*/
package org.apache.maven.internal.transformation;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import javax.xml.stream.XMLStreamException;
-
import java.io.IOException;
-import java.io.Writer;
-import java.lang.reflect.Method;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.stream.Collectors;
-import org.apache.maven.api.Repository;
-import org.apache.maven.api.feature.Features;
-import org.apache.maven.api.model.DistributionManagement;
-import org.apache.maven.api.model.Model;
-import org.apache.maven.api.model.ModelBase;
-import org.apache.maven.api.model.Profile;
-import org.apache.maven.model.building.FileModelSource;
-import org.apache.maven.model.building.ModelBuilder;
-import org.apache.maven.model.building.ModelBuildingRequest;
-import org.apache.maven.model.building.ModelCache;
-import org.apache.maven.model.building.Result;
-import org.apache.maven.model.building.TransformerContext;
-import org.apache.maven.model.v4.MavenModelVersion;
-import org.apache.maven.model.v4.MavenStaxWriter;
import org.apache.maven.project.MavenProject;
-import org.apache.maven.project.artifact.ProjectArtifact;
-import org.apache.maven.repository.internal.DefaultModelCache;
import org.eclipse.aether.RepositorySystemSession;
-import org.eclipse.aether.artifact.Artifact;
-import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.deployment.DeployRequest;
import org.eclipse.aether.installation.InstallRequest;
@@ -65,260 +30,12 @@ import org.eclipse.aether.installation.InstallRequest;
*
* @since TBD
*/
-@Singleton
-@Named("consumer-pom")
-public final class ConsumerPomArtifactTransformer {
-
- private static final String BOM_PACKAGING = "bom";
-
- public static final String POM_PACKAGING = "pom";
-
- private static final String CONSUMER_POM_CLASSIFIER = "consumer";
-
- private static final String BUILD_POM_CLASSIFIER = "build";
-
- private static final String NAMESPACE_FORMAT =
"http://maven.apache.org/POM/%s";
-
- private static final String SCHEMA_LOCATION_FORMAT =
"https://maven.apache.org/xsd/maven-%s.xsd";
-
- private final Set<Path> toDelete = new CopyOnWriteArraySet<>();
-
- private final ModelBuilder modelBuilder;
-
- @Inject
- ConsumerPomArtifactTransformer(ModelBuilder modelBuilder) {
- this.modelBuilder = modelBuilder;
- }
-
- public void injectTransformedArtifacts(MavenProject project,
RepositorySystemSession session) throws IOException {
- if (project.getFile() == null) {
- // If there is no build POM there is no reason to inject artifacts
for the consumer POM.
- return;
- }
- if (Features.buildConsumer(session.getUserProperties())) {
- Path buildDir =
- project.getBuild() != null ?
Paths.get(project.getBuild().getDirectory()) : null;
- if (buildDir != null) {
- Files.createDirectories(buildDir);
- }
- Path consumer = buildDir != null
- ? Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER +
"-", ".pom")
- : Files.createTempFile(CONSUMER_POM_CLASSIFIER + "-",
".pom");
- deferDeleteFile(consumer);
-
- project.addAttachedArtifact(createConsumerPomArtifact(project,
consumer, session));
- } else if (project.getModel().getDelegate().isRoot()) {
- throw new IllegalStateException(
- "The use of the root attribute on the model requires the
buildconsumer feature to be active");
- }
- }
-
- public ConsumerPomArtifact createConsumerPomArtifact(
- MavenProject project, Path consumer, RepositorySystemSession
session) {
- return new ConsumerPomArtifact(project, consumer, session);
- }
-
- private void deferDeleteFile(Path generatedFile) {
- toDelete.add(generatedFile.toAbsolutePath());
- }
-
- @PreDestroy
- private void doDeleteFiles() {
- for (Path file : toDelete) {
- try {
- Files.delete(file);
- } catch (IOException e) {
- // ignore, we did our best...
- }
- }
- }
-
- public InstallRequest remapInstallArtifacts(RepositorySystemSession
session, InstallRequest request) {
- if (Features.buildConsumer(session.getUserProperties()) &&
consumerPomPresent(request.getArtifacts())) {
- request.setArtifacts(replacePom(request.getArtifacts()));
- }
- return request;
- }
-
- public DeployRequest remapDeployArtifacts(RepositorySystemSession session,
DeployRequest request) {
- if (Features.buildConsumer(session.getUserProperties()) &&
consumerPomPresent(request.getArtifacts())) {
- request.setArtifacts(replacePom(request.getArtifacts()));
- }
- return request;
- }
-
- private boolean consumerPomPresent(Collection<Artifact> artifacts) {
- return artifacts.stream()
- .anyMatch(a -> "pom".equals(a.getExtension()) &&
CONSUMER_POM_CLASSIFIER.equals(a.getClassifier()));
- }
-
- private Collection<Artifact> replacePom(Collection<Artifact> artifacts) {
- List<Artifact> consumers = new ArrayList<>();
- List<Artifact> mains = new ArrayList<>();
- for (Artifact artifact : artifacts) {
- if ("pom".equals(artifact.getExtension()) ||
artifact.getExtension().startsWith("pom.")) {
- if (CONSUMER_POM_CLASSIFIER.equals(artifact.getClassifier())) {
- consumers.add(artifact);
- } else if ("".equals(artifact.getClassifier())) {
- mains.add(artifact);
- }
- }
- }
- if (!mains.isEmpty() && !consumers.isEmpty()) {
- ArrayList<Artifact> result = new ArrayList<>(artifacts);
- for (Artifact main : mains) {
- result.remove(main);
- result.add(new DefaultArtifact(
- main.getGroupId(),
- main.getArtifactId(),
- BUILD_POM_CLASSIFIER,
- main.getExtension(),
- main.getVersion(),
- main.getProperties(),
- main.getFile()));
- }
- for (Artifact consumer : consumers) {
- result.remove(consumer);
- result.add(new DefaultArtifact(
- consumer.getGroupId(),
- consumer.getArtifactId(),
- "",
- consumer.getExtension(),
- consumer.getVersion(),
- consumer.getProperties(),
- consumer.getFile()));
- }
- artifacts = result;
- }
- return artifacts;
- }
-
- /**
- * Consumer POM is transformed from original POM.
- */
- class ConsumerPomArtifact extends TransformedArtifact {
-
- private MavenProject project;
- private RepositorySystemSession session;
-
- ConsumerPomArtifact(MavenProject mavenProject, Path target,
RepositorySystemSession session) {
- super(
- new ProjectArtifact(mavenProject),
- () -> mavenProject.getFile().toPath(),
- CONSUMER_POM_CLASSIFIER,
- "pom",
- target);
- this.project = mavenProject;
- this.session = session;
- }
-
- @Override
- public void transform(Path src, Path dest) {
- Model model = project.getModel().getDelegate();
- transform(src, dest, model);
- }
-
- void transform(Path src, Path dest, Model model) {
- Model consumer = null;
- String version;
-
- String packaging = model.getPackaging();
- if (POM_PACKAGING.equals(packaging)) {
- // This is a bit of a hack, but all models are cached, so not
sure why we'd need to parse it again
- ModelCache cache = DefaultModelCache.newInstance(session);
- Object modelData = cache.get(new
FileModelSource(src.toFile()), "raw");
- if (modelData != null) {
- try {
- Method getModel =
modelData.getClass().getMethod("getModel");
- getModel.setAccessible(true);
- org.apache.maven.model.Model cachedModel =
- (org.apache.maven.model.Model)
getModel.invoke(modelData);
- consumer = cachedModel.getDelegate();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- if (consumer == null) {
- TransformerContext context =
- (TransformerContext)
session.getData().get(TransformerContext.KEY);
- Result<? extends org.apache.maven.model.Model> result =
modelBuilder.buildRawModel(
- src.toFile(),
ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL, false, context);
- if (result.hasErrors()) {
- throw new IllegalStateException(
- "Unable to build POM " + src,
-
result.getProblems().iterator().next().getException());
- }
- consumer = result.get().getDelegate();
- }
-
- // raw to consumer transform
- consumer = consumer.withRoot(false).withModules(null);
- if (consumer.getParent() != null) {
- consumer =
consumer.withParent(consumer.getParent().withRelativePath(null));
- }
-
- if (!consumer.isPreserveModelVersion()) {
- consumer = consumer.withPreserveModelVersion(false);
- version = new
MavenModelVersion().getModelVersion(consumer);
- consumer = consumer.withModelVersion(version);
- } else {
- version = consumer.getModelVersion();
- }
- } else {
- Model.Builder builder = prune(
- Model.newBuilder(model, true)
- .preserveModelVersion(false)
- .root(false)
- .parent(null)
- .build(null),
- model);
- boolean isBom = BOM_PACKAGING.equals(packaging);
- if (isBom) {
- builder.packaging(POM_PACKAGING);
- }
- builder.profiles(model.getProfiles().stream()
- .map(p -> prune(Profile.newBuilder(p, true),
p).build())
- .collect(Collectors.toList()));
- consumer = builder.build();
- version = new MavenModelVersion().getModelVersion(consumer);
- consumer = consumer.withModelVersion(version);
- }
+public interface ConsumerPomArtifactTransformer {
- try {
- Files.createDirectories(dest.getParent());
- try (Writer w = Files.newBufferedWriter(dest)) {
- MavenStaxWriter writer = new MavenStaxWriter();
- writer.setNamespace(String.format(NAMESPACE_FORMAT,
version));
-
writer.setSchemaLocation(String.format(SCHEMA_LOCATION_FORMAT, version));
- writer.setAddLocationInformation(false);
- writer.write(w, consumer);
- }
- } catch (XMLStreamException | IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
+ InstallRequest remapInstallArtifacts(RepositorySystemSession session,
InstallRequest request);
- private static <T extends ModelBase.Builder> T prune(T builder, ModelBase
model) {
- builder.properties(null).reporting(null);
- if (model.getDistributionManagement() != null
- && model.getDistributionManagement().getRelocation() != null) {
- // keep relocation only
- builder.distributionManagement(DistributionManagement.newBuilder()
-
.relocation(model.getDistributionManagement().getRelocation())
- .build());
- }
- // only keep repositories others than 'central'
-
builder.pluginRepositories(pruneRepositories(model.getPluginRepositories()));
- builder.repositories(pruneRepositories(model.getRepositories()));
- return builder;
- }
+ DeployRequest remapDeployArtifacts(RepositorySystemSession session,
DeployRequest request);
- private static List<org.apache.maven.api.model.Repository>
pruneRepositories(
- List<org.apache.maven.api.model.Repository> repositories) {
- return repositories.stream()
- .filter(r -> !Repository.CENTRAL_ID.equals(r.getId()))
- .collect(Collectors.toList());
- }
+ void injectTransformedArtifacts(RepositorySystemSession repositorySession,
MavenProject currentProject)
+ throws IOException;
}
diff --git
a/maven-core/src/main/java/org/apache/maven/internal/transformation/OnChangeTransformer.java
b/maven-core/src/main/java/org/apache/maven/internal/transformation/OnChangeTransformer.java
deleted file mode 100644
index d777ed2068..0000000000
---
a/maven-core/src/main/java/org/apache/maven/internal/transformation/OnChangeTransformer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.internal.transformation;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * Keeps transformed file up-to-date relative to its source file. It manages
state (i.e. hashing the content) using
- * passed in stateFunction, and transforms when needed using passed in
transformer bi-consumer.
- * <p>
- * Covered cases:
- * <ul>
- * <li>when source supplier returns {@code null}, this class will return
{@code null}.</li>
- * <li>when source supplier returns non existing path, this class will
return non existing path.</li>
- * <li>when source supplier returns existing path, this class will ensure
transformation is in sync.</li>
- * </ul>
- *
- * @since TBD
- */
-final class OnChangeTransformer implements Supplier<Path> {
-
- private final Supplier<Path> source;
-
- private final Path target;
-
- private final Function<Path, String> stateFunction;
-
- private final BiConsumer<Path, Path> transformerConsumer;
-
- private final AtomicReference<String> sourceState;
-
- OnChangeTransformer(
- Supplier<Path> source,
- Path target,
- Function<Path, String> stateFunction,
- BiConsumer<Path, Path> transformerConsumer) {
- this.source = requireNonNull(source);
- this.target = requireNonNull(target);
- this.stateFunction = requireNonNull(stateFunction);
- this.transformerConsumer = requireNonNull(transformerConsumer);
- this.sourceState = new AtomicReference<>(null);
- }
-
- @Override
- public synchronized Path get() {
- String state = mayUpdate();
- if (state == null) {
- return null;
- }
- return target;
- }
-
- private String mayUpdate() {
- String result;
- try {
- Path src = source.get();
- if (src == null) {
- Files.deleteIfExists(target);
- result = null;
- } else if (!Files.exists(src)) {
- Files.deleteIfExists(target);
- result = "";
- } else {
- String current = stateFunction.apply(src);
- String existing = sourceState.get();
- if (!Objects.equals(current, existing)) {
- transformerConsumer.accept(src, target);
- Files.setLastModifiedTime(target,
Files.getLastModifiedTime(src));
- }
- result = current;
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- sourceState.set(result);
- return result;
- }
-}
diff --git
a/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformationFailedException.java
b/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformationFailedException.java
new file mode 100644
index 0000000000..c45dd758b7
--- /dev/null
+++
b/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformationFailedException.java
@@ -0,0 +1,30 @@
+/*
+ * 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.internal.transformation;
+
+/**
+ * Exception that may be thrown by the {@link
org.apache.maven.artifact.Artifact#getFile()}
+ * implementation.
+ */
+public class TransformationFailedException extends RuntimeException {
+
+ public TransformationFailedException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git
a/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformedArtifact.java
b/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformedArtifact.java
deleted file mode 100644
index 9c88336558..0000000000
---
a/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformedArtifact.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.internal.transformation;
-
-import java.io.File;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.MessageDigest;
-import java.util.function.Supplier;
-
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.artifact.DefaultArtifact;
-import org.apache.maven.artifact.handler.ArtifactHandler;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * Transformed artifact is derived with some transformation from source
artifact.
- *
- * @since TBD
- */
-abstract class TransformedArtifact extends DefaultArtifact {
-
- private final OnChangeTransformer onChangeTransformer;
-
- TransformedArtifact(
- Artifact source, Supplier<Path> sourcePathProvider, String
classifier, String extension, Path targetPath) {
- super(
- source.getGroupId(),
- source.getArtifactId(),
- source.getVersionRange(),
- source.getScope(),
- extension,
- classifier,
- new TransformedArtifactHandler(
- classifier, extension,
source.getArtifactHandler().getPackaging()));
- this.onChangeTransformer =
- new OnChangeTransformer(sourcePathProvider, targetPath,
TransformedArtifact::sha1, this::transform);
- }
-
- @Override
- public boolean isResolved() {
- return getFile() != null;
- }
-
- @Override
- public void setFile(File file) {
- throw new IllegalStateException("transformed artifact file cannot be
set");
- }
-
- @Override
- public File getFile() {
- Path result = onChangeTransformer.get();
- if (result == null) {
- return null;
- }
- return result.toFile();
- }
-
- private static final int BUFFER_SIZE = 8192;
-
- private static String sha1(Path path) {
- try {
- MessageDigest md = MessageDigest.getInstance("SHA-1");
- try (InputStream fis = Files.newInputStream(path)) {
- byte[] buffer = new byte[BUFFER_SIZE];
- int read;
- while ((read = fis.read(buffer)) != -1) {
- md.update(buffer, 0, read);
- }
- }
- StringBuilder result = new StringBuilder();
- for (byte b : md.digest()) {
- result.append(String.format("%02x", b));
- }
- return result.toString();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- protected abstract void transform(Path src, Path dst);
-
- private static class TransformedArtifactHandler implements ArtifactHandler
{
- private final String classifier;
-
- private final String extension;
-
- private final String packaging;
-
- private TransformedArtifactHandler(String classifier, String
extension, String packaging) {
- this.classifier = classifier;
- this.extension = requireNonNull(extension);
- this.packaging = requireNonNull(packaging);
- }
-
- public String getClassifier() {
- return classifier;
- }
-
- public String getDirectory() {
- return null;
- }
-
- public String getExtension() {
- return extension;
- }
-
- public String getLanguage() {
- return "none";
- }
-
- public String getPackaging() {
- return packaging;
- }
-
- public boolean isAddedToClasspath() {
- return false;
- }
-
- public boolean isIncludesDependencies() {
- return false;
- }
- }
-}
diff --git
a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilder.java
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilder.java
new file mode 100644
index 0000000000..f001fdd712
--- /dev/null
+++
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilder.java
@@ -0,0 +1,40 @@
+/*
+ * 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.internal.transformation.impl;
+
+import javax.xml.stream.XMLStreamException;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import org.apache.maven.api.model.Model;
+import org.apache.maven.model.building.ModelBuildingException;
+import org.apache.maven.project.MavenProject;
+import
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystemSession;
+
+/**
+ * This interface is not public and the purpose is to allow easy unit testing
+ * of {@link DefaultConsumerPomArtifactTransformer}.
+ */
+interface ConsumerPomBuilder {
+
+ Model build(RepositorySystemSession session, MavenProject project, Path
src)
+ throws ModelBuildingException, ComponentLookupException,
IOException, XMLStreamException;
+}
diff --git
a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomArtifactTransformer.java
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomArtifactTransformer.java
new file mode 100644
index 0000000000..569ce22338
--- /dev/null
+++
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomArtifactTransformer.java
@@ -0,0 +1,207 @@
+/*
+ * 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.internal.transformation.impl;
+
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import javax.xml.stream.XMLStreamException;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.maven.api.feature.Features;
+import org.apache.maven.api.model.Model;
+import org.apache.maven.internal.transformation.ConsumerPomArtifactTransformer;
+import org.apache.maven.model.building.ModelBuildingException;
+import org.apache.maven.model.v4.MavenStaxWriter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.artifact.ProjectArtifact;
+import
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.deployment.DeployRequest;
+import org.eclipse.aether.installation.InstallRequest;
+
+/**
+ * Consumer POM transformer.
+ *
+ * @since TBD
+ */
+@Singleton
+@Named("consumer-pom")
+class DefaultConsumerPomArtifactTransformer implements
ConsumerPomArtifactTransformer {
+
+ private static final String CONSUMER_POM_CLASSIFIER = "consumer";
+
+ private static final String BUILD_POM_CLASSIFIER = "build";
+
+ private static final String NAMESPACE_FORMAT =
"http://maven.apache.org/POM/%s";
+
+ private static final String SCHEMA_LOCATION_FORMAT =
"https://maven.apache.org/xsd/maven-%s.xsd";
+
+ private final Set<Path> toDelete = new CopyOnWriteArraySet<>();
+
+ private final ConsumerPomBuilder builder;
+
+ @Inject
+ DefaultConsumerPomArtifactTransformer(ConsumerPomBuilder builder) {
+ this.builder = builder;
+ }
+
+ @SuppressWarnings("deprecation")
+ public void injectTransformedArtifacts(RepositorySystemSession session,
MavenProject project) throws IOException {
+ if (project.getFile() == null) {
+ // If there is no build POM there is no reason to inject artifacts
for the consumer POM.
+ return;
+ }
+ if (Features.buildConsumer(session.getUserProperties())) {
+ Path buildDir =
+ project.getBuild() != null ?
Paths.get(project.getBuild().getDirectory()) : null;
+ if (buildDir != null) {
+ Files.createDirectories(buildDir);
+ }
+ Path consumer = buildDir != null
+ ? Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER +
"-", ".pom")
+ : Files.createTempFile(CONSUMER_POM_CLASSIFIER + "-",
".pom");
+ deferDeleteFile(consumer);
+
+ project.addAttachedArtifact(createConsumerPomArtifact(project,
consumer, session));
+ } else if (project.getModel().getDelegate().isRoot()) {
+ throw new IllegalStateException(
+ "The use of the root attribute on the model requires the
buildconsumer feature to be active");
+ }
+ }
+
+ TransformedArtifact createConsumerPomArtifact(
+ MavenProject project, Path consumer, RepositorySystemSession
session) {
+ return new TransformedArtifact(
+ this,
+ project,
+ consumer,
+ session,
+ new ProjectArtifact(project),
+ () -> project.getFile().toPath(),
+ CONSUMER_POM_CLASSIFIER,
+ "pom");
+ }
+
+ void transform(MavenProject project, RepositorySystemSession session, Path
src, Path tgt)
+ throws ModelBuildingException, ComponentLookupException,
XMLStreamException, IOException {
+ Model model = builder.build(session, project, src);
+ write(model, tgt);
+ }
+
+ private void deferDeleteFile(Path generatedFile) {
+ toDelete.add(generatedFile.toAbsolutePath());
+ }
+
+ @PreDestroy
+ private void doDeleteFiles() {
+ for (Path file : toDelete) {
+ try {
+ Files.delete(file);
+ } catch (IOException e) {
+ // ignore, we did our best...
+ }
+ }
+ }
+
+ public InstallRequest remapInstallArtifacts(RepositorySystemSession
session, InstallRequest request) {
+ if (Features.buildConsumer(session.getUserProperties()) &&
consumerPomPresent(request.getArtifacts())) {
+ request.setArtifacts(replacePom(request.getArtifacts()));
+ }
+ return request;
+ }
+
+ public DeployRequest remapDeployArtifacts(RepositorySystemSession session,
DeployRequest request) {
+ if (Features.buildConsumer(session.getUserProperties()) &&
consumerPomPresent(request.getArtifacts())) {
+ request.setArtifacts(replacePom(request.getArtifacts()));
+ }
+ return request;
+ }
+
+ private boolean consumerPomPresent(Collection<Artifact> artifacts) {
+ return artifacts.stream()
+ .anyMatch(a -> "pom".equals(a.getExtension()) &&
CONSUMER_POM_CLASSIFIER.equals(a.getClassifier()));
+ }
+
+ private Collection<Artifact> replacePom(Collection<Artifact> artifacts) {
+ List<Artifact> consumers = new ArrayList<>();
+ List<Artifact> mains = new ArrayList<>();
+ for (Artifact artifact : artifacts) {
+ if ("pom".equals(artifact.getExtension()) ||
artifact.getExtension().startsWith("pom.")) {
+ if (CONSUMER_POM_CLASSIFIER.equals(artifact.getClassifier())) {
+ consumers.add(artifact);
+ } else if ("".equals(artifact.getClassifier())) {
+ mains.add(artifact);
+ }
+ }
+ }
+ if (!mains.isEmpty() && !consumers.isEmpty()) {
+ ArrayList<Artifact> result = new ArrayList<>(artifacts);
+ for (Artifact main : mains) {
+ result.remove(main);
+ result.add(new DefaultArtifact(
+ main.getGroupId(),
+ main.getArtifactId(),
+ BUILD_POM_CLASSIFIER,
+ main.getExtension(),
+ main.getVersion(),
+ main.getProperties(),
+ main.getFile()));
+ }
+ for (Artifact consumer : consumers) {
+ result.remove(consumer);
+ result.add(new DefaultArtifact(
+ consumer.getGroupId(),
+ consumer.getArtifactId(),
+ "",
+ consumer.getExtension(),
+ consumer.getVersion(),
+ consumer.getProperties(),
+ consumer.getFile()));
+ }
+ artifacts = result;
+ }
+ return artifacts;
+ }
+
+ void write(Model model, Path dest) throws IOException, XMLStreamException {
+ String version = model.getModelVersion();
+ Files.createDirectories(dest.getParent());
+ try (Writer w = Files.newBufferedWriter(dest)) {
+ MavenStaxWriter writer = new MavenStaxWriter();
+ writer.setNamespace(String.format(NAMESPACE_FORMAT, version));
+ writer.setSchemaLocation(String.format(SCHEMA_LOCATION_FORMAT,
version));
+ writer.setAddLocationInformation(false);
+ writer.write(w, model);
+ }
+ }
+}
diff --git
a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
new file mode 100644
index 0000000000..cf5bafac3d
--- /dev/null
+++
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
@@ -0,0 +1,230 @@
+/*
+ * 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.internal.transformation.impl;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+import org.apache.maven.api.model.DistributionManagement;
+import org.apache.maven.api.model.Model;
+import org.apache.maven.api.model.ModelBase;
+import org.apache.maven.api.model.Profile;
+import org.apache.maven.api.model.Repository;
+import org.apache.maven.model.building.DefaultModelBuilder;
+import org.apache.maven.model.building.DefaultModelBuilderFactory;
+import org.apache.maven.model.building.DefaultModelBuildingRequest;
+import org.apache.maven.model.building.ModelBuildingException;
+import org.apache.maven.model.building.ModelBuildingRequest;
+import org.apache.maven.model.building.ModelBuildingResult;
+import org.apache.maven.model.building.ModelProblemCollector;
+import org.apache.maven.model.building.ModelProcessor;
+import org.apache.maven.model.composition.DependencyManagementImporter;
+import org.apache.maven.model.inheritance.InheritanceAssembler;
+import org.apache.maven.model.interpolation.ModelInterpolator;
+import org.apache.maven.model.management.DependencyManagementInjector;
+import org.apache.maven.model.management.PluginManagementInjector;
+import org.apache.maven.model.normalization.ModelNormalizer;
+import org.apache.maven.model.path.ModelPathTranslator;
+import org.apache.maven.model.path.ModelUrlNormalizer;
+import org.apache.maven.model.plugin.LifecycleBindingsInjector;
+import org.apache.maven.model.plugin.PluginConfigurationExpander;
+import org.apache.maven.model.plugin.ReportConfigurationExpander;
+import org.apache.maven.model.profile.DefaultProfileSelector;
+import org.apache.maven.model.profile.ProfileActivationContext;
+import org.apache.maven.model.profile.ProfileInjector;
+import org.apache.maven.model.profile.ProfileSelector;
+import org.apache.maven.model.superpom.SuperPomProvider;
+import org.apache.maven.model.v4.MavenModelVersion;
+import org.apache.maven.model.validation.ModelValidator;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.project.ProjectModelResolver;
+import org.apache.maven.repository.internal.ModelCacheFactory;
+import org.codehaus.plexus.PlexusContainer;
+import
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.RequestTrace;
+import org.eclipse.aether.impl.RemoteRepositoryManager;
+
+@Named
+class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
+
+ private static final String BOM_PACKAGING = "bom";
+
+ public static final String POM_PACKAGING = "pom";
+
+ @Inject
+ PlexusContainer container;
+
+ @Inject
+ ModelCacheFactory modelCacheFactory;
+
+ public Model build(RepositorySystemSession session, MavenProject project,
Path src)
+ throws ModelBuildingException, ComponentLookupException {
+ Model model = project.getModel().getDelegate();
+ String packaging = model.getPackaging();
+ if (POM_PACKAGING.equals(packaging)) {
+ return buildPom(session, project, src);
+ } else {
+ return buildNonPom(session, project, src);
+ }
+ }
+
+ protected Model buildPom(RepositorySystemSession session, MavenProject
project, Path src)
+ throws ModelBuildingException, ComponentLookupException {
+ ModelBuildingResult result = buildModel(session, project, src);
+ Model model = result.getRawModel().getDelegate();
+ return transform(model);
+ }
+
+ protected Model buildNonPom(RepositorySystemSession session, MavenProject
project, Path src)
+ throws ModelBuildingException, ComponentLookupException {
+ ModelBuildingResult result = buildModel(session, project, src);
+ Model model = result.getEffectiveModel().getDelegate();
+ return transform(model);
+ }
+
+ private ModelBuildingResult buildModel(RepositorySystemSession session,
MavenProject project, Path src)
+ throws ModelBuildingException, ComponentLookupException {
+ ProfileSelector customSelector = new DefaultProfileSelector() {
+ @Override
+ public List<Profile> getActiveProfilesV4(
+ Collection<Profile> profiles, ProfileActivationContext
context, ModelProblemCollector problems) {
+ return new ArrayList<>();
+ }
+ };
+ DefaultModelBuilder modelBuilder = new DefaultModelBuilderFactory()
+ .setProfileSelector(customSelector)
+ // apply currently active ModelProcessor etc. to support
extensions like jgitver
+ .setProfileInjector(lookup(ProfileInjector.class))
+ .setInheritanceAssembler(lookup(InheritanceAssembler.class))
+
.setDependencyManagementImporter(lookup(DependencyManagementImporter.class))
+
.setDependencyManagementInjector(lookup(DependencyManagementInjector.class))
+
.setLifecycleBindingsInjector(lookup(LifecycleBindingsInjector.class))
+ .setModelInterpolator(lookup(ModelInterpolator.class))
+ .setModelNormalizer(lookup(ModelNormalizer.class))
+ .setModelPathTranslator(lookup(ModelPathTranslator.class))
+ .setModelProcessor(lookup(ModelProcessor.class))
+ .setModelUrlNormalizer(lookup(ModelUrlNormalizer.class))
+ .setModelValidator(lookup(ModelValidator.class))
+
.setPluginConfigurationExpander(lookup(PluginConfigurationExpander.class))
+
.setPluginManagementInjector(lookup(PluginManagementInjector.class))
+
.setReportConfigurationExpander(lookup(ReportConfigurationExpander.class))
+ .setSuperPomProvider(lookup(SuperPomProvider.class))
+ .newInstance();
+ DefaultModelBuildingRequest request = new
DefaultModelBuildingRequest();
+ try {
+ request.setRootDirectory(project.getRootDirectory());
+ } catch (IllegalStateException e) {
+ // ignore if we don't have a root directory
+ }
+ request.setPomFile(src.toFile());
+
request.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
+ request.setLocationTracking(false);
+ request.setModelResolver(new ProjectModelResolver(
+ session,
+ new RequestTrace(null),
+ lookup(RepositorySystem.class),
+ lookup(RemoteRepositoryManager.class),
+ project.getRemoteProjectRepositories(),
+ ProjectBuildingRequest.RepositoryMerging.POM_DOMINANT,
+ null));
+
request.setTransformerContextBuilder(modelBuilder.newTransformerContextBuilder());
+
request.setSystemProperties(toProperties(session.getSystemProperties()));
+ request.setUserProperties(toProperties(session.getUserProperties()));
+ request.setModelCache(modelCacheFactory.createCache(session));
+ return modelBuilder.build(request);
+ }
+
+ private Properties toProperties(Map<String, String> map) {
+ Properties props = new Properties();
+ props.putAll(map);
+ return props;
+ }
+
+ private <T> T lookup(Class<T> clazz) throws ComponentLookupException {
+ return container.lookup(clazz);
+ }
+
+ static Model transform(Model model) {
+ String packaging = model.getPackaging();
+ if (POM_PACKAGING.equals(packaging)) {
+ // raw to consumer transform
+ model = model.withRoot(false).withModules(null);
+ if (model.getParent() != null) {
+ model =
model.withParent(model.getParent().withRelativePath(null));
+ }
+
+ if (!model.isPreserveModelVersion()) {
+ model = model.withPreserveModelVersion(false);
+ String version = new
MavenModelVersion().getModelVersion(model);
+ model = model.withModelVersion(version);
+ }
+ } else {
+ Model.Builder builder = prune(
+ Model.newBuilder(model, true)
+ .preserveModelVersion(false)
+ .root(false)
+ .parent(null)
+ .build(null),
+ model);
+ boolean isBom = BOM_PACKAGING.equals(packaging);
+ if (isBom) {
+ builder.packaging(POM_PACKAGING);
+ }
+ builder.profiles(model.getProfiles().stream()
+ .map(p -> prune(Profile.newBuilder(p, true), p).build())
+ .collect(Collectors.toList()));
+ model = builder.build();
+ String version = new MavenModelVersion().getModelVersion(model);
+ model = model.withModelVersion(version);
+ }
+ return model;
+ }
+
+ private static <T extends ModelBase.Builder> T prune(T builder, ModelBase
model) {
+ builder.properties(null).reporting(null);
+ if (model.getDistributionManagement() != null
+ && model.getDistributionManagement().getRelocation() != null) {
+ // keep relocation only
+ builder.distributionManagement(DistributionManagement.newBuilder()
+
.relocation(model.getDistributionManagement().getRelocation())
+ .build());
+ }
+ // only keep repositories other than 'central'
+
builder.pluginRepositories(pruneRepositories(model.getPluginRepositories()));
+ builder.repositories(pruneRepositories(model.getRepositories()));
+ return builder;
+ }
+
+ private static List<Repository> pruneRepositories(List<Repository>
repositories) {
+ return repositories.stream()
+ .filter(r ->
!org.apache.maven.api.Repository.CENTRAL_ID.equals(r.getId()))
+ .collect(Collectors.toList());
+ }
+}
diff --git
a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifact.java
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifact.java
new file mode 100644
index 0000000000..5800048bc4
--- /dev/null
+++
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifact.java
@@ -0,0 +1,148 @@
+/*
+ * 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.internal.transformation.impl;
+
+import javax.xml.stream.XMLStreamException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.internal.transformation.TransformationFailedException;
+import org.apache.maven.model.building.ModelBuildingException;
+import org.apache.maven.project.MavenProject;
+import
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystemSession;
+
+/**
+ * Transformed artifact is derived with some transformation from source
artifact.
+ *
+ * @since TBD
+ */
+class TransformedArtifact extends DefaultArtifact {
+
+ private static final int SHA1_BUFFER_SIZE = 8192;
+ private final DefaultConsumerPomArtifactTransformer
defaultConsumerPomArtifactTransformer;
+ private final MavenProject project;
+ private final Supplier<Path> sourcePathProvider;
+ private final Path target;
+ private final RepositorySystemSession session;
+ private final AtomicReference<String> sourceState;
+
+ TransformedArtifact(
+ DefaultConsumerPomArtifactTransformer
defaultConsumerPomArtifactTransformer,
+ MavenProject project,
+ Path target,
+ RepositorySystemSession session,
+ org.apache.maven.artifact.Artifact source,
+ Supplier<Path> sourcePathProvider,
+ String classifier,
+ String extension) {
+ super(
+ source.getGroupId(),
+ source.getArtifactId(),
+ source.getVersionRange(),
+ source.getScope(),
+ extension,
+ classifier,
+ new TransformedArtifactHandler(
+ classifier, extension,
source.getArtifactHandler().getPackaging()));
+ this.defaultConsumerPomArtifactTransformer =
defaultConsumerPomArtifactTransformer;
+ this.project = project;
+ this.target = target;
+ this.session = session;
+ this.sourcePathProvider = sourcePathProvider;
+ this.sourceState = new AtomicReference<>(null);
+ }
+
+ @Override
+ public boolean isResolved() {
+ return getFile() != null;
+ }
+
+ @Override
+ public void setFile(File file) {
+ throw new UnsupportedOperationException("transformed artifact file
cannot be set");
+ }
+
+ @Override
+ public synchronized File getFile() {
+ try {
+ String state = mayUpdate();
+ if (state == null) {
+ return null;
+ }
+ return target.toFile();
+ } catch (IOException
+ | NoSuchAlgorithmException
+ | XMLStreamException
+ | ModelBuildingException
+ | ComponentLookupException e) {
+ throw new TransformationFailedException(e);
+ }
+ }
+
+ private String mayUpdate()
+ throws IOException, NoSuchAlgorithmException, XMLStreamException,
ModelBuildingException,
+ ComponentLookupException {
+ String result;
+ Path src = sourcePathProvider.get();
+ if (src == null) {
+ Files.deleteIfExists(target);
+ result = null;
+ } else if (!Files.exists(src)) {
+ Files.deleteIfExists(target);
+ result = "";
+ } else {
+ String current = sha1(src);
+ String existing = sourceState.get();
+ if (!Objects.equals(current, existing)) {
+ defaultConsumerPomArtifactTransformer.transform(project,
session, src, target);
+ Files.setLastModifiedTime(target,
Files.getLastModifiedTime(src));
+ }
+ result = current;
+ }
+ sourceState.set(result);
+ return result;
+ }
+
+ static String sha1(Path path) throws NoSuchAlgorithmException, IOException
{
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ try (InputStream fis = Files.newInputStream(path)) {
+ byte[] buffer = new byte[SHA1_BUFFER_SIZE];
+ int read;
+ while ((read = fis.read(buffer)) != -1) {
+ md.update(buffer, 0, read);
+ }
+ }
+ StringBuilder result = new StringBuilder();
+ for (byte b : md.digest()) {
+ result.append(String.format("%02x", b));
+ }
+ return result.toString();
+ }
+}
diff --git
a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java
new file mode 100644
index 0000000000..80ba4e7cdf
--- /dev/null
+++
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java
@@ -0,0 +1,66 @@
+/*
+ * 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.internal.transformation.impl;
+
+import org.apache.maven.artifact.handler.ArtifactHandler;
+
+import static java.util.Objects.requireNonNull;
+
+class TransformedArtifactHandler implements ArtifactHandler {
+
+ private final String classifier;
+
+ private final String extension;
+
+ private final String packaging;
+
+ TransformedArtifactHandler(String classifier, String extension, String
packaging) {
+ this.classifier = classifier;
+ this.extension = requireNonNull(extension);
+ this.packaging = requireNonNull(packaging);
+ }
+
+ public String getClassifier() {
+ return classifier;
+ }
+
+ public String getDirectory() {
+ return null;
+ }
+
+ public String getExtension() {
+ return extension;
+ }
+
+ public String getLanguage() {
+ return "none";
+ }
+
+ public String getPackaging() {
+ return packaging;
+ }
+
+ public boolean isAddedToClasspath() {
+ return false;
+ }
+
+ public boolean isIncludesDependencies() {
+ return false;
+ }
+}
diff --git
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
index 0b9d28daeb..caec1dc9aa 100644
---
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
+++
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
@@ -94,7 +94,7 @@ public class LifecycleModuleBuilder {
return;
}
-
consumerPomArtifactTransformer.injectTransformedArtifacts(currentProject,
session.getRepositorySession());
+
consumerPomArtifactTransformer.injectTransformedArtifacts(session.getRepositorySession(),
currentProject);
BuilderCommon.attachToThread(currentProject);
diff --git
a/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java
b/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java
index e08814fd48..51f9969258 100644
---
a/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java
+++
b/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java
@@ -34,7 +34,7 @@ import
org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
-import org.apache.maven.internal.impl.DefaultSession;
+import org.apache.maven.internal.impl.DefaultSessionFactory;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Exclusion;
@@ -146,11 +146,14 @@ public abstract class AbstractCoreMavenComponentTestCase {
initRepoSession(configuration);
+ DefaultSessionFactory defaultSessionFactory =
+ new DefaultSessionFactory(mock(RepositorySystem.class), null,
null, null);
+
MavenSession session = new MavenSession(
getContainer(), configuration.getRepositorySession(), request,
new DefaultMavenExecutionResult());
session.setProjects(projects);
session.setAllProjects(session.getProjects());
- session.setSession(new DefaultSession(session,
mock(RepositorySystem.class), null, null, null, null));
+ session.setSession(defaultSessionFactory.getSession(session));
return session;
}
diff --git
a/maven-core/src/test/java/org/apache/maven/internal/transformation/AbstractRepositoryTestCase.java
b/maven-core/src/test/java/org/apache/maven/internal/transformation/AbstractRepositoryTestCase.java
new file mode 100644
index 0000000000..728e7494ba
--- /dev/null
+++
b/maven-core/src/test/java/org/apache/maven/internal/transformation/AbstractRepositoryTestCase.java
@@ -0,0 +1,78 @@
+/*
+ * 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.internal.transformation;
+
+import javax.inject.Inject;
+
+import java.net.MalformedURLException;
+
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.testing.PlexusTest;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositoryListener;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.repository.LocalRepository;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.transfer.TransferListener;
+import org.junit.jupiter.api.BeforeEach;
+import org.mockito.Mockito;
+
+import static org.codehaus.plexus.testing.PlexusExtension.getTestFile;
+
+@PlexusTest
+public abstract class AbstractRepositoryTestCase {
+ @Inject
+ protected RepositorySystem system;
+
+ @Inject
+ protected PlexusContainer container;
+
+ protected RepositorySystemSession session;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ session = newMavenRepositorySystemSession(system);
+ }
+
+ protected PlexusContainer getContainer() {
+ return container;
+ }
+
+ public static RepositorySystemSession
newMavenRepositorySystemSession(RepositorySystem system) {
+ DefaultRepositorySystemSession session =
MavenRepositorySystemUtils.newSession();
+
+ LocalRepository localRepo = new LocalRepository("target/local-repo");
+
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session,
localRepo));
+
+ session.setTransferListener(Mockito.mock(TransferListener.class));
+ session.setRepositoryListener(Mockito.mock(RepositoryListener.class));
+
+ return session;
+ }
+
+ public static RemoteRepository newTestRepository() throws
MalformedURLException {
+ return new RemoteRepository.Builder(
+ "repo",
+ "default",
+
getTestFile("target/test-classes/repo").toURI().toURL().toString())
+ .build();
+ }
+}
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/impl/ConsumerPomArtifactTransformerTest.java
similarity index 85%
rename from
maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
rename to
maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java
index 0d5ca973c3..ceb8f5128c 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/impl/ConsumerPomArtifactTransformerTest.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.maven.internal.transformation;
+package org.apache.maven.internal.transformation.impl;
import java.io.IOException;
import java.io.InputStream;
@@ -25,8 +25,6 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.maven.model.Model;
-import org.apache.maven.model.building.DefaultModelBuilderFactory;
-import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.model.building.TransformerContext;
import org.apache.maven.model.v4.MavenStaxReader;
import org.apache.maven.project.MavenProject;
@@ -42,8 +40,6 @@ import static org.mockito.Mockito.when;
class ConsumerPomArtifactTransformerTest {
- ModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
-
@Test
void transform() throws Exception {
RepositorySystemSession systemSessionMock =
Mockito.mock(RepositorySystemSession.class);
@@ -60,9 +56,13 @@ class ConsumerPomArtifactTransformerTest {
try (InputStream expected = Files.newInputStream(beforePomFile)) {
Model model = new Model(new MavenStaxReader().read(expected));
MavenProject project = new MavenProject(model);
- ConsumerPomArtifactTransformer t = new
ConsumerPomArtifactTransformer(modelBuilder);
- t.createConsumerPomArtifact(project, tempFile, systemSessionMock)
- .transform(beforePomFile, tempFile, model.getDelegate());
+ DefaultConsumerPomArtifactTransformer t = new
DefaultConsumerPomArtifactTransformer((s, p, f) -> {
+ try (InputStream is = Files.newInputStream(f)) {
+ return DefaultConsumerPomBuilder.transform(new
MavenStaxReader().read(is));
+ }
+ });
+
+ t.transform(project, systemSessionMock, beforePomFile, tempFile);
}
XmlAssert.assertThat(afterPomFile.toFile()).and(tempFile.toFile()).areIdentical();
}
@@ -76,7 +76,8 @@ class ConsumerPomArtifactTransformerTest {
when(systemSessionMock.getData()).thenReturn(sessionDataMock);
when(sessionDataMock.get(any())).thenReturn(new
NoTransformerContext());
- new
ConsumerPomArtifactTransformer(modelBuilder).injectTransformedArtifacts(emptyProject,
systemSessionMock);
+ new DefaultConsumerPomArtifactTransformer((session, project, src) ->
null)
+ .injectTransformedArtifacts(systemSessionMock, emptyProject);
assertThat(emptyProject.getAttachedArtifacts()).isEmpty();
}
diff --git
a/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
b/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
new file mode 100644
index 0000000000..6eac02bd9a
--- /dev/null
+++
b/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.internal.transformation.impl;
+
+import javax.inject.Inject;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+
+import org.apache.maven.api.model.Model;
+import org.apache.maven.artifact.repository.MavenArtifactRepository;
+import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
+import org.apache.maven.internal.transformation.AbstractRepositoryTestCase;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ConsumerPomBuilderTest extends AbstractRepositoryTestCase {
+
+ @Inject
+ ConsumerPomBuilder builder;
+
+ @Test
+ void testTrivialConsumer() throws Exception {
+ MavenProject project = new MavenProject();
+
project.setRootDirectory(Paths.get("src/test/resources/consumer/trivial"));
+ project.setRemoteArtifactRepositories(Collections.singletonList(new
MavenArtifactRepository(
+ "central", "http://repo.maven.apache.org/", new
DefaultRepositoryLayout(), null, null)));
+ Path file =
Paths.get("src/test/resources/consumer/trivial/child/pom.xml");
+ Model model = builder.build(session, project, file);
+
+ assertNotNull(model);
+ }
+
+ @Test
+ void testSimpleConsumer() throws Exception {
+ MavenProject project = new MavenProject();
+
project.setRootDirectory(Paths.get("src/test/resources/consumer/simple"));
+ project.setRemoteArtifactRepositories(Collections.singletonList(new
MavenArtifactRepository(
+ "central", "http://repo.maven.apache.org/", new
DefaultRepositoryLayout(), null, null)));
+ Path file =
Paths.get("src/test/resources/consumer/simple/simple-parent/simple-weather/pom.xml");
+ ((DefaultRepositorySystemSession)
session).setUserProperty("changelist", "MNG6957");
+ Model model = builder.build(session, project, file);
+
+ assertNotNull(model);
+ assertTrue(model.getProfiles().isEmpty());
+ }
+}
diff --git
a/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
b/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
index 182df18fac..637d05badd 100644
--- a/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
+++ b/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
@@ -192,8 +192,9 @@ class ProjectBuilderTest extends
AbstractCoreMavenComponentTestCase {
@Test
void testReadErroneousMavenProjectContainsReference() throws Exception {
- File pomFile = new
File("src/test/resources/projects/artifactMissingVersion.xml").getAbsoluteFile();
+ File pomFile = new
File("src/test/resources/projects/artifactMissingVersion/pom.xml").getAbsoluteFile();
MavenSession mavenSession = createMavenSession(null);
+
mavenSession.getRequest().setRootDirectory(pomFile.getParentFile().toPath());
ProjectBuildingRequest configuration = new
DefaultProjectBuildingRequest();
configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
configuration.setRepositorySession(mavenSession.getRepositorySession());
diff --git a/maven-core/src/test/resources/consumer/simple/pom.xml
b/maven-core/src/test/resources/consumer/simple/pom.xml
new file mode 100644
index 0000000000..54e3379aff
--- /dev/null
+++ b/maven-core/src/test/resources/consumer/simple/pom.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project root="true" xmlns="http://maven.apache.org/POM/4.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.1.0
https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
+ <groupId>org.sonatype.mavenbook.multi</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.9-${changelist}-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <name>Multi Chapter Parent Project</name>
+
+ <!-- Optimized from
https://github.com/sonatype/maven-example-en/tree/master/examples/ch-multi -->
+ <modules>
+ <module>simple-parent</module>
+ </modules>
+</project>
diff --git
a/maven-core/src/test/resources/consumer/simple/simple-parent/pom.xml
b/maven-core/src/test/resources/consumer/simple/simple-parent/pom.xml
new file mode 100644
index 0000000000..cec61450ae
--- /dev/null
+++ b/maven-core/src/test/resources/consumer/simple/simple-parent/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0
https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
+ <parent>
+ <groupId>org.sonatype.mavenbook.multi</groupId>
+ <artifactId>parent</artifactId>
+ </parent>
+ <artifactId>simple-parent</artifactId>
+ <packaging>pom</packaging>
+ <name>Multi Chapter Simple Parent Project</name>
+
+ <modules>
+ <module>simple-weather</module>
+ <module>simple-webapp</module>
+
+ <!-- On purpose at the end, project graph is responsible for ordering -->
+ <!-- Build/consumer should not be effected by reverse order -->
+ <module>simple-testutils</module>
+ <module>utils-parent</module>
+ </modules>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>0.1-stub-SNAPSHOT</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+</project>
diff --git
a/maven-core/src/test/resources/consumer/simple/simple-parent/simple-testutils/pom.xml
b/maven-core/src/test/resources/consumer/simple/simple-parent/simple-testutils/pom.xml
new file mode 100644
index 0000000000..ce6a47bf6f
--- /dev/null
+++
b/maven-core/src/test/resources/consumer/simple/simple-parent/simple-testutils/pom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0
https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
+ <parent>
+ <groupId>org.sonatype.mavenbook.multi</groupId>
+ <artifactId>utils-parent</artifactId>
+ <relativePath>../utils-parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>simple-testutils</artifactId>
+</project>
diff --git
a/maven-core/src/test/resources/consumer/simple/simple-parent/simple-weather/pom.xml
b/maven-core/src/test/resources/consumer/simple/simple-parent/simple-weather/pom.xml
new file mode 100644
index 0000000000..7b2e0b29db
--- /dev/null
+++
b/maven-core/src/test/resources/consumer/simple/simple-parent/simple-weather/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0
https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
+ <parent>
+ <groupId>org.sonatype.mavenbook.multi</groupId>
+ <artifactId>simple-parent</artifactId>
+ </parent>
+ <artifactId>simple-weather</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Multi Chapter Simple Weather API</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.sonatype.mavenbook.multi</groupId>
+ <artifactId>simple-testutils</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git
a/maven-core/src/test/resources/consumer/simple/simple-parent/simple-webapp/pom.xml
b/maven-core/src/test/resources/consumer/simple/simple-parent/simple-webapp/pom.xml
new file mode 100644
index 0000000000..655d0ccd17
--- /dev/null
+++
b/maven-core/src/test/resources/consumer/simple/simple-parent/simple-webapp/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.sonatype.mavenbook.multi</groupId>
+ <artifactId>simple-parent</artifactId>
+ </parent>
+
+ <artifactId>simple-webapp</artifactId>
+ <name>Multi Chapter Simple Web Application Project</name>
+ <dependencies>
+ <dependency>
+ <groupId>org.sonatype.mavenbook.multi</groupId>
+ <artifactId>simple-weather</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <finalName>simple-webapp</finalName>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <version>3.3.2</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+</project>
diff --git
a/maven-core/src/test/resources/consumer/simple/simple-parent/utils-parent/pom.xml
b/maven-core/src/test/resources/consumer/simple/simple-parent/utils-parent/pom.xml
new file mode 100644
index 0000000000..52909d70f9
--- /dev/null
+++
b/maven-core/src/test/resources/consumer/simple/simple-parent/utils-parent/pom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0
https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
+ <parent>
+ <groupId>org.sonatype.mavenbook.multi</groupId>
+ <artifactId>simple-parent</artifactId>
+ </parent>
+ <artifactId>utils-parent</artifactId>
+ <packaging>pom</packaging>
+</project>
diff --git a/maven-core/src/test/resources/consumer/trivial/child/pom.xml
b/maven-core/src/test/resources/consumer/trivial/child/pom.xml
new file mode 100644
index 0000000000..25c05afa1d
--- /dev/null
+++ b/maven-core/src/test/resources/consumer/trivial/child/pom.xml
@@ -0,0 +1,18 @@
+<project xmlns="http://maven.apache.org/POM/4.1.0">
+ <parent>
+ <groupId>org.my.group</groupId>
+ <artifactId>parent</artifactId>
+ </parent>
+ <artifactId>child</artifactId>
+ <packaging>jar</packaging>
+
+ <properties>
+ <prop-child>bar</prop-child>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/maven-core/src/test/resources/consumer/trivial/pom.xml
b/maven-core/src/test/resources/consumer/trivial/pom.xml
new file mode 100644
index 0000000000..14fab6c9df
--- /dev/null
+++ b/maven-core/src/test/resources/consumer/trivial/pom.xml
@@ -0,0 +1,47 @@
+<project root="true" xmlns="http://maven.apache.org/POM/4.1.0">
+ <groupId>org.my.group</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>child.xml</module>
+ </modules>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>2.0.9</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>5.10.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>release</id>
+ <properties>
+ <prop>foo</prop>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.11.0</version>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
\ No newline at end of file
diff --git a/maven-core/src/test/resources/projects/artifactMissingVersion.xml
b/maven-core/src/test/resources/projects/artifactMissingVersion/pom.xml
similarity index 100%
rename from maven-core/src/test/resources/projects/artifactMissingVersion.xml
rename to maven-core/src/test/resources/projects/artifactMissingVersion/pom.xml
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 e1db4c34de..033ca11dae 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
@@ -1045,7 +1045,7 @@ public class DefaultModelBuilder implements ModelBuilder {
}
}
- private Model readFileModel(ModelBuildingRequest request,
DefaultModelProblemCollector problems)
+ Model readFileModel(ModelBuildingRequest request,
DefaultModelProblemCollector problems)
throws ModelBuildingException {
ModelSource modelSource = request.getModelSource();
org.apache.maven.api.model.Model model = cache(
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 6b5de94484..f86f1aaa7d 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
@@ -18,9 +18,12 @@
*/
package org.apache.maven.model.building;
+import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -56,6 +59,9 @@ class DefaultTransformerContextBuilder implements
TransformerContextBuilder {
// We must assume the TransformerContext was created using
this.newTransformerContextBuilder()
DefaultModelProblemCollector problems = (DefaultModelProblemCollector)
collector;
return new TransformerContext() {
+
+ private volatile boolean fullReactorLoaded;
+
@Override
public Path locate(Path path) {
return context.locate(path);
@@ -91,6 +97,11 @@ class DefaultTransformerContextBuilder implements
TransformerContextBuilder {
private Model findRawModel(Path from, String groupId, String
artifactId) {
FileModelSource source = getSource(groupId, artifactId);
+ if (source == null) {
+ // we need to check the whole reactor in case it's a
dependency
+ loadFullReactor();
+ source = getSource(groupId, artifactId);
+ }
if (source != null) {
if (!addEdge(from, source.getFile().toPath(), problems)) {
return null;
@@ -106,6 +117,45 @@ class DefaultTransformerContextBuilder implements
TransformerContextBuilder {
return null;
}
+ private void loadFullReactor() {
+ if (!fullReactorLoaded) {
+ synchronized (this) {
+ if (!fullReactorLoaded) {
+ doLoadFullReactor();
+ fullReactorLoaded = true;
+ }
+ }
+ }
+ }
+
+ private void doLoadFullReactor() {
+ Path rootDirectory = request.getRootDirectory();
+ if (rootDirectory == null) {
+ return;
+ }
+ List<File> toLoad = new ArrayList<>();
+ File root =
defaultModelBuilder.getModelProcessor().locateExistingPom(rootDirectory.toFile());
+ toLoad.add(root);
+ while (!toLoad.isEmpty()) {
+ File pom = toLoad.remove(0);
+ try {
+ ModelBuildingRequest gaBuildingRequest =
+ new
DefaultModelBuildingRequest(request).setModelSource(new FileModelSource(pom));
+ Model rawModel =
defaultModelBuilder.readFileModel(gaBuildingRequest, problems);
+ for (String module : rawModel.getModules()) {
+ File moduleFile = defaultModelBuilder
+ .getModelProcessor()
+ .locateExistingPom(new
File(pom.getParent(), module));
+ if (moduleFile != null) {
+ toLoad.add(moduleFile);
+ }
+ }
+ } catch (ModelBuildingException e) {
+ // gathered with problem collector
+ }
+ }
+ }
+
private Model findRawModel(Path from, Path p) {
if (!Files.isRegularFile(p)) {
throw new IllegalArgumentException("Not a regular file: "
+ p);