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