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