View Javadoc
1   package org.apache.maven.plugin.announcement;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.plugin.MojoExecutionException;
23  import org.apache.maven.plugin.changes.ChangesXML;
24  import org.apache.maven.plugin.changes.IssueAdapter;
25  import org.apache.maven.plugin.changes.ProjectUtils;
26  import org.apache.maven.plugin.changes.ReleaseUtils;
27  import org.apache.maven.plugin.github.GitHubDownloader;
28  import org.apache.maven.plugin.github.GitHubIssueManagementSystem;
29  import org.apache.maven.plugin.issues.Issue;
30  import org.apache.maven.plugin.issues.IssueManagementSystem;
31  import org.apache.maven.plugin.issues.IssueUtils;
32  import org.apache.maven.plugin.jira.AbstractJiraDownloader;
33  import org.apache.maven.plugin.jira.AdaptiveJiraDownloader;
34  import org.apache.maven.plugin.jira.JIRAIssueManagmentSystem;
35  import org.apache.maven.plugin.trac.TracDownloader;
36  import org.apache.maven.plugin.trac.TracIssueManagmentSystem;
37  import org.apache.maven.plugins.annotations.Component;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.plugins.annotations.ResolutionScope;
41  import org.apache.maven.plugins.changes.model.Release;
42  import org.apache.maven.project.MavenProject;
43  import org.apache.maven.settings.Settings;
44  import org.apache.velocity.Template;
45  import org.apache.velocity.app.VelocityEngine;
46  import org.apache.velocity.context.Context;
47  import org.apache.velocity.exception.ResourceNotFoundException;
48  import org.apache.velocity.exception.VelocityException;
49  import org.apache.velocity.tools.ToolManager;
50  import org.codehaus.plexus.util.ReaderFactory;
51  import org.codehaus.plexus.util.StringUtils;
52  import org.codehaus.plexus.velocity.VelocityComponent;
53  
54  import java.io.File;
55  import java.io.FileOutputStream;
56  import java.io.OutputStreamWriter;
57  import java.io.Writer;
58  import java.util.ArrayList;
59  import java.util.Collections;
60  import java.util.List;
61  import java.util.Map;
62  
63  /**
64   * Goal which generate an announcement from the announcement template.
65   *
66   * @author aramirez@exist.com
67   * @version $Id: AnnouncementMojo.java 1626681 2014-09-22 07:34:09Z mfriedenhagen $
68   * @since 2.0-beta-2
69   */
70  @Mojo( name = "announcement-generate", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true )
71  public class AnnouncementMojo
72      extends AbstractAnnouncementMojo
73  {
74      private static final String CHANGES_XML = "changes.xml";
75  
76      private static final String JIRA = "JIRA";
77  
78      private static final String TRAC = "Trac";
79  
80      private static final String GIT_HUB = "GitHub";
81  
82      /**
83       * The name of the file which will contain the generated announcement. If
84       * no value is specified the plugin will use the name of the template.
85       *
86       * @since 2.4
87       */
88      @Parameter( property = "changes.announcementFile" )
89      private String announcementFile;
90  
91      /**
92       * Map of custom parameters for the announcement.
93       * This Map will be passed to the template.
94       *
95       * @since 2.1
96       */
97      @Parameter
98      private Map announceParameters;
99  
100     /**
101      */
102     @Parameter( property = "project.artifactId", readonly = true )
103     private String artifactId;
104 
105     /**
106      * Name of the team that develops the artifact.
107      * This parameter will be passed to the template.
108      */
109     @Parameter( property = "changes.developmentTeam", defaultValue = "${project.name} team", required = true )
110     private String developmentTeam;
111 
112     /**
113      * The name of the artifact to be used in the announcement.
114      */
115     @Parameter( property = "changes.finalName", defaultValue = "${project.build.finalName}", required = true )
116     private String finalName;
117 
118     /**
119      */
120     @Parameter( property = "project.groupId", readonly = true )
121     private String groupId;
122 
123     /**
124      * Short description or introduction of the released artifact.
125      * This parameter will be passed to the template.
126      */
127     @Parameter( defaultValue = "${project.description}" )
128     private String introduction;
129 
130     /**
131      * A list of issue management systems to fetch releases from. This parameter
132      * replaces the parameters <code>generateJiraAnnouncement</code> and
133      * <code>jiraMerge</code>.
134      * <p>
135      * Valid values are: <code>changes.xml</code> and <code>JIRA</code>.
136      * </p>
137      * <strong>Note:</strong> Only one issue management system that is
138      * configured in &lt;project&gt;/&lt;issueManagement&gt; can be used. This
139      * currently means that you can combine a changes.xml file with one other
140      * issue management system.
141      *
142      * @since 2.4
143      */
144     @Parameter
145     private List<String> issueManagementSystems;
146 
147     /**
148      * Maps issues types to action types for grouping issues in announcements.
149      * If issue types are not defined for a action type then the default issue type
150      * will be applied.
151      * <p>
152      * Valid action types: <code>add</code>, <code>fix</code> and <code>update</code>.
153      * </p>
154      *
155      * @since 2.6
156      */
157     @Parameter
158     private Map<String, String> issueTypes;
159 
160     /**
161      * Directory where the announcement file will be generated.
162      *
163      * @since 2.10
164      */
165     @Parameter( defaultValue = "${project.build.directory}/announcement", required = true )
166     private File announcementDirectory;
167 
168     /**
169      * Directory where the announcement file will be generated.
170      *
171      * @deprecated Starting with version 2.10 this parameter is no longer used. You must use {@link #announcementDirectory} instead.
172      */
173     @Parameter
174     private File outputDirectory;
175 
176     /**
177      * Packaging structure for the artifact.
178      */
179     @Parameter( property = "project.packaging", readonly = true )
180     private String packaging;
181 
182     /**
183      * The Maven Project.
184      */
185     @Parameter( defaultValue = "${project}", readonly = true, required = true )
186     private MavenProject project;
187 
188     /**
189      * The Velocity template used to format the announcement.
190      */
191     @Parameter( property = "changes.template", defaultValue = "announcement.vm", required = true )
192     private String template;
193 
194     /**
195      * Directory that contains the template.
196      * <p>
197      * <b>Note:</b> This directory must be a subdirectory of
198      * <code>/src/main/resources/ or current project base directory</code>.
199      * </p>
200      */
201     @Parameter( property = "changes.templateDirectory", defaultValue = "org/apache/maven/plugin/announcement",
202                 required = true )
203     private String templateDirectory;
204 
205     /**
206      * The template encoding.
207      *
208      * @since 2.1
209      */
210     @Parameter( property = "changes.templateEncoding", defaultValue = "${project.build.sourceEncoding}" )
211     private String templateEncoding;
212 
213     /**
214      * Use the JIRA query language instead of the JIRA query based on HTTP parameters.
215      * From JIRA 5.1 and up only JQL is supported. JIRA 4.4 supports both JQL and URL parameter based queries.
216      * From 5.1.1 this is obsolete, since REST queries only use JQL.
217      *
218      * @since 2.10
219      */
220     @Parameter( property = "changes.useJql", defaultValue = "false" )
221     private boolean useJql;
222 
223     /**
224      * Distribution URL of the artifact.
225      * This parameter will be passed to the template.
226      */
227     @Parameter( property = "project.url" )
228     private String url;
229 
230     /**
231      * URL where the artifact can be downloaded. If not specified,
232      * no URL is used.
233      * This parameter will be passed to the template.
234      */
235     @Parameter
236     private String urlDownload;
237 
238     /**
239      * Velocity Component.
240      */
241     @Component( role = VelocityComponent.class, hint = "maven-changes-plugin" )
242     private VelocityComponent velocity;
243 
244     /**
245      * Version of the artifact.
246      */
247     @Parameter( property = "changes.version", defaultValue = "${project.version}", required = true )
248     private String version;
249 
250     /**
251      * The path of the changes.xml file.
252      *
253      * @parameter expression="${basedir}/src/changes/changes.xml"
254      * @required
255      */
256     @Parameter( defaultValue = "${basedir}/src/changes/changes.xml" )
257     private File xmlPath;
258 
259     //=======================================//
260     //  JIRA-Announcement Needed Parameters  //
261     //=======================================//
262 
263     /**
264      * Defines the filter parameters to restrict which issues are retrieved
265      * from JIRA. The filter parameter uses the same format of url
266      * parameters that is used in a JIRA search.
267      *
268      * @since 2.4
269      */
270     @Parameter( defaultValue = "" )
271     private String filter;
272 
273     /**
274      * Flag to determine if the plugin will generate a JIRA announcement.
275      *
276      * @deprecated Since version 2.4 this parameter has been deprecated. Please use the issueManagementSystems parameter instead.
277      */
278     @Parameter( property = "generateJiraAnnouncement", defaultValue = "false", required = true )
279     private boolean generateJiraAnnouncement;
280 
281     /**
282      * If releases from JIRA should be merged with the releases from a
283      * changes.xml file.
284      *
285      * @since 2.1
286      * @deprecated Since version 2.4 this parameter has been deprecated. Please use the issueManagementSystems parameter instead.
287      */
288     @Parameter( property = "changes.jiraMerge", defaultValue = "false" )
289     private boolean jiraMerge;
290 
291     /**
292      * Defines the JIRA password for authentication into a private JIRA installation.
293      *
294      * @since 2.1
295      */
296     @Parameter( property = "changes.jiraPassword", defaultValue = "" )
297     private String jiraPassword;
298 
299     /**
300      * Defines the JIRA username for authentication into a private JIRA installation.
301      *
302      * @since 2.1
303      */
304     @Parameter( property = "changes.jiraUser", defaultValue = "" )
305     private String jiraUser;
306 
307     /**
308      * Path to the JIRA XML file, which will be parsed.
309      */
310     @Parameter( defaultValue = "${project.build.directory}/jira-announcement.xml", required = true, readonly = true )
311     private File jiraXML;
312 
313     /**
314      * The maximum number of issues to fetch from JIRA.
315      * <p>
316      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
317      * called "nbEntries".
318      * </p>
319      */
320     @Parameter( property = "changes.maxEntries", defaultValue = "25", required = true )
321     private int maxEntries;
322 
323     /**
324      * Include issues from JIRA with these resolution ids. Multiple resolution
325      * ids can be specified as a comma separated list of ids.
326      * <p>
327      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
328      * called "resolutionId".
329      * </p>
330      */
331     @Parameter( property = "changes.resolutionIds", defaultValue = "Fixed" )
332     private String resolutionIds;
333 
334     /**
335      * Settings XML configuration.
336      */
337     @Parameter( defaultValue = "${settings}", readonly = true, required = true )
338     private Settings settings;
339 
340     /**
341      * Include issues from JIRA with these status ids. Multiple status ids can
342      * be specified as a comma separated list of ids.
343      * <p>
344      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
345      * called "statusId".
346      * </p>
347      */
348     @Parameter( property = "changes.statusIds", defaultValue = "Closed" )
349     private String statusIds;
350 
351     /**
352      * Defines the http user for basic authentication into the JIRA webserver.
353      *
354      * @since 2.4
355      */
356     @Parameter( property = "changes.webUser", defaultValue = "" )
357     private String webUser;
358 
359     /**
360      * Defines the http password for basic authentication into the JIRA webserver.
361      *
362      * @since 2.4
363      */
364     @Parameter( property = "changes.webPassword", defaultValue = "" )
365     private String webPassword;
366 
367     /**
368      * The prefix used when naming versions in JIRA.
369      * <p>
370      * If you have a project in JIRA with several components that have different
371      * release cycles, it is an often used pattern to prefix the version with
372      * the name of the component, e.g. maven-filtering-1.0 etc. To fetch issues
373      * from JIRA for a release of the "maven-filtering" component you would need
374      * to set this parameter to "maven-filtering-".
375      * </p>
376      *
377      * @since 2.5
378      */
379     @Parameter( property = "changes.versionPrefix", defaultValue = "" )
380     private String versionPrefix;
381 
382     /**
383      * Defines the connection timeout in milliseconds when accessing JIRA's REST-API.
384      * <p>
385      * Might help when you have a lot of different resolutions in your JIRA instance.
386      * </p>
387      *
388      * @since 2.11
389      */
390     @Parameter( property = "changes.jiraConnectionTimeout", defaultValue = "36000" )
391     private int jiraConnectionTimeout;
392 
393     /**
394      * Defines the receive timeout in milliseconds when accessing JIRA's REST-API.
395      * <p>
396      * Might help when you have a lot of different resolutions in your JIRA instance.
397      * </p>
398      *
399      * @since 2.11
400      */
401     @Parameter( property = "changes.jiraReceiveTimout", defaultValue = "32000" )
402     private int jiraReceiveTimout;
403 
404     //=======================================//
405     //  Trac Parameters                      //
406     //=======================================//
407 
408     /**
409      * Defines the Trac password for authentication into a private Trac
410      * installation.
411      *
412      * @since 2.4
413      */
414     @Parameter( property = "changes.tracPassword", defaultValue = "" )
415     private String tracPassword;
416 
417     /**
418      * Defines the Trac query for searching for tickets.
419      *
420      * @since 2.4
421      */
422     @Parameter( defaultValue = "order=id" )
423     private String tracQuery;
424 
425     /**
426      * Defines the Trac username for authentication into a private Trac
427      * installation.
428      *
429      * @since 2.4
430      */
431     @Parameter( property = "changes.tracUser", defaultValue = "" )
432     private String tracUser;
433 
434     //=======================================//
435     //  Github Parameters                    //
436     //=======================================//
437 
438     /**
439      * The scheme of your github api domain. Only use if using github enterprise.
440      * 
441      * @since 2.9
442      */
443     @Parameter( defaultValue = "http", property = "changes.githubAPIScheme" )
444     private String githubAPIScheme;
445 
446     /**
447      * The port of your github api domain. Only use if using github enterprise.
448      * 
449      * @since 2.9
450      */
451     @Parameter( defaultValue = "80", property = "changes.githubAPIPort" )
452     private int githubAPIPort;
453 
454     private ReleaseUtils releaseUtils = new ReleaseUtils( getLog() );
455 
456     private ChangesXML xml;
457 
458     //=======================================//
459     //    announcement-generate execution    //
460     //=======================================//
461 
462     /**
463      * Generate the template
464      *
465      * @throws MojoExecutionException
466      */
467     public void execute()
468         throws MojoExecutionException
469     {
470         // Fail build fast if it is using deprecated parameters
471         if ( outputDirectory != null )
472         {
473             throw new MojoExecutionException( "You are using the old parameter 'outputDirectory'. You must use 'announcementDirectory' instead." );
474         }
475 
476         // Run only at the execution root
477         if ( runOnlyAtExecutionRoot && !isThisTheExecutionRoot() )
478         {
479             getLog().info( "Skipping the announcement generation in this project because it's not the Execution Root" );
480         }
481         else
482         {
483             if ( issueManagementSystems == null )
484             {
485                 issueManagementSystems = new ArrayList<String>();
486             }
487 
488             // Handle deprecated parameters, in a backward compatible way
489             if ( issueManagementSystems.isEmpty() )
490             {
491                 if ( this.jiraMerge )
492                 {
493                     issueManagementSystems.add( CHANGES_XML );
494                     issueManagementSystems.add( JIRA );
495                 }
496                 else if ( generateJiraAnnouncement )
497                 {
498                     issueManagementSystems.add( JIRA );
499                 }
500                 else
501                 {
502                     issueManagementSystems.add( CHANGES_XML );
503                 }
504             }
505             
506             // Fetch releases from the configured issue management systems
507             List<Release> releases = null;
508             if ( issueManagementSystems.contains( CHANGES_XML ) )
509             {
510                 if ( getXmlPath().exists() )
511                 {
512                     ChangesXML changesXML = new ChangesXML( getXmlPath(), getLog() );
513                     List<Release> changesReleases = releaseUtils.convertReleaseList( changesXML.getReleaseList() );
514                     releases = releaseUtils.mergeReleases( null, changesReleases );
515                     getLog().info( "Including issues from file " + getXmlPath() + " in announcement..." );
516                 }
517                 else
518                 {
519                     getLog().warn( "changes.xml file " + getXmlPath().getAbsolutePath() + " does not exist." );
520                 }
521             }
522 
523             if ( issueManagementSystems.contains( JIRA ) )
524             {
525                 if ( ProjectUtils.validateIfIssueManagementComplete( project, JIRA, "JIRA announcement", getLog() ) )
526                 {
527                     List<Release> jiraReleases = getJiraReleases();
528                     releases = releaseUtils.mergeReleases( releases, jiraReleases );
529                     getLog().info( "Including issues from JIRA in announcement..." );
530                 }
531                 else
532                 {
533                     throw new MojoExecutionException(
534                         "Something is wrong with the Issue Management section. See previous error messages." );
535                 }
536             }
537 
538             if ( issueManagementSystems.contains( TRAC ) )
539             {
540                 if ( ProjectUtils.validateIfIssueManagementComplete( project, TRAC, "Trac announcement", getLog() ) )
541                 {
542                     List<Release> tracReleases = getTracReleases();
543                     releases = releaseUtils.mergeReleases( releases, tracReleases );
544                     getLog().info( "Including issues from Trac in announcement..." );
545                 }
546                 else
547                 {
548                     throw new MojoExecutionException(
549                         "Something is wrong with the Issue Management section. See previous error messages." );
550                 }
551             }
552 
553             if ( issueManagementSystems.contains( GIT_HUB ) )
554             {
555                 if ( ProjectUtils.validateIfIssueManagementComplete( project, GIT_HUB, "GitHub announcement", getLog() ) )
556                 {
557                     List<Release> gitHubReleases = getGitHubReleases();
558                     releases = releaseUtils.mergeReleases( releases, gitHubReleases );
559                     getLog().info( "Including issues from GitHub in announcement..." );
560                 }
561                 else
562                 {
563                     throw new MojoExecutionException(
564                                                       "Something is wrong with the Issue Management section. See previous error messages." );
565                 }
566             }
567 
568             // @todo Add more issue management systems here.
569 
570             // Follow these steps:
571             // 1. Add a constant for the name of the issue management system
572             // 2. Add the @parameters needed to configure the issue management system
573             // 3. Add a protected List get<IMSname>Releases() method that retrieves a list of releases
574             // 4. Merge those releases into the "releases" variable
575             // For help with these steps, you can have a look at how this has been done for JIRA or Trac
576 
577             // Generate the report
578             if ( releases == null || releases.isEmpty() )
579             {
580                 throw new MojoExecutionException(
581                     "No releases found in any of the configured issue management systems." );
582             }
583             else
584             {
585                 doGenerate( releases );
586             }
587         }
588     }
589 
590     /**
591      * Add the parameters to velocity context
592      *
593      * @param releases A <code>List</code> of <code>Release</code>s
594      * @throws MojoExecutionException
595      */
596     public void doGenerate( List<Release> releases )
597         throws MojoExecutionException
598     {
599         String version = ( versionPrefix == null ? "" : versionPrefix ) + getVersion();
600 
601         getLog().debug( "Generating announcement for version [" + version + "]. Found these releases: "
602                         + ReleaseUtils.toString( releases ) );
603 
604         doGenerate( releases, releaseUtils.getLatestRelease( releases, version ) );
605     }
606 
607     protected void doGenerate( List<Release> releases, Release release )
608         throws MojoExecutionException
609     {
610         try
611         {
612             ToolManager toolManager = new ToolManager( true );
613             Context context = toolManager.createContext();
614 
615             if ( getIntroduction() == null || getIntroduction().equals( "" ) )
616             {
617                 setIntroduction( getUrl() );
618             }
619 
620             context.put( "releases", releases );
621 
622             context.put( "groupId", getGroupId() );
623 
624             context.put( "artifactId", getArtifactId() );
625 
626             context.put( "version", getVersion() );
627 
628             context.put( "packaging", getPackaging() );
629 
630             context.put( "url", getUrl() );
631 
632             context.put( "release", release );
633 
634             context.put( "introduction", getIntroduction() );
635 
636             context.put( "developmentTeam", getDevelopmentTeam() );
637 
638             context.put( "finalName", getFinalName() );
639 
640             context.put( "urlDownload", getUrlDownload() );
641 
642             context.put( "project", project );
643 
644             if ( announceParameters == null )
645             {
646                 // empty Map to prevent NPE in velocity execution
647                 context.put( "announceParameters", Collections.EMPTY_MAP );
648             }
649             else
650             {
651                 context.put( "announceParameters", announceParameters );
652             }
653 
654 
655             processTemplate( context, announcementDirectory, template, announcementFile );
656         }
657         catch ( ResourceNotFoundException rnfe )
658         {
659             throw new MojoExecutionException( "Resource not found.", rnfe );
660         }
661         catch ( VelocityException ve )
662         {
663             throw new MojoExecutionException( ve.toString(), ve );
664         }
665     }
666 
667     /**
668      * Create the velocity template
669      *
670      * @param context velocity context that has the parameter values
671      * @param outputDirectory directory where the file will be generated
672      * @param template velocity template which will the context be merged
673      * @param announcementFile The file name of the generated announcement
674      * @throws ResourceNotFoundException, VelocityException, IOException
675      */
676     public void processTemplate( Context context, File outputDirectory, String template, String announcementFile )
677         throws VelocityException, MojoExecutionException
678     {
679         File f;
680 
681         // Use the name of the template as a default value
682         if ( StringUtils.isEmpty( announcementFile ) )
683         {
684             announcementFile = template;
685         }
686 
687         try
688         {
689             f = new File( outputDirectory, announcementFile );
690 
691             if ( !f.getParentFile().exists() )
692             {
693                 f.getParentFile().mkdirs();
694             }
695 
696             VelocityEngine engine = velocity.getEngine();
697 
698             engine.setApplicationAttribute( "baseDirectory", basedir );
699 
700             if ( StringUtils.isEmpty( templateEncoding ) )
701             {
702                 templateEncoding =  ReaderFactory.FILE_ENCODING;
703                 getLog().warn(
704                                "File encoding has not been set, using platform encoding " + templateEncoding
705                                    + ", i.e. build is platform dependent!" );
706             }
707 
708             Writer writer = new OutputStreamWriter( new FileOutputStream( f ), templateEncoding );
709 
710             Template velocityTemplate = engine.getTemplate( templateDirectory + "/" + template, templateEncoding );
711 
712             velocityTemplate.merge( context, writer );
713 
714             writer.flush();
715 
716             writer.close();
717 
718             getLog().info( "Created template " + f );
719         }
720 
721         catch ( ResourceNotFoundException rnfe )
722         {
723             throw new ResourceNotFoundException( "Template not found. ( " + templateDirectory + "/" + template + " )" );
724         }
725         catch ( VelocityException ve )
726         {
727             throw new VelocityException( ve.toString() );
728         }
729 
730         catch ( Exception e )
731         {
732             if ( e.getCause() != null )
733             {
734                 getLog().warn( e.getCause() );
735             }
736             throw new MojoExecutionException( e.toString(), e.getCause() );
737         }
738     }
739 
740     protected List<Release> getJiraReleases()
741         throws MojoExecutionException
742     {
743         AbstractJiraDownloader jiraDownloader = new AdaptiveJiraDownloader();
744 
745         File jiraXMLFile = jiraXML;
746 
747         jiraDownloader.setLog( getLog() );
748 
749         jiraDownloader.setOutput( jiraXMLFile );
750 
751         jiraDownloader.setStatusIds( statusIds );
752 
753         jiraDownloader.setResolutionIds( resolutionIds );
754 
755         jiraDownloader.setMavenProject( project );
756 
757         jiraDownloader.setSettings( settings );
758 
759         jiraDownloader.setNbEntries( maxEntries );
760 
761         jiraDownloader.setFilter( filter );
762 
763         jiraDownloader.setJiraUser( jiraUser );
764 
765         jiraDownloader.setJiraPassword( jiraPassword );
766 
767         jiraDownloader.setUseJql( useJql );
768 
769         jiraDownloader.setWebUser( webUser );
770 
771         jiraDownloader.setWebPassword( webPassword );
772 
773         jiraDownloader.setConnectionTimeout( jiraConnectionTimeout );
774 
775         jiraDownloader.setReceiveTimout( jiraReceiveTimout );
776 
777         try
778         {
779             jiraDownloader.doExecute();
780 
781             List<Issue> issueList = jiraDownloader.getIssueList();
782 
783             if ( StringUtils.isNotEmpty( versionPrefix ) )
784             {
785                 int originalNumberOfIssues = issueList.size();
786                 issueList = IssueUtils.filterIssuesWithVersionPrefix( issueList, versionPrefix );
787                 getLog().debug( "Filtered out " + issueList.size() + " issues of " + originalNumberOfIssues
788                     + " that matched the versionPrefix '" + versionPrefix + "'." );
789             }
790 
791             return getReleases( issueList, new JIRAIssueManagmentSystem() );
792         }
793         catch ( Exception e )
794         {
795             throw new MojoExecutionException( "Failed to extract issues from JIRA.", e );
796         }
797     }
798 
799     private List<Release> getReleases( List<Issue> issues, IssueManagementSystem ims )
800         throws MojoExecutionException
801     {
802         if ( issueTypes != null ) 
803         {
804             ims.applyConfiguration( issueTypes );
805         }
806         if ( issues.isEmpty() )
807         {
808             return Collections.emptyList();
809         }
810         else
811         {
812             IssueAdapter adapter = new IssueAdapter( ims );
813             return adapter.getReleases( issues );
814         }
815     }
816 
817     protected List<Release> getTracReleases()
818         throws MojoExecutionException
819     {
820         TracDownloader issueDownloader = new TracDownloader();
821 
822         issueDownloader.setProject( project );
823 
824         issueDownloader.setQuery( tracQuery );
825 
826         issueDownloader.setTracPassword( tracPassword );
827 
828         issueDownloader.setTracUser( tracUser );
829 
830         try
831         {
832             return getReleases( issueDownloader.getIssueList(), new TracIssueManagmentSystem() );
833         }
834         catch ( Exception e )
835         {
836             throw new MojoExecutionException( "Failed to extract issues from Trac.", e );
837         }
838     }
839 
840     protected List<Release> getGitHubReleases()
841         throws MojoExecutionException
842     {
843         try
844         {
845             GitHubDownloader issueDownloader =
846                 new GitHubDownloader( project, githubAPIScheme, githubAPIPort, false, true );
847             return getReleases( issueDownloader.getIssueList(), new GitHubIssueManagementSystem() );
848         }
849         catch ( Exception e )
850         {
851             throw new MojoExecutionException( "Failed to extract issues from GitHub.", e );
852         }
853     }
854 
855     /*
856      * accessors
857      */
858 
859     public String getArtifactId()
860     {
861         return artifactId;
862     }
863 
864     public void setArtifactId( String artifactId )
865     {
866         this.artifactId = artifactId;
867     }
868 
869     public String getDevelopmentTeam()
870     {
871         return developmentTeam;
872     }
873 
874     public void setDevelopmentTeam( String developmentTeam )
875     {
876         this.developmentTeam = developmentTeam;
877     }
878 
879     public String getFinalName()
880     {
881         return finalName;
882     }
883 
884     public void setFinalName( String finalName )
885     {
886         this.finalName = finalName;
887     }
888 
889     public String getGroupId()
890     {
891         return groupId;
892     }
893 
894     public void setGroupId( String groupId )
895     {
896         this.groupId = groupId;
897     }
898 
899     public String getIntroduction()
900     {
901         return introduction;
902     }
903 
904     public void setIntroduction( String introduction )
905     {
906         this.introduction = introduction;
907     }
908     
909     public void setIssueTypes( Map<String, String> issueTypes )
910     {
911         this.issueTypes = issueTypes;
912     }
913 
914     public Map<String, String> getIssueTypes()
915     {
916         return issueTypes;
917     }
918 
919     public File getAnnouncementDirectory()
920     {
921         return announcementDirectory;
922     }
923 
924     public void setAnnouncementDirectory( File announcementDirectory )
925     {
926         this.announcementDirectory = announcementDirectory;
927     }
928 
929     public String getPackaging()
930     {
931         return packaging;
932     }
933 
934     public void setPackaging( String packaging )
935     {
936         this.packaging = packaging;
937     }
938 
939     public String getUrl()
940     {
941         return url;
942     }
943 
944     public void setUrl( String url )
945     {
946         this.url = url;
947     }
948 
949     public String getUrlDownload()
950     {
951         return urlDownload;
952     }
953 
954     public void setUrlDownload( String urlDownload )
955     {
956         this.urlDownload = urlDownload;
957     }
958 
959     public VelocityComponent getVelocity()
960     {
961         return velocity;
962     }
963 
964     public void setVelocity( VelocityComponent velocity )
965     {
966         this.velocity = velocity;
967     }
968 
969     public String getVersion()
970     {
971         return version;
972     }
973 
974     public void setVersion( String version )
975     {
976         this.version = version;
977     }
978 
979     public ChangesXML getXml()
980     {
981         return xml;
982     }
983 
984     public void setXml( ChangesXML xml )
985     {
986         this.xml = xml;
987     }
988 
989     public File getXmlPath()
990     {
991         return xmlPath;
992     }
993 
994     public void setXmlPath( File xmlPath )
995     {
996         this.xmlPath = xmlPath;
997     }
998 }