View Javadoc
1   package org.apache.maven.repository.legacy.resolver;
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.util.ArrayList;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.LinkedHashMap;
26  import java.util.LinkedHashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
33  import org.apache.maven.artifact.metadata.ResolutionGroup;
34  import org.apache.maven.artifact.repository.ArtifactRepository;
35  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
36  import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
37  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
38  import org.apache.maven.artifact.resolver.CyclicDependencyException;
39  import org.apache.maven.artifact.resolver.ResolutionListener;
40  import org.apache.maven.artifact.resolver.ResolutionListenerForDepMgmt;
41  import org.apache.maven.artifact.resolver.ResolutionNode;
42  import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
43  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
44  import org.apache.maven.artifact.versioning.ArtifactVersion;
45  import org.apache.maven.artifact.versioning.ManagedVersionMap;
46  import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
47  import org.apache.maven.artifact.versioning.VersionRange;
48  import org.apache.maven.execution.MavenSession;
49  import org.apache.maven.plugin.LegacySupport;
50  import org.apache.maven.repository.legacy.metadata.ArtifactMetadataRetrievalException;
51  import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest;
52  import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
53  import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver;
54  import org.codehaus.plexus.component.annotations.Component;
55  import org.codehaus.plexus.component.annotations.Requirement;
56  import org.codehaus.plexus.logging.Logger;
57  
58  /**
59   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
60   * @author Jason van Zyl
61   */
62  @Component( role = LegacyArtifactCollector.class )
63  public class DefaultLegacyArtifactCollector
64      implements LegacyArtifactCollector
65  {
66  
67      @Requirement( hint = "nearest" )
68      private ConflictResolver defaultConflictResolver;
69  
70      @Requirement
71      private Logger logger;
72  
73      @Requirement
74      private LegacySupport legacySupport;
75  
76      private void injectSession( ArtifactResolutionRequest request )
77      {
78          MavenSession session = legacySupport.getSession();
79  
80          if ( session != null )
81          {
82              request.setOffline( session.isOffline() );
83              request.setForceUpdate( session.getRequest().isUpdateSnapshots() );
84              request.setServers( session.getRequest().getServers() );
85              request.setMirrors( session.getRequest().getMirrors() );
86              request.setProxies( session.getRequest().getProxies() );
87          }
88      }
89  
90      public ArtifactResolutionResult collect( Set<Artifact> artifacts, Artifact originatingArtifact,
91                                               Map<String, Artifact> managedVersions, ArtifactRepository localRepository,
92                                               List<ArtifactRepository> remoteRepositories,
93                                               ArtifactMetadataSource source, ArtifactFilter filter,
94                                               List<ResolutionListener> listeners,
95                                               List<ConflictResolver> conflictResolvers )
96      {
97          ArtifactResolutionRequest request = new ArtifactResolutionRequest();
98          request.setLocalRepository( localRepository );
99          request.setRemoteRepositories( remoteRepositories );
100         injectSession( request );
101         return collect( artifacts, originatingArtifact, managedVersions, request, source, filter, listeners,
102                         conflictResolvers );
103     }
104 
105     public ArtifactResolutionResult collect( Set<Artifact> artifacts, Artifact originatingArtifact,
106                                              Map<String, Artifact> managedVersions,
107                                              ArtifactResolutionRequest repositoryRequest,
108                                              ArtifactMetadataSource source, ArtifactFilter filter,
109                                              List<ResolutionListener> listeners,
110                                              List<ConflictResolver> conflictResolvers )
111     {
112         ArtifactResolutionResult result = new ArtifactResolutionResult();
113 
114         result.setOriginatingArtifact( originatingArtifact );
115 
116         if ( conflictResolvers == null )
117         {
118             conflictResolvers = Collections.singletonList( defaultConflictResolver );
119         }
120 
121         Map<Object, List<ResolutionNode>> resolvedArtifacts = new LinkedHashMap<>();
122 
123         ResolutionNode root = new ResolutionNode( originatingArtifact, repositoryRequest.getRemoteRepositories() );
124 
125         try
126         {
127             root.addDependencies( artifacts, repositoryRequest.getRemoteRepositories(), filter );
128         }
129         catch ( CyclicDependencyException e )
130         {
131             result.addCircularDependencyException( e );
132 
133             return result;
134         }
135         catch ( OverConstrainedVersionException e )
136         {
137             result.addVersionRangeViolation( e );
138 
139             return result;
140         }
141 
142         ManagedVersionMap versionMap = getManagedVersionsMap( originatingArtifact, managedVersions );
143 
144         try
145         {
146             recurse( result, root, resolvedArtifacts, versionMap, repositoryRequest, source, filter, listeners,
147                      conflictResolvers );
148         }
149         catch ( CyclicDependencyException e )
150         {
151             logger.debug( "While recursing: " + e.getMessage(), e );
152             result.addCircularDependencyException( e );
153         }
154         catch ( OverConstrainedVersionException e )
155         {
156             logger.debug( "While recursing: " + e.getMessage(), e );
157             result.addVersionRangeViolation( e );
158         }
159         catch ( ArtifactResolutionException e )
160         {
161             logger.debug( "While recursing: " + e.getMessage(), e );
162             result.addErrorArtifactException( e );
163         }
164 
165         Set<ResolutionNode> set = new LinkedHashSet<>();
166 
167         for ( List<ResolutionNode> nodes : resolvedArtifacts.values() )
168         {
169             for ( ResolutionNode node : nodes )
170             {
171                 if ( !node.equals( root ) && node.isActive() )
172                 {
173                     Artifact artifact = node.getArtifact();
174 
175                     try
176                     {
177                         if ( node.filterTrail( filter ) )
178                         {
179                             // If it was optional and not a direct dependency,
180                             // we don't add it or its children, just allow the update of the version and artifactScope
181                             if ( node.isChildOfRootNode() || !artifact.isOptional() )
182                             {
183                                 artifact.setDependencyTrail( node.getDependencyTrail() );
184 
185                                 set.add( node );
186 
187                                 // This is required right now.
188                                 result.addArtifact( artifact );
189                             }
190                         }
191                     }
192                     catch ( OverConstrainedVersionException e )
193                     {
194                         result.addVersionRangeViolation( e );
195                     }
196                 }
197             }
198         }
199 
200         result.setArtifactResolutionNodes( set );
201 
202         return result;
203     }
204 
205     /**
206      * Get the map of managed versions, removing the originating artifact if it is also in managed versions
207      *
208      * @param originatingArtifact artifact we are processing
209      * @param managedVersions original managed versions
210      */
211     private ManagedVersionMap getManagedVersionsMap( Artifact originatingArtifact,
212                                                      Map<String, Artifact> managedVersions )
213     {
214         ManagedVersionMap versionMap;
215         if ( ( managedVersions != null ) && ( managedVersions instanceof ManagedVersionMap ) )
216         {
217             versionMap = (ManagedVersionMap) managedVersions;
218         }
219         else
220         {
221             versionMap = new ManagedVersionMap( managedVersions );
222         }
223 
224         // remove the originating artifact if it is also in managed versions to avoid being modified during resolution
225         Artifact managedOriginatingArtifact = versionMap.get( originatingArtifact.getDependencyConflictId() );
226 
227         if ( managedOriginatingArtifact != null )
228         {
229             // TODO we probably want to warn the user that he is building an artifact with
230             // different values than in dependencyManagement
231             if ( managedVersions instanceof ManagedVersionMap )
232             {
233                 /* avoid modifying the managedVersions parameter creating a new map */
234                 versionMap = new ManagedVersionMap( managedVersions );
235             }
236             versionMap.remove( originatingArtifact.getDependencyConflictId() );
237         }
238 
239         return versionMap;
240     }
241 
242     private void recurse( ArtifactResolutionResult result, ResolutionNode node,
243                           Map<Object, List<ResolutionNode>> resolvedArtifacts, ManagedVersionMap managedVersions,
244                           ArtifactResolutionRequest request, ArtifactMetadataSource source, ArtifactFilter filter,
245                           List<ResolutionListener> listeners, List<ConflictResolver> conflictResolvers )
246         throws ArtifactResolutionException
247     {
248         fireEvent( ResolutionListener.TEST_ARTIFACT, listeners, node );
249 
250         Object key = node.getKey();
251 
252         // TODO Does this check need to happen here? Had to add the same call
253         // below when we iterate on child nodes -- will that suffice?
254         if ( managedVersions.containsKey( key ) )
255         {
256             manageArtifact( node, managedVersions, listeners );
257         }
258 
259         List<ResolutionNode> previousNodes = resolvedArtifacts.get( key );
260 
261         if ( previousNodes != null )
262         {
263             for ( ResolutionNode previous : previousNodes )
264             {
265                 try
266                 {
267                     if ( previous.isActive() )
268                     {
269                         // Version mediation
270                         VersionRange previousRange = previous.getArtifact().getVersionRange();
271                         VersionRange currentRange = node.getArtifact().getVersionRange();
272 
273                         if ( ( previousRange != null ) && ( currentRange != null ) )
274                         {
275                             // TODO shouldn't need to double up on this work, only done for simplicity of handling
276                             // recommended
277                             // version but the restriction is identical
278                             VersionRange newRange = previousRange.restrict( currentRange );
279                             // TODO ick. this forces the OCE that should have come from the previous call. It is still
280                             // correct
281                             if ( newRange.isSelectedVersionKnown( previous.getArtifact() ) )
282                             {
283                                 fireEvent( ResolutionListener.RESTRICT_RANGE, listeners, node, previous.getArtifact(),
284                                            newRange );
285                             }
286                             previous.getArtifact().setVersionRange( newRange );
287                             node.getArtifact().setVersionRange( currentRange.restrict( previousRange ) );
288 
289                             // Select an appropriate available version from the (now restricted) range
290                             // Note this version was selected before to get the appropriate POM
291                             // But it was reset by the call to setVersionRange on restricting the version
292                             ResolutionNode[] resetNodes =
293                             {
294                                 previous, node
295                             };
296                             for ( int j = 0; j < 2; j++ )
297                             {
298                                 Artifact resetArtifact = resetNodes[j].getArtifact();
299 
300                                 // MNG-2123: if the previous node was not a range, then it wouldn't have any available
301                                 // versions. We just clobbered the selected version above. (why? i have no idea.)
302                                 // So since we are here and this is ranges we must go figure out the version (for a
303                                 // third time...)
304                                 if ( resetArtifact.getVersion() == null && resetArtifact.getVersionRange() != null )
305                                 {
306 
307                                     // go find the version. This is a total hack. See previous comment.
308                                     List<ArtifactVersion> versions = resetArtifact.getAvailableVersions();
309                                     if ( versions == null )
310                                     {
311                                         try
312                                         {
313                                             MetadataResolutionRequest metadataRequest =
314                                                 new DefaultMetadataResolutionRequest( request );
315 
316                                             metadataRequest.setArtifact( resetArtifact );
317                                             versions = source.retrieveAvailableVersions( metadataRequest );
318                                             resetArtifact.setAvailableVersions( versions );
319                                         }
320                                         catch ( ArtifactMetadataRetrievalException e )
321                                         {
322                                             resetArtifact.setDependencyTrail( node.getDependencyTrail() );
323                                             throw new ArtifactResolutionException(
324                                                 "Unable to get dependency information: "
325                                                     + e.getMessage(), resetArtifact, request.getRemoteRepositories(),
326                                                 e );
327 
328                                         }
329                                     }
330                                     // end hack
331 
332                                     // MNG-2861: match version can return null
333                                     ArtifactVersion selectedVersion = resetArtifact.getVersionRange().
334                                         matchVersion( resetArtifact.getAvailableVersions() );
335 
336                                     if ( selectedVersion != null )
337                                     {
338                                         resetArtifact.selectVersion( selectedVersion.toString() );
339                                     }
340                                     else
341                                     {
342                                         throw new OverConstrainedVersionException(
343                                             "Unable to find a version in " + resetArtifact.getAvailableVersions()
344                                                 + " to match the range " + resetArtifact.getVersionRange(),
345                                             resetArtifact );
346 
347                                     }
348 
349                                     fireEvent( ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, resetNodes[j] );
350                                 }
351                             }
352                         }
353 
354                         // Conflict Resolution
355                         ResolutionNode resolved = null;
356                         for ( Iterator<ConflictResolver> j = conflictResolvers.iterator();
357                               resolved == null && j.hasNext(); )
358                         {
359                             ConflictResolver conflictResolver = j.next();
360 
361                             resolved = conflictResolver.resolveConflict( previous, node );
362                         }
363 
364                         if ( resolved == null )
365                         {
366                             // TODO add better exception that can detail the two conflicting artifacts
367                             ArtifactResolutionException are =
368                                 new ArtifactResolutionException( "Cannot resolve artifact version conflict between "
369                                                                      + previous.getArtifact().getVersion() + " and "
370                                                                      + node.getArtifact().getVersion(),
371                                                                  previous.getArtifact() );
372 
373                             result.addVersionRangeViolation( are );
374                         }
375 
376                         if ( ( resolved != previous ) && ( resolved != node ) )
377                         {
378                             // TODO add better exception
379                             result.addVersionRangeViolation( new ArtifactResolutionException(
380                                 "Conflict resolver returned unknown resolution node: ",
381                                 resolved.getArtifact() ) );
382 
383                         }
384 
385                         // TODO should this be part of mediation?
386                         // previous one is more dominant
387                         ResolutionNode nearest;
388                         ResolutionNode farthest;
389 
390                         if ( resolved == previous )
391                         {
392                             nearest = previous;
393                             farthest = node;
394                         }
395                         else
396                         {
397                             nearest = node;
398                             farthest = previous;
399                         }
400 
401                         if ( checkScopeUpdate( farthest, nearest, listeners ) )
402                         {
403                             // if we need to update artifactScope of nearest to use farthest artifactScope, use the
404                             // nearest version, but farthest artifactScope
405                             nearest.disable();
406                             farthest.getArtifact().setVersion( nearest.getArtifact().getVersion() );
407                             fireEvent( ResolutionListener.OMIT_FOR_NEARER, listeners, nearest, farthest.getArtifact() );
408                         }
409                         else
410                         {
411                             farthest.disable();
412                             fireEvent( ResolutionListener.OMIT_FOR_NEARER, listeners, farthest, nearest.getArtifact() );
413                         }
414                     }
415                 }
416                 catch ( OverConstrainedVersionException e )
417                 {
418                     result.addVersionRangeViolation( e );
419                 }
420             }
421         }
422         else
423         {
424             previousNodes = new ArrayList<>();
425 
426             resolvedArtifacts.put( key, previousNodes );
427         }
428         previousNodes.add( node );
429 
430         if ( node.isActive() )
431         {
432             fireEvent( ResolutionListener.INCLUDE_ARTIFACT, listeners, node );
433         }
434 
435         // don't pull in the transitive deps of a system-scoped dependency.
436         if ( node.isActive() && !Artifact.SCOPE_SYSTEM.equals( node.getArtifact().getScope() ) )
437         {
438             fireEvent( ResolutionListener.PROCESS_CHILDREN, listeners, node );
439 
440             Artifact parentArtifact = node.getArtifact();
441 
442             for ( Iterator<ResolutionNode> i = node.getChildrenIterator(); i.hasNext(); )
443             {
444                 ResolutionNode child = i.next();
445 
446                 try
447                 {
448 
449                     // We leave in optional ones, but don't pick up its dependencies
450                     if ( !child.isResolved() && ( !child.getArtifact().isOptional() || child.isChildOfRootNode() ) )
451                     {
452                         Artifact artifact = child.getArtifact();
453                         artifact.setDependencyTrail( node.getDependencyTrail() );
454                         List<ArtifactRepository> childRemoteRepositories = child.getRemoteRepositories();
455 
456                         MetadataResolutionRequest metadataRequest =
457                             new DefaultMetadataResolutionRequest( request );
458                         metadataRequest.setArtifact( artifact );
459                         metadataRequest.setRemoteRepositories( childRemoteRepositories );
460 
461                         try
462                         {
463                             ResolutionGroup rGroup;
464 
465                             Object childKey;
466                             do
467                             {
468                                 childKey = child.getKey();
469 
470                                 if ( managedVersions.containsKey( childKey ) )
471                                 {
472                                     // If this child node is a managed dependency, ensure
473                                     // we are using the dependency management version
474                                     // of this child if applicable b/c we want to use the
475                                     // managed version's POM, *not* any other version's POM.
476                                     // We retrieve the POM below in the retrieval step.
477                                     manageArtifact( child, managedVersions, listeners );
478 
479                                     // Also, we need to ensure that any exclusions it presents are
480                                     // added to the artifact before we retrieve the metadata
481                                     // for the artifact; otherwise we may end up with unwanted
482                                     // dependencies.
483                                     Artifact ma = managedVersions.get( childKey );
484                                     ArtifactFilter managedExclusionFilter = ma.getDependencyFilter();
485                                     if ( null != managedExclusionFilter )
486                                     {
487                                         if ( null != artifact.getDependencyFilter() )
488                                         {
489                                             AndArtifactFilter aaf = new AndArtifactFilter();
490                                             aaf.add( artifact.getDependencyFilter() );
491                                             aaf.add( managedExclusionFilter );
492                                             artifact.setDependencyFilter( aaf );
493                                         }
494                                         else
495                                         {
496                                             artifact.setDependencyFilter( managedExclusionFilter );
497                                         }
498                                     }
499                                 }
500 
501                                 if ( artifact.getVersion() == null )
502                                 {
503                                     // set the recommended version
504                                     // TODO maybe its better to just pass the range through to retrieval and use a
505                                     // transformation?
506                                     ArtifactVersion version;
507                                     if ( !artifact.isSelectedVersionKnown() )
508                                     {
509                                         List<ArtifactVersion> versions = artifact.getAvailableVersions();
510                                         if ( versions == null )
511                                         {
512                                             versions = source.retrieveAvailableVersions( metadataRequest );
513                                             artifact.setAvailableVersions( versions );
514                                         }
515 
516                                         Collections.sort( versions );
517 
518                                         VersionRange versionRange = artifact.getVersionRange();
519 
520                                         version = versionRange.matchVersion( versions );
521 
522                                         if ( version == null )
523                                         {
524                                             if ( versions.isEmpty() )
525                                             {
526                                                 throw new OverConstrainedVersionException(
527                                                     "No versions are present in the repository for the artifact"
528                                                         + " with a range " + versionRange, artifact,
529                                                     childRemoteRepositories );
530 
531                                             }
532 
533                                             throw new OverConstrainedVersionException(
534                                                 "Couldn't find a version in " + versions + " to match range "
535                                                     + versionRange, artifact, childRemoteRepositories );
536 
537                                         }
538                                     }
539                                     else
540                                     {
541                                         version = artifact.getSelectedVersion();
542                                     }
543 
544                                     artifact.selectVersion( version.toString() );
545                                     fireEvent( ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, child );
546                                 }
547 
548                                 rGroup = source.retrieve( metadataRequest );
549 
550                                 if ( rGroup == null )
551                                 {
552                                     break;
553                                 }
554                             }
555                             while ( !childKey.equals( child.getKey() ) );
556 
557                             if ( parentArtifact != null && parentArtifact.getDependencyFilter() != null
558                                      && !parentArtifact.getDependencyFilter().include( artifact ) )
559                             {
560                                 // MNG-3769: the [probably relocated] artifact is excluded.
561                                 // We could process exclusions on relocated artifact details in the
562                                 // MavenMetadataSource.createArtifacts(..) step, BUT that would
563                                 // require resolving the POM from the repository very early on in
564                                 // the build.
565                                 continue;
566                             }
567 
568                             // TODO might be better to have source.retrieve() throw a specific exception for this
569                             // situation
570                             // and catch here rather than have it return null
571                             if ( rGroup == null )
572                             {
573                                 // relocated dependency artifact is declared excluded, no need to add and recurse
574                                 // further
575                                 continue;
576                             }
577 
578                             child.addDependencies( rGroup.getArtifacts(), rGroup.getResolutionRepositories(), filter );
579 
580                         }
581                         catch ( CyclicDependencyException e )
582                         {
583                             // would like to throw this, but we have crappy stuff in the repo
584 
585                             fireEvent( ResolutionListener.OMIT_FOR_CYCLE, listeners,
586                                        new ResolutionNode( e.getArtifact(), childRemoteRepositories, child ) );
587                         }
588                         catch ( ArtifactMetadataRetrievalException e )
589                         {
590                             artifact.setDependencyTrail( node.getDependencyTrail() );
591 
592                             throw new ArtifactResolutionException( "Unable to get dependency information for "
593                                                                        + artifact.getId() + ": " + e.getMessage(),
594                                                                    artifact, childRemoteRepositories, e );
595 
596                         }
597 
598                         ArtifactResolutionRequest subRequest = new ArtifactResolutionRequest( metadataRequest );
599                         subRequest.setServers( request.getServers() );
600                         subRequest.setMirrors( request.getMirrors() );
601                         subRequest.setProxies( request.getProxies() );
602                         recurse( result, child, resolvedArtifacts, managedVersions, subRequest, source, filter,
603                                  listeners, conflictResolvers );
604 
605                     }
606                 }
607                 catch ( OverConstrainedVersionException e )
608                 {
609                     result.addVersionRangeViolation( e );
610                 }
611                 catch ( ArtifactResolutionException e )
612                 {
613                     result.addMetadataResolutionException( e );
614                 }
615             }
616 
617             fireEvent( ResolutionListener.FINISH_PROCESSING_CHILDREN, listeners, node );
618         }
619     }
620 
621     private void manageArtifact( ResolutionNode node, ManagedVersionMap managedVersions,
622                                  List<ResolutionListener> listeners )
623     {
624         Artifact artifact = managedVersions.get( node.getKey() );
625 
626         // Before we update the version of the artifact, we need to know
627         // whether we are working on a transitive dependency or not. This
628         // allows depMgmt to always override transitive dependencies, while
629         // explicit child override depMgmt (viz. depMgmt should only
630         // provide defaults to children, but should override transitives).
631         // We can do this by calling isChildOfRootNode on the current node.
632         if ( ( artifact.getVersion() != null )
633                  && ( !node.isChildOfRootNode() || node.getArtifact().getVersion() == null ) )
634         {
635             fireEvent( ResolutionListener.MANAGE_ARTIFACT_VERSION, listeners, node, artifact );
636             node.getArtifact().setVersion( artifact.getVersion() );
637         }
638 
639         if ( ( artifact.getScope() != null ) && ( !node.isChildOfRootNode() || node.getArtifact().getScope() == null ) )
640         {
641             fireEvent( ResolutionListener.MANAGE_ARTIFACT_SCOPE, listeners, node, artifact );
642             node.getArtifact().setScope( artifact.getScope() );
643         }
644 
645         if ( Artifact.SCOPE_SYSTEM.equals( node.getArtifact().getScope() ) && ( node.getArtifact().getFile() == null )
646                  && ( artifact.getFile() != null ) )
647         {
648             fireEvent( ResolutionListener.MANAGE_ARTIFACT_SYSTEM_PATH, listeners, node, artifact );
649             node.getArtifact().setFile( artifact.getFile() );
650         }
651     }
652 
653     /**
654      * Check if the artifactScope needs to be updated. <a
655      * href="http://docs.codehaus.org/x/IGU#DependencyMediationandConflictResolution-Scoperesolution">More info</a>.
656      *
657      * @param farthest farthest resolution node
658      * @param nearest nearest resolution node
659      * @param listeners
660      */
661     boolean checkScopeUpdate( ResolutionNode farthest, ResolutionNode nearest, List<ResolutionListener> listeners )
662     {
663         boolean updateScope = false;
664         Artifact farthestArtifact = farthest.getArtifact();
665         Artifact nearestArtifact = nearest.getArtifact();
666 
667         /* farthest is runtime and nearest has lower priority, change to runtime */
668         if ( Artifact.SCOPE_RUNTIME.equals( farthestArtifact.getScope() )
669                  && ( Artifact.SCOPE_TEST.equals( nearestArtifact.getScope() )
670                       || Artifact.SCOPE_PROVIDED.equals( nearestArtifact.getScope() ) ) )
671         {
672             updateScope = true;
673         }
674 
675         /* farthest is compile and nearest is not (has lower priority), change to compile */
676         if ( Artifact.SCOPE_COMPILE.equals( farthestArtifact.getScope() )
677                  && !Artifact.SCOPE_COMPILE.equals( nearestArtifact.getScope() ) )
678         {
679             updateScope = true;
680         }
681 
682         /* current POM rules all, if nearest is in current pom, do not update its artifactScope */
683         if ( ( nearest.getDepth() < 2 ) && updateScope )
684         {
685             updateScope = false;
686 
687             fireEvent( ResolutionListener.UPDATE_SCOPE_CURRENT_POM, listeners, nearest, farthestArtifact );
688         }
689 
690         if ( updateScope )
691         {
692             fireEvent( ResolutionListener.UPDATE_SCOPE, listeners, nearest, farthestArtifact );
693 
694             // previously we cloned the artifact, but it is more efficient to just update the artifactScope
695             // if problems are later discovered that the original object needs its original artifactScope value, 
696             // cloning may
697             // again be appropriate
698             nearestArtifact.setScope( farthestArtifact.getScope() );
699         }
700 
701         return updateScope;
702     }
703 
704     private void fireEvent( int event, List<ResolutionListener> listeners, ResolutionNode node )
705     {
706         fireEvent( event, listeners, node, null );
707     }
708 
709     private void fireEvent( int event, List<ResolutionListener> listeners, ResolutionNode node, Artifact replacement )
710     {
711         fireEvent( event, listeners, node, replacement, null );
712     }
713 
714     private void fireEvent( int event, List<ResolutionListener> listeners, ResolutionNode node, Artifact replacement,
715                             VersionRange newRange )
716     {
717         for ( ResolutionListener listener : listeners )
718         {
719             switch ( event )
720             {
721                 case ResolutionListener.TEST_ARTIFACT:
722                     listener.testArtifact( node.getArtifact() );
723                     break;
724                 case ResolutionListener.PROCESS_CHILDREN:
725                     listener.startProcessChildren( node.getArtifact() );
726                     break;
727                 case ResolutionListener.FINISH_PROCESSING_CHILDREN:
728                     listener.endProcessChildren( node.getArtifact() );
729                     break;
730                 case ResolutionListener.INCLUDE_ARTIFACT:
731                     listener.includeArtifact( node.getArtifact() );
732                     break;
733                 case ResolutionListener.OMIT_FOR_NEARER:
734                     listener.omitForNearer( node.getArtifact(), replacement );
735                     break;
736                 case ResolutionListener.OMIT_FOR_CYCLE:
737                     listener.omitForCycle( node.getArtifact() );
738                     break;
739                 case ResolutionListener.UPDATE_SCOPE:
740                     listener.updateScope( node.getArtifact(), replacement.getScope() );
741                     break;
742                 case ResolutionListener.UPDATE_SCOPE_CURRENT_POM:
743                     listener.updateScopeCurrentPom( node.getArtifact(), replacement.getScope() );
744                     break;
745                 case ResolutionListener.MANAGE_ARTIFACT_VERSION:
746                     if ( listener instanceof ResolutionListenerForDepMgmt )
747                     {
748                         ResolutionListenerForDepMgmt asImpl = (ResolutionListenerForDepMgmt) listener;
749                         asImpl.manageArtifactVersion( node.getArtifact(), replacement );
750                     }
751                     else
752                     {
753                         listener.manageArtifact( node.getArtifact(), replacement );
754                     }
755                     break;
756                 case ResolutionListener.MANAGE_ARTIFACT_SCOPE:
757                     if ( listener instanceof ResolutionListenerForDepMgmt )
758                     {
759                         ResolutionListenerForDepMgmt asImpl = (ResolutionListenerForDepMgmt) listener;
760                         asImpl.manageArtifactScope( node.getArtifact(), replacement );
761                     }
762                     else
763                     {
764                         listener.manageArtifact( node.getArtifact(), replacement );
765                     }
766                     break;
767                 case ResolutionListener.MANAGE_ARTIFACT_SYSTEM_PATH:
768                     if ( listener instanceof ResolutionListenerForDepMgmt )
769                     {
770                         ResolutionListenerForDepMgmt asImpl = (ResolutionListenerForDepMgmt) listener;
771                         asImpl.manageArtifactSystemPath( node.getArtifact(), replacement );
772                     }
773                     else
774                     {
775                         listener.manageArtifact( node.getArtifact(), replacement );
776                     }
777                     break;
778                 case ResolutionListener.SELECT_VERSION_FROM_RANGE:
779                     listener.selectVersionFromRange( node.getArtifact() );
780                     break;
781                 case ResolutionListener.RESTRICT_RANGE:
782                     if ( node.getArtifact().getVersionRange().hasRestrictions()
783                              || replacement.getVersionRange().hasRestrictions() )
784                     {
785                         listener.restrictRange( node.getArtifact(), replacement, newRange );
786                     }
787                     break;
788                 default:
789                     throw new IllegalStateException( "Unknown event: " + event );
790             }
791         }
792     }
793 
794     public ArtifactResolutionResult collect( Set<Artifact> artifacts, Artifact originatingArtifact,
795                                              Map<String, Artifact> managedVersions, ArtifactRepository localRepository,
796                                              List<ArtifactRepository> remoteRepositories,
797                                              ArtifactMetadataSource source, ArtifactFilter filter,
798                                              List<ResolutionListener> listeners )
799     {
800         return collect( artifacts, originatingArtifact, managedVersions, localRepository, remoteRepositories, source,
801                         filter, listeners, null );
802     }
803 
804     public ArtifactResolutionResult collect( Set<Artifact> artifacts, Artifact originatingArtifact,
805                                              ArtifactRepository localRepository,
806                                              List<ArtifactRepository> remoteRepositories,
807                                              ArtifactMetadataSource source, ArtifactFilter filter,
808                                              List<ResolutionListener> listeners )
809     {
810         return collect( artifacts, originatingArtifact, null, localRepository, remoteRepositories, source, filter,
811                         listeners );
812     }
813 
814 }