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