1 package org.apache.maven.plugin.version.internal;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.TreeSet;
29
30 import org.apache.maven.artifact.repository.metadata.Metadata;
31 import org.apache.maven.artifact.repository.metadata.Versioning;
32 import org.apache.maven.artifact.repository.metadata.io.MetadataReader;
33 import org.apache.maven.model.Build;
34 import org.apache.maven.model.Plugin;
35 import org.apache.maven.plugin.MavenPluginManager;
36 import org.apache.maven.plugin.PluginResolutionException;
37 import org.apache.maven.plugin.descriptor.PluginDescriptor;
38 import org.apache.maven.plugin.version.PluginVersionRequest;
39 import org.apache.maven.plugin.version.PluginVersionResolutionException;
40 import org.apache.maven.plugin.version.PluginVersionResolver;
41 import org.apache.maven.plugin.version.PluginVersionResult;
42 import org.codehaus.plexus.component.annotations.Component;
43 import org.codehaus.plexus.component.annotations.Requirement;
44 import org.codehaus.plexus.logging.Logger;
45 import org.codehaus.plexus.util.StringUtils;
46 import org.sonatype.aether.RepositoryEvent.EventType;
47 import org.sonatype.aether.RepositoryListener;
48 import org.sonatype.aether.RepositorySystem;
49 import org.sonatype.aether.RepositorySystemSession;
50 import org.sonatype.aether.RequestTrace;
51 import org.sonatype.aether.repository.ArtifactRepository;
52 import org.sonatype.aether.repository.RemoteRepository;
53 import org.sonatype.aether.resolution.MetadataRequest;
54 import org.sonatype.aether.resolution.MetadataResult;
55 import org.sonatype.aether.util.DefaultRequestTrace;
56 import org.sonatype.aether.util.listener.DefaultRepositoryEvent;
57 import org.sonatype.aether.util.metadata.DefaultMetadata;
58 import org.sonatype.aether.util.version.GenericVersionScheme;
59 import org.sonatype.aether.version.InvalidVersionSpecificationException;
60 import org.sonatype.aether.version.Version;
61 import org.sonatype.aether.version.VersionScheme;
62
63
64
65
66
67
68
69 @Component( role = PluginVersionResolver.class )
70 public class DefaultPluginVersionResolver
71 implements PluginVersionResolver
72 {
73
74 private static final String REPOSITORY_CONTEXT = "plugin";
75
76 @Requirement
77 private Logger logger;
78
79 @Requirement
80 private RepositorySystem repositorySystem;
81
82 @Requirement
83 private MetadataReader metadataReader;
84
85 @Requirement
86 private MavenPluginManager pluginManager;
87
88 public PluginVersionResult resolve( PluginVersionRequest request )
89 throws PluginVersionResolutionException
90 {
91 logger.debug( "Resolving plugin version for " + request.getGroupId() + ":" + request.getArtifactId() );
92
93 PluginVersionResult result = resolveFromProject( request );
94
95 if ( result == null )
96 {
97 result = resolveFromRepository( request );
98
99 if ( logger.isDebugEnabled() )
100 {
101 logger.debug( "Resolved plugin version for " + request.getGroupId() + ":" + request.getArtifactId()
102 + " to " + result.getVersion() + " from repository " + result.getRepository() );
103 }
104 }
105 else if ( logger.isDebugEnabled() )
106 {
107 logger.debug( "Resolved plugin version for " + request.getGroupId() + ":" + request.getArtifactId()
108 + " to " + result.getVersion() + " from POM " + request.getPom() );
109 }
110
111 return result;
112 }
113
114 private PluginVersionResult resolveFromRepository( PluginVersionRequest request )
115 throws PluginVersionResolutionException
116 {
117 RequestTrace trace = DefaultRequestTrace.newChild( null, request );
118
119 DefaultPluginVersionResult result = new DefaultPluginVersionResult();
120
121 org.sonatype.aether.metadata.Metadata metadata =
122 new DefaultMetadata( request.getGroupId(), request.getArtifactId(), "maven-metadata.xml",
123 DefaultMetadata.Nature.RELEASE_OR_SNAPSHOT );
124
125 List<MetadataRequest> requests = new ArrayList<MetadataRequest>();
126
127 requests.add( new MetadataRequest( metadata, null, REPOSITORY_CONTEXT ).setTrace( trace ) );
128
129 for ( RemoteRepository repository : request.getRepositories() )
130 {
131 requests.add( new MetadataRequest( metadata, repository, REPOSITORY_CONTEXT ).setTrace( trace ) );
132 }
133
134 List<MetadataResult> results = repositorySystem.resolveMetadata( request.getRepositorySession(), requests );
135
136 Versions versions = new Versions();
137
138 for ( MetadataResult res : results )
139 {
140 ArtifactRepository repository = res.getRequest().getRepository();
141 if ( repository == null )
142 {
143 repository = request.getRepositorySession().getLocalRepository();
144 }
145
146 mergeMetadata( request.getRepositorySession(), trace, versions, res.getMetadata(), repository );
147 }
148
149 selectVersion( result, request, versions );
150
151 return result;
152 }
153
154 private void selectVersion( DefaultPluginVersionResult result, PluginVersionRequest request, Versions versions )
155 throws PluginVersionResolutionException
156 {
157 String version = null;
158 ArtifactRepository repo = null;
159
160 if ( StringUtils.isNotEmpty( versions.releaseVersion ) )
161 {
162 version = versions.releaseVersion;
163 repo = versions.releaseRepository;
164 }
165 else if ( StringUtils.isNotEmpty( versions.latestVersion ) )
166 {
167 version = versions.latestVersion;
168 repo = versions.latestRepository;
169 }
170 if ( version != null && !isCompatible( request, version ) )
171 {
172 versions.versions.remove( version );
173 version = null;
174 }
175
176 if ( version == null )
177 {
178 VersionScheme versionScheme = new GenericVersionScheme();
179
180 TreeSet<Version> releases = new TreeSet<Version>( Collections.reverseOrder() );
181 TreeSet<Version> snapshots = new TreeSet<Version>( Collections.reverseOrder() );
182
183 for ( String ver : versions.versions.keySet() )
184 {
185 try
186 {
187 Version v = versionScheme.parseVersion( ver );
188
189 if ( ver.endsWith( "-SNAPSHOT" ) )
190 {
191 snapshots.add( v );
192 }
193 else
194 {
195 releases.add( v );
196 }
197 }
198 catch ( InvalidVersionSpecificationException e )
199 {
200 continue;
201 }
202 }
203
204 for ( Version v : releases )
205 {
206 String ver = v.toString();
207 if ( isCompatible( request, ver ) )
208 {
209 version = ver;
210 repo = versions.versions.get( version );
211 break;
212 }
213 }
214
215 if ( version == null )
216 {
217 for ( Version v : snapshots )
218 {
219 String ver = v.toString();
220 if ( isCompatible( request, ver ) )
221 {
222 version = ver;
223 repo = versions.versions.get( version );
224 break;
225 }
226 }
227 }
228 }
229
230 if ( version != null )
231 {
232 result.setVersion( version );
233 result.setRepository( repo );
234 }
235 else
236 {
237 throw new PluginVersionResolutionException( request.getGroupId(), request.getArtifactId(),
238 request.getRepositorySession().getLocalRepository(),
239 request.getRepositories(),
240 "Plugin not found in any plugin repository" );
241 }
242 }
243
244 private boolean isCompatible( PluginVersionRequest request, String version )
245 {
246 Plugin plugin = new Plugin();
247 plugin.setGroupId( request.getGroupId() );
248 plugin.setArtifactId( request.getArtifactId() );
249 plugin.setVersion( version );
250
251 PluginDescriptor pluginDescriptor;
252
253 try
254 {
255 pluginDescriptor =
256 pluginManager.getPluginDescriptor( plugin, request.getRepositories(), request.getRepositorySession() );
257 }
258 catch ( PluginResolutionException e )
259 {
260 logger.debug( "Ignoring unresolvable plugin version " + version, e );
261 return false;
262 }
263 catch ( Exception e )
264 {
265
266 return true;
267 }
268
269 try
270 {
271 pluginManager.checkRequiredMavenVersion( pluginDescriptor );
272 }
273 catch ( Exception e )
274 {
275 logger.debug( "Ignoring incompatible plugin version " + version + ": " + e.getMessage() );
276 return false;
277 }
278
279 return true;
280 }
281
282 private void mergeMetadata( RepositorySystemSession session, RequestTrace trace, Versions versions,
283 org.sonatype.aether.metadata.Metadata metadata, ArtifactRepository repository )
284 {
285 if ( metadata != null && metadata.getFile() != null && metadata.getFile().isFile() )
286 {
287 try
288 {
289 Map<String, ?> options = Collections.singletonMap( MetadataReader.IS_STRICT, Boolean.FALSE );
290
291 Metadata repoMetadata = metadataReader.read( metadata.getFile(), options );
292
293 mergeMetadata( versions, repoMetadata, repository );
294 }
295 catch ( IOException e )
296 {
297 invalidMetadata( session, trace, metadata, repository, e );
298 }
299 }
300 }
301
302 private void invalidMetadata( RepositorySystemSession session, RequestTrace trace,
303 org.sonatype.aether.metadata.Metadata metadata, ArtifactRepository repository,
304 Exception exception )
305 {
306 RepositoryListener listener = session.getRepositoryListener();
307 if ( listener != null )
308 {
309 DefaultRepositoryEvent event = new DefaultRepositoryEvent( EventType.METADATA_INVALID, session, trace );
310 event.setMetadata( metadata );
311 event.setException( exception );
312 event.setRepository( repository );
313 listener.metadataInvalid( event );
314 }
315 }
316
317 private void mergeMetadata( Versions versions, Metadata source, ArtifactRepository repository )
318 {
319 Versioning versioning = source.getVersioning();
320 if ( versioning != null )
321 {
322 String timestamp = StringUtils.clean( versioning.getLastUpdated() );
323
324 if ( StringUtils.isNotEmpty( versioning.getRelease() )
325 && timestamp.compareTo( versions.releaseTimestamp ) > 0 )
326 {
327 versions.releaseVersion = versioning.getRelease();
328 versions.releaseTimestamp = timestamp;
329 versions.releaseRepository = repository;
330 }
331
332 if ( StringUtils.isNotEmpty( versioning.getLatest() )
333 && timestamp.compareTo( versions.latestTimestamp ) > 0 )
334 {
335 versions.latestVersion = versioning.getLatest();
336 versions.latestTimestamp = timestamp;
337 versions.latestRepository = repository;
338 }
339
340 for ( String version : versioning.getVersions() )
341 {
342 if ( !versions.versions.containsKey( version ) )
343 {
344 versions.versions.put( version, repository );
345 }
346 }
347 }
348 }
349
350 private PluginVersionResult resolveFromProject( PluginVersionRequest request )
351 {
352 PluginVersionResult result = null;
353
354 if ( request.getPom() != null && request.getPom().getBuild() != null )
355 {
356 Build build = request.getPom().getBuild();
357
358 result = resolveFromProject( request, build.getPlugins() );
359
360 if ( result == null && build.getPluginManagement() != null )
361 {
362 result = resolveFromProject( request, build.getPluginManagement().getPlugins() );
363 }
364 }
365
366 return result;
367 }
368
369 private PluginVersionResult resolveFromProject( PluginVersionRequest request, List<Plugin> plugins )
370 {
371 for ( Plugin plugin : plugins )
372 {
373 if ( request.getGroupId().equals( plugin.getGroupId() )
374 && request.getArtifactId().equals( plugin.getArtifactId() ) )
375 {
376 if ( plugin.getVersion() != null )
377 {
378 return new DefaultPluginVersionResult( plugin.getVersion() );
379 }
380 else
381 {
382 return null;
383 }
384 }
385 }
386 return null;
387 }
388
389 static class Versions
390 {
391
392 String releaseVersion = "";
393
394 String releaseTimestamp = "";
395
396 ArtifactRepository releaseRepository;
397
398 String latestVersion = "";
399
400 String latestTimestamp = "";
401
402 ArtifactRepository latestRepository;
403
404 Map<String, ArtifactRepository> versions = new LinkedHashMap<String, ArtifactRepository>();
405
406 }
407
408 }