You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by cs...@apache.org on 2023/01/25 10:27:05 UTC
[maven] branch master updated: [MNG-7622] Maven Transformation and Consumer POM (#907)
This is an automated email from the ASF dual-hosted git repository.
cstamas 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 8918c8144 [MNG-7622] Maven Transformation and Consumer POM (#907)
8918c8144 is described below
commit 8918c8144f341fb9585025540c4571c0358ad4f8
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Wed Jan 25 11:27:00 2023 +0100
[MNG-7622] Maven Transformation and Consumer POM (#907)
Maven Consumer POM redone, it happens only in "maven3 realm" (before resolver), and allows use cases like m-gog-p and checksum-m-p work as before.
Key aspects:
* consumer POM is injected to build earliest possible as attached artifact
* it lives and is visible just like any other attached artifact (so m-gpg-p can process it)
* just before the install/deploy, they are "swapped out" to replace POM along with all "extras" it may have (checksum, signature)
* to support use cases like MNG-7067 (in memory model is changed, but not POM file), OnChangeTransformer could be extended to take into account both: file content and model content.
---
https://issues.apache.org/jira/browse/MNG-7622
---
.../aether/ConsumerModelSourceTransformer.java | 47 ------
.../DefaultRepositorySystemSessionFactory.java | 42 -----
.../maven/internal/aether/MavenDeployer.java | 58 +++++++
.../maven/internal/aether/MavenInstaller.java | 58 +++++++
.../maven/internal/aether/ResolverLifecycle.java | 4 +-
.../ConsumerPomArtifactTransformer.java | 169 +++++++++++++++++++++
.../transformation/OnChangeTransformer.java | 104 +++++++++++++
.../transformation/TransformedArtifact.java | 146 ++++++++++++++++++
.../lifecycle/internal/LifecycleModuleBuilder.java | 6 +
.../lifecycle/internal/builder/BuilderCommon.java | 21 ---
.../ConsumerPomArtifactTransformerTest.java} | 9 +-
.../internal/LifecycleModuleBuilderTest.java | 4 +-
.../java/org/apache/maven/feature/Features.java | 20 ++-
13 files changed, 568 insertions(+), 120 deletions(-)
diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java b/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java
deleted file mode 100644
index c07dcc0c7..000000000
--- a/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java
+++ /dev/null
@@ -1,47 +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.aether;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory;
-import org.apache.maven.model.building.TransformerContext;
-import org.apache.maven.model.transform.RawToConsumerPomXMLFilterFactory;
-import org.apache.maven.model.transform.pull.XmlUtils;
-import org.codehaus.plexus.util.ReaderFactory;
-import org.codehaus.plexus.util.xml.XmlStreamReader;
-import org.codehaus.plexus.util.xml.pull.EntityReplacementMap;
-import org.codehaus.plexus.util.xml.pull.MXParser;
-import org.codehaus.plexus.util.xml.pull.XmlPullParser;
-import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
-
-class ConsumerModelSourceTransformer {
- public InputStream transform(Path pomFile, TransformerContext context) throws IOException, XmlPullParserException {
- XmlStreamReader reader = ReaderFactory.newXmlReader(Files.newInputStream(pomFile));
- XmlPullParser parser = new MXParser(EntityReplacementMap.defaultEntityReplacementMap);
- parser.setInput(reader);
- parser = new RawToConsumerPomXMLFilterFactory(new DefaultBuildPomXMLFilterFactory(context, true))
- .get(parser, pomFile);
-
- return XmlUtils.writeDocument(reader, parser);
- }
-}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java b/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
index ae4efb1e6..1f03faf11 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
@@ -21,13 +21,8 @@ package org.apache.maven.internal.aether;
import javax.inject.Inject;
import javax.inject.Named;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -41,11 +36,9 @@ import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import org.apache.maven.bridge.MavenRepositorySystem;
import org.apache.maven.eventspy.internal.EventSpyDispatcher;
import org.apache.maven.execution.MavenExecutionRequest;
-import org.apache.maven.feature.Features;
import org.apache.maven.internal.xml.XmlNodeImpl;
import org.apache.maven.internal.xml.XmlPlexusConfiguration;
import org.apache.maven.model.ModelBase;
-import org.apache.maven.model.building.TransformerContext;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.rtinfo.RuntimeInformation;
import org.apache.maven.settings.Mirror;
@@ -56,12 +49,9 @@ import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
import org.apache.maven.settings.crypto.SettingsDecrypter;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.codehaus.plexus.configuration.PlexusConfiguration;
-import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.aether.ConfigurationProperties;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
-import org.eclipse.aether.SessionData;
-import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
@@ -69,8 +59,6 @@ import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.resolution.ResolutionErrorPolicy;
import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
-import org.eclipse.aether.transform.FileTransformer;
-import org.eclipse.aether.transform.TransformException;
import org.eclipse.aether.util.ConfigUtils;
import org.eclipse.aether.util.listener.ChainedRepositoryListener;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
@@ -377,10 +365,6 @@ public class DefaultRepositorySystemSessionFactory {
setUpLocalRepositoryManager(request, session);
- if (Features.buildConsumer(request.getUserProperties()).isActive()) {
- session.setFileTransformerManager(a -> getTransformersForArtifact(a, session.getData()));
- }
-
return session;
}
@@ -437,30 +421,4 @@ public class DefaultRepositorySystemSessionFactory {
return "Apache-Maven" + version + " (Java " + System.getProperty("java.version") + "; "
+ System.getProperty("os.name") + " " + System.getProperty("os.version") + ")";
}
-
- private Collection<FileTransformer> getTransformersForArtifact(
- final Artifact artifact, final SessionData sessionData) {
- TransformerContext context = (TransformerContext) sessionData.get(TransformerContext.KEY);
- Collection<FileTransformer> transformers = new ArrayList<>();
-
- // In case of install:install-file there's no transformer context, as the goal is unrelated to the lifecycle.
- if ("pom".equals(artifact.getExtension()) && context != null) {
- transformers.add(new FileTransformer() {
- @Override
- public InputStream transformData(File pomFile) throws IOException, TransformException {
- try {
- return new ConsumerModelSourceTransformer().transform(pomFile.toPath(), context);
- } catch (XmlPullParserException e) {
- throw new TransformException(e);
- }
- }
-
- @Override
- public Artifact transformArtifact(Artifact artifact) {
- return artifact;
- }
- });
- }
- return Collections.unmodifiableCollection(transformers);
- }
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/MavenDeployer.java b/maven-core/src/main/java/org/apache/maven/internal/aether/MavenDeployer.java
new file mode 100644
index 000000000..93aa84418
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/aether/MavenDeployer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.aether;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.apache.maven.internal.transformation.ConsumerPomArtifactTransformer;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.deployment.DeployRequest;
+import org.eclipse.aether.deployment.DeployResult;
+import org.eclipse.aether.deployment.DeploymentException;
+import org.eclipse.aether.impl.Deployer;
+import org.eclipse.aether.internal.impl.DefaultDeployer;
+import org.eclipse.sisu.Priority;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Maven specific deployer.
+ */
+@Singleton
+@Named
+@Priority(100)
+final class MavenDeployer implements Deployer {
+
+ private final DefaultDeployer deployer;
+
+ private final ConsumerPomArtifactTransformer consumerPomArtifactTransformer;
+
+ @Inject
+ MavenDeployer(DefaultDeployer deployer, ConsumerPomArtifactTransformer consumerPomArtifactTransformer) {
+ this.deployer = requireNonNull(deployer);
+ this.consumerPomArtifactTransformer = requireNonNull(consumerPomArtifactTransformer);
+ }
+
+ @Override
+ public DeployResult deploy(RepositorySystemSession session, DeployRequest request) throws DeploymentException {
+ return deployer.deploy(session, consumerPomArtifactTransformer.remapDeployArtifacts(session, request));
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/MavenInstaller.java b/maven-core/src/main/java/org/apache/maven/internal/aether/MavenInstaller.java
new file mode 100644
index 000000000..d7f2f95f1
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/aether/MavenInstaller.java
@@ -0,0 +1,58 @@
+/*
+ * 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.aether;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.apache.maven.internal.transformation.ConsumerPomArtifactTransformer;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.impl.Installer;
+import org.eclipse.aether.installation.InstallRequest;
+import org.eclipse.aether.installation.InstallResult;
+import org.eclipse.aether.installation.InstallationException;
+import org.eclipse.aether.internal.impl.DefaultInstaller;
+import org.eclipse.sisu.Priority;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Maven specific installer.
+ */
+@Singleton
+@Named
+@Priority(100)
+final class MavenInstaller implements Installer {
+
+ private final DefaultInstaller installer;
+
+ private final ConsumerPomArtifactTransformer consumerPomArtifactTransformer;
+
+ @Inject
+ MavenInstaller(DefaultInstaller installer, ConsumerPomArtifactTransformer consumerPomArtifactTransformer) {
+ this.installer = requireNonNull(installer);
+ this.consumerPomArtifactTransformer = requireNonNull(consumerPomArtifactTransformer);
+ }
+
+ @Override
+ public InstallResult install(RepositorySystemSession session, InstallRequest request) throws InstallationException {
+ return installer.install(session, consumerPomArtifactTransformer.remapInstallArtifacts(session, request));
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/ResolverLifecycle.java b/maven-core/src/main/java/org/apache/maven/internal/aether/ResolverLifecycle.java
index 576e915a6..11b6da681 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/aether/ResolverLifecycle.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/aether/ResolverLifecycle.java
@@ -35,11 +35,11 @@ import static java.util.Objects.requireNonNull;
*/
@Named
@EagerSingleton
-public final class ResolverLifecycle {
+final class ResolverLifecycle {
private final Provider<RepositorySystem> repositorySystemProvider;
@Inject
- public ResolverLifecycle(Provider<RepositorySystem> repositorySystemProvider) {
+ ResolverLifecycle(Provider<RepositorySystem> repositorySystemProvider) {
this.repositorySystemProvider = requireNonNull(repositorySystemProvider);
}
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
new file mode 100644
index 000000000..1b6c5a8d0
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java
@@ -0,0 +1,169 @@
+/*
+ * 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.Named;
+import javax.inject.Singleton;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.function.BiConsumer;
+
+import org.apache.maven.feature.Features;
+import org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory;
+import org.apache.maven.model.building.TransformerContext;
+import org.apache.maven.model.transform.RawToConsumerPomXMLFilterFactory;
+import org.apache.maven.model.transform.pull.XmlUtils;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.artifact.ProjectArtifact;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.xml.XmlStreamReader;
+import org.codehaus.plexus.util.xml.pull.EntityReplacementMap;
+import org.codehaus.plexus.util.xml.pull.MXParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+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")
+public final class ConsumerPomArtifactTransformer {
+
+ private static final String CONSUMER_POM_CLASSIFIER = "consumer";
+
+ public void injectTransformedArtifacts(MavenProject project, RepositorySystemSession session) throws IOException {
+ if (isActive(session)) {
+ Path generatedFile;
+ String buildDirectory =
+ project.getBuild() != null ? project.getBuild().getDirectory() : null;
+ if (buildDirectory == null) {
+ generatedFile = Files.createTempFile(CONSUMER_POM_CLASSIFIER, "pom");
+ } else {
+ Path buildDir = Paths.get(buildDirectory);
+ Files.createDirectories(buildDir);
+ generatedFile = Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER, "pom");
+ }
+ project.addAttachedArtifact(new ConsumerPomArtifact(project, generatedFile, session));
+ }
+ }
+
+ public InstallRequest remapInstallArtifacts(RepositorySystemSession session, InstallRequest request) {
+ if (isActive(session) && consumerPomPresent(request.getArtifacts())) {
+ request.setArtifacts(replacePom(request.getArtifacts()));
+ }
+ return request;
+ }
+
+ public DeployRequest remapDeployArtifacts(RepositorySystemSession session, DeployRequest request) {
+ if (isActive(session) && consumerPomPresent(request.getArtifacts())) {
+ request.setArtifacts(replacePom(request.getArtifacts()));
+ }
+ return request;
+ }
+
+ private boolean isActive(RepositorySystemSession session) {
+ return Features.buildConsumer(session.getUserProperties()).isActive();
+ }
+
+ private boolean consumerPomPresent(Collection<Artifact> artifacts) {
+ return artifacts.stream().anyMatch(a -> CONSUMER_POM_CLASSIFIER.equals(a.getClassifier()));
+ }
+
+ private Collection<Artifact> replacePom(Collection<Artifact> artifacts) {
+ ArrayList<Artifact> result = new ArrayList<>(artifacts.size());
+ for (Artifact artifact : artifacts) {
+ if (CONSUMER_POM_CLASSIFIER.equals(artifact.getClassifier())) {
+ // if under CONSUMER_POM_CLASSIFIER, move it to "" classifier
+ DefaultArtifact remapped = new DefaultArtifact(
+ artifact.getGroupId(),
+ artifact.getArtifactId(),
+ "",
+ artifact.getExtension(),
+ artifact.getVersion(),
+ artifact.getProperties(),
+ artifact.getFile());
+ result.add(remapped);
+ } else if ("".equals(artifact.getClassifier())
+ && (artifact.getExtension().equals("pom"))
+ || artifact.getExtension().startsWith("pom.")) {
+ // skip POM and POM subordinates
+ continue;
+ } else {
+ // everything else: add as is
+ result.add(artifact);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Consumer POM is transformed from original POM.
+ */
+ private static class ConsumerPomArtifact extends TransformedArtifact {
+
+ private ConsumerPomArtifact(MavenProject mavenProject, Path target, RepositorySystemSession session) {
+ super(
+ new ProjectArtifact(mavenProject),
+ () -> mavenProject.getFile().toPath(),
+ CONSUMER_POM_CLASSIFIER,
+ "pom",
+ target,
+ transformer(session));
+ }
+
+ private static BiConsumer<Path, Path> transformer(RepositorySystemSession session) {
+ TransformerContext context = (TransformerContext) session.getData().get(TransformerContext.KEY);
+ return (src, dest) -> {
+ try (InputStream inputStream = transform(src, context)) {
+ Files.createDirectories(dest.getParent());
+ Files.copy(inputStream, dest, StandardCopyOption.REPLACE_EXISTING);
+ } catch (XmlPullParserException | IOException e) {
+ throw new RuntimeException(e);
+ }
+ };
+ }
+ }
+
+ /**
+ * The actual transformation: visible for testing.
+ */
+ static InputStream transform(Path pomFile, TransformerContext context) throws IOException, XmlPullParserException {
+ XmlStreamReader reader = ReaderFactory.newXmlReader(Files.newInputStream(pomFile));
+ XmlPullParser parser = new MXParser(EntityReplacementMap.defaultEntityReplacementMap);
+ parser.setInput(reader);
+ parser = new RawToConsumerPomXMLFilterFactory(new DefaultBuildPomXMLFilterFactory(context, true))
+ .get(parser, pomFile);
+
+ return XmlUtils.writeDocument(reader, parser);
+ }
+}
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
new file mode 100644
index 000000000..d777ed206
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/transformation/OnChangeTransformer.java
@@ -0,0 +1,104 @@
+/*
+ * 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/TransformedArtifact.java b/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformedArtifact.java
new file mode 100644
index 000000000..84e88c399
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformedArtifact.java
@@ -0,0 +1,146 @@
+/*
+ * 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.BiConsumer;
+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,
+ BiConsumer<Path, Path> transformerConsumer) {
+ 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, transformerConsumer);
+ }
+
+ @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);
+ }
+ }
+
+ 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/lifecycle/internal/LifecycleModuleBuilder.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
index a210b07d9..2ecb1d4fd 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
@@ -30,6 +30,7 @@ import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectExecutionEvent;
import org.apache.maven.execution.ProjectExecutionListener;
+import org.apache.maven.internal.transformation.ConsumerPomArtifactTransformer;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.plugin.MojoExecution;
@@ -55,6 +56,7 @@ public class LifecycleModuleBuilder {
private final BuilderCommon builderCommon;
private final ExecutionEventCatapult eventCatapult;
private final ProjectExecutionListener projectExecutionListener;
+ private final ConsumerPomArtifactTransformer consumerPomArtifactTransformer;
private final SessionScope sessionScope;
@Inject
@@ -63,11 +65,13 @@ public class LifecycleModuleBuilder {
BuilderCommon builderCommon,
ExecutionEventCatapult eventCatapult,
List<ProjectExecutionListener> listeners,
+ ConsumerPomArtifactTransformer consumerPomArtifactTransformer,
SessionScope sessionScope) {
this.mojoExecutor = mojoExecutor;
this.builderCommon = builderCommon;
this.eventCatapult = eventCatapult;
this.projectExecutionListener = new CompoundProjectExecutionListener(listeners);
+ this.consumerPomArtifactTransformer = consumerPomArtifactTransformer;
this.sessionScope = sessionScope;
}
@@ -93,6 +97,8 @@ public class LifecycleModuleBuilder {
return;
}
+ consumerPomArtifactTransformer.injectTransformedArtifacts(currentProject, session.getRepositorySession());
+
BuilderCommon.attachToThread(currentProject);
projectExecutionListener.beforeProjectExecution(new ProjectExecutionEvent(session, currentProject));
diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
index 80779df1d..26874e3ac 100644
--- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
+++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
@@ -23,8 +23,6 @@ import javax.inject.Named;
import javax.inject.Singleton;
import java.util.List;
-import java.util.Optional;
-import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
@@ -33,7 +31,6 @@ import org.apache.maven.execution.BuildFailure;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
-import org.apache.maven.feature.Features;
import org.apache.maven.internal.MultilineMessageHelper;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.LifecycleNotFoundException;
@@ -112,24 +109,6 @@ public class BuilderCommon {
lifecycleDebugLogger.debugProjectPlan(project, executionPlan);
- // With Maven 4's build/consumer the POM will always rewrite during distribution.
- // The maven-gpg-plugin uses the original POM, causing an invalid signature.
- // Fail as long as there's no solution available yet
- Properties userProperties = session.getUserProperties();
- if (Features.buildConsumer(userProperties).isActive()) {
- Optional<MojoExecution> gpgMojo = executionPlan.getMojoExecutions().stream()
- .filter(m -> "maven-gpg-plugin".equals(m.getArtifactId())
- && "org.apache.maven.plugins".equals(m.getGroupId()))
- .findAny();
-
- if (gpgMojo.isPresent()) {
- throw new LifecycleExecutionException("The maven-gpg-plugin is not supported by Maven 4."
- + " Verify if there is a compatible signing solution,"
- + " add -D" + Features.buildConsumer(userProperties).propertyName() + "=false"
- + " or use Maven 3.");
- }
- }
-
if (session.getRequest().getDegreeOfConcurrency() > 1
&& session.getProjects().size() > 1) {
final Set<Plugin> unsafePlugins = executionPlan.getNonThreadSafePlugins();
diff --git a/maven-core/src/test/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformerTest.java b/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
similarity index 87%
rename from maven-core/src/test/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformerTest.java
rename to maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
index 457cbed26..64f95422b 100644
--- a/maven-core/src/test/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformerTest.java
+++ b/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.maven.internal.aether;
+package org.apache.maven.internal.transformation;
import java.io.InputStream;
import java.nio.file.Files;
@@ -28,9 +28,7 @@ import org.apache.maven.model.building.TransformerContext;
import org.junit.jupiter.api.Test;
import org.xmlunit.assertj.XmlAssert;
-public class ConsumerModelSourceTransformerTest {
- private ConsumerModelSourceTransformer transformer = new ConsumerModelSourceTransformer();
-
+public class ConsumerPomArtifactTransformerTest {
@Test
public void transform() throws Exception {
Path beforePomFile =
@@ -39,7 +37,8 @@ public class ConsumerModelSourceTransformerTest {
Paths.get("src/test/resources/projects/transform/after.pom").toAbsolutePath();
try (InputStream expected = Files.newInputStream(afterPomFile);
- InputStream result = transformer.transform(beforePomFile, new NoTransformerContext())) {
+ InputStream result =
+ ConsumerPomArtifactTransformer.transform(beforePomFile, new NoTransformerContext())) {
XmlAssert.assertThat(result).and(expected).areIdentical();
}
}
diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilderTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilderTest.java
index abda9fddd..c162463f6 100644
--- a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilderTest.java
+++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilderTest.java
@@ -37,6 +37,7 @@ import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.testing.PlexusTest;
+import org.eclipse.aether.DefaultRepositorySystemSession;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -63,7 +64,8 @@ public class LifecycleModuleBuilderTest {
MavenExecutionRequest mavenExecutionRequest = new DefaultMavenExecutionRequest();
mavenExecutionRequest.setExecutionListener(new AbstractExecutionListener());
mavenExecutionRequest.setGoals(Arrays.asList("clean"));
- final MavenSession session = new MavenSession(null, null, mavenExecutionRequest, defaultMavenExecutionResult);
+ final MavenSession session = new MavenSession(
+ null, new DefaultRepositorySystemSession(), mavenExecutionRequest, defaultMavenExecutionResult);
final ProjectDependencyGraphStub dependencyGraphStub = new ProjectDependencyGraphStub();
session.setProjectDependencyGraph(dependencyGraphStub);
session.setProjects(dependencyGraphStub.getSortedProjects());
diff --git a/maven-model-builder/src/main/java/org/apache/maven/feature/Features.java b/maven-model-builder/src/main/java/org/apache/maven/feature/Features.java
index 2da5b61f4..fba0531e3 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/feature/Features.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/feature/Features.java
@@ -18,7 +18,10 @@
*/
package org.apache.maven.feature;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Properties;
+import java.util.stream.Collectors;
/**
* Centralized class for feature information
@@ -30,9 +33,22 @@ public final class Features {
private Features() {}
public static Feature buildConsumer(Properties userProperties) {
+ return buildConsumer(toMap(userProperties));
+ }
+
+ public static Feature buildConsumer(Map<String, String> userProperties) {
return new Feature(userProperties, "maven.experimental.buildconsumer", "true");
}
+ private static Map<String, String> toMap(Properties properties) {
+ return properties.entrySet().stream()
+ .collect(Collectors.toMap(
+ e -> String.valueOf(e.getKey()),
+ e -> String.valueOf(e.getValue()),
+ (prev, next) -> next,
+ HashMap::new));
+ }
+
/**
* Represents some feature
*
@@ -44,9 +60,9 @@ public final class Features {
private final String name;
- Feature(Properties userProperties, String name, String defaultValue) {
+ Feature(Map<String, String> userProperties, String name, String defaultValue) {
this.name = name;
- this.active = "true".equals(userProperties.getProperty(name, defaultValue));
+ this.active = "true".equals(userProperties.getOrDefault(name, defaultValue));
}
public boolean isActive() {