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