1 package org.apache.maven.plugin.announcement;
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.FileOutputStream;
24 import java.io.OutputStreamWriter;
25 import java.io.Writer;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.changes.ChangesXML;
34 import org.apache.maven.plugin.jira.JiraXML;
35 import org.apache.maven.plugins.changes.model.Action;
36 import org.apache.maven.plugins.changes.model.Release;
37 import org.apache.maven.project.MavenProject;
38 import org.apache.maven.settings.Settings;
39 import org.apache.velocity.Template;
40 import org.apache.velocity.VelocityContext;
41 import org.apache.velocity.app.VelocityEngine;
42 import org.apache.velocity.context.Context;
43 import org.apache.velocity.exception.ResourceNotFoundException;
44 import org.apache.velocity.exception.VelocityException;
45 import org.codehaus.plexus.util.ReaderFactory;
46 import org.codehaus.plexus.util.StringUtils;
47 import org.codehaus.plexus.velocity.VelocityComponent;
48
49
50
51
52
53
54
55
56
57 public class AnnouncementMojo
58 extends AbstractAnnouncementMojo
59 {
60 private static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
61
62
63
64
65
66
67
68 private File outputDirectory;
69
70
71
72
73
74 private String groupId;
75
76
77
78
79
80 private String artifactId;
81
82
83
84
85
86
87
88 private String version;
89
90
91
92
93
94
95
96 private String url;
97
98
99
100
101
102
103
104 private String packaging;
105
106
107
108
109
110
111
112 private String finalName;
113
114
115
116
117
118
119
120
121 private String urlDownload;
122
123
124
125
126
127
128
129 private File xmlPath;
130
131
132
133
134
135
136
137
138 private String developmentTeam;
139
140
141
142
143
144
145
146 private String introduction;
147
148
149
150
151
152
153
154 private VelocityComponent velocity;
155
156
157
158
159
160
161
162 private String template;
163
164
165
166
167
168
169
170
171
172
173
174 private String templateDirectory;
175
176 private ChangesXML xml;
177
178
179
180
181
182
183
184
185
186
187
188
189 private MavenProject project;
190
191
192
193
194
195
196
197
198 private Settings settings;
199
200
201
202
203
204
205
206 private boolean generateJiraAnnouncement;
207
208
209
210
211
212
213
214
215
216
217
218 private String statusIds;
219
220
221
222
223
224
225
226
227
228
229
230 private String resolutionIds;
231
232
233
234
235
236
237
238
239 private File jiraXML;
240
241
242
243
244
245
246
247
248
249
250
251 private int maxEntries;
252
253
254
255
256
257
258
259 private String jiraUser;
260
261
262
263
264
265
266
267 private String jiraPassword;
268
269
270
271
272
273
274
275 private String templateEncoding;
276
277
278
279
280
281
282
283
284 private boolean jiraMerge;
285
286
287
288
289
290
291
292
293 private Map announceParameters;
294
295
296
297
298
299
300
301
302
303
304 public void execute()
305 throws MojoExecutionException
306 {
307
308 if ( runOnlyAtExecutionRoot && !isThisTheExecutionRoot() )
309 {
310 getLog().info( "Skipping the announcement generation in this project because it's not the Execution Root" );
311 }
312 else
313 {
314 if ( this.jiraMerge )
315 {
316 ChangesXML changesXML = new ChangesXML( getXmlPath(), getLog() );
317 List changesReleases = changesXML.getReleaseList();
318 if ( validateIfIssueManagementComplete() )
319 {
320 List jiraReleases = getJiraReleases();
321 List mergedReleases = mergeReleases( changesReleases, jiraReleases );
322 doGenerate( mergedReleases );
323 }
324 else
325 {
326 throw new MojoExecutionException( "Something is wrong with the Issue Management section. See previous error messages." );
327 }
328 }
329 else
330 {
331 if ( !generateJiraAnnouncement )
332 {
333 if ( getXmlPath().exists() )
334 {
335 setXml( new ChangesXML( getXmlPath(), getLog() ) );
336
337 getLog().info( "Creating announcement file from " + getXmlPath() + "..." );
338
339 doGenerate( getXml().getReleaseList() );
340 }
341 else
342 {
343 getLog().warn( "changes.xml file " + getXmlPath().getAbsolutePath() + " does not exist." );
344 }
345 }
346 else
347 {
348 doJiraGenerate();
349 }
350 }
351 }
352 }
353
354
355
356
357
358
359
360 public void doGenerate( List releases )
361 throws MojoExecutionException
362 {
363 doGenerate( releases, getLatestRelease( releases ) );
364 }
365
366 protected void doGenerate( List releases, Release release )
367 throws MojoExecutionException
368 {
369 try
370 {
371 Context context = new VelocityContext();
372
373 if ( getIntroduction() == null || getIntroduction().equals( "" ) )
374 {
375 setIntroduction( getUrl() );
376 }
377
378 context.put( "releases", releases );
379
380 context.put( "groupId", getGroupId() );
381
382 context.put( "artifactId", getArtifactId() );
383
384 context.put( "version", getVersion() );
385
386 context.put( "packaging", getPackaging() );
387
388 context.put( "url", getUrl() );
389
390 context.put( "release", release );
391
392 context.put( "introduction", getIntroduction() );
393
394 context.put( "developmentTeam", getDevelopmentTeam() );
395
396 context.put( "finalName", getFinalName() );
397
398 context.put( "urlDownload", getUrlDownload() );
399
400 context.put( "project", project );
401
402 if ( announceParameters == null )
403 {
404
405 context.put( "announceParameters", Collections.EMPTY_MAP );
406 }
407 else
408 {
409 context.put( "announceParameters", announceParameters );
410 }
411
412
413 processTemplate( context, getOutputDirectory(), template );
414 }
415 catch ( ResourceNotFoundException rnfe )
416 {
417 throw new MojoExecutionException( "Resource not found.", rnfe );
418 }
419 catch ( VelocityException ve )
420 {
421 throw new MojoExecutionException( ve.toString(), ve );
422 }
423 }
424
425
426
427
428
429
430
431
432
433 public Release getLatestRelease( List releases )
434 throws MojoExecutionException
435 {
436 boolean isFound = false;
437
438 Release release = null;
439
440
441 String pomVersion = getVersion();
442 if ( pomVersion != null && pomVersion.endsWith( SNAPSHOT_SUFFIX ) )
443 {
444 pomVersion = pomVersion.substring( 0, pomVersion.length() - SNAPSHOT_SUFFIX.length() );
445 }
446 getLog().debug( "Found " + releases.size() + " releases." );
447
448 for ( int i = 0; i < releases.size(); i++ )
449 {
450 release = (Release) releases.get( i );
451 if ( getLog().isDebugEnabled() )
452 {
453 getLog().debug( "The release: " + release.getVersion()
454 + " has " + release.getActions().size() + " actions." );
455 }
456
457 if ( release.getVersion() != null && release.getVersion().equals( pomVersion ) )
458 {
459 isFound = true;
460 if ( getLog().isDebugEnabled() )
461 {
462 getLog().debug( "Found the correct release: " + release.getVersion() );
463 logRelease( release );
464 }
465 return release;
466 }
467 }
468
469 release = getRelease( releases, pomVersion );
470 isFound = ( release != null );
471
472 if ( !isFound )
473 {
474 throw new MojoExecutionException( "Couldn't find the release '" + pomVersion
475 + "' among the supplied releases." );
476 }
477 else
478 {
479
480 }
481 return release;
482 }
483
484
485
486
487
488
489
490
491 protected Release getRelease( List releases, String version )
492 {
493 Release release = null;
494 for ( int i = 0; i < releases.size(); i++ )
495 {
496 release = (Release) releases.get( i );
497 if ( getLog().isDebugEnabled() )
498 {
499 getLog().debug( "The release: " + release.getVersion()
500 + " has " + release.getActions().size() + " actions." );
501 }
502
503 if ( release.getVersion() != null && release.getVersion().equals( version ) )
504 {
505 if ( getLog().isDebugEnabled() )
506 {
507 getLog().debug( "Found the correct release: " + release.getVersion() );
508 logRelease( release );
509 }
510 return release;
511 }
512 }
513 return null;
514 }
515
516 private void logRelease( Release release )
517 {
518 Action action;
519 for ( Iterator iterator = release.getActions().iterator(); iterator.hasNext(); )
520 {
521 action = (Action) iterator.next();
522 getLog().debug( "o " + action.getType() );
523 getLog().debug( "issue : " + action.getIssue() );
524 getLog().debug( "action : " + action.getAction() );
525 getLog().debug( "dueTo : " + action.getDueTo() );
526 }
527 }
528
529
530
531
532
533
534
535
536
537 public void processTemplate( Context context, File outputDirectory, String template )
538 throws ResourceNotFoundException, VelocityException, MojoExecutionException
539 {
540 File f;
541
542 try
543 {
544 f = new File( outputDirectory, template );
545
546 if ( !f.getParentFile().exists() )
547 {
548 f.getParentFile().mkdirs();
549 }
550
551 VelocityEngine engine = velocity.getEngine();
552
553 engine.setApplicationAttribute( "baseDirectory", basedir );
554
555 if ( StringUtils.isEmpty( templateEncoding ) )
556 {
557 templateEncoding = ReaderFactory.FILE_ENCODING;
558 getLog().warn(
559 "File encoding has not been set, using platform encoding " + templateEncoding
560 + ", i.e. build is platform dependent!" );
561 }
562
563 Writer writer = new OutputStreamWriter( new FileOutputStream( f ), templateEncoding );
564
565 Template velocityTemplate = engine.getTemplate( templateDirectory + "/" + template, templateEncoding );
566
567 velocityTemplate.merge( context, writer );
568
569 writer.flush();
570
571 writer.close();
572
573 getLog().info( "Created template " + f );
574 }
575
576 catch ( ResourceNotFoundException rnfe )
577 {
578 throw new ResourceNotFoundException( "Template not found. ( " + templateDirectory + "/" + template + " )" );
579 }
580 catch ( VelocityException ve )
581 {
582 throw new VelocityException( ve.toString() );
583 }
584
585 catch ( Exception e )
586 {
587 if ( e.getCause() != null )
588 {
589 getLog().warn( e.getCause() );
590 }
591 throw new MojoExecutionException( e.toString(), e.getCause() );
592 }
593 }
594
595 public void doJiraGenerate()
596 throws MojoExecutionException
597 {
598 if ( validateIfIssueManagementComplete() )
599 {
600 List releases = getJiraReleases();
601
602 getLog().info( "Creating announcement file from JIRA releases..." );
603
604 doGenerate( releases );
605 }
606 else
607 {
608 throw new MojoExecutionException( "Something is wrong with the Issue Management section. See previous error messages." );
609 }
610 }
611
612 protected List getJiraReleases()
613 throws MojoExecutionException
614 {
615 JiraDownloader jiraDownloader = new JiraDownloader();
616
617 File jiraXMLFile = jiraXML;
618
619 jiraDownloader.setLog( getLog() );
620
621 jiraDownloader.setOutput( jiraXMLFile );
622
623 jiraDownloader.setStatusIds( statusIds );
624
625 jiraDownloader.setResolutionIds( resolutionIds );
626
627 jiraDownloader.setMavenProject( project );
628
629 jiraDownloader.setSettings( settings );
630
631 jiraDownloader.setNbEntries( maxEntries );
632
633 jiraDownloader.setJiraUser( jiraUser );
634
635 jiraDownloader.setJiraPassword( jiraPassword );
636
637 try
638 {
639 jiraDownloader.doExecute();
640
641 if ( jiraXMLFile.exists() )
642 {
643 JiraXML jiraParser = new JiraXML( jiraXMLFile );
644
645 List issues = jiraParser.getIssueList();
646
647 return JiraXML.getReleases( issues );
648 }
649 else
650 {
651 getLog().warn( "jira file " + jiraXMLFile.getPath() + " doesn't exists " );
652 }
653 return Collections.EMPTY_LIST;
654 }
655 catch ( Exception e )
656 {
657 throw new MojoExecutionException( "Failed to extract JIRA issues from the downloaded file", e );
658 }
659 }
660
661
662
663
664
665
666
667
668
669
670 protected List mergeReleases( final List firstReleases, final List secondReleases )
671 {
672 if ( firstReleases == null && secondReleases == null )
673 {
674 return Collections.EMPTY_LIST;
675 }
676 if ( firstReleases == null )
677 {
678 return secondReleases;
679 }
680 if ( secondReleases == null )
681 {
682 return firstReleases;
683 }
684
685 List mergedReleases = new ArrayList();
686
687
688
689
690 for ( Iterator iterator = firstReleases.iterator(); iterator.hasNext(); )
691 {
692 Release firstRelease = (Release) iterator.next();
693 Release secondRelease = getRelease( secondReleases, firstRelease.getVersion() );
694 if ( secondRelease != null )
695 {
696 if ( secondRelease.getActions() != null )
697 {
698 firstRelease.getActions().addAll( secondRelease.getActions() );
699 }
700 }
701 mergedReleases.add(firstRelease);
702 }
703
704
705 for ( Iterator iterator = secondReleases.iterator(); iterator.hasNext(); )
706 {
707 Release secondRelease = (Release) iterator.next();
708 Release mergedRelease = getRelease( mergedReleases, secondRelease.getVersion() );
709 if ( mergedRelease == null )
710 {
711 mergedReleases.add(secondRelease);
712 }
713 }
714 return mergedReleases;
715 }
716
717
718
719
720 private boolean validateIfIssueManagementComplete()
721 {
722 if ( project.getIssueManagement() == null )
723 {
724 getLog().error( "No Issue Management set. No JIRA announcement will be made." );
725
726 return false;
727 }
728 else if ( ( project.getIssueManagement().getUrl() == null )
729 || ( project.getIssueManagement().getUrl().trim().equals( "" ) ) )
730 {
731 getLog().error( "No URL set in Issue Management. No JIRA announcement will be made." );
732
733 return false;
734 }
735 else if ( ( project.getIssueManagement().getSystem() != null )
736 && !( project.getIssueManagement().getSystem().equalsIgnoreCase( "jira" ) ) )
737 {
738 getLog().error( "No JIRA Issue Management system configured. No JIRA announcement will be made." );
739
740 return false;
741 }
742 return true;
743 }
744
745
746
747
748
749 public File getXmlPath()
750 {
751 return xmlPath;
752 }
753
754 public void setXmlPath( File xmlPath )
755 {
756 this.xmlPath = xmlPath;
757 }
758
759 public File getOutputDirectory()
760 {
761 return outputDirectory;
762 }
763
764 public void setOutputDirectory( File outputDirectory )
765 {
766 this.outputDirectory = outputDirectory;
767 }
768
769 public String getGroupId()
770 {
771 return groupId;
772 }
773
774 public void setGroupId( String groupId )
775 {
776 this.groupId = groupId;
777 }
778
779 public String getArtifactId()
780 {
781 return artifactId;
782 }
783
784 public void setArtifactId( String artifactId )
785 {
786 this.artifactId = artifactId;
787 }
788
789 public String getVersion()
790 {
791 return version;
792 }
793
794 public void setVersion( String version )
795 {
796 this.version = version;
797 }
798
799 public String getUrl()
800 {
801 return url;
802 }
803
804 public void setUrl( String url )
805 {
806 this.url = url;
807 }
808
809 public ChangesXML getXml()
810 {
811 return xml;
812 }
813
814 public void setXml( ChangesXML xml )
815 {
816 this.xml = xml;
817 }
818
819 public String getPackaging()
820 {
821 return packaging;
822 }
823
824 public void setPackaging( String packaging )
825 {
826 this.packaging = packaging;
827 }
828
829 public String getDevelopmentTeam()
830 {
831 return developmentTeam;
832 }
833
834 public void setDevelopmentTeam( String developmentTeam )
835 {
836 this.developmentTeam = developmentTeam;
837 }
838
839 public String getIntroduction()
840 {
841 return introduction;
842 }
843
844 public void setIntroduction( String introduction )
845 {
846 this.introduction = introduction;
847 }
848
849 public VelocityComponent getVelocity()
850 {
851 return velocity;
852 }
853
854 public void setVelocity( VelocityComponent velocity )
855 {
856 this.velocity = velocity;
857 }
858
859 public String getFinalName()
860 {
861 return finalName;
862 }
863
864 public void setFinalName( String finalName )
865 {
866 this.finalName = finalName;
867 }
868
869 public String getUrlDownload()
870 {
871 return urlDownload;
872 }
873
874 public void setUrlDownload( String urlDownload )
875 {
876 this.urlDownload = urlDownload;
877 }
878 }