1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.gpg;
20
21 import java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.Reader;
25 import java.io.Writer;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.List;
29
30 import org.apache.maven.artifact.Artifact;
31 import org.apache.maven.artifact.factory.ArtifactFactory;
32 import org.apache.maven.artifact.metadata.ArtifactMetadata;
33 import org.apache.maven.artifact.repository.ArtifactRepository;
34 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
35 import org.apache.maven.artifact.repository.MavenArtifactRepository;
36 import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
37 import org.apache.maven.execution.MavenSession;
38 import org.apache.maven.model.Model;
39 import org.apache.maven.model.Parent;
40 import org.apache.maven.model.building.DefaultModelBuildingRequest;
41 import org.apache.maven.model.building.ModelBuildingRequest;
42 import org.apache.maven.model.building.ModelProblem;
43 import org.apache.maven.model.building.ModelProblemCollector;
44 import org.apache.maven.model.building.ModelProblemCollectorRequest;
45 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
46 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
47 import org.apache.maven.model.validation.ModelValidator;
48 import org.apache.maven.plugin.MojoExecutionException;
49 import org.apache.maven.plugin.MojoFailureException;
50 import org.apache.maven.plugins.annotations.Component;
51 import org.apache.maven.plugins.annotations.Mojo;
52 import org.apache.maven.plugins.annotations.Parameter;
53 import org.apache.maven.project.MavenProject;
54 import org.apache.maven.project.MavenProjectHelper;
55 import org.apache.maven.project.ProjectBuildingRequest;
56 import org.apache.maven.project.artifact.ProjectArtifactMetadata;
57 import org.apache.maven.shared.transfer.artifact.deploy.ArtifactDeployer;
58 import org.apache.maven.shared.transfer.artifact.deploy.ArtifactDeployerException;
59 import org.codehaus.plexus.util.FileUtils;
60 import org.codehaus.plexus.util.ReaderFactory;
61 import org.codehaus.plexus.util.StringUtils;
62 import org.codehaus.plexus.util.WriterFactory;
63 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
64
65
66
67
68
69
70
71 @Mojo(name = "sign-and-deploy-file", requiresProject = false, threadSafe = true)
72 public class SignAndDeployFileMojo extends AbstractGpgMojo {
73
74
75
76
77 @Parameter(property = "gpg.ascDirectory")
78 private File ascDirectory;
79
80
81
82
83 @Parameter(defaultValue = "${settings.offline}", readonly = true)
84 private boolean offline;
85
86
87
88
89 @Parameter(property = "groupId")
90 private String groupId;
91
92
93
94
95 @Parameter(property = "artifactId")
96 private String artifactId;
97
98
99
100
101 @Parameter(property = "version")
102 private String version;
103
104
105
106
107
108 @Parameter(property = "packaging")
109 private String packaging;
110
111
112
113
114 @Parameter(property = "classifier")
115 private String classifier;
116
117
118
119
120 @Parameter(property = "generatePom.description")
121 private String description;
122
123
124
125
126 @Parameter(property = "file", required = true)
127 private File file;
128
129
130
131
132 @Parameter(property = "pomFile")
133 private File pomFile;
134
135
136
137
138 @Parameter(property = "generatePom", defaultValue = "true")
139 private boolean generatePom;
140
141
142
143
144
145 @Parameter(property = "url", required = true)
146 private String url;
147
148
149
150
151
152 @Parameter(property = "repositoryId", defaultValue = "remote-repository", required = true)
153 private String repositoryId;
154
155
156
157
158 @Parameter(property = "repositoryLayout", defaultValue = "default")
159 private String repositoryLayout;
160
161
162
163 @Component
164 private ArtifactDeployer deployer;
165
166
167
168 @Parameter(defaultValue = "${localRepository}", required = true, readonly = true)
169 private ArtifactRepository localRepository;
170
171
172
173
174 @Component
175 private ArtifactFactory artifactFactory;
176
177
178
179
180 @Component
181 private ModelValidator modelValidator;
182
183
184
185
186
187
188 @Parameter(defaultValue = "${project}", readonly = true, required = true)
189 private MavenProject project;
190
191
192
193
194 @Parameter(defaultValue = "${session}", readonly = true, required = true)
195 private MavenSession session;
196
197
198
199
200
201
202 @Component
203 private MavenProjectHelper projectHelper;
204
205
206
207
208
209
210 @Parameter(property = "javadoc")
211 private File javadoc;
212
213
214
215
216
217
218 @Parameter(property = "sources")
219 private File sources;
220
221
222
223
224
225
226
227 @Parameter(property = "retryFailedDeploymentCount", defaultValue = "1")
228 private int retryFailedDeploymentCount;
229
230
231
232
233
234
235 @Parameter(property = "updateReleaseInfo", defaultValue = "false")
236 protected boolean updateReleaseInfo;
237
238
239
240
241
242 @Parameter(property = "types")
243 private String types;
244
245
246
247
248
249 @Parameter(property = "classifiers")
250 private String classifiers;
251
252
253
254
255
256 @Parameter(property = "files")
257 private String files;
258
259 private void initProperties() throws MojoExecutionException {
260
261 if (pomFile != null) {
262 generatePom = false;
263
264 Model model = readModel(pomFile);
265
266 processModel(model);
267 }
268
269 if (packaging == null && file != null) {
270 packaging = FileUtils.getExtension(file.getName());
271 }
272 }
273
274 @Override
275 public void execute() throws MojoExecutionException, MojoFailureException {
276 AbstractGpgSigner signer = newSigner(null);
277 signer.setOutputDirectory(ascDirectory);
278 signer.setBaseDirectory(new File("").getAbsoluteFile());
279
280 if (offline) {
281 throw new MojoFailureException("Cannot deploy artifacts when Maven is in offline mode");
282 }
283
284 initProperties();
285
286 validateArtifactInformation();
287
288 if (!file.exists()) {
289 throw new MojoFailureException(file.getPath() + " not found.");
290 }
291
292 ArtifactRepository deploymentRepository = createDeploymentArtifactRepository(repositoryId, url);
293
294 if (StringUtils.isEmpty(deploymentRepository.getProtocol())) {
295 throw new MojoFailureException("No transfer protocol found.");
296 }
297
298 Artifact artifact =
299 artifactFactory.createArtifactWithClassifier(groupId, artifactId, version, packaging, classifier);
300
301 if (file.equals(getLocalRepoFile(artifact))) {
302 throw new MojoFailureException("Cannot deploy artifact from the local repository: " + file);
303 }
304 artifact.setFile(file);
305
306 File fileSig = signer.generateSignatureForArtifact(file);
307 ArtifactMetadata metadata = new AscArtifactMetadata(artifact, fileSig, false);
308 artifact.addMetadata(metadata);
309
310 if (!"pom".equals(packaging)) {
311 if (pomFile == null && generatePom) {
312 pomFile = generatePomFile();
313 }
314 if (pomFile != null) {
315 metadata = new ProjectArtifactMetadata(artifact, pomFile);
316 artifact.addMetadata(metadata);
317
318 fileSig = signer.generateSignatureForArtifact(pomFile);
319 metadata = new AscArtifactMetadata(artifact, fileSig, true);
320 artifact.addMetadata(metadata);
321 }
322 }
323
324 if (updateReleaseInfo) {
325 artifact.setRelease(true);
326 }
327
328 project.setArtifact(artifact);
329
330 try {
331 deploy(artifact, deploymentRepository);
332 } catch (ArtifactDeployerException e) {
333 throw new MojoExecutionException(e.getMessage(), e);
334 }
335
336 if (sources != null) {
337 projectHelper.attachArtifact(project, "jar", "sources", sources);
338 }
339
340 if (javadoc != null) {
341 projectHelper.attachArtifact(project, "jar", "javadoc", javadoc);
342 }
343
344 if (files != null) {
345 if (types == null) {
346 throw new MojoExecutionException("You must specify 'types' if you specify 'files'");
347 }
348 if (classifiers == null) {
349 throw new MojoExecutionException("You must specify 'classifiers' if you specify 'files'");
350 }
351 int filesLength = StringUtils.countMatches(files, ",");
352 int typesLength = StringUtils.countMatches(types, ",");
353 int classifiersLength = StringUtils.countMatches(classifiers, ",");
354 if (typesLength != filesLength) {
355 throw new MojoExecutionException("You must specify the same number of entries in 'files' and "
356 + "'types' (respectively " + filesLength + " and " + typesLength + " entries )");
357 }
358 if (classifiersLength != filesLength) {
359 throw new MojoExecutionException("You must specify the same number of entries in 'files' and "
360 + "'classifiers' (respectively " + filesLength + " and " + classifiersLength + " entries )");
361 }
362 int fi = 0;
363 int ti = 0;
364 int ci = 0;
365 for (int i = 0; i <= filesLength; i++) {
366 int nfi = files.indexOf(',', fi);
367 if (nfi == -1) {
368 nfi = files.length();
369 }
370 int nti = types.indexOf(',', ti);
371 if (nti == -1) {
372 nti = types.length();
373 }
374 int nci = classifiers.indexOf(',', ci);
375 if (nci == -1) {
376 nci = classifiers.length();
377 }
378 File file = new File(files.substring(fi, nfi));
379 if (!file.isFile()) {
380
381 file = new File(project.getBasedir(), files.substring(fi, nfi));
382 }
383 if (file.isFile()) {
384 if (StringUtils.isWhitespace(classifiers.substring(ci, nci))) {
385 projectHelper.attachArtifact(
386 project, types.substring(ti, nti).trim(), file);
387 } else {
388 projectHelper.attachArtifact(
389 project,
390 types.substring(ti, nti).trim(),
391 classifiers.substring(ci, nci).trim(),
392 file);
393 }
394 } else {
395 throw new MojoExecutionException("Specified side artifact " + file + " does not exist");
396 }
397 fi = nfi + 1;
398 ti = nti + 1;
399 ci = nci + 1;
400 }
401 } else {
402 if (types != null) {
403 throw new MojoExecutionException("You must specify 'files' if you specify 'types'");
404 }
405 if (classifiers != null) {
406 throw new MojoExecutionException("You must specify 'files' if you specify 'classifiers'");
407 }
408 }
409
410 for (Artifact attached : project.getAttachedArtifacts()) {
411 fileSig = signer.generateSignatureForArtifact(attached.getFile());
412 attached = new AttachedSignedArtifact(attached, new AscArtifactMetadata(attached, fileSig, false));
413 try {
414 deploy(attached, deploymentRepository);
415 } catch (ArtifactDeployerException e) {
416 throw new MojoExecutionException(
417 "Error deploying attached artifact " + attached.getFile() + ": " + e.getMessage(), e);
418 }
419 }
420 }
421
422
423
424
425
426
427
428
429 private File getLocalRepoFile(Artifact artifact) {
430 String path = localRepository.pathOf(artifact);
431 return new File(localRepository.getBasedir(), path);
432 }
433
434
435
436
437
438
439 private void processModel(Model model) {
440 Parent parent = model.getParent();
441
442 if (this.groupId == null) {
443 this.groupId = model.getGroupId();
444 if (this.groupId == null && parent != null) {
445 this.groupId = parent.getGroupId();
446 }
447 }
448 if (this.artifactId == null) {
449 this.artifactId = model.getArtifactId();
450 }
451 if (this.version == null) {
452 this.version = model.getVersion();
453 if (this.version == null && parent != null) {
454 this.version = parent.getVersion();
455 }
456 }
457 if (this.packaging == null) {
458 this.packaging = model.getPackaging();
459 }
460 }
461
462
463
464
465
466
467
468
469 private Model readModel(File pomFile) throws MojoExecutionException {
470 try (Reader reader = ReaderFactory.newXmlReader(pomFile)) {
471 final Model model = new MavenXpp3Reader().read(reader);
472 return model;
473 } catch (FileNotFoundException e) {
474 throw new MojoExecutionException("POM not found " + pomFile, e);
475 } catch (IOException e) {
476 throw new MojoExecutionException("Error reading POM " + pomFile, e);
477 } catch (XmlPullParserException e) {
478 throw new MojoExecutionException("Error parsing POM " + pomFile, e);
479 }
480 }
481
482
483
484
485
486
487
488 private File generatePomFile() throws MojoExecutionException {
489 Model model = generateModel();
490
491 try {
492 File tempFile = File.createTempFile("mvndeploy", ".pom");
493 tempFile.deleteOnExit();
494
495 try (Writer fw = WriterFactory.newXmlWriter(tempFile)) {
496 new MavenXpp3Writer().write(fw, model);
497 }
498
499 return tempFile;
500 } catch (IOException e) {
501 throw new MojoExecutionException("Error writing temporary pom file: " + e.getMessage(), e);
502 }
503 }
504
505
506
507
508
509
510 private void validateArtifactInformation() throws MojoFailureException {
511 Model model = generateModel();
512
513 ModelBuildingRequest request =
514 new DefaultModelBuildingRequest().setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0);
515
516 List<String> result = new ArrayList<>();
517
518 SimpleModelProblemCollector problemCollector = new SimpleModelProblemCollector(result);
519
520 modelValidator.validateEffectiveModel(model, request, problemCollector);
521
522 if (!result.isEmpty()) {
523 StringBuilder msg = new StringBuilder("The artifact information is incomplete or not valid:\n");
524 for (String e : result) {
525 msg.append(" - " + e + '\n');
526 }
527 throw new MojoFailureException(msg.toString());
528 }
529 }
530
531
532
533
534
535
536 private Model generateModel() {
537 Model model = new Model();
538
539 model.setModelVersion("4.0.0");
540
541 model.setGroupId(groupId);
542 model.setArtifactId(artifactId);
543 model.setVersion(version);
544 model.setPackaging(packaging);
545
546 model.setDescription(description);
547
548 return model;
549 }
550
551
552
553
554
555
556
557
558 protected void deploy(Artifact artifact, ArtifactRepository deploymentRepository) throws ArtifactDeployerException {
559 final ProjectBuildingRequest buildingRequest = session.getProjectBuildingRequest();
560
561 int retryFailedDeploymentCount = Math.max(1, Math.min(10, this.retryFailedDeploymentCount));
562 ArtifactDeployerException exception = null;
563 for (int count = 0; count < retryFailedDeploymentCount; count++) {
564 try {
565 if (count > 0) {
566
567 getLog().info("Retrying deployment attempt " + (count + 1) + " of " + retryFailedDeploymentCount);
568
569 }
570 deployer.deploy(buildingRequest, deploymentRepository, Collections.singletonList(artifact));
571
572 for (Object o : artifact.getMetadataList()) {
573 ArtifactMetadata metadata = (ArtifactMetadata) o;
574 getLog().info("Metadata[" + metadata.getKey() + "].filename = " + metadata.getRemoteFilename());
575 }
576 exception = null;
577 break;
578 } catch (ArtifactDeployerException e) {
579 if (count + 1 < retryFailedDeploymentCount) {
580 getLog().warn("Encountered issue during deployment: " + e.getLocalizedMessage());
581 getLog().debug(e);
582 }
583 if (exception == null) {
584 exception = e;
585 }
586 }
587 }
588 if (exception != null) {
589 throw exception;
590 }
591 }
592
593 protected ArtifactRepository createDeploymentArtifactRepository(String id, String url) {
594 return new MavenArtifactRepository(
595 id, url, new DefaultRepositoryLayout(), new ArtifactRepositoryPolicy(), new ArtifactRepositoryPolicy());
596 }
597
598 private static class SimpleModelProblemCollector implements ModelProblemCollector {
599
600 private final List<String> result;
601
602 SimpleModelProblemCollector(List<String> result) {
603 this.result = result;
604 }
605
606 public void add(ModelProblemCollectorRequest req) {
607 if (!ModelProblem.Severity.WARNING.equals(req.getSeverity())) {
608 result.add(req.getMessage());
609 }
610 }
611 }
612 }