View Javadoc

1   package org.apache.maven.plugin.jira;
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 java.io.File;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Locale;
26  import java.util.Map;
27  import java.util.ResourceBundle;
28  
29  import org.apache.commons.lang.StringUtils;
30  import org.apache.maven.plugin.changes.AbstractChangesReport;
31  import org.apache.maven.plugin.changes.ProjectUtils;
32  import org.apache.maven.plugin.issues.Issue;
33  import org.apache.maven.plugin.issues.IssueUtils;
34  import org.apache.maven.plugin.issues.IssuesReportGenerator;
35  import org.apache.maven.plugin.issues.IssuesReportHelper;
36  import org.apache.maven.reporting.MavenReportException;
37  import org.apache.maven.settings.Settings;
38  
39  /**
40   * Goal which downloads issues from the Issue Tracking System and generates a report.
41   *
42   * @goal jira-report
43   * @author <a href="mailto:jruiz@exist.com">Johnny R. Ruiz III</a>
44   * @version $Id: JiraMojo.java 1134780 2011-06-11 21:47:41Z hboutemy $
45   * @threadSafe
46   */
47  public class JiraMojo
48      extends AbstractChangesReport
49  {
50      /**
51       * Valid JIRA columns.
52       */
53      private static final Map<String,Integer> JIRA_COLUMNS = new HashMap<String,Integer>( 16 );
54  
55      static
56      {
57          JIRA_COLUMNS.put( "Assignee", new Integer( IssuesReportHelper.COLUMN_ASSIGNEE ) );
58          JIRA_COLUMNS.put( "Component", new Integer( IssuesReportHelper.COLUMN_COMPONENT ) );
59          JIRA_COLUMNS.put( "Created", new Integer( IssuesReportHelper.COLUMN_CREATED ) );
60          JIRA_COLUMNS.put( "Fix Version", new Integer( IssuesReportHelper.COLUMN_FIX_VERSION ) );
61          JIRA_COLUMNS.put( "Id", new Integer( IssuesReportHelper.COLUMN_ID ) );
62          JIRA_COLUMNS.put( "Key", new Integer( IssuesReportHelper.COLUMN_KEY ) );
63          JIRA_COLUMNS.put( "Priority", new Integer( IssuesReportHelper.COLUMN_PRIORITY ) );
64          JIRA_COLUMNS.put( "Reporter", new Integer( IssuesReportHelper.COLUMN_REPORTER ) );
65          JIRA_COLUMNS.put( "Resolution", new Integer( IssuesReportHelper.COLUMN_RESOLUTION ) );
66          JIRA_COLUMNS.put( "Status", new Integer( IssuesReportHelper.COLUMN_STATUS ) );
67          JIRA_COLUMNS.put( "Summary", new Integer( IssuesReportHelper.COLUMN_SUMMARY ) );
68          JIRA_COLUMNS.put( "Type", new Integer( IssuesReportHelper.COLUMN_TYPE ) );
69          JIRA_COLUMNS.put( "Updated", new Integer( IssuesReportHelper.COLUMN_UPDATED ) );
70          JIRA_COLUMNS.put( "Version", new Integer( IssuesReportHelper.COLUMN_VERSION ) );
71      }
72  
73      /**
74       * Sets the names of the columns that you want in the report. The columns
75       * will appear in the report in the same order as you specify them here.
76       * Multiple values can be separated by commas.
77       * <p>
78       * Valid columns are: <code>Assignee</code>, <code>Component</code>,
79       * <code>Created</code>, <code>Fix Version</code>, <code>Id</code>,
80       * <code>Key</code>, <code>Priority</code>, <code>Reporter</code>,
81       * <code>Resolution</code>, <code>Status</code>, <code>Summary</code>,
82       * <code>Type</code>, <code>Updated</code> and <code>Version</code>.
83       * </p>
84       *
85       * @parameter default-value="Key,Summary,Status,Resolution,Assignee"
86       * @since 2.0
87       */
88      private String columnNames;
89  
90      /**
91       * Sets the component(s) that you want to limit your report to include.
92       * Multiple values can be separated by commas (such as 10011,10012).
93       * If this is set to empty - that means all components will be included.
94       *
95       * @parameter default-value=""
96       */
97      private String component;
98  
99      /**
100      * Defines the filter parameters to restrict which issues are retrieved
101      * from JIRA. The filter parameter uses the same format of url
102      * parameters that is used in a JIRA search.
103      *
104      * @parameter default-value=""
105      */
106     private String filter;
107 
108     /**
109      * Sets the fix version id(s) that you want to limit your report to include.
110      * These are JIRA's internal version ids, <b>NOT</b> the human readable display ones.
111      * Multiple fix versions can be separated by commas.
112      * If this is set to empty - that means all fix versions will be included.
113      *
114      * @parameter default-value=""
115      * @since 2.0
116      */
117     private String fixVersionIds;
118 
119     /**
120      * The pattern used by dates in the JIRA XML-file. This is used to parse
121      * the Created and Updated fields.
122      *
123      * @parameter default-value="EEE, d MMM yyyy HH:mm:ss Z"
124      * @since 2.4
125      */
126     private String jiraDatePattern;
127 
128     /**
129      * Defines the JIRA password for authentication into a private JIRA installation.
130      *
131      * @parameter default-value=""
132      */
133     private String jiraPassword;
134 
135     /**
136      * Defines the JIRA username for authentication into a private JIRA installation.
137      *
138      * @parameter default-value=""
139      */
140     private String jiraUser;
141 
142     /**
143      * Path to the JIRA XML file, which will be parsed.
144      *
145      * @parameter expression="${project.build.directory}/jira-results.xml"
146      * @required
147      * @readonly
148      */
149     private File jiraXmlPath;
150 
151     /**
152      * Maximum number of entries to be fetched from JIRA.
153      *
154      * @parameter default-value=100
155      *
156      */
157     private int maxEntries;
158 
159     /**
160      * If you only want to show issues for the current version in the report.
161      * The current version being used is <code>${project.version}</code> minus
162      * any "-SNAPSHOT" suffix.
163      *
164      * @parameter default-value="false"
165      * @since 2.0
166      */
167     private boolean onlyCurrentVersion;
168 
169     /**
170      * Sets the priority(s) that you want to limit your report to include.
171      * Valid statuses are <code>Blocker</code>, <code>Critical</code>,
172      * <code>Major</code>, <code>Minor</code> and <code>Trivial</code>.
173      * Multiple values can be separated by commas.
174      * If this is set to empty - that means all priorities will be included.
175      *
176      * @parameter default-value=""
177      */
178     private String priorityIds;
179 
180     /**
181      * Sets the resolution(s) that you want to fetch from JIRA.
182      * Valid resolutions are: <code>Unresolved</code>, <code>Fixed</code>,
183      * <code>Won't Fix</code>, <code>Duplicate</code>, <code>Incomplete</code>
184      * and <code>Cannot Reproduce</code>.
185      * Multiple values can be separated by commas.
186      * <p>
187      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter had no
188      * default value.
189      * </p>
190      *
191      * @parameter default-value="Fixed"
192      */
193     private String resolutionIds;
194 
195     /**
196      * Settings XML configuration.
197      *
198      * @parameter expression="${settings}"
199      * @required
200      * @readonly
201      */
202     private Settings settings;
203 
204     /**
205      * Sets the column names that you want to sort the report by. Add
206      * <code>DESC</code> following the column name
207      * to specify <i>descending</i> sequence. For
208      * example <code>Fix Version DESC, Type</code> sorts first by
209      * the Fix Version in descending order and then by Type in
210      * ascending order. By default sorting is done in ascending order, but is
211      * possible to specify <code>ASC</code> for consistency. The previous
212      * example would then become <code>Fix Version DESC, Type ASC</code>.
213      * <p>
214      * Valid columns are: <code>Assignee</code>, <code>Component</code>,
215      * <code>Created</code>, <code>Fix Version</code>, <code>Id</code>,
216      * <code>Key</code>, <code>Priority</code>, <code>Reporter</code>,
217      * <code>Resolution</code>, <code>Status</code>, <code>Summary</code>,
218      * <code>Type</code>, <code>Updated</code> and <code>Version</code>.
219      * </p>
220      * <p>
221      * <strong>Note:</strong> If you are using JIRA 4 you need to put your
222      * sort column names in the reverse order. The handling of this changed
223      * between JIRA 3 and JIRA 4. The current default value is suitable for
224      * JIRA 3. This may change in the future, so please configure your sort
225      * column names in an order that works for your own JIRA version. 
226      * </p>
227      *
228      * @parameter default-value="Priority DESC, Created DESC"
229      * @since 2.0
230      */
231     private String sortColumnNames;
232 
233     /**
234      * Sets the status(es) that you want to fetch from JIRA.
235      * Valid statuses are: <code>Open</code>, <code>In Progress</code>,
236      * <code>Reopened</code>, <code>Resolved</code> and <code>Closed</code>.
237      * Multiple values can be separated by commas.
238      * <p>
239      * If your installation of JIRA uses custom status IDs, you can reference
240      * them here by their numeric values.
241      * You can obtain them on the Statuses page 
242      * (in 4.0.2 it's under Administration > Issue Settings > Statuses) 
243      * - just hover over the Edit link for the status you want and 
244      * you'll see something like 
245      * &lt;your JIRA URL&gt;/secure/admin/EditStatus!default.jspa?id=12345;
246      * in this case the value is 12345.
247      * </p>
248      * <p>
249      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter had no
250      * default value.
251      * </p>
252      *
253      * @parameter default-value="Closed"
254      */
255     private String statusIds;
256 
257     /**
258      * Sets the types(s) that you want to limit your report to include.
259      * Valid types are: <code>Bug</code>, <code>New Feature</code>,
260      * <code>Task</code>, <code>Improvement</code>, <code>Wish</code>,
261      * <code>Test</code> and <code>Sub-task</code>.
262      * Multiple values can be separated by commas.
263      * If this is set to empty - that means all types will be included.
264      *
265      * @parameter default-value=""
266      * @since 2.0
267      */
268     private String typeIds;
269 
270     /**
271      * The prefix used when naming versions in JIRA.
272      * <p>
273      * If you have a project in JIRA with several components that have different
274      * release cycles, it is an often used pattern to prefix the version with
275      * the name of the component, e.g. maven-filtering-1.0 etc. To fetch issues
276      * from JIRA for a release of the "maven-filtering" component you would need
277      * to set this parameter to "maven-filtering-".
278      * </p>
279      *
280      * @parameter default-value=""
281      * @since 2.4
282      */
283     private String versionPrefix;
284 
285     /**
286      * Defines the http password for basic authentication into the JIRA webserver.
287      *
288      * @parameter default-value=""
289      */
290     private String webPassword;
291 
292     /**
293      * Defines the http user for basic authentication into the JIRA webserver.
294      *
295      * @parameter default-value=""
296      */
297     private String webUser;
298     
299     /*
300      * Used for tests.
301      */
302     private AbstractJiraDownloader mockDownloader;
303 
304     /* --------------------------------------------------------------------- */
305     /* Public methods                                                        */
306     /* --------------------------------------------------------------------- */
307 
308     /**
309      * @see org.apache.maven.reporting.AbstractMavenReport#canGenerateReport()
310      */
311     public boolean canGenerateReport()
312     {
313         if ( mockDownloader != null ) 
314         {
315             return true;
316         }
317         return ProjectUtils.validateIfIssueManagementComplete( project, "JIRA", "JIRA Report", getLog() );
318     }
319 
320     public void executeReport( Locale locale )
321         throws MavenReportException
322     {
323         // Validate parameters
324         List<Integer> columnIds = IssuesReportHelper.getColumnIds( columnNames, JIRA_COLUMNS );
325         if ( columnIds.isEmpty() )
326         {
327             // This can happen if the user has configured column names and they are all invalid
328             throw new MavenReportException(
329                 "maven-changes-plugin: None of the configured columnNames '" + columnNames + "' are valid." );
330         }
331 
332         try
333         {
334             // Download issues
335             AbstractJiraDownloader issueDownloader;
336             if ( mockDownloader != null )
337             {
338                 issueDownloader = mockDownloader;
339             }
340             else
341             {
342                 issueDownloader = new JiraDownloader();
343             }
344             configureIssueDownloader( issueDownloader );
345             issueDownloader.doExecute();
346 
347             List<Issue> issueList = issueDownloader.getIssueList();
348 
349             if ( StringUtils.isNotEmpty( versionPrefix ) )
350             {
351                 int originalNumberOfIssues = issueList.size();
352                 issueList = IssueUtils.filterIssuesWithVersionPrefix( issueList, versionPrefix );
353                 getLog().debug( "Filtered out " + issueList.size() + " issues of " + originalNumberOfIssues
354                     + " that matched the versionPrefix '" + versionPrefix + "'." );
355             }
356 
357             if ( onlyCurrentVersion )
358             {
359                 String version = ( versionPrefix == null ? "" : versionPrefix ) + project.getVersion();
360                 issueList = IssueUtils.getIssuesForVersion( issueList, version );
361                 getLog().info( "The JIRA Report will contain issues only for the current version." );
362             }
363 
364             // Generate the report
365             IssuesReportGenerator report = new IssuesReportGenerator( IssuesReportHelper.toIntArray( columnIds ) );
366 
367             if ( issueList.isEmpty() )
368             {
369                 report.doGenerateEmptyReport( getBundle( locale ), getSink() );
370             }
371             else
372             {
373                 report.doGenerateReport( getBundle( locale ), getSink(), issueList );
374             }
375         }
376         catch ( Exception e )
377         {
378             getLog().warn( e );
379         }
380     }
381 
382     public String getDescription( Locale locale )
383     {
384         return getBundle( locale ).getString( "report.issues.description" );
385     }
386 
387     public String getName( Locale locale )
388     {
389         return getBundle( locale ).getString( "report.issues.name" );
390     }
391 
392     public String getOutputName()
393     {
394         return "jira-report";
395     }
396 
397     /* --------------------------------------------------------------------- */
398     /* Private methods                                                       */
399     /* --------------------------------------------------------------------- */
400 
401     private ResourceBundle getBundle( Locale locale )
402     {
403         return ResourceBundle.getBundle( "jira-report", locale, this.getClass().getClassLoader() );
404     }
405 
406     private void configureIssueDownloader( AbstractJiraDownloader issueDownloader )
407     {
408         issueDownloader.setLog( getLog() );
409 
410         issueDownloader.setMavenProject( project );
411 
412         issueDownloader.setOutput( jiraXmlPath );
413 
414         issueDownloader.setNbEntries( maxEntries );
415 
416         issueDownloader.setComponent( component );
417 
418         issueDownloader.setFixVersionIds( fixVersionIds );
419 
420         issueDownloader.setStatusIds( statusIds );
421 
422         issueDownloader.setResolutionIds( resolutionIds );
423 
424         issueDownloader.setPriorityIds( priorityIds );
425 
426         issueDownloader.setSortColumnNames( sortColumnNames );
427 
428         issueDownloader.setFilter( filter );
429 
430         issueDownloader.setJiraDatePattern( jiraDatePattern );
431 
432         issueDownloader.setJiraUser( jiraUser );
433 
434         issueDownloader.setJiraPassword( jiraPassword );
435 
436         issueDownloader.setTypeIds( typeIds );
437 
438         issueDownloader.setWebUser( webUser );
439 
440         issueDownloader.setWebPassword( webPassword );
441 
442         issueDownloader.setSettings( settings );
443     }
444 
445     public void setMockDownloader( AbstractJiraDownloader mockDownloader )
446     {
447         this.mockDownloader = mockDownloader;
448     }
449 
450     public AbstractJiraDownloader getMockDownloader()
451     {
452         return mockDownloader;
453     }
454 }