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