Coverage Report - org.apache.maven.shared.release.phase.CheckDependencySnapshotsPhase
 
Classes in this File Line Coverage Branch Coverage Complexity
CheckDependencySnapshotsPhase
89%
124/138
89%
73/82
6
 
 1  
 package org.apache.maven.shared.release.phase;
 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.maven.artifact.Artifact;
 23  
 import org.apache.maven.artifact.ArtifactUtils;
 24  
 import org.apache.maven.artifact.factory.ArtifactFactory;
 25  
 import org.apache.maven.project.MavenProject;
 26  
 import org.apache.maven.project.artifact.InvalidDependencyVersionException;
 27  
 import org.apache.maven.shared.release.ReleaseExecutionException;
 28  
 import org.apache.maven.shared.release.ReleaseFailureException;
 29  
 import org.apache.maven.shared.release.ReleaseResult;
 30  
 import org.apache.maven.shared.release.config.ReleaseDescriptor;
 31  
 import org.apache.maven.shared.release.env.ReleaseEnvironment;
 32  
 import org.apache.maven.shared.release.versions.DefaultVersionInfo;
 33  
 import org.apache.maven.shared.release.versions.VersionInfo;
 34  
 import org.apache.maven.shared.release.versions.VersionParseException;
 35  
 import org.codehaus.plexus.components.interactivity.Prompter;
 36  
 import org.codehaus.plexus.components.interactivity.PrompterException;
 37  
 
 38  
 import java.util.ArrayList;
 39  
 import java.util.Arrays;
 40  
 import java.util.Collections;
 41  
 import java.util.HashMap;
 42  
 import java.util.HashSet;
 43  
 import java.util.Iterator;
 44  
 import java.util.List;
 45  
 import java.util.Locale;
 46  
 import java.util.Map;
 47  
 import java.util.Set;
 48  
 
 49  
 /**
 50  
  * Check the dependencies of all projects being released to see if there are any unreleased snapshots.
 51  
  *
 52  
  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
 53  
  * @todo plugins with no version will be resolved to RELEASE which is not a snapshot, but remains unresolved to this point. This is a potential hole in the check, and should be revisited after the release pom writing is done and resolving versions to verify whether it is.
 54  
  * @todo plugins injected by the lifecycle are not tested here. They will be injected with a RELEASE version so are covered under the above point.
 55  
  * @plexus.component role="org.apache.maven.shared.release.phase.ReleasePhase" role-hint="check-dependency-snapshots"
 56  
  */
 57  142
 public class CheckDependencySnapshotsPhase
 58  
     extends AbstractReleasePhase
 59  
 {
 60  
     public static final String RESOLVE_SNAPSHOT_MESSAGE = "There are still some remaining snapshot dependencies.\n";
 61  
 
 62  
     public static final String RESOLVE_SNAPSHOT_PROMPT = "Do you want to resolve them now?";
 63  
 
 64  
     public static final String RESOLVE_SNAPSHOT_TYPE_MESSAGE = "Dependency type to resolve,";
 65  
 
 66  
     public static final String RESOLVE_SNAPSHOT_TYPE_PROMPT =
 67  
         "specify the selection number ( 0:All 1:Project Dependencies 2:Plugins 3:Reports 4:Extensions ):";
 68  
 
 69  
     /**
 70  
      * Component used to prompt for input.
 71  
      *
 72  
      * @plexus.requirement
 73  
      */
 74  
     private Prompter prompter;
 75  
 
 76  
     /**
 77  
      * Component used to create artifacts
 78  
      *
 79  
      * @plexus.requirement
 80  
      */
 81  
     private ArtifactFactory artifactFactory;
 82  
 
 83  
     public ReleaseResult execute( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment, List<MavenProject> reactorProjects )
 84  
         throws ReleaseExecutionException, ReleaseFailureException
 85  
     {
 86  154
         ReleaseResult result = new ReleaseResult();
 87  
 
 88  154
         if ( !releaseDescriptor.isAllowTimestampedSnapshots() )
 89  
         {
 90  150
             logInfo( result, "Checking dependencies and plugins for snapshots ..." );
 91  
 
 92  150
             Map<String, String> originalVersions = releaseDescriptor.getOriginalVersions( reactorProjects );
 93  
 
 94  150
             for ( MavenProject project : reactorProjects )
 95  
             {
 96  234
                 checkProject( project, originalVersions, releaseDescriptor );
 97  
             }
 98  84
         }
 99  
         else
 100  
         {
 101  4
             logInfo( result, "Ignoring SNAPSHOT depenedencies and plugins ..." );
 102  
         }
 103  88
         result.setResultCode( ReleaseResult.SUCCESS );
 104  
 
 105  88
         return result;
 106  
     }
 107  
 
 108  
     private void checkProject( MavenProject project, Map<String, String> originalVersions, ReleaseDescriptor releaseDescriptor )
 109  
         throws ReleaseFailureException, ReleaseExecutionException
 110  
     {
 111  
         @SuppressWarnings( "unchecked" )
 112  234
         Map<String, Artifact> artifactMap = ArtifactUtils.artifactMapByVersionlessId( project.getArtifacts() );
 113  
 
 114  234
         Set<Artifact> snapshotDependencies = new HashSet<Artifact>();
 115  234
         Set<Artifact> snapshotReportDependencies = new HashSet<Artifact>();
 116  234
         Set<Artifact> snapshotExtensionsDependencies = new HashSet<Artifact>();
 117  234
         Set<Artifact> snapshotPluginDependencies = new HashSet<Artifact>();
 118  
 
 119  234
         if ( project.getParentArtifact() != null )
 120  
         {
 121  96
             if ( checkArtifact( project.getParentArtifact(), originalVersions, artifactMap, releaseDescriptor ) )
 122  
             {
 123  6
                 snapshotDependencies.add( project.getParentArtifact() );
 124  
             }
 125  
         }
 126  
 
 127  
         try
 128  
         {
 129  
             @SuppressWarnings( "unchecked" )
 130  234
             Set<Artifact> dependencyArtifacts = project.createArtifacts( artifactFactory, null, null );
 131  
 
 132  234
             for ( Artifact artifact : dependencyArtifacts )
 133  
             {
 134  54
                 if ( checkArtifact( artifact, originalVersions, artifactMap, releaseDescriptor ) )
 135  
                 {
 136  42
                     snapshotDependencies.add( getArtifactFromMap( artifact, artifactMap ) );
 137  
                 }
 138  
             }
 139  
         }
 140  0
         catch ( InvalidDependencyVersionException e )
 141  
         {
 142  0
             throw new ReleaseExecutionException( "Failed to create dependency artifacts", e );
 143  234
         }
 144  
 
 145  
         @SuppressWarnings( "unchecked" )
 146  234
         Set<Artifact> pluginArtifacts = project.getPluginArtifacts();
 147  
 
 148  234
         for ( Artifact artifact : pluginArtifacts )
 149  
         {
 150  46
             if ( checkArtifact( artifact, originalVersions, artifactMap, releaseDescriptor ) )
 151  
             {
 152  
                 boolean addToFailures;
 153  
 
 154  34
                 if ( "org.apache.maven.plugins".equals( artifact.getGroupId() ) && "maven-release-plugin".equals(
 155  
                     artifact.getArtifactId() ) )
 156  
                 {
 157  
                     // It's a snapshot of the release plugin. Maybe just testing - ask
 158  
                     // By default, we fail as for any other plugin
 159  20
                     if ( releaseDescriptor.isSnapshotReleasePluginAllowed() )
 160  
                     {
 161  0
                         addToFailures = false;
 162  
                     }
 163  20
                     else if ( releaseDescriptor.isInteractive() )
 164  
                     {
 165  
                         try
 166  
                         {
 167  
                             String result;
 168  16
                             if ( !releaseDescriptor.isSnapshotReleasePluginAllowed() )
 169  
                             {
 170  16
                                 prompter.showMessage( "This project relies on a SNAPSHOT of the release plugin. "
 171  
                                                           + "This may be necessary during testing.\n" );
 172  16
                                 result = prompter.prompt( "Do you want to continue with the release?",
 173  
                                                           Arrays.asList( new String[]{ "yes", "no" } ), "no" );
 174  
                             }
 175  
                             else
 176  
                             {
 177  0
                                 result = "yes";
 178  
                             }
 179  
 
 180  12
                             if ( result.toLowerCase( Locale.ENGLISH ).startsWith( "y" ) )
 181  
                             {
 182  4
                                 addToFailures = false;
 183  4
                                 releaseDescriptor.setSnapshotReleasePluginAllowed( true );
 184  
                             }
 185  
                             else
 186  
                             {
 187  8
                                 addToFailures = true;
 188  
                             }
 189  
                         }
 190  4
                         catch ( PrompterException e )
 191  
                         {
 192  4
                             throw new ReleaseExecutionException( e.getMessage(), e );
 193  12
                         }
 194  
                     }
 195  
                     else
 196  
                     {
 197  4
                         addToFailures = true;
 198  
                     }
 199  
                 }
 200  
                 else
 201  
                 {
 202  14
                     addToFailures = true;
 203  
                 }
 204  
 
 205  30
                 if ( addToFailures )
 206  
                 {
 207  26
                     snapshotPluginDependencies.add( artifact );
 208  
                 }
 209  42
             }
 210  
         }
 211  
 
 212  
         @SuppressWarnings( "unchecked" )
 213  230
         Set<Artifact> reportArtifacts = project.getReportArtifacts();
 214  
 
 215  230
         for ( Artifact artifact : reportArtifacts )
 216  
         {
 217  18
             if ( checkArtifact( artifact, originalVersions, artifactMap, releaseDescriptor ) )
 218  
             {
 219  
                 //snapshotDependencies.add( artifact );
 220  10
                 snapshotReportDependencies.add( artifact );
 221  
             }
 222  
         }
 223  
 
 224  
         @SuppressWarnings( "unchecked" )
 225  230
         Set<Artifact> extensionArtifacts = project.getExtensionArtifacts();
 226  
 
 227  230
         for ( Artifact artifact : extensionArtifacts )
 228  
         {
 229  14
             if ( checkArtifact( artifact, originalVersions, artifactMap, releaseDescriptor ) )
 230  
             {
 231  6
                 snapshotExtensionsDependencies.add( artifact );
 232  
             }
 233  
         }
 234  
 
 235  230
         if ( !snapshotDependencies.isEmpty() || !snapshotReportDependencies.isEmpty()
 236  
                         || !snapshotExtensionsDependencies.isEmpty() || !snapshotPluginDependencies.isEmpty() )
 237  
         {
 238  84
             if ( releaseDescriptor.isInteractive() )
 239  
             {
 240  66
                 resolveSnapshots( snapshotDependencies, snapshotReportDependencies, snapshotExtensionsDependencies,
 241  
                                   snapshotPluginDependencies, releaseDescriptor );
 242  
             }
 243  
 
 244  84
             if ( !snapshotDependencies.isEmpty() || !snapshotReportDependencies.isEmpty()
 245  
                             || !snapshotExtensionsDependencies.isEmpty() || !snapshotPluginDependencies.isEmpty() )
 246  
             {
 247  62
                 StringBuilder message = new StringBuilder();
 248  
 
 249  62
                 printSnapshotDependencies( snapshotDependencies, message );
 250  62
                 printSnapshotDependencies( snapshotReportDependencies, message );
 251  62
                 printSnapshotDependencies( snapshotExtensionsDependencies, message );
 252  62
                 printSnapshotDependencies( snapshotPluginDependencies, message );
 253  62
                 message.append( "in project '" + project.getName() + "' (" + project.getId() + ")" );
 254  
 
 255  62
                 throw new ReleaseFailureException(
 256  
                     "Can't release project due to non released dependencies :\n" + message );
 257  
             }
 258  
         }
 259  168
     }
 260  
 
 261  
     private static boolean checkArtifact( Artifact artifact, Map<String, String> originalVersions, Map<String, Artifact> artifactMapByVersionlessId, ReleaseDescriptor releaseDescriptor )
 262  
     {
 263  228
         Artifact checkArtifact = getArtifactFromMap( artifact, artifactMapByVersionlessId );
 264  
 
 265  228
         return checkArtifact( checkArtifact, originalVersions, releaseDescriptor );
 266  
     }
 267  
 
 268  
     private static Artifact getArtifactFromMap( Artifact artifact, Map<String, Artifact> artifactMapByVersionlessId )
 269  
     {
 270  270
         String versionlessId = ArtifactUtils.versionlessKey( artifact );
 271  270
         Artifact checkArtifact = artifactMapByVersionlessId.get( versionlessId );
 272  
 
 273  270
         if ( checkArtifact == null )
 274  
         {
 275  174
             checkArtifact = artifact;
 276  
         }
 277  270
         return checkArtifact;
 278  
     }
 279  
 
 280  
     private static boolean checkArtifact( Artifact artifact, Map<String, String> originalVersions, ReleaseDescriptor releaseDescriptor )
 281  
     {
 282  228
         String versionlessArtifactKey = ArtifactUtils.versionlessKey( artifact.getGroupId(), artifact.getArtifactId() );
 283  
 
 284  
         // We are only looking at dependencies external to the project - ignore anything found in the reactor as
 285  
         // it's version will be updated
 286  228
         boolean result =
 287  
             artifact.isSnapshot() && !artifact.getBaseVersion().equals( originalVersions.get( versionlessArtifactKey ) );
 288  
 
 289  
         // If we have a snapshot but allowTimestampedSnapshots is true, accept the artifact if the version
 290  
         // indicates that it is a timestamped snapshot.
 291  228
         if ( result && releaseDescriptor.isAllowTimestampedSnapshots() )
 292  
         {
 293  0
             result = artifact.getVersion().indexOf( Artifact.SNAPSHOT_VERSION ) >= 0;
 294  
         }
 295  
 
 296  228
         return result;
 297  
     }
 298  
 
 299  
     public ReleaseResult simulate( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment, List<MavenProject> reactorProjects )
 300  
         throws ReleaseExecutionException, ReleaseFailureException
 301  
     {
 302  
         // It makes no modifications, so simulate is the same as execute
 303  70
         return execute( releaseDescriptor, releaseEnvironment, reactorProjects );
 304  
     }
 305  
 
 306  
     public void setPrompter( Prompter prompter )
 307  
     {
 308  70
         this.prompter = prompter;
 309  70
     }
 310  
 
 311  
     private StringBuilder printSnapshotDependencies( Set<Artifact> snapshotsSet, StringBuilder message )
 312  
     {
 313  248
         List<Artifact> snapshotsList = new ArrayList<Artifact>( snapshotsSet );
 314  
 
 315  248
         Collections.sort( snapshotsList );
 316  
 
 317  248
         for ( Artifact artifact : snapshotsList )
 318  
         {
 319  62
             message.append( "    " );
 320  
 
 321  62
             message.append( artifact );
 322  
 
 323  62
             message.append( "\n" );
 324  
         }
 325  
 
 326  248
         return message;
 327  
     }
 328  
 
 329  
     private void resolveSnapshots( Set<Artifact> projectDependencies, Set<Artifact> reportDependencies, Set<Artifact> extensionDependencies,
 330  
                                    Set<Artifact> pluginDependencies, ReleaseDescriptor releaseDescriptor )
 331  
         throws ReleaseExecutionException
 332  
     {
 333  
         try
 334  
         {
 335  66
             prompter.showMessage( RESOLVE_SNAPSHOT_MESSAGE );
 336  66
             String result =
 337  
                 prompter.prompt( RESOLVE_SNAPSHOT_PROMPT, Arrays.asList( new String[]{"yes", "no"} ), "no" );
 338  
 
 339  66
             if ( result.toLowerCase( Locale.ENGLISH ).startsWith( "y" ) )
 340  
             {
 341  22
                 Map<String, Map<String, String>> resolvedSnapshots = null;
 342  22
                 prompter.showMessage( RESOLVE_SNAPSHOT_TYPE_MESSAGE );
 343  22
                 result = prompter.prompt( RESOLVE_SNAPSHOT_TYPE_PROMPT,
 344  
                                           Arrays.asList( new String[]{"0", "1", "2", "3"} ), "1" );
 345  
 
 346  22
                 switch ( Integer.parseInt( result.toLowerCase( Locale.ENGLISH ) ) )
 347  
                 {
 348  
                     // all
 349  
                     case 0:
 350  4
                         resolvedSnapshots = processSnapshot( projectDependencies );
 351  4
                         resolvedSnapshots.putAll( processSnapshot( pluginDependencies ) );
 352  4
                         resolvedSnapshots.putAll( processSnapshot( reportDependencies ) );
 353  4
                         resolvedSnapshots.putAll( processSnapshot( extensionDependencies ) );
 354  4
                         break;
 355  
 
 356  
                         // project dependencies
 357  
                     case 1:
 358  18
                         resolvedSnapshots = processSnapshot( projectDependencies );
 359  18
                         break;
 360  
 
 361  
                         // plugins
 362  
                     case 2:
 363  0
                         resolvedSnapshots = processSnapshot( pluginDependencies );
 364  0
                         break;
 365  
 
 366  
                         // reports
 367  
                     case 3:
 368  0
                         resolvedSnapshots = processSnapshot( reportDependencies );
 369  0
                         break;
 370  
 
 371  
                         // extensions
 372  
                     case 4:
 373  0
                         resolvedSnapshots = processSnapshot( extensionDependencies );
 374  
                         break;
 375  
                 }
 376  
 
 377  22
                 releaseDescriptor.getResolvedSnapshotDependencies().putAll( resolvedSnapshots );
 378  
             }
 379  
         }
 380  0
         catch ( PrompterException e )
 381  
         {
 382  0
             throw new ReleaseExecutionException( e.getMessage(), e );
 383  
         }
 384  0
         catch ( VersionParseException e )
 385  
         {
 386  0
             throw new ReleaseExecutionException( e.getMessage(), e );
 387  66
         }
 388  66
     }
 389  
 
 390  
     private Map<String, Map<String, String>> processSnapshot( Set<Artifact> snapshotSet )
 391  
         throws PrompterException, VersionParseException
 392  
     {
 393  34
         Map<String, Map<String, String>> resolvedSnapshots = new HashMap<String, Map<String, String>>();
 394  34
         Iterator<Artifact> iterator = snapshotSet.iterator();
 395  
 
 396  62
         while ( iterator.hasNext() )
 397  
         {
 398  28
             Artifact currentArtifact = iterator.next();
 399  28
             String versionlessKey = ArtifactUtils.versionlessKey( currentArtifact );
 400  
 
 401  28
             Map<String, String> versionMap = new HashMap<String, String>();
 402  28
             VersionInfo versionInfo = new DefaultVersionInfo( currentArtifact.getVersion() );
 403  28
             versionMap.put( ReleaseDescriptor.ORIGINAL_VERSION, versionInfo.toString() );
 404  
 
 405  28
             prompter.showMessage(
 406  
                 "Dependency '" + versionlessKey + "' is a snapshot (" + currentArtifact.getVersion() + ")\n" );
 407  28
             String result = prompter.prompt( "Which release version should it be set to?",
 408  
                                              versionInfo.getReleaseVersionString() );
 409  28
             versionMap.put( ReleaseDescriptor.RELEASE_KEY, result );
 410  
 
 411  28
             iterator.remove();
 412  
 
 413  
             // by default, keep the same version for the dependency after release, unless it was previously newer
 414  
             // the user may opt to type in something different
 415  28
             VersionInfo nextVersionInfo = new DefaultVersionInfo( result );
 416  
 
 417  
             String nextVersion;
 418  28
             if ( nextVersionInfo.compareTo( versionInfo ) > 0 )
 419  
             {
 420  26
                 nextVersion = nextVersionInfo.toString();
 421  
             }
 422  
             else
 423  
             {
 424  2
                 nextVersion = versionInfo.toString();
 425  
             }
 426  
 
 427  28
             result = prompter.prompt( "What version should the dependency be reset to for development?", nextVersion );
 428  28
             versionMap.put( ReleaseDescriptor.DEVELOPMENT_KEY, result );
 429  
 
 430  28
             resolvedSnapshots.put( versionlessKey, versionMap );
 431  28
         }
 432  
 
 433  34
         return resolvedSnapshots;
 434  
     }
 435  
 }