View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.transformation;
20  
21  import javax.inject.Named;
22  import javax.inject.Singleton;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.nio.file.Paths;
29  import java.nio.file.StandardCopyOption;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.function.BiConsumer;
33  
34  import org.apache.maven.feature.Features;
35  import org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory;
36  import org.apache.maven.model.building.TransformerContext;
37  import org.apache.maven.model.transform.RawToConsumerPomXMLFilterFactory;
38  import org.apache.maven.model.transform.pull.XmlUtils;
39  import org.apache.maven.project.MavenProject;
40  import org.apache.maven.project.artifact.ProjectArtifact;
41  import org.codehaus.plexus.util.ReaderFactory;
42  import org.codehaus.plexus.util.xml.XmlStreamReader;
43  import org.codehaus.plexus.util.xml.pull.EntityReplacementMap;
44  import org.codehaus.plexus.util.xml.pull.MXParser;
45  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
46  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
47  import org.eclipse.aether.RepositorySystemSession;
48  import org.eclipse.aether.artifact.Artifact;
49  import org.eclipse.aether.artifact.DefaultArtifact;
50  import org.eclipse.aether.deployment.DeployRequest;
51  import org.eclipse.aether.installation.InstallRequest;
52  
53  /**
54   * Consumer POM transformer.
55   *
56   * @since TBD
57   */
58  @Singleton
59  @Named("consumer-pom")
60  public final class ConsumerPomArtifactTransformer {
61  
62      private static final String CONSUMER_POM_CLASSIFIER = "consumer";
63  
64      public void injectTransformedArtifacts(MavenProject project, RepositorySystemSession session) throws IOException {
65          if (isActive(session)) {
66              Path generatedFile;
67              String buildDirectory =
68                      project.getBuild() != null ? project.getBuild().getDirectory() : null;
69              if (buildDirectory == null) {
70                  generatedFile = Files.createTempFile(CONSUMER_POM_CLASSIFIER, "pom");
71              } else {
72                  Path buildDir = Paths.get(buildDirectory);
73                  Files.createDirectories(buildDir);
74                  generatedFile = Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER, "pom");
75              }
76              project.addAttachedArtifact(new ConsumerPomArtifact(project, generatedFile, session));
77          }
78      }
79  
80      public InstallRequest remapInstallArtifacts(RepositorySystemSession session, InstallRequest request) {
81          if (isActive(session) && consumerPomPresent(request.getArtifacts())) {
82              request.setArtifacts(replacePom(request.getArtifacts()));
83          }
84          return request;
85      }
86  
87      public DeployRequest remapDeployArtifacts(RepositorySystemSession session, DeployRequest request) {
88          if (isActive(session) && consumerPomPresent(request.getArtifacts())) {
89              request.setArtifacts(replacePom(request.getArtifacts()));
90          }
91          return request;
92      }
93  
94      private boolean isActive(RepositorySystemSession session) {
95          return Features.buildConsumer(session.getUserProperties()).isActive();
96      }
97  
98      private boolean consumerPomPresent(Collection<Artifact> artifacts) {
99          return artifacts.stream().anyMatch(a -> CONSUMER_POM_CLASSIFIER.equals(a.getClassifier()));
100     }
101 
102     private Collection<Artifact> replacePom(Collection<Artifact> artifacts) {
103         ArrayList<Artifact> result = new ArrayList<>(artifacts.size());
104         for (Artifact artifact : artifacts) {
105             if (CONSUMER_POM_CLASSIFIER.equals(artifact.getClassifier())) {
106                 // if under CONSUMER_POM_CLASSIFIER, move it to "" classifier
107                 DefaultArtifact remapped = new DefaultArtifact(
108                         artifact.getGroupId(),
109                         artifact.getArtifactId(),
110                         "",
111                         artifact.getExtension(),
112                         artifact.getVersion(),
113                         artifact.getProperties(),
114                         artifact.getFile());
115                 result.add(remapped);
116             } else if ("".equals(artifact.getClassifier())
117                             && (artifact.getExtension().equals("pom"))
118                     || artifact.getExtension().startsWith("pom.")) {
119                 // skip POM and POM subordinates
120                 continue;
121             } else {
122                 // everything else: add as is
123                 result.add(artifact);
124             }
125         }
126         return result;
127     }
128 
129     /**
130      * Consumer POM is transformed from original POM.
131      */
132     private static class ConsumerPomArtifact extends TransformedArtifact {
133 
134         private ConsumerPomArtifact(MavenProject mavenProject, Path target, RepositorySystemSession session) {
135             super(
136                     new ProjectArtifact(mavenProject),
137                     () -> mavenProject.getFile().toPath(),
138                     CONSUMER_POM_CLASSIFIER,
139                     "pom",
140                     target,
141                     transformer(session));
142         }
143 
144         private static BiConsumer<Path, Path> transformer(RepositorySystemSession session) {
145             TransformerContext context = (TransformerContext) session.getData().get(TransformerContext.KEY);
146             return (src, dest) -> {
147                 try (InputStream inputStream = transform(src, context)) {
148                     Files.createDirectories(dest.getParent());
149                     Files.copy(inputStream, dest, StandardCopyOption.REPLACE_EXISTING);
150                 } catch (XmlPullParserException | IOException e) {
151                     throw new RuntimeException(e);
152                 }
153             };
154         }
155     }
156 
157     /**
158      * The actual transformation: visible for testing.
159      */
160     static InputStream transform(Path pomFile, TransformerContext context) throws IOException, XmlPullParserException {
161         XmlStreamReader reader = ReaderFactory.newXmlReader(Files.newInputStream(pomFile));
162         XmlPullParser parser = new MXParser(EntityReplacementMap.defaultEntityReplacementMap);
163         parser.setInput(reader);
164         parser = new RawToConsumerPomXMLFilterFactory(new DefaultBuildPomXMLFilterFactory(context, true))
165                 .get(parser, pomFile);
166 
167         return XmlUtils.writeDocument(reader, parser);
168     }
169 }