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
203 for ( Version v : releases )
204 {
205 String ver = v.toString();
206 if ( isCompatible( request, ver ) )
207 {
208 version = ver;
209 repo = versions.versions.get( version );
210 break;
211 }
212 }
213
214 if ( version == null )
215 {
216 for ( Version v : snapshots )
217 {
218 String ver = v.toString();
219 if ( isCompatible( request, ver ) )
220 {
221 version = ver;
222 repo = versions.versions.get( version );
223 break;
224 }
225 }
226 }
227 }
228
229 if ( version != null )
230 {
231 result.setVersion( version );
232 result.setRepository( repo );
233 }
234 else
235 {
236 throw new PluginVersionResolutionException( request.getGroupId(), request.getArtifactId(),
237 request.getRepositorySession().getLocalRepository(),
238 request.getRepositories(),
239 "Plugin not found in any plugin repository" );
240 }
241 }
242
243 private boolean isCompatible( PluginVersionRequest request, String version )
244 {
245 Plugin plugin = new Plugin();
246 plugin.setGroupId( request.getGroupId() );
247 plugin.setArtifactId( request.getArtifactId() );
248 plugin.setVersion( version );
249
250 PluginDescriptor pluginDescriptor;
251
252 try
253 {
254 pluginDescriptor =
255 pluginManager.getPluginDescriptor( plugin, request.getRepositories(), request.getRepositorySession() );
256 }
257 catch ( PluginResolutionException e )
258 {
259 logger.debug( "Ignoring unresolvable plugin version " + version, e );
260 return false;
261 }
262 catch ( Exception e )
263 {
264
265 return true;
266 }
267
268 try
269 {
270 pluginManager.checkRequiredMavenVersion( pluginDescriptor );
271 }
272 catch ( Exception e )
273 {
274 logger.debug( "Ignoring incompatible plugin version " + version + ": " + e.getMessage() );
275 return false;
276 }
277
278 return true;
279 }
280
281 private void mergeMetadata( RepositorySystemSession session, RequestTrace trace, Versions versions,
282 org.eclipse.aether.metadata.Metadata metadata, ArtifactRepository repository )
283 {
284 if ( metadata != null && metadata.getFile() != null && metadata.getFile().isFile() )
285 {
286 try
287 {
288 Map<String, ?> options = Collections.singletonMap( MetadataReader.IS_STRICT, Boolean.FALSE );
289
290 Metadata repoMetadata = metadataReader.read( metadata.getFile(), options );
291
292 mergeMetadata( versions, repoMetadata, repository );
293 }
294 catch ( IOException e )
295 {
296 invalidMetadata( session, trace, metadata, repository, e );
297 }
298 }
299 }
300
301 private void invalidMetadata( RepositorySystemSession session, RequestTrace trace,
302 org.eclipse.aether.metadata.Metadata metadata, ArtifactRepository repository,
303 Exception exception )
304 {
305 RepositoryListener listener = session.getRepositoryListener();
306 if ( listener != null )
307 {
308 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INVALID );
309 event.setTrace( trace );
310 event.setMetadata( metadata );
311 event.setException( exception );
312 event.setRepository( repository );
313 listener.metadataInvalid( event.build() );
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 }