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