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