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