1 package org.apache.maven.plugin.dependency;
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.factory.ArtifactFactory;
24 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
25 import org.apache.maven.artifact.repository.ArtifactRepository;
26 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
27 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
28 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
29 import org.apache.maven.artifact.resolver.ArtifactResolver;
30 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
31 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
32 import org.apache.maven.plugin.AbstractMojo;
33 import org.apache.maven.plugin.MojoExecutionException;
34 import org.apache.maven.plugin.MojoFailureException;
35 import org.apache.maven.plugins.annotations.Component;
36 import org.apache.maven.plugins.annotations.Mojo;
37 import org.apache.maven.plugins.annotations.Parameter;
38 import org.apache.maven.project.MavenProject;
39 import org.apache.maven.project.artifact.InvalidDependencyVersionException;
40 import org.apache.maven.shared.artifact.filter.PatternExcludesArtifactFilter;
41 import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
42 import org.codehaus.plexus.util.FileUtils;
43 import org.codehaus.plexus.util.StringUtils;
44
45 import java.io.File;
46 import java.io.IOException;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.LinkedHashSet;
50 import java.util.List;
51 import java.util.Set;
52
53
54
55
56
57
58
59
60 @Mojo( name = "purge-local-repository", threadSafe = true )
61 public class PurgeLocalRepositoryMojo
62 extends AbstractMojo
63 {
64
65 public static final String FILE_FUZZINESS = "file";
66
67 public static final String VERSION_FUZZINESS = "version";
68
69 public static final String ARTIFACT_ID_FUZZINESS = "artifactId";
70
71 public static final String GROUP_ID_FUZZINESS = "groupId";
72
73
74
75
76 @Component
77 private MavenProject project;
78
79
80
81
82
83
84
85
86
87 @Parameter
88 private List<String> manualIncludes;
89
90
91
92
93
94
95
96
97 @Parameter( property = "manualInclude" )
98 private String manualInclude;
99
100
101
102
103
104
105 @Parameter
106 private List<String> includes;
107
108
109
110
111
112
113
114
115 @Parameter( property = "include" )
116 private String include;
117
118
119
120
121 @Parameter
122 private List<String> excludes;
123
124
125
126
127
128
129 @Parameter( property = "exclude" )
130 private String exclude;
131
132
133
134
135
136 @Parameter( property = "reResolve", defaultValue = "true" )
137 private boolean reResolve;
138
139
140
141
142 @Parameter( defaultValue = "${localRepository}", readonly = true, required = true )
143 private ArtifactRepository localRepository;
144
145
146
147
148 @Parameter( defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true )
149 protected List<ArtifactRepository> remoteRepositories;
150
151
152
153
154 @Component
155 private ArtifactResolver resolver;
156
157
158
159
160 @Component
161 private ArtifactMetadataSource metadataSource;
162
163
164
165
166
167
168
169
170
171
172 @Parameter( property = "resolutionFuzziness", defaultValue = "version" )
173 private String resolutionFuzziness;
174
175
176
177
178 @Parameter( property = "actTransitively", defaultValue = "true" )
179 private boolean actTransitively;
180
181
182
183
184 @Component
185 private ArtifactFactory factory;
186
187
188
189
190 @Parameter( property = "verbose", defaultValue = "false" )
191 private boolean verbose;
192
193
194
195
196
197
198 @Parameter( property = "snapshotsOnly", defaultValue = "false" )
199 private boolean snapshotsOnly;
200
201
202
203
204 private class DirectDependencyFilter
205 implements ArtifactFilter
206 {
207
208 private Artifact projectArtifact;
209
210 private Set<Artifact> directDependencyArtifacts;
211
212
213
214
215
216
217
218 public DirectDependencyFilter( Artifact projectArtifact, Set<Artifact> directDependencyArtifacts )
219 {
220 this.projectArtifact = projectArtifact;
221 this.directDependencyArtifacts = directDependencyArtifacts;
222 }
223
224 public boolean include( Artifact artifact )
225 {
226 if ( artifactsGAMatch( artifact, projectArtifact ) )
227 {
228 return true;
229 }
230 for ( Artifact depArtifact : directDependencyArtifacts )
231 {
232 if ( this.artifactsGAMatch( artifact, depArtifact ) )
233 {
234 return true;
235 }
236 }
237 return false;
238 }
239
240
241
242
243 private boolean artifactsGAMatch( Artifact artifact1, Artifact artifact2 )
244 {
245 if ( artifact1 == artifact2 )
246 {
247 return true;
248 }
249
250 if ( !artifact1.getGroupId().equals( artifact2.getGroupId() ) )
251 {
252 getLog().debug( "Different groupId: " + artifact1 + " " + artifact2 );
253 return false;
254 }
255 if ( !artifact1.getArtifactId().equals( artifact2.getArtifactId() ) )
256 {
257 getLog().debug( "Different artifactId: " + artifact1 + " " + artifact2 );
258 return false;
259 }
260 return true;
261 }
262 }
263
264
265
266
267 private class SystemScopeExcludeFilter
268 implements ArtifactFilter
269 {
270 public boolean include( Artifact artifact )
271 {
272 return !Artifact.SCOPE_SYSTEM.equals( artifact.getScope() );
273 }
274 }
275
276
277
278
279 private class SnapshotsFilter
280 implements ArtifactFilter
281 {
282 public boolean include( Artifact artifact )
283 {
284 return artifact.isSnapshot();
285 }
286 }
287
288 public void execute()
289 throws MojoExecutionException, MojoFailureException
290 {
291 if ( !StringUtils.isEmpty( manualInclude ) )
292 {
293 manualIncludes = this.parseIncludes( manualInclude );
294 }
295
296 if ( manualIncludes != null && manualIncludes.size() > 0 )
297 {
298 manualPurge( manualIncludes );
299 return;
300 }
301
302 Set<Artifact> dependencyArtifacts = null;
303
304 try
305 {
306 dependencyArtifacts = project.createArtifacts( factory, null, null );
307 }
308 catch ( InvalidDependencyVersionException e )
309 {
310 throw new MojoFailureException( "Unable to purge dependencies due to invalid dependency version ", e );
311 }
312
313 ArtifactFilter artifactFilter = createPurgeArtifactsFilter( dependencyArtifacts );
314
315 Set<Artifact> resolvedArtifactsToPurge =
316 getFilteredResolvedArtifacts( project, dependencyArtifacts, artifactFilter );
317
318 if ( resolvedArtifactsToPurge.isEmpty() )
319 {
320 getLog().info( "No artifacts included for purge for project: " + project.getId() );
321 return;
322 }
323
324 verbose( "Purging dependencies for project: " + project.getId() );
325 purgeArtifacts( resolvedArtifactsToPurge );
326
327 if ( reResolve )
328 {
329 try
330 {
331 reResolveArtifacts( project, resolvedArtifactsToPurge, artifactFilter );
332 }
333 catch ( ArtifactResolutionException e )
334 {
335 String failureMessage = "Failed to refresh project dependencies for: " + project.getId();
336 MojoFailureException failure = new MojoFailureException( failureMessage );
337 failure.initCause( e );
338
339 throw failure;
340 }
341 catch ( ArtifactNotFoundException e )
342 {
343 String failureMessage = "Failed to refresh project dependencies for: " + project.getId();
344 MojoFailureException failure = new MojoFailureException( failureMessage );
345 failure.initCause( e );
346
347 throw failure;
348 }
349 }
350 }
351
352
353
354
355
356
357
358 private void manualPurge( List<String> includes )
359 throws MojoExecutionException
360 {
361 for ( String gavPattern : includes )
362 {
363 if ( StringUtils.isEmpty( gavPattern ) )
364 {
365 getLog().debug( "Skipping empty gav pattern: " + gavPattern );
366 continue;
367 }
368
369 String relativePath = gavToPath( gavPattern );
370 if ( StringUtils.isEmpty( relativePath ) )
371 {
372 continue;
373 }
374
375 File purgeDir = new File( localRepository.getBasedir(), relativePath );
376 if ( purgeDir.exists() )
377 {
378 getLog().debug( "Deleting directory: " + purgeDir );
379 try
380 {
381 FileUtils.deleteDirectory( purgeDir );
382 }
383 catch ( IOException e )
384 {
385 throw new MojoExecutionException( "Unable to purge directory: " + purgeDir );
386 }
387 }
388 }
389 }
390
391
392
393
394
395
396
397 private String gavToPath( String gav )
398 {
399 if ( StringUtils.isEmpty( gav ) )
400 {
401 return null;
402 }
403
404 String[] pathComponents = gav.split( ":" );
405
406 StringBuffer path = new StringBuffer( pathComponents[0].replace( '.', '/' ) );
407
408 for ( int i = 1; i < pathComponents.length; ++i )
409 {
410 path.append( "/" + pathComponents[i] );
411 }
412
413 return path.toString();
414 }
415
416
417
418
419
420
421
422
423
424 private ArtifactFilter createPurgeArtifactsFilter( Set<Artifact> dependencyArtifacts )
425 {
426 AndArtifactFilter andFilter = new AndArtifactFilter();
427
428
429 andFilter.add( new SystemScopeExcludeFilter() );
430
431 if ( this.snapshotsOnly )
432 {
433 andFilter.add( new SnapshotsFilter() );
434 }
435
436
437 if ( !StringUtils.isEmpty( this.include ) )
438 {
439 this.includes = parseIncludes( this.include );
440 }
441 if ( this.includes != null )
442 {
443 andFilter.add( new PatternIncludesArtifactFilter( includes ) );
444 }
445
446 if ( !StringUtils.isEmpty( this.exclude ) )
447 {
448 this.excludes = parseIncludes( this.exclude );
449 }
450 if ( this.excludes != null )
451 {
452 andFilter.add( new PatternExcludesArtifactFilter( excludes ) );
453 }
454
455 if ( !actTransitively )
456 {
457 andFilter.add( new DirectDependencyFilter( project.getArtifact(), dependencyArtifacts ) );
458 }
459
460 return andFilter;
461 }
462
463
464
465
466
467
468
469 private List<String> parseIncludes( String include )
470 {
471 List<String> includes = new ArrayList<String>();
472
473 if ( include != null )
474 {
475 String[] elements = include.split( "," );
476 includes.addAll( Arrays.asList( elements ) );
477 }
478
479 return includes;
480 }
481
482 private Set<Artifact> getFilteredResolvedArtifacts( MavenProject project, Set<Artifact> artifacts,
483 ArtifactFilter filter )
484 {
485 try
486 {
487 ArtifactResolutionResult result =
488 resolver.resolveTransitively( artifacts, project.getArtifact(), localRepository, remoteRepositories,
489 metadataSource, filter );
490
491 @SuppressWarnings( "unchecked" )
492 Set<Artifact> resolvedArtifacts = result.getArtifacts();
493
494 return resolvedArtifacts;
495 }
496 catch ( ArtifactResolutionException e )
497 {
498 getLog().info( "Unable to resolve all dependencies for : " + e.getGroupId() + ":" + e.getArtifactId() + ":"
499 + e.getVersion()
500 + ". Falling back to non-transitive mode for initial artifact resolution." );
501 }
502 catch ( ArtifactNotFoundException e )
503 {
504 getLog().info( "Unable to resolve all dependencies for : " + e.getGroupId() + ":" + e.getArtifactId() + ":"
505 + e.getVersion()
506 + ". Falling back to non-transitive mode for initial artifact resolution." );
507 }
508
509 Set<Artifact> resolvedArtifacts = new LinkedHashSet<Artifact>();
510
511
512 for ( Artifact artifact : artifacts )
513 {
514 if ( filter.include( artifact ) )
515 {
516 try
517 {
518 resolvedArtifacts.add( artifact );
519 resolver.resolve( artifact, remoteRepositories, localRepository );
520 }
521 catch ( ArtifactResolutionException e )
522 {
523 getLog().debug( "Unable to resolve artifact: " + artifact );
524 }
525 catch ( ArtifactNotFoundException e )
526 {
527 getLog().debug( "Unable to resolve artifact: " + artifact );
528 }
529 }
530 }
531 return resolvedArtifacts;
532 }
533
534 private void purgeArtifacts( Set<Artifact> artifacts )
535 throws MojoFailureException
536 {
537 for ( Artifact artifact : artifacts )
538 {
539 verbose( "Purging artifact: " + artifact.getId() );
540
541 File deleteTarget = findDeleteTarget( artifact );
542
543 verbose( "Deleting: " + deleteTarget );
544
545 if ( deleteTarget.isDirectory() )
546 {
547 try
548 {
549 FileUtils.deleteDirectory( deleteTarget );
550 }
551 catch ( IOException e )
552 {
553 getLog().warn( "Unable to purge local repository location: " + deleteTarget, e );
554 }
555 }
556 else
557 {
558 if ( !deleteTarget.delete() )
559 {
560 getLog().warn( "Unable to purge local repository location: " + deleteTarget );
561 }
562 }
563 artifact.setResolved( false );
564 }
565 }
566
567 private void reResolveArtifacts( MavenProject project, Set<Artifact> artifacts, ArtifactFilter filter )
568 throws ArtifactResolutionException, ArtifactNotFoundException
569 {
570
571
572
573 for ( Artifact artifact : artifacts )
574 {
575 try
576 {
577 Artifact pomArtifact =
578 factory.createArtifact( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(),
579 null, "pom" );
580 resolver.resolveAlways( pomArtifact, remoteRepositories, localRepository );
581 }
582 catch ( ArtifactResolutionException e )
583 {
584 verbose( e.getMessage() );
585 }
586 catch ( ArtifactNotFoundException e )
587 {
588 verbose( e.getMessage() );
589 }
590 }
591
592 List<Artifact> missingArtifacts = new ArrayList<Artifact>();
593
594 for ( Artifact artifact : artifacts )
595 {
596 verbose( "Resolving artifact: " + artifact.getId() );
597
598 try
599 {
600 resolver.resolveAlways( artifact, project.getRemoteArtifactRepositories(), localRepository );
601 }
602 catch ( ArtifactResolutionException e )
603 {
604 verbose( e.getMessage() );
605 missingArtifacts.add( artifact );
606 }
607 catch ( ArtifactNotFoundException e )
608 {
609 verbose( e.getMessage() );
610 missingArtifacts.add( artifact );
611 }
612 }
613
614 if ( missingArtifacts.size() > 0 )
615 {
616 String message = "required artifacts missing:\n";
617 for ( Artifact missingArtifact : missingArtifacts )
618 {
619 message += " " + missingArtifact.getId() + "\n";
620 }
621 message += "\nfor the artifact:";
622
623 throw new ArtifactResolutionException( message, project.getArtifact(),
624 project.getRemoteArtifactRepositories() );
625 }
626 }
627
628 private File findDeleteTarget( Artifact artifact )
629 {
630
631 File deleteTarget = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) );
632
633 if ( GROUP_ID_FUZZINESS.equals( resolutionFuzziness ) )
634 {
635
636 deleteTarget = deleteTarget.getParentFile().getParentFile().getParentFile();
637 }
638 else if ( ARTIFACT_ID_FUZZINESS.equals( resolutionFuzziness ) )
639 {
640
641 deleteTarget = deleteTarget.getParentFile().getParentFile();
642 }
643 else if ( VERSION_FUZZINESS.equals( resolutionFuzziness ) )
644 {
645
646 deleteTarget = deleteTarget.getParentFile();
647 }
648
649 return deleteTarget;
650 }
651
652 private void verbose( String message )
653 {
654 if ( verbose || getLog().isDebugEnabled() )
655 {
656 getLog().info( message );
657 }
658 }
659
660 }