View Javadoc
1   package org.apache.maven.plugins.dependency;
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.File;
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.HashSet;
27  import java.util.LinkedHashSet;
28  import java.util.List;
29  import java.util.Set;
30  
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.artifact.ArtifactUtils;
33  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
34  import org.apache.maven.artifact.repository.ArtifactRepository;
35  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
36  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
37  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
38  import org.apache.maven.execution.MavenSession;
39  import org.apache.maven.model.Dependency;
40  import org.apache.maven.plugin.AbstractMojo;
41  import org.apache.maven.plugin.MojoExecution;
42  import org.apache.maven.plugin.MojoExecutionException;
43  import org.apache.maven.plugin.MojoFailureException;
44  import org.apache.maven.plugin.MojoExecution.Source;
45  import org.apache.maven.plugins.annotations.Component;
46  import org.apache.maven.plugins.annotations.Mojo;
47  import org.apache.maven.plugins.annotations.Parameter;
48  import org.apache.maven.project.MavenProject;
49  import org.apache.maven.shared.dependencies.resolve.DependencyResolver;
50  import org.apache.maven.shared.dependencies.resolve.DependencyResolverException;
51  import org.apache.maven.shared.artifact.DefaultArtifactCoordinate;
52  import org.apache.maven.shared.artifact.filter.resolve.AbstractFilter;
53  import org.apache.maven.shared.artifact.filter.resolve.AndFilter;
54  import org.apache.maven.shared.artifact.filter.resolve.Node;
55  import org.apache.maven.shared.artifact.filter.resolve.PatternExclusionsFilter;
56  import org.apache.maven.shared.artifact.filter.resolve.PatternInclusionsFilter;
57  import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
58  import org.apache.maven.shared.artifact.filter.resolve.TransformableFilter;
59  import org.apache.maven.shared.artifact.filter.resolve.transform.ArtifactIncludeFilterTransformer;
60  import org.apache.maven.shared.artifact.resolve.ArtifactResolver;
61  import org.apache.maven.shared.artifact.resolve.ArtifactResolverException;
62  import org.apache.maven.shared.artifact.resolve.ArtifactResult;
63  import org.codehaus.plexus.util.FileUtils;
64  import org.codehaus.plexus.util.StringUtils;
65  
66  /**
67   * When run on a project, remove the project dependencies from the local repository, and optionally re-resolve them.
68   * Outside of a project, remove the manually given dependencies.
69   *
70   * @author jdcasey
71   * @since 2.0
72   */
73  @Mojo( name = "purge-local-repository", threadSafe = true, requiresProject = false )
74  public class PurgeLocalRepositoryMojo
75      extends AbstractMojo
76  {
77  
78      private static final String VERSION_FUZZINESS = "version";
79  
80      private static final String ARTIFACT_ID_FUZZINESS = "artifactId";
81  
82      private static final String GROUP_ID_FUZZINESS = "groupId";
83  
84      /**
85       * The Maven projects in the reactor.
86       */
87      @Parameter( defaultValue = "${reactorProjects}", readonly = true, required = true )
88      private List<MavenProject> reactorProjects;
89  
90      /**
91       * The current Maven project.
92       */
93      @Parameter( defaultValue = "${project}", readonly = true, required = true )
94      private MavenProject project;
95  
96      @Parameter( defaultValue = "${session}", readonly = true, required = true )
97      private MavenSession session;
98  
99      /**
100      * This mojo execution, used to determine if it was launched from the lifecycle or the command-line.
101      */
102     @Parameter( defaultValue = "${mojo}", required = true, readonly = true )
103     private MojoExecution mojoExecution;
104 
105     /**
106      * Artifact handler manager.
107      */
108     @Component
109     private ArtifactHandlerManager artifactHandlerManager;
110 
111     /**
112      * The list of dependencies in the form of groupId:artifactId which should BE deleted/purged from the local
113      * repository. Note that using this parameter will deactivate the normal process for purging the current project
114      * dependency tree. If this parameter is used, only the included artifacts will be purged. The manualIncludes
115      * parameter should not be used in combination with the includes/excludes parameters.
116      *
117      * @since 2.6
118      */
119     @Parameter
120     private List<String> manualIncludes;
121 
122     /**
123      * Comma-separated list of groupId:artifactId entries, which should be used to manually include artifacts for
124      * deletion. This is a command-line alternative to the <code>manualIncludes</code> parameter, since List parameters
125      * are not currently compatible with CLI specification.
126      *
127      * @since 2.6
128      */
129     @Parameter( property = "manualInclude" )
130     private String manualInclude;
131 
132     /**
133      * The list of dependencies in the form of groupId:artifactId which should BE deleted/refreshed.
134      *
135      * @since 2.6
136      */
137     @Parameter
138     private List<String> includes;
139 
140     /**
141      * Comma-separated list of groupId:artifactId entries, which should be used to include artifacts for
142      * deletion/refresh. This is a command-line alternative to the <code>includes</code> parameter, since List
143      * parameters are not currently compatible with CLI specification.
144      *
145      * @since 2.6
146      */
147     @Parameter( property = "include" )
148     private String include;
149 
150     /**
151      * The list of dependencies in the form of groupId:artifactId which should NOT be deleted/refreshed.
152      */
153     @Parameter
154     private List<String> excludes;
155 
156     /**
157      * Comma-separated list of groupId:artifactId entries, which should be used to exclude artifacts from
158      * deletion/refresh. This is a command-line alternative to the <code>excludes</code> parameter, since List
159      * parameters are not currently compatible with CLI specification.
160      */
161     @Parameter( property = "exclude" )
162     private String exclude;
163 
164     /**
165      * Whether to re-resolve the artifacts once they have been deleted from the local repository. If you are running
166      * this mojo from the command-line, you may want to disable this. By default, artifacts will be re-resolved.
167      */
168     @Parameter( property = "reResolve", defaultValue = "true" )
169     private boolean reResolve;
170 
171     /**
172      * The local repository, from which to delete artifacts.
173      */
174     @Parameter( defaultValue = "${localRepository}", readonly = true, required = true )
175     private ArtifactRepository localRepository;
176 
177     /**
178      * The dependency resolver
179      */
180     @Component
181     private DependencyResolver dependencyResolver;
182 
183     /**
184      * The artifact resolver used to re-resolve dependencies, if that option is enabled.
185      */
186     @Component
187     private ArtifactResolver artifactResolver;
188 
189     /**
190      * Determines how liberally the plugin will delete an artifact from the local repository. Values are: <br/>
191      * <ul>
192      * <li><b>file</b> - Eliminate only the artifact's file.</li>
193      * <li><b>version</b> <i>(default)</i> - Eliminate all files associated with the version of the artifact.</li>
194      * <li><b>artifactId</b> - Eliminate all files associated with the artifact's artifactId.</li>
195      * <li><b>groupId</b> - Eliminate all files associated with the artifact's groupId.</li>
196      * </ul>
197      */
198     @Parameter( property = "resolutionFuzziness", defaultValue = "version" )
199     private String resolutionFuzziness;
200 
201     /**
202      * Whether this mojo should act on all transitive dependencies. Default value is true.
203      */
204     @Parameter( property = "actTransitively", defaultValue = "true" )
205     private boolean actTransitively;
206 
207     /**
208      * Whether this plugin should output verbose messages. Default is false.
209      */
210     @Parameter( property = "verbose", defaultValue = "false" )
211     private boolean verbose;
212 
213     /**
214      * Whether to purge only snapshot artifacts.
215      *
216      * @since 2.4
217      */
218     @Parameter( property = "snapshotsOnly", defaultValue = "false" )
219     private boolean snapshotsOnly;
220 
221     /**
222      * Skip plugin execution completely.
223      *
224      * @since 2.7
225      */
226     @Parameter( property = "skip", defaultValue = "false" )
227     private boolean skip;
228 
229     /**
230      * Includes only direct project dependencies.
231      */
232     private class DirectDependencyFilter
233         extends AbstractFilter
234     {
235 
236         private Artifact projectArtifact;
237 
238         private List<Dependency> directDependencies;
239 
240         /**
241          * Default constructor
242          *
243          * @param directDependencies Set of dependencies objects which represent the direct dependencies of the project
244          */
245         DirectDependencyFilter( Artifact projectArtifact, List<Dependency> directDependencies )
246         {
247             this.projectArtifact = projectArtifact;
248             this.directDependencies = directDependencies;
249         }
250 
251         @Override
252         public boolean accept( Node node, List<Node> parents )
253         {
254 
255             if ( artifactsGAMatch( node, projectArtifact.getGroupId(), projectArtifact.getArtifactId() ) )
256             {
257                 return true;
258             }
259             for ( Dependency dep : directDependencies )
260             {
261                 if ( this.artifactsGAMatch( node, dep.getGroupId(), dep.getArtifactId() ) )
262                 {
263                     return true;
264                 }
265             }
266             return false;
267         }
268 
269         /*
270          * Compare the groupId:artifactId of two artifacts.
271          */
272         private boolean artifactsGAMatch( Node node, String groupId, String artifactId )
273         {
274             if ( node.getDependency() == null )
275             {
276                 return false;
277             }
278 
279             if ( !node.getDependency().getGroupId().equals( groupId ) )
280             {
281                 getLog().debug( "Different groupId: " + node.getDependency() + "  " + groupId );
282                 return false;
283             }
284             if ( !node.getDependency().getArtifactId().equals( artifactId ) )
285             {
286                 getLog().debug( "Different artifactId: " + node.getDependency() + "  " + artifactId );
287                 return false;
288             }
289             return true;
290         }
291     }
292 
293     /**
294      * Includes only snapshot artifacts
295      */
296     private class SnapshotsFilter
297         extends AbstractFilter
298     {
299         @Override
300         public boolean accept( Node node, List<Node> parents )
301         {
302             if ( node.getDependency() == null )
303             {
304                 return false;
305             }
306             else
307             {
308                 return ArtifactUtils.isSnapshot( node.getDependency().getVersion() );
309             }
310         }
311     }
312 
313     @Override
314     public void execute()
315         throws MojoExecutionException, MojoFailureException
316     {
317         if ( isSkip() )
318         {
319             getLog().info( "Skipping plugin execution" );
320             return;
321         }
322 
323         if ( !StringUtils.isEmpty( manualInclude ) )
324         {
325             manualIncludes = this.parseIncludes( manualInclude );
326         }
327         // If it's a manual purge, the only step is to delete from the local repo
328         if ( manualIncludes != null && manualIncludes.size() > 0 )
329         {
330             manualPurge( manualIncludes );
331             return;
332         }
333 
334         Set<Artifact> purgedArtifacts = new HashSet<Artifact>();
335         if ( shouldPurgeAllProjectsInReactor() )
336         {
337             for ( MavenProject reactorProject : reactorProjects )
338             {
339                 purgeLocalRepository( reactorProject, purgedArtifacts );
340             }
341         }
342         else
343         {
344             purgeLocalRepository( project, purgedArtifacts );
345         }
346     }
347 
348     /**
349      * Determines if all projects in the reactor should be purged from their dependencies. When this goal is started on
350      * the command-line, it is always the case. When it is bound to a phase in the lifecycle, it is never the case.
351      * 
352      * @return <code>true</code> if all projects in the reactor should be purged, <code>false</code> otherwise.
353      */
354     private boolean shouldPurgeAllProjectsInReactor()
355     {
356         Source source = mojoExecution.getSource();
357         return reactorProjects.size() > 1 && source == Source.CLI;
358     }
359 
360     /**
361      * Purges the local repository for the dependencies in the given Maven project.
362      *
363      * @param theProject Maven project.
364      * @param resolvedArtifactsToPurge The artifacts that were already purged.
365      * @throws MojoFailureException in case of errors during the purge.
366      */
367     private void purgeLocalRepository( MavenProject theProject, Set<Artifact> purgedArtifacts )
368         throws MojoFailureException
369     {
370         List<Dependency> dependencies = theProject.getDependencies();
371 
372         TransformableFilter dependencyFilter = createPurgeArtifactsFilter( theProject, dependencies, purgedArtifacts );
373 
374         Set<Artifact> resolvedArtifactsToPurge =
375             getFilteredResolvedArtifacts( theProject, dependencies, dependencyFilter );
376 
377         if ( resolvedArtifactsToPurge.isEmpty() )
378         {
379             getLog().info( "No artifacts included for purge for project: " + theProject.getId() );
380             return;
381         }
382 
383         verbose( "Purging dependencies for project: " + theProject.getId() );
384         purgeArtifacts( resolvedArtifactsToPurge );
385         purgedArtifacts.addAll( resolvedArtifactsToPurge );
386 
387         if ( reResolve )
388         {
389             ArtifactFilter artifactFilter = dependencyFilter.transform( new ArtifactIncludeFilterTransformer() );
390             try
391             {
392                 reResolveArtifacts( theProject, resolvedArtifactsToPurge, artifactFilter );
393             }
394             catch ( ArtifactResolutionException e )
395             {
396                 String failureMessage = "Failed to refresh project dependencies for: " + theProject.getId();
397                 MojoFailureException failure = new MojoFailureException( failureMessage );
398                 failure.initCause( e );
399 
400                 throw failure;
401             }
402             catch ( ArtifactNotFoundException e )
403             {
404                 String failureMessage = "Failed to refresh project dependencies for: " + theProject.getId();
405                 MojoFailureException failure = new MojoFailureException( failureMessage );
406                 failure.initCause( e );
407 
408                 throw failure;
409             }
410         }
411     }
412 
413     /**
414      * Purge/Delete artifacts from the local repository according to the given patterns.
415      *
416      * @param theIncludes The includes.
417      * @throws MojoExecutionException in case of an error.
418      */
419     private void manualPurge( List<String> theIncludes )
420         throws MojoExecutionException
421     {
422         for ( String gavPattern : theIncludes )
423         {
424             if ( StringUtils.isEmpty( gavPattern ) )
425             {
426                 getLog().debug( "Skipping empty gav pattern: " + gavPattern );
427                 continue;
428             }
429 
430             String relativePath = gavToPath( gavPattern );
431             if ( StringUtils.isEmpty( relativePath ) )
432             {
433                 continue;
434             }
435 
436             File purgeDir = new File( localRepository.getBasedir(), relativePath );
437             if ( purgeDir.exists() )
438             {
439                 getLog().debug( "Deleting directory: " + purgeDir );
440                 try
441                 {
442                     FileUtils.deleteDirectory( purgeDir );
443                 }
444                 catch ( IOException e )
445                 {
446                     throw new MojoExecutionException( "Unable to purge directory: " + purgeDir );
447                 }
448             }
449         }
450     }
451 
452     /**
453      * Convert a groupId:artifactId:version to a file system path
454      *
455      * @param gav, the groupId:artifactId:version string
456      * @return the corresponding path
457      */
458     private String gavToPath( String gav )
459     {
460         if ( StringUtils.isEmpty( gav ) )
461         {
462             return null;
463         }
464 
465         String[] pathComponents = gav.split( ":" );
466 
467         StringBuilder path = new StringBuilder( pathComponents[0].replace( '.', '/' ) );
468 
469         for ( int i = 1; i < pathComponents.length; ++i )
470         {
471             path.append( "/" ).append( pathComponents[i] );
472         }
473 
474         return path.toString();
475     }
476 
477     /**
478      * Create the includes exclude filter to use when resolving and purging dependencies Also excludes any "system"
479      * scope dependencies
480      *
481      * @param theProject The Maven project.
482      * @param dependencies The dependencies to use as a reference if we're excluding transitive dependencies
483      * @param purgedArtifacts The artifacts already purged.
484      * @return the created filter
485      */
486     private TransformableFilter createPurgeArtifactsFilter( MavenProject theProject, List<Dependency> dependencies,
487                                                             Set<Artifact> purgedArtifacts )
488     {
489         List<TransformableFilter> subFilters = new ArrayList<TransformableFilter>();
490 
491         // System dependencies should never be purged
492         subFilters.add( ScopeFilter.excluding( Artifact.SCOPE_SYSTEM ) );
493 
494         if ( this.snapshotsOnly )
495         {
496             subFilters.add( new SnapshotsFilter() );
497         }
498 
499         // The CLI includes/excludes overrides configuration in the pom
500         if ( !StringUtils.isEmpty( this.include ) )
501         {
502             this.includes = parseIncludes( this.include );
503         }
504         if ( this.includes != null )
505         {
506             subFilters.add( new PatternInclusionsFilter( includes ) );
507         }
508 
509         if ( !StringUtils.isEmpty( this.exclude ) )
510         {
511             this.excludes = parseIncludes( this.exclude );
512         }
513         if ( this.excludes != null )
514         {
515             subFilters.add( new PatternExclusionsFilter( excludes ) );
516         }
517 
518         if ( !actTransitively )
519         {
520             subFilters.add( new DirectDependencyFilter( theProject.getArtifact(), dependencies ) );
521         }
522 
523         List<String> exclusions = new ArrayList<String>( reactorProjects.size() );
524         // It doesn't make sense to include projects from the reactor here since they're likely not able to be resolved
525         for ( MavenProject reactorProject : reactorProjects )
526         {
527             exclusions.add( toPatternExcludes( reactorProject.getArtifact() ) );
528         }
529         // There is no need to consider a second time artifacts that were already purged (re-resolved or not)
530         for ( Artifact purgedArtifact : purgedArtifacts )
531         {
532             exclusions.add( toPatternExcludes( purgedArtifact ) );
533         }
534         subFilters.add( new PatternExclusionsFilter( exclusions ) );
535 
536         return new AndFilter( subFilters );
537     }
538 
539     /**
540      * Returns a string that represents a pattern for an exclude filter for the given artifact.
541      *
542      * @param artifact Artifact.
543      * @return String representation of a pattern for an exclude filter for the given artifact.
544      */
545     private String toPatternExcludes( Artifact artifact )
546     {
547         return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
548             + artifact.getArtifactHandler().getExtension() + ":" + artifact.getVersion();
549     }
550 
551     /**
552      * Convert comma separated list of includes to List object
553      *
554      * @param theInclude The list of includes
555      * @return the includes list
556      */
557     private List<String> parseIncludes( String theInclude )
558     {
559         List<String> theIncludes = new ArrayList<String>();
560 
561         if ( theInclude != null )
562         {
563             String[] elements = theInclude.split( "," );
564             theIncludes.addAll( Arrays.asList( elements ) );
565         }
566 
567         return theIncludes;
568     }
569 
570     private Set<Artifact> getFilteredResolvedArtifacts( MavenProject theProject, List<Dependency> dependencies,
571                                                         TransformableFilter filter )
572     {
573         try
574         {
575             Iterable<ArtifactResult> results =
576                 dependencyResolver.resolveDependencies( session.getProjectBuildingRequest(), theProject.getModel(),
577                                                         filter );
578 
579             Set<Artifact> resolvedArtifacts = new LinkedHashSet<Artifact>();
580 
581             for ( ArtifactResult artResult : results )
582             {
583                 resolvedArtifacts.add( artResult.getArtifact() );
584             }
585 
586             return resolvedArtifacts;
587         }
588         catch ( DependencyResolverException e )
589         {
590             getLog().info( "Unable to resolve all dependencies for: " + theProject.getGroupId() + ":"
591                 + theProject.getArtifactId() + ":" + theProject.getVersion()
592                 + ". Falling back to non-transitive mode for initial artifact resolution." );
593         }
594 
595         Set<Artifact> resolvedArtifacts = new LinkedHashSet<Artifact>();
596 
597         ArtifactFilter artifactFilter = filter.transform( new ArtifactIncludeFilterTransformer() );
598 
599         for ( Dependency dependency : dependencies )
600         {
601             DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
602             coordinate.setGroupId( dependency.getGroupId() );
603             coordinate.setArtifactId( dependency.getArtifactId() );
604             coordinate.setVersion( dependency.getVersion() );
605             coordinate.setExtension( artifactHandlerManager.getArtifactHandler( dependency.getType() ).getExtension() );
606             try
607             {
608                 Artifact artifact =
609                     artifactResolver.resolveArtifact( session.getProjectBuildingRequest(), coordinate ).getArtifact();
610                 if ( artifactFilter.include( artifact ) )
611                 {
612                     resolvedArtifacts.add( artifact );
613                 }
614             }
615             catch ( ArtifactResolverException e )
616             {
617                 getLog().debug( "Unable to resolve artifact: " + coordinate );
618             }
619         }
620         return resolvedArtifacts;
621     }
622 
623     private void purgeArtifacts( Set<Artifact> artifacts )
624         throws MojoFailureException
625     {
626         for ( Artifact artifact : artifacts )
627         {
628             verbose( "Purging artifact: " + artifact.getId() );
629 
630             File deleteTarget = findDeleteTarget( artifact );
631 
632             verbose( "Deleting: " + deleteTarget );
633 
634             if ( deleteTarget.isDirectory() )
635             {
636                 try
637                 {
638                     FileUtils.deleteDirectory( deleteTarget );
639                 }
640                 catch ( IOException e )
641                 {
642                     getLog().warn( "Unable to purge local repository location: " + deleteTarget, e );
643                 }
644             }
645             else
646             {
647                 if ( !deleteTarget.delete() )
648                 {
649                     deleteTarget.deleteOnExit();
650                     getLog().warn( "Unable to purge local repository location immediately: " + deleteTarget );
651                 }
652             }
653             artifact.setResolved( false );
654         }
655     }
656 
657     private void reResolveArtifacts( MavenProject theProject, Set<Artifact> artifacts, ArtifactFilter filter )
658         throws ArtifactResolutionException, ArtifactNotFoundException
659     {
660 
661         // Always need to re-resolve the poms in case they were purged along with the artifact
662         // because Maven 2 will not automatically re-resolve them when resolving the artifact
663         for ( Artifact artifact : artifacts )
664         {
665             try
666             {
667                 //CHECKSTYLE_OFF: LineLength
668                 artifactResolver.resolveArtifact( session.getProjectBuildingRequest(),
669                                                   org.apache.maven.shared.artifact.TransferUtils.toArtifactCoordinate( artifact ) );
670                 //CHECKSTYLE_ON: LineLength
671             }
672             catch ( ArtifactResolverException e )
673             {
674                 verbose( e.getMessage() );
675             }
676         }
677 
678         List<Artifact> missingArtifacts = new ArrayList<Artifact>();
679 
680         for ( Artifact artifact : artifacts )
681         {
682             verbose( "Resolving artifact: " + artifact.getId() );
683 
684             try
685             {
686                 artifactResolver.resolveArtifact( session.getProjectBuildingRequest(), artifact );
687             }
688             catch ( ArtifactResolverException e )
689             {
690                 verbose( e.getMessage() );
691                 missingArtifacts.add( artifact );
692             }
693         }
694 
695         if ( missingArtifacts.size() > 0 )
696         {
697             StringBuffer message = new StringBuffer( "required artifacts missing:\n" );
698             for ( Artifact missingArtifact : missingArtifacts )
699             {
700                 message.append( "  " ).append( missingArtifact.getId() ).append( '\n' );
701             }
702             message.append( "\nfor the artifact:" );
703 
704             throw new ArtifactResolutionException( message.toString(), theProject.getArtifact(),
705                                                    theProject.getRemoteArtifactRepositories() );
706         }
707     }
708 
709     private File findDeleteTarget( Artifact artifact )
710     {
711         // Use localRepository.pathOf() in case artifact.getFile() is not set
712         File deleteTarget = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) );
713 
714         if ( GROUP_ID_FUZZINESS.equals( resolutionFuzziness ) )
715         {
716             // get the groupId dir.
717             deleteTarget = deleteTarget.getParentFile().getParentFile().getParentFile();
718         }
719         else if ( ARTIFACT_ID_FUZZINESS.equals( resolutionFuzziness ) )
720         {
721             // get the artifactId dir.
722             deleteTarget = deleteTarget.getParentFile().getParentFile();
723         }
724         else if ( VERSION_FUZZINESS.equals( resolutionFuzziness ) )
725         {
726             // get the version dir.
727             deleteTarget = deleteTarget.getParentFile();
728         }
729         // else it's file fuzziness.
730         return deleteTarget;
731     }
732 
733     private void verbose( String message )
734     {
735         if ( verbose || getLog().isDebugEnabled() )
736         {
737             getLog().info( message );
738         }
739     }
740 
741     /**
742      * @return {@link #skip}
743      */
744     public boolean isSkip()
745     {
746         return skip;
747     }
748 
749     /**
750      * @param skip {@link #skip}
751      */
752     public void setSkip( boolean skip )
753     {
754         this.skip = skip;
755     }
756 }