1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.internal.transformation;
20
21 import javax.annotation.PreDestroy;
22 import javax.inject.Inject;
23 import javax.inject.Named;
24 import javax.inject.Singleton;
25 import javax.xml.stream.XMLStreamException;
26
27 import java.io.IOException;
28 import java.io.Writer;
29 import java.lang.reflect.Method;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.List;
36 import java.util.Set;
37 import java.util.concurrent.CopyOnWriteArraySet;
38 import java.util.stream.Collectors;
39
40 import org.apache.maven.api.Repository;
41 import org.apache.maven.api.feature.Features;
42 import org.apache.maven.api.model.DistributionManagement;
43 import org.apache.maven.api.model.Model;
44 import org.apache.maven.api.model.ModelBase;
45 import org.apache.maven.api.model.Profile;
46 import org.apache.maven.model.building.FileModelSource;
47 import org.apache.maven.model.building.ModelBuilder;
48 import org.apache.maven.model.building.ModelBuildingRequest;
49 import org.apache.maven.model.building.ModelCache;
50 import org.apache.maven.model.building.Result;
51 import org.apache.maven.model.building.TransformerContext;
52 import org.apache.maven.model.v4.MavenModelVersion;
53 import org.apache.maven.model.v4.MavenStaxWriter;
54 import org.apache.maven.project.MavenProject;
55 import org.apache.maven.project.artifact.ProjectArtifact;
56 import org.apache.maven.repository.internal.DefaultModelCache;
57 import org.eclipse.aether.RepositorySystemSession;
58 import org.eclipse.aether.artifact.Artifact;
59 import org.eclipse.aether.artifact.DefaultArtifact;
60 import org.eclipse.aether.deployment.DeployRequest;
61 import org.eclipse.aether.installation.InstallRequest;
62
63
64
65
66
67
68 @Singleton
69 @Named("consumer-pom")
70 public final class ConsumerPomArtifactTransformer {
71
72 private static final String BOM_PACKAGING = "bom";
73
74 public static final String POM_PACKAGING = "pom";
75
76 private static final String CONSUMER_POM_CLASSIFIER = "consumer";
77
78 private static final String BUILD_POM_CLASSIFIER = "build";
79
80 private static final String NAMESPACE_FORMAT = "http://maven.apache.org/POM/%s";
81
82 private static final String SCHEMA_LOCATION_FORMAT = "https://maven.apache.org/xsd/maven-%s.xsd";
83
84 private final Set<Path> toDelete = new CopyOnWriteArraySet<>();
85
86 private final ModelBuilder modelBuilder;
87
88 @Inject
89 ConsumerPomArtifactTransformer(ModelBuilder modelBuilder) {
90 this.modelBuilder = modelBuilder;
91 }
92
93 public void injectTransformedArtifacts(MavenProject project, RepositorySystemSession session) throws IOException {
94 if (project.getFile() == null) {
95
96 return;
97 }
98 if (Features.buildConsumer(session.getUserProperties())) {
99 Path buildDir =
100 project.getBuild() != null ? Paths.get(project.getBuild().getDirectory()) : null;
101 if (buildDir != null) {
102 Files.createDirectories(buildDir);
103 }
104 Path consumer = buildDir != null
105 ? Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER + "-", ".pom")
106 : Files.createTempFile(CONSUMER_POM_CLASSIFIER + "-", ".pom");
107 deferDeleteFile(consumer);
108
109 project.addAttachedArtifact(createConsumerPomArtifact(project, consumer, session));
110 } else if (project.getModel().isRoot()) {
111 throw new IllegalStateException(
112 "The use of the root attribute on the model requires the buildconsumer feature to be active");
113 }
114 }
115
116 public ConsumerPomArtifact createConsumerPomArtifact(
117 MavenProject project, Path consumer, RepositorySystemSession session) {
118 return new ConsumerPomArtifact(project, consumer, session);
119 }
120
121 private void deferDeleteFile(Path generatedFile) {
122 toDelete.add(generatedFile.toAbsolutePath());
123 }
124
125 @PreDestroy
126 private void doDeleteFiles() {
127 for (Path file : toDelete) {
128 try {
129 Files.delete(file);
130 } catch (IOException e) {
131
132 }
133 }
134 }
135
136 public InstallRequest remapInstallArtifacts(RepositorySystemSession session, InstallRequest request) {
137 if (Features.buildConsumer(session.getUserProperties()) && consumerPomPresent(request.getArtifacts())) {
138 request.setArtifacts(replacePom(request.getArtifacts()));
139 }
140 return request;
141 }
142
143 public DeployRequest remapDeployArtifacts(RepositorySystemSession session, DeployRequest request) {
144 if (Features.buildConsumer(session.getUserProperties()) && consumerPomPresent(request.getArtifacts())) {
145 request.setArtifacts(replacePom(request.getArtifacts()));
146 }
147 return request;
148 }
149
150 private boolean consumerPomPresent(Collection<Artifact> artifacts) {
151 return artifacts.stream()
152 .anyMatch(a -> "pom".equals(a.getExtension()) && CONSUMER_POM_CLASSIFIER.equals(a.getClassifier()));
153 }
154
155 private Collection<Artifact> replacePom(Collection<Artifact> artifacts) {
156 List<Artifact> consumers = new ArrayList<>();
157 List<Artifact> mains = new ArrayList<>();
158 for (Artifact artifact : artifacts) {
159 if ("pom".equals(artifact.getExtension()) || artifact.getExtension().startsWith("pom.")) {
160 if (CONSUMER_POM_CLASSIFIER.equals(artifact.getClassifier())) {
161 consumers.add(artifact);
162 } else if ("".equals(artifact.getClassifier())) {
163 mains.add(artifact);
164 }
165 }
166 }
167 if (!mains.isEmpty() && !consumers.isEmpty()) {
168 ArrayList<Artifact> result = new ArrayList<>(artifacts);
169 for (Artifact main : mains) {
170 result.remove(main);
171 result.add(new DefaultArtifact(
172 main.getGroupId(),
173 main.getArtifactId(),
174 BUILD_POM_CLASSIFIER,
175 main.getExtension(),
176 main.getVersion(),
177 main.getProperties(),
178 main.getFile()));
179 }
180 for (Artifact consumer : consumers) {
181 result.remove(consumer);
182 result.add(new DefaultArtifact(
183 consumer.getGroupId(),
184 consumer.getArtifactId(),
185 "",
186 consumer.getExtension(),
187 consumer.getVersion(),
188 consumer.getProperties(),
189 consumer.getFile()));
190 }
191 artifacts = result;
192 }
193 return artifacts;
194 }
195
196
197
198
199 class ConsumerPomArtifact extends TransformedArtifact {
200
201 private MavenProject project;
202 private RepositorySystemSession session;
203
204 ConsumerPomArtifact(MavenProject mavenProject, Path target, RepositorySystemSession session) {
205 super(
206 new ProjectArtifact(mavenProject),
207 () -> mavenProject.getFile().toPath(),
208 CONSUMER_POM_CLASSIFIER,
209 "pom",
210 target);
211 this.project = mavenProject;
212 this.session = session;
213 }
214
215 @Override
216 public void transform(Path src, Path dest) {
217 Model model = project.getModel().getDelegate();
218 transform(src, dest, model);
219 }
220
221 void transform(Path src, Path dest, Model model) {
222 Model consumer = null;
223 String version;
224
225 String packaging = model.getPackaging();
226 if (POM_PACKAGING.equals(packaging)) {
227
228 ModelCache cache = DefaultModelCache.newInstance(session);
229 Object modelData = cache.get(new FileModelSource(src.toFile()), "raw");
230 if (modelData != null) {
231 try {
232 Method getModel = modelData.getClass().getMethod("getModel");
233 getModel.setAccessible(true);
234 org.apache.maven.model.Model cachedModel =
235 (org.apache.maven.model.Model) getModel.invoke(modelData);
236 consumer = cachedModel.getDelegate();
237 } catch (Exception e) {
238 throw new RuntimeException(e);
239 }
240 }
241
242 if (consumer == null) {
243 TransformerContext context =
244 (TransformerContext) session.getData().get(TransformerContext.KEY);
245 Result<? extends org.apache.maven.model.Model> result = modelBuilder.buildRawModel(
246 src.toFile(), ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL, false, context);
247 if (result.hasErrors()) {
248 throw new IllegalStateException(
249 "Unable to build POM " + src,
250 result.getProblems().iterator().next().getException());
251 }
252 consumer = result.get().getDelegate();
253 }
254
255
256 consumer = consumer.withRoot(false).withModules(null);
257 if (consumer.getParent() != null) {
258 consumer = consumer.withParent(consumer.getParent().withRelativePath(null));
259 }
260
261 if (!consumer.isPreserveModelVersion()) {
262 consumer = consumer.withPreserveModelVersion(false);
263 version = new MavenModelVersion().getModelVersion(consumer);
264 consumer = consumer.withModelVersion(version);
265 } else {
266 version = consumer.getModelVersion();
267 }
268 } else {
269 Model.Builder builder = prune(
270 Model.newBuilder(model, true)
271 .preserveModelVersion(false)
272 .root(false)
273 .parent(null)
274 .build(null),
275 model);
276 boolean isBom = BOM_PACKAGING.equals(packaging);
277 if (isBom) {
278 builder.packaging(POM_PACKAGING);
279 }
280 builder.profiles(model.getProfiles().stream()
281 .map(p -> prune(Profile.newBuilder(p, true), p).build())
282 .collect(Collectors.toList()));
283 consumer = builder.build();
284 version = new MavenModelVersion().getModelVersion(consumer);
285 consumer = consumer.withModelVersion(version);
286 }
287
288 try {
289 Files.createDirectories(dest.getParent());
290 try (Writer w = Files.newBufferedWriter(dest)) {
291 MavenStaxWriter writer = new MavenStaxWriter();
292 writer.setNamespace(String.format(NAMESPACE_FORMAT, version));
293 writer.setSchemaLocation(String.format(SCHEMA_LOCATION_FORMAT, version));
294 writer.setAddLocationInformation(false);
295 writer.write(w, consumer);
296 }
297 } catch (XMLStreamException | IOException e) {
298 throw new RuntimeException(e);
299 }
300 }
301 }
302
303 private static <T extends ModelBase.Builder> T prune(T builder, ModelBase model) {
304 builder.properties(null).reporting(null);
305 if (model.getDistributionManagement() != null
306 && model.getDistributionManagement().getRelocation() != null) {
307
308 builder.distributionManagement(DistributionManagement.newBuilder()
309 .relocation(model.getDistributionManagement().getRelocation())
310 .build());
311 }
312
313 builder.pluginRepositories(pruneRepositories(model.getPluginRepositories()));
314 builder.repositories(pruneRepositories(model.getRepositories()));
315 return builder;
316 }
317
318 private static List<org.apache.maven.api.model.Repository> pruneRepositories(
319 List<org.apache.maven.api.model.Repository> repositories) {
320 return repositories.stream()
321 .filter(r -> !Repository.CENTRAL_ID.equals(r.getId()))
322 .collect(Collectors.toList());
323 }
324 }