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