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