1 package org.apache.maven.plugin.version;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
78 private PluginRegistry pluginRegistry;
79
80 private MavenProjectBuilder mavenProjectBuilder;
81
82 private RuntimeInformation runtimeInformation;
83
84
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
106 String version = getVersionFromPluginConfig( groupId, artifactId, project, resolveAsReportPlugin );
107
108
109
110
111
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
126 String updatedVersion = null;
127
128
129 boolean promptToPersist = false;
130
131 RuntimeInfo settingsRTInfo = settings.getRuntimeInfo();
132
133
134 Boolean pluginUpdateOverride = settingsRTInfo.getPluginUpdateOverride();
135
136
137
138 if ( StringUtils.isEmpty( version ) && settings.isUsePluginRegistry() )
139 {
140
141 version = resolveExistingFromPluginRegistry( groupId, artifactId );
142
143 if ( StringUtils.isNotEmpty( version ) )
144 {
145
146
147
148
149
150
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
160 boolean isRejected = checkForRejectedStatus( groupId, artifactId, updatedVersion );
161
162
163 promptToPersist = !isRejected;
164
165
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
183
184 if ( StringUtils.isEmpty( version ) )
185 {
186
187 version = resolveMetaVersion( groupId, artifactId, project, localRepository, Artifact.LATEST_VERSION );
188
189 if ( version != null )
190 {
191
192 updatedVersion = version;
193
194
195 forcePersist = true;
196 promptToPersist = false;
197 }
198 }
199
200
201
202 if ( StringUtils.isEmpty( version ) )
203 {
204
205 version = resolveMetaVersion( groupId, artifactId, project, localRepository, Artifact.RELEASE_VERSION );
206
207 if ( version != null )
208 {
209
210 updatedVersion = version;
211
212
213 forcePersist = true;
214 promptToPersist = false;
215 }
216 }
217
218
219
220 if ( StringUtils.isEmpty( version ) && project.getGroupId().equals( groupId ) &&
221 project.getArtifactId().equals( artifactId ) )
222 {
223 version = project.getVersion();
224 }
225
226
227 if ( StringUtils.isEmpty( version ) )
228 {
229 throw new PluginVersionNotFoundException( groupId, artifactId );
230 }
231
232
233 if ( settings.isUsePluginRegistry() )
234 {
235
236
237
238
239 boolean inInteractiveMode = settings.isInteractiveMode();
240
241
242
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
251
252
253
254
255
256
257
258
259
260
261
262
263 boolean persistUpdate = forcePersist || ( promptToPersist &&
264 !Boolean.FALSE.equals( pluginUpdateOverride ) && ( inInteractiveMode || autoUpdate ) );
265
266
267 Boolean applyToAll = settings.getRuntimeInfo().getApplyToAllPluginUpdates();
268
269
270
271
272
273
274
275
276
277
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
287
288
289
290
291 if ( !Boolean.FALSE.equals( applyToAll ) && persistUpdate )
292 {
293 updatePluginVersionInRegistry( groupId, artifactId, updatedVersion );
294
295
296 version = updatedVersion;
297 }
298
299
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
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
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
393
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
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
560 if ( plugin != null )
561 {
562 if ( PluginRegistry.GLOBAL_LEVEL.equals( plugin.getSourceLevel() ) )
563 {
564
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
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
671 try
672 {
673 ResolutionGroup resolutionGroup =
674 artifactMetadataSource.retrieve( artifact, localRepository, project.getPluginArtifactRepositories() );
675
676
677
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
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
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 }