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 }