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