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