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