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