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.impl;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  import javax.xml.stream.XMLStreamException;
25  
26  import java.io.IOException;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.nio.file.Paths;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.List;
33  import java.util.Set;
34  import java.util.concurrent.CopyOnWriteArraySet;
35  
36  import org.apache.maven.api.feature.Features;
37  import org.apache.maven.api.model.Model;
38  import org.apache.maven.api.services.ModelBuilderException;
39  import org.apache.maven.project.MavenProject;
40  import org.apache.maven.project.artifact.ProjectArtifact;
41  import org.eclipse.aether.RepositorySystemSession;
42  import org.eclipse.aether.artifact.Artifact;
43  import org.eclipse.aether.artifact.DefaultArtifact;
44  import org.eclipse.aether.deployment.DeployRequest;
45  import org.eclipse.aether.installation.InstallRequest;
46  import org.eclipse.sisu.PreDestroy;
47  
48  /**
49   * Consumer POM transformer.
50   *
51   * @since TBD
52   */
53  @Singleton
54  @Named
55  class ConsumerPomArtifactTransformer extends TransformerSupport {
56      private static final String CONSUMER_POM_CLASSIFIER = "consumer";
57  
58      private static final String BUILD_POM_CLASSIFIER = "build";
59  
60      private final Set<Path> toDelete = new CopyOnWriteArraySet<>();
61  
62      private final PomBuilder builder;
63  
64      @Inject
65      ConsumerPomArtifactTransformer(PomBuilder builder) {
66          this.builder = builder;
67      }
68  
69      @SuppressWarnings("deprecation")
70      @Override
71      public void injectTransformedArtifacts(RepositorySystemSession session, MavenProject project) throws IOException {
72          if (project.getFile() == null) {
73              // If there is no build POM there is no reason to inject artifacts for the consumer POM.
74              return;
75          }
76          if (Features.consumerPom(session.getConfigProperties())) {
77              Path buildDir =
78                      project.getBuild() != null ? Paths.get(project.getBuild().getDirectory()) : null;
79              if (buildDir != null) {
80                  Files.createDirectories(buildDir);
81              }
82              Path consumer = buildDir != null
83                      ? Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER + "-", ".pom")
84                      : Files.createTempFile(CONSUMER_POM_CLASSIFIER + "-", ".pom");
85              deferDeleteFile(consumer);
86  
87              project.addAttachedArtifact(createConsumerPomArtifact(project, consumer, session));
88          } else if (project.getModel().getDelegate().isRoot()) {
89              throw new IllegalStateException(
90                      "The use of the root attribute on the model requires the buildconsumer feature to be active");
91          }
92      }
93  
94      TransformedArtifact createConsumerPomArtifact(
95              MavenProject project, Path consumer, RepositorySystemSession session) {
96          return new TransformedArtifact(
97                  this,
98                  project,
99                  consumer,
100                 session,
101                 new ProjectArtifact(project),
102                 () -> project.getFile().toPath(),
103                 CONSUMER_POM_CLASSIFIER,
104                 "pom");
105     }
106 
107     @Override
108     public void transform(MavenProject project, RepositorySystemSession session, Path src, Path tgt)
109             throws ModelBuilderException, XMLStreamException, IOException {
110         Model model = builder.build(session, project, src);
111         write(model, tgt);
112     }
113 
114     private void deferDeleteFile(Path generatedFile) {
115         toDelete.add(generatedFile.toAbsolutePath());
116     }
117 
118     @PreDestroy
119     private void doDeleteFiles() {
120         for (Path file : toDelete) {
121             try {
122                 Files.delete(file);
123             } catch (IOException e) {
124                 // ignore, we did our best...
125             }
126         }
127     }
128 
129     @Override
130     public InstallRequest remapInstallArtifacts(RepositorySystemSession session, InstallRequest request) {
131         if (consumerPomPresent(request.getArtifacts())) {
132             request.setArtifacts(replacePom(request.getArtifacts()));
133         }
134         return request;
135     }
136 
137     @Override
138     public DeployRequest remapDeployArtifacts(RepositorySystemSession session, DeployRequest request) {
139         if (consumerPomPresent(request.getArtifacts())) {
140             request.setArtifacts(replacePom(request.getArtifacts()));
141         }
142         return request;
143     }
144 
145     private boolean consumerPomPresent(Collection<Artifact> artifacts) {
146         return artifacts.stream()
147                 .anyMatch(a -> "pom".equals(a.getExtension()) && CONSUMER_POM_CLASSIFIER.equals(a.getClassifier()));
148     }
149 
150     private Collection<Artifact> replacePom(Collection<Artifact> artifacts) {
151         List<Artifact> consumers = new ArrayList<>();
152         List<Artifact> mains = new ArrayList<>();
153         for (Artifact artifact : artifacts) {
154             if ("pom".equals(artifact.getExtension()) || artifact.getExtension().startsWith("pom.")) {
155                 if (CONSUMER_POM_CLASSIFIER.equals(artifact.getClassifier())) {
156                     consumers.add(artifact);
157                 } else if ("".equals(artifact.getClassifier())) {
158                     mains.add(artifact);
159                 }
160             }
161         }
162         if (!mains.isEmpty() && !consumers.isEmpty()) {
163             ArrayList<Artifact> result = new ArrayList<>(artifacts);
164             for (Artifact main : mains) {
165                 result.remove(main);
166                 result.add(new DefaultArtifact(
167                         main.getGroupId(),
168                         main.getArtifactId(),
169                         BUILD_POM_CLASSIFIER,
170                         main.getExtension(),
171                         main.getVersion(),
172                         main.getProperties(),
173                         main.getPath()));
174             }
175             for (Artifact consumer : consumers) {
176                 result.remove(consumer);
177                 result.add(new DefaultArtifact(
178                         consumer.getGroupId(),
179                         consumer.getArtifactId(),
180                         "",
181                         consumer.getExtension(),
182                         consumer.getVersion(),
183                         consumer.getProperties(),
184                         consumer.getPath()));
185             }
186             artifacts = result;
187         }
188         return artifacts;
189     }
190 }