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