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