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