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.RequestTrace;
51  import org.sonatype.aether.repository.ArtifactRepository;
52  import org.sonatype.aether.repository.RemoteRepository;
53  import org.sonatype.aether.resolution.MetadataRequest;
54  import org.sonatype.aether.resolution.MetadataResult;
55  import org.sonatype.aether.util.DefaultRequestTrace;
56  import org.sonatype.aether.util.listener.DefaultRepositoryEvent;
57  import org.sonatype.aether.util.metadata.DefaultMetadata;
58  import org.sonatype.aether.util.version.GenericVersionScheme;
59  import org.sonatype.aether.version.InvalidVersionSpecificationException;
60  import org.sonatype.aether.version.Version;
61  import org.sonatype.aether.version.VersionScheme;
62  
63  /**
64   * Resolves a version for a plugin.
65   * 
66   * @since 3.0
67   * @author Benjamin Bentmann
68   */
69  @Component( role = PluginVersionResolver.class )
70  public class DefaultPluginVersionResolver
71      implements PluginVersionResolver
72  {
73  
74      private static final String REPOSITORY_CONTEXT = "plugin";
75  
76      @Requirement
77      private Logger logger;
78  
79      @Requirement
80      private RepositorySystem repositorySystem;
81  
82      @Requirement
83      private MetadataReader metadataReader;
84  
85      @Requirement
86      private MavenPluginManager pluginManager;
87  
88      public PluginVersionResult resolve( PluginVersionRequest request )
89          throws PluginVersionResolutionException
90      {
91          logger.debug( "Resolving plugin version for " + request.getGroupId() + ":" + request.getArtifactId() );
92  
93          PluginVersionResult result = resolveFromProject( request );
94  
95          if ( result == null )
96          {
97              result = resolveFromRepository( request );
98  
99              if ( logger.isDebugEnabled() )
100             {
101                 logger.debug( "Resolved plugin version for " + request.getGroupId() + ":" + request.getArtifactId()
102                     + " to " + result.getVersion() + " from repository " + result.getRepository() );
103             }
104         }
105         else if ( logger.isDebugEnabled() )
106         {
107             logger.debug( "Resolved plugin version for " + request.getGroupId() + ":" + request.getArtifactId()
108                 + " to " + result.getVersion() + " from POM " + request.getPom() );
109         }
110 
111         return result;
112     }
113 
114     private PluginVersionResult resolveFromRepository( PluginVersionRequest request )
115         throws PluginVersionResolutionException
116     {
117         RequestTrace trace = DefaultRequestTrace.newChild( null, request );
118 
119         DefaultPluginVersionResult result = new DefaultPluginVersionResult();
120 
121         org.sonatype.aether.metadata.Metadata metadata =
122             new DefaultMetadata( request.getGroupId(), request.getArtifactId(), "maven-metadata.xml",
123                                  DefaultMetadata.Nature.RELEASE_OR_SNAPSHOT );
124 
125         List<MetadataRequest> requests = new ArrayList<MetadataRequest>();
126 
127         requests.add( new MetadataRequest( metadata, null, REPOSITORY_CONTEXT ).setTrace( trace ) );
128 
129         for ( RemoteRepository repository : request.getRepositories() )
130         {
131             requests.add( new MetadataRequest( metadata, repository, REPOSITORY_CONTEXT ).setTrace( trace ) );
132         }
133 
134         List<MetadataResult> results = repositorySystem.resolveMetadata( request.getRepositorySession(), requests );
135 
136         Versions versions = new Versions();
137 
138         for ( MetadataResult res : results )
139         {
140             ArtifactRepository repository = res.getRequest().getRepository();
141             if ( repository == null )
142             {
143                 repository = request.getRepositorySession().getLocalRepository();
144             }
145 
146             mergeMetadata( request.getRepositorySession(), trace, versions, res.getMetadata(), repository );
147         }
148 
149         selectVersion( result, request, versions );
150 
151         return result;
152     }
153 
154     private void selectVersion( DefaultPluginVersionResult result, PluginVersionRequest request, Versions versions )
155         throws PluginVersionResolutionException
156     {
157         String version = null;
158         ArtifactRepository repo = null;
159 
160         if ( StringUtils.isNotEmpty( versions.releaseVersion ) )
161         {
162             version = versions.releaseVersion;
163             repo = versions.releaseRepository;
164         }
165         else if ( StringUtils.isNotEmpty( versions.latestVersion ) )
166         {
167             version = versions.latestVersion;
168             repo = versions.latestRepository;
169         }
170         if ( version != null && !isCompatible( request, version ) )
171         {
172             versions.versions.remove( version );
173             version = null;
174         }
175 
176         if ( version == null )
177         {
178             VersionScheme versionScheme = new GenericVersionScheme();
179 
180             TreeSet<Version> releases = new TreeSet<Version>( Collections.reverseOrder() );
181             TreeSet<Version> snapshots = new TreeSet<Version>( Collections.reverseOrder() );
182 
183             for ( String ver : versions.versions.keySet() )
184             {
185                 try
186                 {
187                     Version v = versionScheme.parseVersion( ver );
188 
189                     if ( ver.endsWith( "-SNAPSHOT" ) )
190                     {
191                         snapshots.add( v );
192                     }
193                     else
194                     {
195                         releases.add( v );
196                     }
197                 }
198                 catch ( InvalidVersionSpecificationException e )
199                 {
200                     continue;
201                 }
202             }
203 
204             for ( Version v : releases )
205             {
206                 String ver = v.toString();
207                 if ( isCompatible( request, ver ) )
208                 {
209                     version = ver;
210                     repo = versions.versions.get( version );
211                     break;
212                 }
213             }
214 
215             if ( version == null )
216             {
217                 for ( Version v : snapshots )
218                 {
219                     String ver = v.toString();
220                     if ( isCompatible( request, ver ) )
221                     {
222                         version = ver;
223                         repo = versions.versions.get( version );
224                         break;
225                     }
226                 }
227             }
228         }
229 
230         if ( version != null )
231         {
232             result.setVersion( version );
233             result.setRepository( repo );
234         }
235         else
236         {
237             throw new PluginVersionResolutionException( request.getGroupId(), request.getArtifactId(),
238                                                         request.getRepositorySession().getLocalRepository(),
239                                                         request.getRepositories(),
240                                                         "Plugin not found in any plugin repository" );
241         }
242     }
243 
244     private boolean isCompatible( PluginVersionRequest request, String version )
245     {
246         Plugin plugin = new Plugin();
247         plugin.setGroupId( request.getGroupId() );
248         plugin.setArtifactId( request.getArtifactId() );
249         plugin.setVersion( version );
250 
251         PluginDescriptor pluginDescriptor;
252 
253         try
254         {
255             pluginDescriptor =
256                 pluginManager.getPluginDescriptor( plugin, request.getRepositories(), request.getRepositorySession() );
257         }
258         catch ( PluginResolutionException e )
259         {
260             logger.debug( "Ignoring unresolvable plugin version " + version, e );
261             return false;
262         }
263         catch ( Exception e )
264         {
265             // ignore for now and delay failure to higher level processing
266             return true;
267         }
268 
269         try
270         {
271             pluginManager.checkRequiredMavenVersion( pluginDescriptor );
272         }
273         catch ( Exception e )
274         {
275             logger.debug( "Ignoring incompatible plugin version " + version + ": " + e.getMessage() );
276             return false;
277         }
278 
279         return true;
280     }
281 
282     private void mergeMetadata( RepositorySystemSession session, RequestTrace trace, Versions versions,
283                                 org.sonatype.aether.metadata.Metadata metadata, ArtifactRepository repository )
284     {
285         if ( metadata != null && metadata.getFile() != null && metadata.getFile().isFile() )
286         {
287             try
288             {
289                 Map<String, ?> options = Collections.singletonMap( MetadataReader.IS_STRICT, Boolean.FALSE );
290 
291                 Metadata repoMetadata = metadataReader.read( metadata.getFile(), options );
292 
293                 mergeMetadata( versions, repoMetadata, repository );
294             }
295             catch ( IOException e )
296             {
297                 invalidMetadata( session, trace, metadata, repository, e );
298             }
299         }
300     }
301 
302     private void invalidMetadata( RepositorySystemSession session, RequestTrace trace,
303                                   org.sonatype.aether.metadata.Metadata metadata, ArtifactRepository repository,
304                                   Exception exception )
305     {
306         RepositoryListener listener = session.getRepositoryListener();
307         if ( listener != null )
308         {
309             DefaultRepositoryEvent event = new DefaultRepositoryEvent( EventType.METADATA_INVALID, session, trace );
310             event.setMetadata( metadata );
311             event.setException( exception );
312             event.setRepository( repository );
313             listener.metadataInvalid( event );
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 }