Coverage Report - org.apache.maven.plugin.announcement.AnnouncementMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
AnnouncementMojo
46%
98/209
40%
35/86
2,811
 
 1  
 package org.apache.maven.plugin.announcement;
 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.io.FileOutputStream;
 24  
 import java.io.OutputStreamWriter;
 25  
 import java.io.Writer;
 26  
 import java.util.ArrayList;
 27  
 import java.util.Collections;
 28  
 import java.util.Iterator;
 29  
 import java.util.List;
 30  
 import java.util.Map;
 31  
 
 32  
 import org.apache.maven.plugin.MojoExecutionException;
 33  
 import org.apache.maven.plugin.changes.ChangesXML;
 34  
 import org.apache.maven.plugin.jira.JiraXML;
 35  
 import org.apache.maven.plugins.changes.model.Action;
 36  
 import org.apache.maven.plugins.changes.model.Release;
 37  
 import org.apache.maven.project.MavenProject;
 38  
 import org.apache.maven.settings.Settings;
 39  
 import org.apache.velocity.Template;
 40  
 import org.apache.velocity.VelocityContext;
 41  
 import org.apache.velocity.app.VelocityEngine;
 42  
 import org.apache.velocity.context.Context;
 43  
 import org.apache.velocity.exception.ResourceNotFoundException;
 44  
 import org.apache.velocity.exception.VelocityException;
 45  
 import org.codehaus.plexus.util.ReaderFactory;
 46  
 import org.codehaus.plexus.util.StringUtils;
 47  
 import org.codehaus.plexus.velocity.VelocityComponent;
 48  
 
 49  
 /**
 50  
  * Goal which generate the template for an announcement.
 51  
  *
 52  
  * @goal announcement-generate
 53  
  * @requiresDependencyResolution test
 54  
  * @author aramirez@exist.com
 55  
  * @version $Id: org.apache.maven.plugin.announcement.AnnouncementMojo.html 816595 2012-05-08 12:43:00Z hboutemy $
 56  
  */
 57  2
 public class AnnouncementMojo
 58  
     extends AbstractAnnouncementMojo
 59  
 {
 60  
     private static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
 61  
 
 62  
     /**
 63  
      * Directory where the template file will be generated.
 64  
      *
 65  
      * @parameter expression="${project.build.directory}/announcement"
 66  
      * @required
 67  
      */
 68  
     private File outputDirectory;
 69  
 
 70  
     /**
 71  
      * @parameter expression="${project.groupId}"
 72  
      * @readonly
 73  
      */
 74  
     private String groupId;
 75  
 
 76  
     /**
 77  
      * @parameter expression="${project.artifactId}"
 78  
      * @readonly
 79  
      */
 80  
     private String artifactId;
 81  
 
 82  
     /**
 83  
      * Version of the artifact.
 84  
      *
 85  
      * @parameter expression="${changes.version}" default-value="${project.version}"
 86  
      * @required
 87  
      */
 88  
     private String version;
 89  
 
 90  
     /**
 91  
      * Distribution URL of the artifact.
 92  
      * This parameter will be passed to the template.
 93  
      *
 94  
      * @parameter expression="${project.url}"
 95  
      */
 96  
     private String url;
 97  
 
 98  
     /**
 99  
      * Packaging structure for the artifact.
 100  
      *
 101  
      * @parameter expression="${project.packaging}"
 102  
      * @readonly
 103  
      */
 104  
     private String packaging;
 105  
 
 106  
     /**
 107  
      * The name of the artifact to be used in the announcement.
 108  
      *
 109  
      * @parameter expression="${changes.finalName}" default-value="${project.build.finalName}"
 110  
      * @required
 111  
      */
 112  
     private String finalName;
 113  
 
 114  
     /**
 115  
      * URL where the artifact can be downloaded. If not specified,
 116  
      * no URL is used.
 117  
      * This parameter will be passed to the template.
 118  
      *
 119  
      * @parameter
 120  
      */
 121  
     private String urlDownload;
 122  
 
 123  
     /**
 124  
      * The path of the changes.xml file.
 125  
      *
 126  
      * @parameter expression="${basedir}/src/changes/changes.xml"
 127  
      * @required
 128  
      */
 129  
     private File xmlPath;
 130  
 
 131  
     /**
 132  
      * Name of the team that develops the artifact.
 133  
      * This parameter will be passed to the template.
 134  
      *
 135  
      * @parameter default-value="${project.name} team" expression="${changes.developmentTeam}"
 136  
      * @required
 137  
      */
 138  
     private String developmentTeam;
 139  
 
 140  
     /**
 141  
      * Short description or introduction of the released artifact.
 142  
      * This parameter will be passed to the template.
 143  
      *
 144  
      * @parameter default-value="${project.description}"
 145  
      */
 146  
     private String introduction;
 147  
 
 148  
     /**
 149  
      * Velocity Component.
 150  
      *
 151  
      * @component role="org.codehaus.plexus.velocity.VelocityComponent" roleHint="maven-changes-plugin"
 152  
      * @readonly
 153  
      */
 154  
     private VelocityComponent velocity;
 155  
 
 156  
     /**
 157  
      * The Velocity template used to format the announcement.
 158  
      *
 159  
      * @parameter default-value="announcement.vm" expression="${changes.template}"
 160  
      * @required
 161  
      */
 162  
     private String template;
 163  
 
 164  
     /**
 165  
      * Directory that contains the template.
 166  
      * <p>
 167  
      * <b>Note:</b> This directory must be a subdirectory of
 168  
      * <code>/src/main/resources/ or current project base directory</code>.
 169  
      * </p>
 170  
      *
 171  
      * @parameter default-value="org/apache/maven/plugin/announcement" expression="${changes.templateDirectory}"
 172  
      * @required
 173  
      */
 174  
     private String templateDirectory;
 175  
 
 176  
     private ChangesXML xml;
 177  
 
 178  
     //=======================================//
 179  
     //  JIRA-Announcement Needed Parameters  //
 180  
     //=======================================//
 181  
 
 182  
     /**
 183  
      * The Maven Project.
 184  
      *
 185  
      * @parameter expression="${project}"
 186  
      * @required
 187  
      * @readonly
 188  
      */
 189  
     private MavenProject project;
 190  
 
 191  
     /**
 192  
      * Settings XML configuration.
 193  
      *
 194  
      * @parameter expression="${settings}"
 195  
      * @required
 196  
      * @readonly
 197  
      */
 198  
     private Settings settings;
 199  
 
 200  
     /**
 201  
      * Flag to determine if the plugin will generate a JIRA announcement.
 202  
      *
 203  
      * @parameter expression="${generateJiraAnnouncement}" default-value="false"
 204  
      * @required
 205  
      */
 206  
     private boolean generateJiraAnnouncement;
 207  
 
 208  
     /**
 209  
      * Include issues from JIRA with these status ids. Multiple status ids can
 210  
      * be specified as a comma separated list of ids.
 211  
      * <p>
 212  
      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
 213  
      * called "statusId".
 214  
      * </p>
 215  
      *
 216  
      * @parameter default-value="Closed" expression="${changes.statusIds}"
 217  
      */
 218  
     private String statusIds;
 219  
 
 220  
     /**
 221  
      * Include issues from JIRA with these resolution ids. Multiple resolution
 222  
      * ids can be specified as a comma separated list of ids.
 223  
      * <p>
 224  
      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
 225  
      * called "resolutionId".
 226  
      * </p>
 227  
      *
 228  
      * @parameter default-value="Fixed" expression="${changes.resolutionIds}"
 229  
      */
 230  
     private String resolutionIds;
 231  
 
 232  
     /**
 233  
      * The path of the XML file of JIRA-announcements to be parsed.
 234  
      *
 235  
      * @parameter expression="${project.build.directory}/jira-announcement.xml"
 236  
      * @required
 237  
      * @readonly
 238  
      */
 239  
     private File jiraXML;
 240  
 
 241  
     /**
 242  
      * The maximum number of issues to fetch from JIRA.
 243  
      * <p>
 244  
      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
 245  
      * called "nbEntries".
 246  
      * </p>
 247  
      *
 248  
      * @parameter default-value="25"  expression="${changes.maxEntries}"
 249  
      * @required
 250  
      */
 251  
     private int maxEntries;
 252  
 
 253  
     /**
 254  
      * Defines the JIRA username for authentication into a private JIRA installation.
 255  
      *
 256  
      * @parameter default-value="" expression="${changes.jiraUser}"
 257  
      * @since 2.1
 258  
      */
 259  
     private String jiraUser;
 260  
 
 261  
     /**
 262  
      * Defines the JIRA password for authentication into a private JIRA installation.
 263  
      *
 264  
      * @parameter default-value="" expression="${changes.jiraPassword}"
 265  
      * @since 2.1
 266  
      */
 267  
     private String jiraPassword;
 268  
 
 269  
     /**
 270  
      * The template encoding.
 271  
      *
 272  
      * @parameter expression="${changes.templateEncoding}" default-value="${project.build.sourceEncoding}"
 273  
      * @since 2.1
 274  
      */
 275  
     private String templateEncoding;
 276  
 
 277  
     /**
 278  
      * If releases from JIRA should be merged with the releases from a
 279  
      * changes.xml file.
 280  
      *
 281  
      * @parameter expression="${changes.jiraMerge}" default-value="false"
 282  
      * @since 2.1
 283  
      */
 284  
     private boolean jiraMerge;
 285  
 
 286  
     /**
 287  
      * Map of custom parameters for the announcement.
 288  
      * This Map will be passed to the template.
 289  
      *
 290  
      * @parameter
 291  
      * @since 2.1
 292  
      */
 293  
     private Map announceParameters;
 294  
 
 295  
     //=======================================//
 296  
     //    announcement-generate execution    //
 297  
     //=======================================//
 298  
 
 299  
     /**
 300  
      * Generate the template
 301  
      *
 302  
      * @throws MojoExecutionException
 303  
      */
 304  
     public void execute()
 305  
         throws MojoExecutionException
 306  
     {
 307  
         // Run only at the execution root
 308  1
         if ( runOnlyAtExecutionRoot && !isThisTheExecutionRoot() )
 309  
         {
 310  0
             getLog().info( "Skipping the announcement generation in this project because it's not the Execution Root" );
 311  
         }
 312  
         else
 313  
         {
 314  1
             if ( this.jiraMerge )
 315  
             {
 316  0
                 ChangesXML changesXML =  new ChangesXML( getXmlPath(), getLog() );
 317  0
                 List changesReleases = changesXML.getReleaseList();
 318  0
                 if ( validateIfIssueManagementComplete() )
 319  
                 {
 320  0
                     List jiraReleases = getJiraReleases();
 321  0
                     List mergedReleases = mergeReleases( changesReleases, jiraReleases );
 322  0
                     doGenerate( mergedReleases );
 323  0
                 }
 324  
                 else
 325  
                 {
 326  0
                     throw new MojoExecutionException( "Something is wrong with the Issue Management section. See previous error messages." );
 327  
                 }
 328  0
             }
 329  
             else
 330  
             {
 331  1
                 if ( !generateJiraAnnouncement )
 332  
                 {
 333  1
                     if ( getXmlPath().exists() )
 334  
                     {
 335  1
                         setXml( new ChangesXML( getXmlPath(), getLog() ) );
 336  
 
 337  1
                         getLog().info( "Creating announcement file from " + getXmlPath() + "..." );
 338  
 
 339  1
                         doGenerate( getXml().getReleaseList() );
 340  
                     }
 341  
                     else
 342  
                     {
 343  0
                         getLog().warn( "changes.xml file " + getXmlPath().getAbsolutePath() + " does not exist." );
 344  
                     }
 345  
                 }
 346  
                 else
 347  
                 {
 348  0
                     doJiraGenerate();
 349  
                 }
 350  
             }
 351  
         }
 352  1
     }
 353  
 
 354  
     /**
 355  
      * Add the parameters to velocity context
 356  
      *
 357  
      * @param releases A <code>List</code> of <code>Release</code>s
 358  
      * @throws MojoExecutionException
 359  
      */
 360  
     public void doGenerate( List releases )
 361  
         throws MojoExecutionException
 362  
     {
 363  1
         doGenerate( releases, getLatestRelease( releases )  );
 364  1
     }
 365  
 
 366  
     protected void doGenerate( List releases, Release release )
 367  
         throws MojoExecutionException
 368  
     {
 369  
         try
 370  
         {
 371  1
             Context context = new VelocityContext();
 372  
 
 373  1
             if ( getIntroduction() == null || getIntroduction().equals( "" ) )
 374  
             {
 375  0
                 setIntroduction( getUrl() );
 376  
             }
 377  
 
 378  1
             context.put( "releases", releases );
 379  
 
 380  1
             context.put( "groupId", getGroupId() );
 381  
 
 382  1
             context.put( "artifactId", getArtifactId() );
 383  
 
 384  1
             context.put( "version", getVersion() );
 385  
 
 386  1
             context.put( "packaging", getPackaging() );
 387  
 
 388  1
             context.put( "url", getUrl() );
 389  
 
 390  1
             context.put( "release", release );
 391  
 
 392  1
             context.put( "introduction", getIntroduction() );
 393  
 
 394  1
             context.put( "developmentTeam", getDevelopmentTeam() );
 395  
 
 396  1
             context.put( "finalName", getFinalName() );
 397  
 
 398  1
             context.put( "urlDownload", getUrlDownload() );
 399  
 
 400  1
             context.put( "project", project );
 401  
 
 402  1
             if ( announceParameters == null )
 403  
             {
 404  
                 // empty Map to prevent NPE in velocity execution
 405  1
                 context.put( "announceParameters", Collections.EMPTY_MAP );
 406  
             }
 407  
             else
 408  
             {
 409  0
                 context.put( "announceParameters", announceParameters );
 410  
             }
 411  
 
 412  
 
 413  1
             processTemplate( context, getOutputDirectory(), template );
 414  
         }
 415  0
         catch ( ResourceNotFoundException rnfe )
 416  
         {
 417  0
             throw new MojoExecutionException( "Resource not found.", rnfe );
 418  
         }
 419  0
         catch ( VelocityException ve )
 420  
         {
 421  0
             throw new MojoExecutionException( ve.toString(), ve );
 422  1
         }
 423  1
     }
 424  
 
 425  
     /**
 426  
      * Get the latest release by matching the supplied releases
 427  
      * with the version from the pom.
 428  
      *
 429  
      * @param releases list of releases
 430  
      * @return A <code>Release</code> that matches the next release of the current project
 431  
      * @throws MojoExecutionException
 432  
      */
 433  
     public Release getLatestRelease( List releases )
 434  
         throws MojoExecutionException
 435  
     {
 436  1
         boolean isFound = false;
 437  
 
 438  1
         Release release = null;
 439  
 
 440  
         // Remove "-SNAPSHOT" from the end, if it's there
 441  1
         String pomVersion = getVersion();
 442  1
         if ( pomVersion != null && pomVersion.endsWith( SNAPSHOT_SUFFIX ) )
 443  
         {
 444  0
             pomVersion = pomVersion.substring( 0, pomVersion.length() - SNAPSHOT_SUFFIX.length() );
 445  
         }
 446  1
         getLog().debug( "Found " + releases.size() + " releases." );
 447  
 
 448  1
         for ( int i = 0; i < releases.size(); i++ )
 449  
         {
 450  1
             release = (Release) releases.get( i );
 451  1
             if ( getLog().isDebugEnabled() )
 452  
             {
 453  0
                 getLog().debug( "The release: " + release.getVersion()
 454  
                     + " has " + release.getActions().size() + " actions." );
 455  
             }
 456  
 
 457  1
             if ( release.getVersion() != null && release.getVersion().equals( pomVersion ) )
 458  
             {
 459  1
                 isFound = true;
 460  1
                 if ( getLog().isDebugEnabled() )
 461  
                 {
 462  0
                     getLog().debug( "Found the correct release: " + release.getVersion() );
 463  0
                     logRelease( release );
 464  
                 }
 465  1
                 return release;
 466  
             }
 467  
         }
 468  
 
 469  0
         release = getRelease( releases, pomVersion );
 470  0
         isFound = ( release != null );
 471  
 
 472  0
         if ( !isFound )
 473  
         {
 474  0
             throw new MojoExecutionException( "Couldn't find the release '" + pomVersion
 475  
                 + "' among the supplied releases." );
 476  
         }
 477  
         else
 478  
         {
 479  
 
 480  
         }
 481  0
         return release;
 482  
     }
 483  
 
 484  
     /**
 485  
      * Get a release with the specified version from the list of releases.
 486  
      *
 487  
      * @param releases A list of releases
 488  
      * @param version The version we want
 489  
      * @return A Release, or null if no release with the specified version can be found
 490  
      */
 491  
     protected Release getRelease( List releases, String version )
 492  
     {
 493  10
         Release release = null;
 494  19
         for ( int i = 0; i < releases.size(); i++ )
 495  
         {
 496  13
             release = (Release) releases.get( i );
 497  13
             if ( getLog().isDebugEnabled() )
 498  
             {
 499  0
                 getLog().debug( "The release: " + release.getVersion()
 500  
                     + " has " + release.getActions().size() + " actions." );
 501  
             }
 502  
 
 503  13
             if ( release.getVersion() != null && release.getVersion().equals( version ) )
 504  
             {
 505  4
                 if ( getLog().isDebugEnabled() )
 506  
                 {
 507  0
                     getLog().debug( "Found the correct release: " + release.getVersion() );
 508  0
                     logRelease( release );
 509  
                 }
 510  4
                 return release;
 511  
             }
 512  
         }
 513  6
         return null;
 514  
     }
 515  
 
 516  
     private void logRelease( Release release )
 517  
     {
 518  
         Action action;
 519  0
         for ( Iterator iterator = release.getActions().iterator(); iterator.hasNext(); )
 520  
         {
 521  0
             action = (Action) iterator.next();
 522  0
             getLog().debug( "o " + action.getType() );
 523  0
             getLog().debug( "issue : " + action.getIssue() );
 524  0
             getLog().debug( "action : " + action.getAction() );
 525  0
             getLog().debug( "dueTo : " + action.getDueTo() );
 526  
         }
 527  0
     }
 528  
 
 529  
     /**
 530  
      * Create the velocity template
 531  
      *
 532  
      * @param context velocity context that has the parameter values
 533  
      * @param outputDirectory directory where the file will be generated
 534  
      * @param template velocity template which will the context be merged
 535  
      * @throws ResourceNotFoundException, VelocityException, IOException
 536  
      */
 537  
     public void processTemplate( Context context, File outputDirectory, String template )
 538  
         throws ResourceNotFoundException, VelocityException, MojoExecutionException
 539  
     {
 540  
         File f;
 541  
 
 542  
         try
 543  
         {
 544  1
             f = new File( outputDirectory, template );
 545  
 
 546  1
             if ( !f.getParentFile().exists() )
 547  
             {
 548  0
                 f.getParentFile().mkdirs();
 549  
             }
 550  
 
 551  1
             VelocityEngine engine = velocity.getEngine();
 552  
 
 553  1
             engine.setApplicationAttribute( "baseDirectory", basedir );
 554  
 
 555  1
             if ( StringUtils.isEmpty( templateEncoding ) )
 556  
             {
 557  1
                 templateEncoding =  ReaderFactory.FILE_ENCODING;
 558  1
                 getLog().warn(
 559  
                                "File encoding has not been set, using platform encoding " + templateEncoding
 560  
                                    + ", i.e. build is platform dependent!" );
 561  
             }
 562  
 
 563  1
             Writer writer = new OutputStreamWriter( new FileOutputStream( f ), templateEncoding );
 564  
 
 565  1
             Template velocityTemplate = engine.getTemplate( templateDirectory + "/" + template, templateEncoding );
 566  
 
 567  1
             velocityTemplate.merge( context, writer );
 568  
 
 569  1
             writer.flush();
 570  
 
 571  1
             writer.close();
 572  
 
 573  1
             getLog().info( "Created template " + f );
 574  
         }
 575  
 
 576  0
         catch ( ResourceNotFoundException rnfe )
 577  
         {
 578  0
             throw new ResourceNotFoundException( "Template not found. ( " + templateDirectory + "/" + template + " )" );
 579  
         }
 580  0
         catch ( VelocityException ve )
 581  
         {
 582  0
             throw new VelocityException( ve.toString() );
 583  
         }
 584  
 
 585  0
         catch ( Exception e )
 586  
         {
 587  0
             if ( e.getCause() != null )
 588  
             {
 589  0
                 getLog().warn( e.getCause() );
 590  
             }
 591  0
             throw new MojoExecutionException( e.toString(), e.getCause() );
 592  1
         }
 593  1
     }
 594  
 
 595  
     public void doJiraGenerate()
 596  
         throws MojoExecutionException
 597  
     {
 598  0
         if ( validateIfIssueManagementComplete() )
 599  
         {
 600  0
             List releases = getJiraReleases();
 601  
 
 602  0
             getLog().info( "Creating announcement file from JIRA releases..." );
 603  
 
 604  0
             doGenerate( releases );
 605  0
         }
 606  
         else
 607  
         {
 608  0
             throw new MojoExecutionException( "Something is wrong with the Issue Management section. See previous error messages." );
 609  
         }
 610  0
     }
 611  
 
 612  
     protected List getJiraReleases()
 613  
         throws MojoExecutionException
 614  
     {
 615  0
         JiraDownloader jiraDownloader = new JiraDownloader();
 616  
 
 617  0
         File jiraXMLFile = jiraXML;
 618  
 
 619  0
         jiraDownloader.setLog( getLog() );
 620  
 
 621  0
         jiraDownloader.setOutput( jiraXMLFile );
 622  
 
 623  0
         jiraDownloader.setStatusIds( statusIds );
 624  
 
 625  0
         jiraDownloader.setResolutionIds( resolutionIds );
 626  
 
 627  0
         jiraDownloader.setMavenProject( project );
 628  
 
 629  0
         jiraDownloader.setSettings( settings );
 630  
 
 631  0
         jiraDownloader.setNbEntries( maxEntries );
 632  
 
 633  0
         jiraDownloader.setJiraUser( jiraUser );
 634  
 
 635  0
         jiraDownloader.setJiraPassword( jiraPassword );
 636  
 
 637  
         try
 638  
         {
 639  0
             jiraDownloader.doExecute();
 640  
 
 641  0
             if ( jiraXMLFile.exists() )
 642  
             {
 643  0
                 JiraXML jiraParser = new JiraXML( jiraXMLFile );
 644  
 
 645  0
                 List issues = jiraParser.getIssueList();
 646  
 
 647  0
                 return JiraXML.getReleases( issues );
 648  
             }
 649  
             else
 650  
             {
 651  0
                 getLog().warn( "jira file " + jiraXMLFile.getPath() + " doesn't exists " );
 652  
             }
 653  0
             return Collections.EMPTY_LIST;
 654  
         }
 655  0
         catch ( Exception e )
 656  
         {
 657  0
             throw new MojoExecutionException( "Failed to extract JIRA issues from the downloaded file", e );
 658  
         }
 659  
     }
 660  
 
 661  
     /**
 662  
      * Merge releases from one issue tracker with releases from another issue
 663  
      * tracker. If a release is found in both issue trackers, i.e. they have
 664  
      * the same version, their issues are merged into one release.
 665  
      *
 666  
      * @param firstReleases Releases from the first issue tracker
 667  
      * @param secondReleases Releases from the second issue tracker
 668  
      * @return A list containing the merged releases
 669  
      */
 670  
     protected List mergeReleases( final List firstReleases, final List secondReleases )
 671  
     {
 672  5
         if ( firstReleases == null && secondReleases == null )
 673  
         {
 674  0
             return Collections.EMPTY_LIST;
 675  
         }
 676  5
         if ( firstReleases == null )
 677  
         {
 678  0
             return secondReleases;
 679  
         }
 680  5
         if ( secondReleases == null )
 681  
         {
 682  0
             return firstReleases;
 683  
         }
 684  
 
 685  5
         List mergedReleases = new ArrayList();
 686  
 
 687  
         // Loop through the releases from the first issue tracker, merging in
 688  
         // actions from releases with the same version from the second issue
 689  
         // tracker
 690  5
         for ( Iterator iterator = firstReleases.iterator(); iterator.hasNext(); )
 691  
         {
 692  6
             Release firstRelease = (Release) iterator.next();
 693  6
             Release secondRelease = getRelease( secondReleases, firstRelease.getVersion() );
 694  6
             if ( secondRelease != null )
 695  
             {
 696  2
                 if ( secondRelease.getActions() != null )
 697  
                 {
 698  2
                     firstRelease.getActions().addAll( secondRelease.getActions() );
 699  
                 }
 700  
             }
 701  6
             mergedReleases.add(firstRelease);
 702  6
         }
 703  
 
 704  
         // Handle releases that are only in the second issue tracker
 705  5
         for ( Iterator iterator = secondReleases.iterator(); iterator.hasNext(); )
 706  
         {
 707  4
             Release secondRelease = (Release) iterator.next();
 708  4
             Release mergedRelease = getRelease( mergedReleases, secondRelease.getVersion() );
 709  4
             if ( mergedRelease == null )
 710  
             {
 711  2
                 mergedReleases.add(secondRelease);
 712  
             }
 713  4
         }
 714  5
         return mergedReleases;
 715  
     }
 716  
 
 717  
     /**
 718  
      * This method was copied from JiraMojo and modified.
 719  
      */
 720  
     private boolean validateIfIssueManagementComplete()
 721  
     {
 722  0
         if ( project.getIssueManagement() == null )
 723  
         {
 724  0
             getLog().error( "No Issue Management set. No JIRA announcement will be made." );
 725  
 
 726  0
             return false;
 727  
         }
 728  0
         else if ( ( project.getIssueManagement().getUrl() == null )
 729  
             || ( project.getIssueManagement().getUrl().trim().equals( "" ) ) )
 730  
         {
 731  0
             getLog().error( "No URL set in Issue Management. No JIRA announcement will be made." );
 732  
 
 733  0
             return false;
 734  
         }
 735  0
         else if ( ( project.getIssueManagement().getSystem() != null )
 736  
             && !( project.getIssueManagement().getSystem().equalsIgnoreCase( "jira" ) ) )
 737  
         {
 738  0
             getLog().error( "No JIRA Issue Management system configured. No JIRA announcement will be made." );
 739  
 
 740  0
             return false;
 741  
         }
 742  0
         return true;
 743  
     }
 744  
 
 745  
     /*
 746  
      * accessors
 747  
      */
 748  
 
 749  
     public File getXmlPath()
 750  
     {
 751  3
         return xmlPath;
 752  
     }
 753  
 
 754  
     public void setXmlPath( File xmlPath )
 755  
     {
 756  0
         this.xmlPath = xmlPath;
 757  0
     }
 758  
 
 759  
     public File getOutputDirectory()
 760  
     {
 761  1
         return outputDirectory;
 762  
     }
 763  
 
 764  
     public void setOutputDirectory( File outputDirectory )
 765  
     {
 766  0
         this.outputDirectory = outputDirectory;
 767  0
     }
 768  
 
 769  
     public String getGroupId()
 770  
     {
 771  1
         return groupId;
 772  
     }
 773  
 
 774  
     public void setGroupId( String groupId )
 775  
     {
 776  0
         this.groupId = groupId;
 777  0
     }
 778  
 
 779  
     public String getArtifactId()
 780  
     {
 781  1
         return artifactId;
 782  
     }
 783  
 
 784  
     public void setArtifactId( String artifactId )
 785  
     {
 786  0
         this.artifactId = artifactId;
 787  0
     }
 788  
 
 789  
     public String getVersion()
 790  
     {
 791  2
         return version;
 792  
     }
 793  
 
 794  
     public void setVersion( String version )
 795  
     {
 796  0
         this.version = version;
 797  0
     }
 798  
 
 799  
     public String getUrl()
 800  
     {
 801  1
         return url;
 802  
     }
 803  
 
 804  
     public void setUrl( String url )
 805  
     {
 806  0
         this.url = url;
 807  0
     }
 808  
 
 809  
     public ChangesXML getXml()
 810  
     {
 811  1
         return xml;
 812  
     }
 813  
 
 814  
     public void setXml( ChangesXML xml )
 815  
     {
 816  1
         this.xml = xml;
 817  1
     }
 818  
 
 819  
     public String getPackaging()
 820  
     {
 821  1
         return packaging;
 822  
     }
 823  
 
 824  
     public void setPackaging( String packaging )
 825  
     {
 826  0
         this.packaging = packaging;
 827  0
     }
 828  
 
 829  
     public String getDevelopmentTeam()
 830  
     {
 831  1
         return developmentTeam;
 832  
     }
 833  
 
 834  
     public void setDevelopmentTeam( String developmentTeam )
 835  
     {
 836  0
         this.developmentTeam = developmentTeam;
 837  0
     }
 838  
 
 839  
     public String getIntroduction()
 840  
     {
 841  3
         return introduction;
 842  
     }
 843  
 
 844  
     public void setIntroduction( String introduction )
 845  
     {
 846  0
         this.introduction = introduction;
 847  0
     }
 848  
 
 849  
     public VelocityComponent getVelocity()
 850  
     {
 851  0
         return velocity;
 852  
     }
 853  
 
 854  
     public void setVelocity( VelocityComponent velocity )
 855  
     {
 856  0
         this.velocity = velocity;
 857  0
     }
 858  
 
 859  
     public String getFinalName()
 860  
     {
 861  1
         return finalName;
 862  
     }
 863  
 
 864  
     public void setFinalName( String finalName )
 865  
     {
 866  0
         this.finalName = finalName;
 867  0
     }
 868  
 
 869  
     public String getUrlDownload()
 870  
     {
 871  1
         return urlDownload;
 872  
     }
 873  
 
 874  
     public void setUrlDownload( String urlDownload )
 875  
     {
 876  0
         this.urlDownload = urlDownload;
 877  0
     }
 878  
 }