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