[MNG-7945] Fix profile settings being injected into consumer POM (#1323)

This commit is contained in:
Guillaume Nodet 2023-11-28 18:17:10 +01:00 committed by GitHub
parent 3927ca1843
commit 69bc993b80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1228 additions and 548 deletions

View File

@ -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 {
public interface ConsumerPomArtifactTransformer {
private static final String BOM_PACKAGING = "bom";
InstallRequest remapInstallArtifacts(RepositorySystemSession session, InstallRequest request);
public static final String POM_PACKAGING = "pom";
DeployRequest remapDeployArtifacts(RepositorySystemSession session, DeployRequest request);
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);
}
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);
}
}
}
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;
}
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;
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -94,7 +94,7 @@ public class LifecycleModuleBuilder {
return;
}
consumerPomArtifactTransformer.injectTransformedArtifacts(currentProject, session.getRepositorySession());
consumerPomArtifactTransformer.injectTransformedArtifacts(session.getRepositorySession(), currentProject);
BuilderCommon.attachToThread(currentProject);

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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());
}
}

View File

@ -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());

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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(

View File

@ -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);