1 package org.apache.maven.shared.release.phase;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Set;
32
33 import org.apache.maven.artifact.Artifact;
34 import org.apache.maven.artifact.ArtifactUtils;
35 import org.apache.maven.artifact.factory.ArtifactFactory;
36 import org.apache.maven.project.MavenProject;
37 import org.apache.maven.project.artifact.InvalidDependencyVersionException;
38 import org.apache.maven.shared.release.ReleaseExecutionException;
39 import org.apache.maven.shared.release.ReleaseFailureException;
40 import org.apache.maven.shared.release.ReleaseResult;
41 import org.apache.maven.shared.release.config.ReleaseDescriptor;
42 import org.apache.maven.shared.release.env.ReleaseEnvironment;
43 import org.apache.maven.shared.release.versions.DefaultVersionInfo;
44 import org.apache.maven.shared.release.versions.VersionInfo;
45 import org.apache.maven.shared.release.versions.VersionParseException;
46 import org.codehaus.plexus.components.interactivity.Prompter;
47 import org.codehaus.plexus.components.interactivity.PrompterException;
48
49
50
51
52
53
54
55
56
57 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
71
72
73
74 private Prompter prompter;
75
76
77
78
79
80
81 private ArtifactFactory artifactFactory;
82
83
84
85
86
87
88
89 private Set<Artifact> usedSnapshotDependencies = new HashSet<Artifact>();
90 private Set<Artifact> usedSnapshotReports = new HashSet<Artifact>();
91 private Set<Artifact> usedSnapshotExtensions = new HashSet<Artifact>();
92 private Set<Artifact> usedSnapshotPlugins = new HashSet<Artifact>();
93
94 public ReleaseResult execute( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
95 List<MavenProject> reactorProjects )
96 throws ReleaseExecutionException, ReleaseFailureException
97 {
98 ReleaseResult result = new ReleaseResult();
99
100 if ( !releaseDescriptor.isAllowTimestampedSnapshots() )
101 {
102 logInfo( result, "Checking dependencies and plugins for snapshots ..." );
103
104 Map<String, String> originalVersions = releaseDescriptor.getOriginalVersions( reactorProjects );
105
106 for ( MavenProject project : reactorProjects )
107 {
108 checkProject( project, originalVersions, releaseDescriptor );
109 }
110 }
111 else
112 {
113 logInfo( result, "Ignoring SNAPSHOT depenedencies and plugins ..." );
114 }
115 result.setResultCode( ReleaseResult.SUCCESS );
116
117 return result;
118 }
119
120 private void checkProject( MavenProject project, Map<String, String> originalVersions,
121 ReleaseDescriptor releaseDescriptor )
122 throws ReleaseFailureException, ReleaseExecutionException
123 {
124 @SuppressWarnings( "unchecked" )
125 Map<String, Artifact> artifactMap = ArtifactUtils.artifactMapByVersionlessId( project.getArtifacts() );
126
127 if ( project.getParentArtifact() != null )
128 {
129 if ( checkArtifact( project.getParentArtifact(), originalVersions, artifactMap, releaseDescriptor ) )
130 {
131 usedSnapshotDependencies.add( project.getParentArtifact() );
132 }
133 }
134
135 try
136 {
137 @SuppressWarnings( "unchecked" )
138 Set<Artifact> dependencyArtifacts = project.createArtifacts( artifactFactory, null, null );
139 checkDependencies( originalVersions, releaseDescriptor, artifactMap, dependencyArtifacts );
140 }
141 catch ( InvalidDependencyVersionException e )
142 {
143 throw new ReleaseExecutionException( "Failed to create dependency artifacts", e );
144 }
145
146
147 @SuppressWarnings( "unchecked" )
148 Set<Artifact> pluginArtifacts = project.getPluginArtifacts();
149 checkPlugins( originalVersions, releaseDescriptor, artifactMap, pluginArtifacts );
150
151
152 @SuppressWarnings( "unchecked" )
153 Set<Artifact> reportArtifacts = project.getReportArtifacts();
154 checkReports( originalVersions, releaseDescriptor, artifactMap, reportArtifacts );
155
156 @SuppressWarnings( "unchecked" )
157 Set<Artifact> extensionArtifacts = project.getExtensionArtifacts();
158 checkExtensions( originalVersions, releaseDescriptor, artifactMap, extensionArtifacts );
159
160
161
162 if ( !usedSnapshotDependencies.isEmpty() || !usedSnapshotReports.isEmpty()
163 || !usedSnapshotExtensions.isEmpty() || !usedSnapshotPlugins.isEmpty() )
164 {
165 if ( releaseDescriptor.isInteractive() )
166 {
167 resolveSnapshots( usedSnapshotDependencies, usedSnapshotReports, usedSnapshotExtensions,
168 usedSnapshotPlugins, releaseDescriptor );
169 }
170
171 if ( !usedSnapshotDependencies.isEmpty() || !usedSnapshotReports.isEmpty()
172 || !usedSnapshotExtensions.isEmpty() || !usedSnapshotPlugins.isEmpty() )
173 {
174 StringBuilder message = new StringBuilder();
175
176 printSnapshotDependencies( usedSnapshotDependencies, message );
177 printSnapshotDependencies( usedSnapshotReports, message );
178 printSnapshotDependencies( usedSnapshotExtensions, message );
179 printSnapshotDependencies( usedSnapshotPlugins, message );
180 message.append( "in project '" + project.getName() + "' (" + project.getId() + ")" );
181
182 throw new ReleaseFailureException(
183 "Can't release project due to non released dependencies :\n" + message );
184 }
185 }
186 }
187
188 private void checkPlugins( Map<String, String> originalVersions, ReleaseDescriptor releaseDescriptor,
189 Map<String, Artifact> artifactMap, Set<Artifact> pluginArtifacts )
190 throws ReleaseExecutionException
191 {
192 for ( Artifact artifact : pluginArtifacts )
193 {
194 if ( checkArtifact( artifact, originalVersions, artifactMap, releaseDescriptor ) )
195 {
196 boolean addToFailures;
197
198 if ( "org.apache.maven.plugins".equals( artifact.getGroupId() ) && "maven-release-plugin".equals(
199 artifact.getArtifactId() ) )
200 {
201
202
203 if ( releaseDescriptor.isSnapshotReleasePluginAllowed() )
204 {
205 addToFailures = false;
206 }
207 else if ( releaseDescriptor.isInteractive() )
208 {
209 try
210 {
211 String result;
212 if ( !releaseDescriptor.isSnapshotReleasePluginAllowed() )
213 {
214 prompter.showMessage( "This project relies on a SNAPSHOT of the release plugin. "
215 + "This may be necessary during testing.\n" );
216 result = prompter.prompt( "Do you want to continue with the release?",
217 Arrays.asList( "yes", "no" ), "no" );
218 }
219 else
220 {
221 result = "yes";
222 }
223
224 if ( result.toLowerCase( Locale.ENGLISH ).startsWith( "y" ) )
225 {
226 addToFailures = false;
227 releaseDescriptor.setSnapshotReleasePluginAllowed( true );
228 }
229 else
230 {
231 addToFailures = true;
232 }
233 }
234 catch ( PrompterException e )
235 {
236 throw new ReleaseExecutionException( e.getMessage(), e );
237 }
238 }
239 else
240 {
241 addToFailures = true;
242 }
243 }
244 else
245 {
246 addToFailures = true;
247 }
248
249 if ( addToFailures )
250 {
251 usedSnapshotPlugins.add( artifact );
252 }
253 }
254 }
255 }
256
257 private void checkDependencies( Map<String, String> originalVersions, ReleaseDescriptor releaseDescriptor,
258 Map<String, Artifact> artifactMap, Set<Artifact> dependencyArtifacts )
259 {
260 for ( Artifact artifact : dependencyArtifacts )
261 {
262 if ( checkArtifact( artifact, originalVersions, artifactMap, releaseDescriptor ) )
263 {
264 usedSnapshotDependencies.add( getArtifactFromMap( artifact, artifactMap ) );
265 }
266 }
267 }
268
269 private void checkReports( Map<String, String> originalVersions, ReleaseDescriptor releaseDescriptor,
270 Map<String, Artifact> artifactMap, Set<Artifact> reportArtifacts )
271 {
272 for ( Artifact artifact : reportArtifacts )
273 {
274 if ( checkArtifact( artifact, originalVersions, artifactMap, releaseDescriptor ) )
275 {
276
277 usedSnapshotReports.add( artifact );
278 }
279 }
280 }
281
282 private void checkExtensions( Map<String, String> originalVersions, ReleaseDescriptor releaseDescriptor,
283 Map<String, Artifact> artifactMap, Set<Artifact> extensionArtifacts )
284 {
285 for ( Artifact artifact : extensionArtifacts )
286 {
287 if ( checkArtifact( artifact, originalVersions, artifactMap, releaseDescriptor ) )
288 {
289 usedSnapshotExtensions.add( artifact );
290 }
291 }
292 }
293
294 private static boolean checkArtifact( Artifact artifact, Map<String, String> originalVersions,
295 Map<String, Artifact> artifactMapByVersionlessId,
296 ReleaseDescriptor releaseDescriptor )
297 {
298 Artifact checkArtifact = getArtifactFromMap( artifact, artifactMapByVersionlessId );
299
300 return checkArtifact( checkArtifact, originalVersions, releaseDescriptor );
301 }
302
303 private static Artifact getArtifactFromMap( Artifact artifact, Map<String, Artifact> artifactMapByVersionlessId )
304 {
305 String versionlessId = ArtifactUtils.versionlessKey( artifact );
306 Artifact checkArtifact = artifactMapByVersionlessId.get( versionlessId );
307
308 if ( checkArtifact == null )
309 {
310 checkArtifact = artifact;
311 }
312 return checkArtifact;
313 }
314
315 private static boolean checkArtifact( Artifact artifact, Map<String, String> originalVersions,
316 ReleaseDescriptor releaseDescriptor )
317 {
318 String versionlessArtifactKey = ArtifactUtils.versionlessKey( artifact.getGroupId(), artifact.getArtifactId() );
319
320
321
322 boolean result =
323 artifact.isSnapshot()
324 && !artifact.getBaseVersion().equals( originalVersions.get( versionlessArtifactKey ) );
325
326
327
328 if ( result && releaseDescriptor.isAllowTimestampedSnapshots() )
329 {
330 result = artifact.getVersion().indexOf( Artifact.SNAPSHOT_VERSION ) >= 0;
331 }
332
333 return result;
334 }
335
336 public ReleaseResult simulate( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
337 List<MavenProject> reactorProjects )
338 throws ReleaseExecutionException, ReleaseFailureException
339 {
340
341 return execute( releaseDescriptor, releaseEnvironment, reactorProjects );
342 }
343
344 public void setPrompter( Prompter prompter )
345 {
346 this.prompter = prompter;
347 }
348
349 private StringBuilder printSnapshotDependencies( Set<Artifact> snapshotsSet, StringBuilder message )
350 {
351 List<Artifact> snapshotsList = new ArrayList<Artifact>( snapshotsSet );
352
353 Collections.sort( snapshotsList );
354
355 for ( Artifact artifact : snapshotsList )
356 {
357 message.append( " " );
358
359 message.append( artifact );
360
361 message.append( "\n" );
362 }
363
364 return message;
365 }
366
367 private void resolveSnapshots( Set<Artifact> projectDependencies, Set<Artifact> reportDependencies,
368 Set<Artifact> extensionDependencies, Set<Artifact> pluginDependencies,
369 ReleaseDescriptor releaseDescriptor )
370 throws ReleaseExecutionException
371 {
372 try
373 {
374 prompter.showMessage( RESOLVE_SNAPSHOT_MESSAGE );
375 String result =
376 prompter.prompt( RESOLVE_SNAPSHOT_PROMPT, Arrays.asList( "yes", "no" ), "no" );
377
378 if ( result.toLowerCase( Locale.ENGLISH ).startsWith( "y" ) )
379 {
380 Map<String, Map<String, String>> resolvedSnapshots = null;
381 prompter.showMessage( RESOLVE_SNAPSHOT_TYPE_MESSAGE );
382 result = prompter.prompt( RESOLVE_SNAPSHOT_TYPE_PROMPT,
383 Arrays.asList( "0", "1", "2", "3" ), "1" );
384
385 switch ( Integer.parseInt( result.toLowerCase( Locale.ENGLISH ) ) )
386 {
387
388 case 0:
389 resolvedSnapshots = processSnapshot( projectDependencies );
390 resolvedSnapshots.putAll( processSnapshot( pluginDependencies ) );
391 resolvedSnapshots.putAll( processSnapshot( reportDependencies ) );
392 resolvedSnapshots.putAll( processSnapshot( extensionDependencies ) );
393 break;
394
395
396 case 1:
397 resolvedSnapshots = processSnapshot( projectDependencies );
398 break;
399
400
401 case 2:
402 resolvedSnapshots = processSnapshot( pluginDependencies );
403 break;
404
405
406 case 3:
407 resolvedSnapshots = processSnapshot( reportDependencies );
408 break;
409
410
411 case 4:
412 resolvedSnapshots = processSnapshot( extensionDependencies );
413 break;
414
415 default:
416 }
417
418 releaseDescriptor.getResolvedSnapshotDependencies().putAll( resolvedSnapshots );
419 }
420 }
421 catch ( PrompterException e )
422 {
423 throw new ReleaseExecutionException( e.getMessage(), e );
424 }
425 catch ( VersionParseException e )
426 {
427 throw new ReleaseExecutionException( e.getMessage(), e );
428 }
429 }
430
431 private Map<String, Map<String, String>> processSnapshot( Set<Artifact> snapshotSet )
432 throws PrompterException, VersionParseException
433 {
434 Map<String, Map<String, String>> resolvedSnapshots = new HashMap<String, Map<String, String>>();
435 Iterator<Artifact> iterator = snapshotSet.iterator();
436
437 while ( iterator.hasNext() )
438 {
439 Artifact currentArtifact = iterator.next();
440 String versionlessKey = ArtifactUtils.versionlessKey( currentArtifact );
441
442 Map<String, String> versionMap = new HashMap<String, String>();
443 VersionInfo versionInfo = new DefaultVersionInfo( currentArtifact.getVersion() );
444 versionMap.put( ReleaseDescriptor.ORIGINAL_VERSION, versionInfo.toString() );
445
446 prompter.showMessage(
447 "Dependency '" + versionlessKey + "' is a snapshot (" + currentArtifact.getVersion() + ")\n" );
448 String result = prompter.prompt( "Which release version should it be set to?",
449 versionInfo.getReleaseVersionString() );
450 versionMap.put( ReleaseDescriptor.RELEASE_KEY, result );
451
452 iterator.remove();
453
454
455
456 VersionInfo nextVersionInfo = new DefaultVersionInfo( result );
457
458 String nextVersion;
459 if ( nextVersionInfo.compareTo( versionInfo ) > 0 )
460 {
461 nextVersion = nextVersionInfo.toString();
462 }
463 else
464 {
465 nextVersion = versionInfo.toString();
466 }
467
468 result = prompter.prompt( "What version should the dependency be reset to for development?", nextVersion );
469 versionMap.put( ReleaseDescriptor.DEVELOPMENT_KEY, result );
470
471 resolvedSnapshots.put( versionlessKey, versionMap );
472 }
473
474 return resolvedSnapshots;
475 }
476 }