View Javadoc

1   package org.apache.maven.plugin.version.internal;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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   * Resolves a version for a plugin.
63   * 
64   * @since 3.0
65   * @author Benjamin Bentmann
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             // ignore for now and delay failure to higher level processing
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 }