View Javadoc

1   package org.apache.maven.plugin.version;
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 org.apache.maven.artifact.Artifact;
23  import org.apache.maven.artifact.ArtifactUtils;
24  import org.apache.maven.artifact.factory.ArtifactFactory;
25  import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
26  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
27  import org.apache.maven.artifact.metadata.ResolutionGroup;
28  import org.apache.maven.artifact.repository.ArtifactRepository;
29  import org.apache.maven.artifact.versioning.ArtifactVersion;
30  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
31  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
32  import org.apache.maven.artifact.versioning.VersionRange;
33  import org.apache.maven.execution.RuntimeInformation;
34  import org.apache.maven.model.Plugin;
35  import org.apache.maven.model.ReportPlugin;
36  import org.apache.maven.plugin.InvalidPluginException;
37  import org.apache.maven.plugin.registry.MavenPluginRegistryBuilder;
38  import org.apache.maven.plugin.registry.PluginRegistry;
39  import org.apache.maven.plugin.registry.PluginRegistryUtils;
40  import org.apache.maven.plugin.registry.TrackableBase;
41  import org.apache.maven.plugin.registry.io.xpp3.PluginRegistryXpp3Writer;
42  import org.apache.maven.project.MavenProject;
43  import org.apache.maven.project.MavenProjectBuilder;
44  import org.apache.maven.project.ProjectBuildingException;
45  import org.apache.maven.settings.RuntimeInfo;
46  import org.apache.maven.settings.Settings;
47  import org.codehaus.plexus.components.interactivity.InputHandler;
48  import org.codehaus.plexus.logging.AbstractLogEnabled;
49  import org.codehaus.plexus.util.IOUtil;
50  import org.codehaus.plexus.util.StringUtils;
51  import org.codehaus.plexus.util.WriterFactory;
52  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
53  
54  import java.io.File;
55  import java.io.IOException;
56  import java.io.Writer;
57  import java.text.ParseException;
58  import java.text.SimpleDateFormat;
59  import java.util.Date;
60  import java.util.HashMap;
61  import java.util.Iterator;
62  import java.util.List;
63  import java.util.Map;
64  
65  public class DefaultPluginVersionManager
66      extends AbstractLogEnabled
67      implements PluginVersionManager
68  {
69      private MavenPluginRegistryBuilder mavenPluginRegistryBuilder;
70  
71      private ArtifactFactory artifactFactory;
72  
73      private InputHandler inputHandler;
74  
75      private ArtifactMetadataSource artifactMetadataSource;
76  
77      // TODO: [jc] Revisit to remove this piece of state. PLUGIN REGISTRY MAY BE UPDATED ON DISK OUT-OF-PROCESS!
78      private PluginRegistry pluginRegistry;
79  
80      private MavenProjectBuilder mavenProjectBuilder;
81  
82      private RuntimeInformation runtimeInformation;
83  
84      // TODO: Revisit to remove this piece of state. PLUGIN REGISTRY MAY BE UPDATED ON DISK OUT-OF-PROCESS!
85      private Map resolvedMetaVersions = new HashMap();
86  
87      public String resolvePluginVersion( String groupId, String artifactId, MavenProject project, Settings settings,
88                                          ArtifactRepository localRepository )
89          throws PluginVersionResolutionException, InvalidPluginException, PluginVersionNotFoundException
90      {
91          return resolvePluginVersion( groupId, artifactId, project, settings, localRepository, false );
92      }
93  
94      public String resolveReportPluginVersion( String groupId, String artifactId, MavenProject project,
95                                                Settings settings, ArtifactRepository localRepository )
96          throws PluginVersionResolutionException, InvalidPluginException, PluginVersionNotFoundException
97      {
98          return resolvePluginVersion( groupId, artifactId, project, settings, localRepository, true );
99      }
100 
101     private String resolvePluginVersion( String groupId, String artifactId, MavenProject project, Settings settings,
102                                          ArtifactRepository localRepository, boolean resolveAsReportPlugin )
103         throws PluginVersionResolutionException, InvalidPluginException, PluginVersionNotFoundException
104     {
105         // first pass...if the plugin is specified in the pom, try to retrieve the version from there.
106         String version = getVersionFromPluginConfig( groupId, artifactId, project, resolveAsReportPlugin );
107 
108         // NOTE: We CANNOT check the current project version here, so delay it until later.
109         // It will prevent plugins from building themselves, if they are part of the lifecycle mapping.
110 
111         // if there was no explicit version, try for one in the reactor
112         if ( version == null )
113         {
114             if ( project.getProjectReferences() != null )
115             {
116                 String refId = ArtifactUtils.versionlessKey( groupId, artifactId );
117                 MavenProject ref = (MavenProject) project.getProjectReferences().get( refId );
118                 if ( ref != null )
119                 {
120                     version = ref.getVersion();
121                 }
122             }
123         }
124 
125         // we're NEVER going to persist POM-derived plugin versions.
126         String updatedVersion = null;
127 
128         // we're not going to prompt the user to accept a plugin update until we find one.
129         boolean promptToPersist = false;
130 
131         RuntimeInfo settingsRTInfo = settings.getRuntimeInfo();
132 
133         // determine the behavior WRT prompting the user and installing plugin updates.
134         Boolean pluginUpdateOverride = settingsRTInfo.getPluginUpdateOverride();
135 
136         // second pass...if we're using the plugin registry, and the plugin is listed in the plugin-registry.xml, use
137         // the version from <useVersion/>.
138         if ( StringUtils.isEmpty( version ) && settings.isUsePluginRegistry() )
139         {
140             // resolve existing useVersion.
141             version = resolveExistingFromPluginRegistry( groupId, artifactId );
142 
143             if ( StringUtils.isNotEmpty( version ) )
144             {
145                 // 2. check for updates. Determine whether this is the right time to attempt to update the version.
146                 // Only check for plugin updates if:
147                 //
148                 //  a. the CLI switch to force plugin updates is set, OR BOTH OF THE FOLLOWING:
149                 //  b. the CLI switch to suppress plugin updates is NOT set, AND
150                 //  c. the update interval for the plugin has triggered an update check.
151                 if ( Boolean.TRUE.equals( pluginUpdateOverride ) ||
152                     ( !Boolean.FALSE.equals( pluginUpdateOverride ) && shouldCheckForUpdates( groupId, artifactId ) ) )
153                 {
154                     updatedVersion =
155                         resolveMetaVersion( groupId, artifactId, project, localRepository, Artifact.LATEST_VERSION );
156 
157                     if ( StringUtils.isNotEmpty( updatedVersion ) && !updatedVersion.equals( version ) )
158                     {
159                         // see if this version we've resolved is on our previously rejected list.
160                         boolean isRejected = checkForRejectedStatus( groupId, artifactId, updatedVersion );
161 
162                         // we should only prompt to use this version if the user has not previously rejected it.
163                         promptToPersist = !isRejected;
164 
165                         // if we've rejected this version previously, forget about updating.
166                         if ( isRejected )
167                         {
168                             updatedVersion = null;
169                         }
170                         else
171                         {
172                             getLogger().info(
173                                 "Plugin \'" + constructPluginKey( groupId, artifactId ) + "\' has updates." );
174                         }
175                     }
176                 }
177             }
178         }
179 
180         boolean forcePersist = false;
181 
182         // third pass...we're always checking for latest install/deploy, so retrieve the version for LATEST metadata and
183         // also set that resolved version as the <useVersion/> in settings.xml.
184         if ( StringUtils.isEmpty( version ) )
185         {
186             // 1. resolve the version to be used
187             version = resolveMetaVersion( groupId, artifactId, project, localRepository, Artifact.LATEST_VERSION );
188 
189             if ( version != null )
190             {
191                 // 2. Set the updatedVersion so the user will be prompted whether to make this version permanent.
192                 updatedVersion = version;
193 
194                 // 3. Persist this version without prompting.
195                 forcePersist = true;
196                 promptToPersist = false;
197             }
198         }
199 
200         // final pass...retrieve the version for RELEASE and also set that resolved version as the <useVersion/>
201         // in settings.xml.
202         if ( StringUtils.isEmpty( version ) )
203         {
204             // 1. resolve the version to be used
205             version = resolveMetaVersion( groupId, artifactId, project, localRepository, Artifact.RELEASE_VERSION );
206 
207             if ( version != null )
208             {
209                 // 2. Set the updatedVersion so the user will be prompted whether to make this version permanent.
210                 updatedVersion = version;
211 
212                 // 3. Persist this version without prompting.
213                 forcePersist = true;
214                 promptToPersist = false;
215             }
216         }
217 
218         // if we're still empty here, and the current project matches the plugin in question, use the current project's
219         // version, I guess...
220         if ( StringUtils.isEmpty( version ) && project.getGroupId().equals( groupId ) &&
221             project.getArtifactId().equals( artifactId ) )
222         {
223             version = project.getVersion();
224         }
225 
226         // if we still haven't found a version, then fail early before we get into the update goop.
227         if ( StringUtils.isEmpty( version ) )
228         {
229             throw new PluginVersionNotFoundException( groupId, artifactId );
230         }
231 
232         // if the plugin registry is inactive, then the rest of this goop is useless...
233         if ( settings.isUsePluginRegistry() )
234         {
235             // determine whether this build is running in interactive mode
236             // If it's not, then we'll defer to the autoUpdate setting from the registry
237             // for a decision on updating the plugin in the registry...rather than prompting
238             // the user.
239             boolean inInteractiveMode = settings.isInteractiveMode();
240 
241             // determines what should be done if we're in non-interactive mode.
242             // if true, then just update the registry with the new versions.
243             String s = getPluginRegistry( groupId, artifactId ).getAutoUpdate();
244             boolean autoUpdate = true;
245             if ( s != null )
246             {
247                 autoUpdate = Boolean.valueOf( s ).booleanValue();
248             }
249 
250             // We should persist by default if:
251             //
252             // 0. RELEASE or LATEST was used to resolve the plugin version (it's not in the registry)
253             //
254             // -OR-
255             //
256             // 1. we detected a change in the plugin version from what was in the registry, or
257             //      a. the plugin is not registered
258             // 2. the pluginUpdateOverride flag has NOT been set to Boolean.FALSE (suppression mode)
259             // 3. we're in interactive mode, or
260             //      a. the registry is declared to be in autoUpdate mode
261             //
262             // NOTE: This is only the default value; it may be changed as the result of prompting the user.
263             boolean persistUpdate = forcePersist || ( promptToPersist &&
264                 !Boolean.FALSE.equals( pluginUpdateOverride ) && ( inInteractiveMode || autoUpdate ) );
265 
266             // retrieve the apply-to-all flag, if it's been set previously.
267             Boolean applyToAll = settings.getRuntimeInfo().getApplyToAllPluginUpdates();
268 
269             // Incorporate interactive-mode CLI overrides, and previous decisions on apply-to-all, if appropriate.
270             //
271             // don't prompt if RELEASE or LATEST was used to resolve the plugin version
272             // don't prompt if not in interactive mode.
273             // don't prompt if the CLI pluginUpdateOverride is set (either suppression or force mode will stop prompting)
274             // don't prompt if the user has selected ALL/NONE previously in this session
275             //
276             // NOTE: We're incorporating here, to make the usages of this check more consistent and 
277             // resistant to change.
278             promptToPersist =
279                 promptToPersist && pluginUpdateOverride == null && applyToAll == null && inInteractiveMode;
280 
281             if ( promptToPersist )
282             {
283                 persistUpdate = promptToPersistPluginUpdate( version, updatedVersion, groupId, artifactId, settings );
284             }
285 
286             // if it is determined that we should use this version, persist it as useVersion.
287             // cases where this version will be persisted:
288             // 1. the user is prompted and answers yes or all
289             // 2. the user has previously answered all in this session
290             // 3. the build is running in non-interactive mode, and the registry setting is for auto-update
291             if ( !Boolean.FALSE.equals( applyToAll ) && persistUpdate )
292             {
293                 updatePluginVersionInRegistry( groupId, artifactId, updatedVersion );
294 
295                 // we're using the updated version of the plugin in this session as well.
296                 version = updatedVersion;
297             }
298             // otherwise, if we prompted the user to update, we should treat this as a rejectedVersion, and
299             // persist it iff the plugin pre-exists and is in the user-level registry.
300             else if ( promptToPersist )
301             {
302                 addNewVersionToRejectedListInExisting( groupId, artifactId, updatedVersion );
303             }
304         }
305 
306         return version;
307     }
308 
309     private boolean shouldCheckForUpdates( String groupId, String artifactId )
310         throws PluginVersionResolutionException
311     {
312         PluginRegistry pluginRegistry = getPluginRegistry( groupId, artifactId );
313 
314         org.apache.maven.plugin.registry.Plugin plugin = getPlugin( groupId, artifactId, pluginRegistry );
315 
316         if ( plugin == null )
317         {
318             return true;
319         }
320         else
321         {
322             String lastChecked = plugin.getLastChecked();
323 
324             if ( StringUtils.isEmpty( lastChecked ) )
325             {
326                 return true;
327             }
328             else
329             {
330                 SimpleDateFormat format =
331                     new SimpleDateFormat( org.apache.maven.plugin.registry.Plugin.LAST_CHECKED_DATE_FORMAT );
332 
333                 try
334                 {
335                     Date lastCheckedDate = format.parse( lastChecked );
336 
337                     return IntervalUtils.isExpired( pluginRegistry.getUpdateInterval(), lastCheckedDate );
338                 }
339                 catch ( ParseException e )
340                 {
341                     getLogger().warn( "Last-checked date for plugin {" + constructPluginKey( groupId, artifactId ) +
342                         "} is invalid. Checking for updates." );
343 
344                     return true;
345                 }
346             }
347         }
348     }
349 
350     private boolean checkForRejectedStatus( String groupId, String artifactId, String version )
351         throws PluginVersionResolutionException
352     {
353         PluginRegistry pluginRegistry = getPluginRegistry( groupId, artifactId );
354 
355         org.apache.maven.plugin.registry.Plugin plugin = getPlugin( groupId, artifactId, pluginRegistry );
356 
357         return plugin.getRejectedVersions().contains( version );
358     }
359 
360     private boolean promptToPersistPluginUpdate( String version, String updatedVersion, String groupId,
361                                                  String artifactId, Settings settings )
362         throws PluginVersionResolutionException
363     {
364         try
365         {
366             StringBuffer message = new StringBuffer();
367 
368             // this means that the plugin is not registered.
369             if ( version != null && version.equals( updatedVersion ) )
370             {
371                 message.append( "Unregistered plugin detected.\n\n" );
372             }
373             else
374             {
375                 message.append( "New plugin version detected.\n\n" );
376             }
377 
378             message.append( "Group ID: " ).append( groupId ).append( "\n" );
379             message.append( "Artifact ID: " ).append( artifactId ).append( "\n" );
380             message.append( "\n" );
381 
382             // this means that we've detected a new, non-rejected plugin version.
383             if ( version != null && !version.equals( updatedVersion ) )
384             {
385                 message.append( "Registered Version: " ).append( version ).append( "\n" );
386             }
387 
388             message.append( "Detected plugin version: " ).append( updatedVersion ).append( "\n" );
389             message.append( "\n" );
390             message.append( "Would you like to use this new version from now on? ( [Y]es, [n]o, [a]ll, n[o]ne ) " );
391 
392             // TODO: check the GUI-friendliness of this approach to collecting input.
393             // If we can't port this prompt into a GUI, IDE-integration will not work well.
394             getLogger().info( message.toString() );
395 
396             String persistAnswer = inputHandler.readLine();
397 
398             boolean shouldPersist = true;
399 
400             if ( !StringUtils.isEmpty( persistAnswer ) )
401             {
402                 persistAnswer = persistAnswer.toLowerCase();
403 
404                 if ( persistAnswer.startsWith( "y" ) )
405                 {
406                     shouldPersist = true;
407                 }
408                 else if ( persistAnswer.startsWith( "a" ) )
409                 {
410                     shouldPersist = true;
411 
412                     settings.getRuntimeInfo().setApplyToAllPluginUpdates( Boolean.TRUE );
413                 }
414                 else if ( persistAnswer.indexOf( "o" ) > -1 )
415                 {
416                     settings.getRuntimeInfo().setApplyToAllPluginUpdates( Boolean.FALSE );
417                 }
418                 else if ( persistAnswer.startsWith( "n" ) )
419                 {
420                     shouldPersist = false;
421                 }
422                 else
423                 {
424                     // default to yes.
425                     shouldPersist = true;
426                 }
427             }
428 
429             if ( shouldPersist )
430             {
431                 getLogger().info( "Updating plugin version to " + updatedVersion );
432             }
433             else
434             {
435                 getLogger().info( "NOT updating plugin version to " + updatedVersion );
436             }
437 
438             return shouldPersist;
439 
440         }
441         catch ( IOException e )
442         {
443             throw new PluginVersionResolutionException( groupId, artifactId, "Can't read user input.", e );
444         }
445     }
446 
447     private void addNewVersionToRejectedListInExisting( String groupId, String artifactId, String rejectedVersion )
448         throws PluginVersionResolutionException
449     {
450         PluginRegistry pluginRegistry = getPluginRegistry( groupId, artifactId );
451 
452         org.apache.maven.plugin.registry.Plugin plugin = getPlugin( groupId, artifactId, pluginRegistry );
453 
454         String pluginKey = constructPluginKey( groupId, artifactId );
455 
456         if ( plugin != null && !TrackableBase.GLOBAL_LEVEL.equals( plugin.getSourceLevel() ) )
457         {
458             plugin.addRejectedVersion( rejectedVersion );
459 
460             writeUserRegistry( groupId, artifactId, pluginRegistry );
461 
462             getLogger().warn( "Plugin version: " + rejectedVersion + " added to your rejectedVersions list.\n" +
463                 "You will not be prompted for this version again.\n\nPlugin: " + pluginKey );
464         }
465         else
466         {
467             getLogger().warn(
468                 "Cannot add rejectedVersion entry for: " + rejectedVersion + ".\n\nPlugin: " + pluginKey );
469         }
470     }
471 
472     private String resolveExistingFromPluginRegistry( String groupId, String artifactId )
473         throws PluginVersionResolutionException
474     {
475         PluginRegistry pluginRegistry = getPluginRegistry( groupId, artifactId );
476 
477         org.apache.maven.plugin.registry.Plugin plugin = getPlugin( groupId, artifactId, pluginRegistry );
478 
479         String version = null;
480 
481         if ( plugin != null )
482         {
483             version = plugin.getUseVersion();
484         }
485 
486         return version;
487     }
488 
489     private org.apache.maven.plugin.registry.Plugin getPlugin( String groupId, String artifactId,
490                                                                PluginRegistry pluginRegistry )
491     {
492         Map pluginsByKey;
493 
494         if ( pluginRegistry != null )
495         {
496             pluginsByKey = pluginRegistry.getPluginsByKey();
497         }
498         else
499         {
500             pluginsByKey = new HashMap();
501         }
502 
503         String pluginKey = constructPluginKey( groupId, artifactId );
504 
505         return (org.apache.maven.plugin.registry.Plugin) pluginsByKey.get( pluginKey );
506     }
507 
508     private String constructPluginKey( String groupId, String artifactId )
509     {
510         return groupId + ":" + artifactId;
511     }
512 
513     private String getVersionFromPluginConfig( String groupId, String artifactId, MavenProject project,
514                                                boolean resolveAsReportPlugin )
515     {
516         String version = null;
517 
518         if ( resolveAsReportPlugin )
519         {
520             if ( project.getReportPlugins() != null )
521             {
522                 for ( Iterator it = project.getReportPlugins().iterator(); it.hasNext() && version == null; )
523                 {
524                     ReportPlugin plugin = (ReportPlugin) it.next();
525 
526                     if ( groupId.equals( plugin.getGroupId() ) && artifactId.equals( plugin.getArtifactId() ) )
527                     {
528                         version = plugin.getVersion();
529                     }
530                 }
531             }
532         }
533         else
534         {
535             if ( project.getBuildPlugins() != null )
536             {
537                 for ( Iterator it = project.getBuildPlugins().iterator(); it.hasNext() && version == null; )
538                 {
539                     Plugin plugin = (Plugin) it.next();
540 
541                     if ( groupId.equals( plugin.getGroupId() ) && artifactId.equals( plugin.getArtifactId() ) )
542                     {
543                         version = plugin.getVersion();
544                     }
545                 }
546             }
547         }
548 
549         return version;
550     }
551 
552     private void updatePluginVersionInRegistry( String groupId, String artifactId, String version )
553         throws PluginVersionResolutionException
554     {
555         PluginRegistry pluginRegistry = getPluginRegistry( groupId, artifactId );
556 
557         org.apache.maven.plugin.registry.Plugin plugin = getPlugin( groupId, artifactId, pluginRegistry );
558 
559         // if we can find the plugin, but we've gotten here, the useVersion must be missing; fill it in.
560         if ( plugin != null )
561         {
562             if ( PluginRegistry.GLOBAL_LEVEL.equals( plugin.getSourceLevel() ) )
563             {
564                 // do nothing. We don't rewrite the globals, under any circumstances.
565                 getLogger().warn( "Cannot update registered version for plugin {" + groupId + ":" + artifactId +
566                     "}; it is specified in the global registry." );
567             }
568             else
569             {
570                 plugin.setUseVersion( version );
571 
572                 SimpleDateFormat format =
573                     new SimpleDateFormat( org.apache.maven.plugin.registry.Plugin.LAST_CHECKED_DATE_FORMAT );
574 
575                 plugin.setLastChecked( format.format( new Date() ) );
576             }
577         }
578         else
579         {
580             plugin = new org.apache.maven.plugin.registry.Plugin();
581 
582             plugin.setGroupId( groupId );
583             plugin.setArtifactId( artifactId );
584             plugin.setUseVersion( version );
585 
586             pluginRegistry.addPlugin( plugin );
587 
588             pluginRegistry.flushPluginsByKey();
589         }
590 
591         writeUserRegistry( groupId, artifactId, pluginRegistry );
592     }
593 
594     private void writeUserRegistry( String groupId, String artifactId, PluginRegistry pluginRegistry )
595     {
596         File pluginRegistryFile = pluginRegistry.getRuntimeInfo().getFile();
597 
598         PluginRegistry extractedUserRegistry = PluginRegistryUtils.extractUserPluginRegistry( pluginRegistry );
599 
600         // only rewrite the user-level registry if one existed before, or if we've created user-level data here.
601         if ( extractedUserRegistry != null )
602         {
603             Writer fWriter = null;
604 
605             try
606             {
607                 pluginRegistryFile.getParentFile().mkdirs();
608                 fWriter = WriterFactory.newXmlWriter( pluginRegistryFile );
609 
610                 PluginRegistryXpp3Writer writer = new PluginRegistryXpp3Writer();
611 
612                 writer.write( fWriter, extractedUserRegistry );
613             }
614             catch ( IOException e )
615             {
616                 getLogger().warn(
617                     "Cannot rewrite user-level plugin-registry.xml with new plugin version of plugin: \'" + groupId +
618                         ":" + artifactId + "\'.", e );
619             }
620             finally
621             {
622                 IOUtil.close( fWriter );
623             }
624         }
625     }
626 
627     private PluginRegistry getPluginRegistry( String groupId, String artifactId )
628         throws PluginVersionResolutionException
629     {
630         if ( pluginRegistry == null )
631         {
632             try
633             {
634                 pluginRegistry = mavenPluginRegistryBuilder.buildPluginRegistry();
635             }
636             catch ( IOException e )
637             {
638                 throw new PluginVersionResolutionException( groupId, artifactId,
639                                                             "Error reading plugin registry: " + e.getMessage(), e );
640             }
641             catch ( XmlPullParserException e )
642             {
643                 throw new PluginVersionResolutionException( groupId, artifactId,
644                                                             "Error parsing plugin registry: " + e.getMessage(), e );
645             }
646 
647             if ( pluginRegistry == null )
648             {
649                 pluginRegistry = mavenPluginRegistryBuilder.createUserPluginRegistry();
650             }
651         }
652 
653         return pluginRegistry;
654     }
655 
656     private String resolveMetaVersion( String groupId, String artifactId, MavenProject project,
657                                        ArtifactRepository localRepository, String metaVersionId )
658         throws PluginVersionResolutionException, InvalidPluginException
659     {
660         Artifact artifact = artifactFactory.createProjectArtifact( groupId, artifactId, metaVersionId );
661 
662         String key = artifact.getDependencyConflictId();
663         if ( resolvedMetaVersions.containsKey( key ) )
664         {
665             return (String) resolvedMetaVersions.get( key );
666         }
667 
668         String version = null;
669 
670         // This takes the spec version and resolves a real version
671         try
672         {
673             ResolutionGroup resolutionGroup =
674                 artifactMetadataSource.retrieve( artifact, localRepository, project.getPluginArtifactRepositories() );
675 
676             // switching this out with the actual resolved artifact instance, since the MMSource re-creates the pom
677             // artifact.
678             artifact = resolutionGroup.getPomArtifact();
679         }
680         catch ( ArtifactMetadataRetrievalException e )
681         {
682             throw new PluginVersionResolutionException( groupId, artifactId, e.getMessage(), e );
683         }
684 
685         String artifactVersion = artifact.getVersion();
686 
687         // make sure this artifact was actually resolved to a file in the repo...
688         if ( artifact.getFile() != null )
689         {
690             boolean pluginValid = false;
691 
692             while ( !pluginValid && artifactVersion != null )
693             {
694                 pluginValid = true;
695                 MavenProject pluginProject;
696                 try
697                 {
698                     artifact = artifactFactory.createProjectArtifact( groupId, artifactId, artifactVersion );
699                     pluginProject = mavenProjectBuilder.buildFromRepository( artifact,
700                                                                              project.getPluginArtifactRepositories(),
701                                                                              localRepository, false );
702                 }
703                 catch ( ProjectBuildingException e )
704                 {
705                     throw new InvalidPluginException( "Unable to build project information for plugin '" +
706                         ArtifactUtils.versionlessKey( groupId, artifactId ) + "': " + e.getMessage(), e );
707                 }
708 
709                 // if we don't have the required Maven version, then ignore an update
710                 if ( pluginProject.getPrerequisites() != null && pluginProject.getPrerequisites().getMaven() != null )
711                 {
712                     DefaultArtifactVersion requiredVersion =
713                         new DefaultArtifactVersion( pluginProject.getPrerequisites().getMaven() );
714 
715                     if ( runtimeInformation.getApplicationVersion().compareTo( requiredVersion ) < 0 )
716                     {
717                         getLogger().info( "Ignoring available plugin update: " + artifactVersion +
718                             " as it requires Maven version " + requiredVersion );
719 
720                         VersionRange vr;
721                         try
722                         {
723                             vr = VersionRange.createFromVersionSpec( "(," + artifactVersion + ")" );
724                         }
725                         catch ( InvalidVersionSpecificationException e )
726                         {
727                             throw new PluginVersionResolutionException( groupId, artifactId,
728                                                                         "Error getting available plugin versions: " +
729                                                                             e.getMessage(), e );
730                         }
731 
732                         getLogger().debug( "Trying " + vr );
733                         try
734                         {
735                             List versions = artifactMetadataSource.retrieveAvailableVersions( artifact, localRepository,
736                                                                                               project.getPluginArtifactRepositories() );
737                             ArtifactVersion v = vr.matchVersion( versions );
738                             artifactVersion = v != null ? v.toString() : null;
739                         }
740                         catch ( ArtifactMetadataRetrievalException e )
741                         {
742                             throw new PluginVersionResolutionException( groupId, artifactId,
743                                                                         "Error getting available plugin versions: " +
744                                                                             e.getMessage(), e );
745                         }
746 
747                         if ( artifactVersion != null )
748                         {
749                             getLogger().debug( "Found " + artifactVersion );
750                             pluginValid = false;
751                         }
752                     }
753                 }
754             }
755         }
756 
757         if ( !metaVersionId.equals( artifactVersion ) )
758         {
759             version = artifactVersion;
760             resolvedMetaVersions.put( key, version );
761         }
762 
763         return version;
764     }
765 
766 }