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 c07dcc0c70..0000000000 --- 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 ae4efb1e6b..1f03faf118 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 getTransformersForArtifact( - final Artifact artifact, final SessionData sessionData) { - TransformerContext context = (TransformerContext) sessionData.get(TransformerContext.KEY); - Collection 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 0000000000..93aa84418e --- /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 0000000000..d7f2f95f14 --- /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 576e915a6d..11b6da6818 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 repositorySystemProvider; @Inject - public ResolverLifecycle(Provider repositorySystemProvider) { + ResolverLifecycle(Provider 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 0000000000..1b6c5a8d0c --- /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 artifacts) { + return artifacts.stream().anyMatch(a -> CONSUMER_POM_CLASSIFIER.equals(a.getClassifier())); + } + + private Collection replacePom(Collection artifacts) { + ArrayList 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 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 0000000000..d777ed2068 --- /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. + *

+ * Covered cases: + *

    + *
  • when source supplier returns {@code null}, this class will return {@code null}.
  • + *
  • when source supplier returns non existing path, this class will return non existing path.
  • + *
  • when source supplier returns existing path, this class will ensure transformation is in sync.
  • + *
+ * + * @since TBD + */ +final class OnChangeTransformer implements Supplier { + + private final Supplier source; + + private final Path target; + + private final Function stateFunction; + + private final BiConsumer transformerConsumer; + + private final AtomicReference sourceState; + + OnChangeTransformer( + Supplier source, + Path target, + Function stateFunction, + BiConsumer 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 0000000000..84e88c399b --- /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 sourcePathProvider, + String classifier, + String extension, + Path targetPath, + BiConsumer 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 a210b07d93..2ecb1d4fd1 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 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 80779df1df..26874e3acd 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 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 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 457cbed267..64f95422bb 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 abda9fddd9..c162463f66 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 2da5b61f47..fba0531e33 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 userProperties) { return new Feature(userProperties, "maven.experimental.buildconsumer", "true"); } + private static Map 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 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() {