All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.mercury.metadata.DependencyTreeBuilder Maven / Gradle / Ivy

The newest version!
/**
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package org.apache.maven.mercury.metadata;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.maven.mercury.artifact.ArtifactExclusionList;
import org.apache.maven.mercury.artifact.ArtifactInclusionList;
import org.apache.maven.mercury.artifact.ArtifactMetadata;
import org.apache.maven.mercury.artifact.ArtifactQueryList;
import org.apache.maven.mercury.artifact.ArtifactScopeEnum;
import org.apache.maven.mercury.artifact.MetadataTreeNode;
import org.apache.maven.mercury.artifact.api.ArtifactListProcessor;
import org.apache.maven.mercury.artifact.api.ConfigurationException;
import org.apache.maven.mercury.artifact.version.VersionException;
import org.apache.maven.mercury.event.EventGenerator;
import org.apache.maven.mercury.event.EventManager;
import org.apache.maven.mercury.event.EventTypeEnum;
import org.apache.maven.mercury.event.GenericEvent;
import org.apache.maven.mercury.event.MercuryEventListener;
import org.apache.maven.mercury.logging.IMercuryLogger;
import org.apache.maven.mercury.logging.MercuryLoggerManager;
import org.apache.maven.mercury.metadata.sat.DefaultSatSolver;
import org.apache.maven.mercury.metadata.sat.SatException;
import org.apache.maven.mercury.repository.api.MetadataResults;
import org.apache.maven.mercury.repository.api.Repository;
import org.apache.maven.mercury.repository.api.RepositoryException;
import org.apache.maven.mercury.repository.virtual.VirtualRepositoryReader;
import org.apache.maven.mercury.util.Util;
import org.codehaus.plexus.lang.DefaultLanguage;
import org.codehaus.plexus.lang.Language;

/**
 * This is the new entry point into Artifact resolution process.
 * 
 * @author Oleg Gusakov
 * @version $Id: DependencyTreeBuilder.java 762963 2009-04-07 21:01:07Z ogusakov $
 */
class DependencyTreeBuilder
    implements DependencyBuilder, EventGenerator
{
    public static final ArtifactMetadata DUMMY_ROOT = new ArtifactMetadata( "__fake:__fake:1.0" );

    private static final Language LANG = new DefaultLanguage( DependencyTreeBuilder.class );
    
    public static final String SYSTEM_PROPERTY_DUMP_DEPENDENCY_TREE = "mercury.dump.tree";
    
    private static final String _depTreeDumpFileName = System.getProperty( SYSTEM_PROPERTY_DUMP_DEPENDENCY_TREE );

    private static final boolean _dumpDepTree = _depTreeDumpFileName != null;
    
    private static final DependencyTreeDumper _dumper = _dumpDepTree ? new DependencyTreeDumper(_depTreeDumpFileName ) : null;
    
    private static final IMercuryLogger LOG = MercuryLoggerManager.getLogger( DependencyTreeBuilder.class );

    private Collection _filters;

    private List> _comparators;

    private Map _processors;

    private VirtualRepositoryReader _reader;

    private Map _existingNodes;

    private EventManager _eventManager;
    
    private boolean _buildIndividualTrees = true;
    
    private boolean _allowCircularDependencies = Boolean.parseBoolean( System.getProperty( SYSTEM_PROPERTY_ALLOW_CIRCULAR_DEPENDENCIES, "false" ) );
    
    /** mandated versions in the format G:A -> V */
    private Map _versionMap;
    
    class TruckLoad
    {
        List cp;
        MetadataTreeNode root;
        
        public TruckLoad()
        {
        }
        
        public TruckLoad( List cp )
        {
            this.cp = cp;
        }
        
        public TruckLoad( MetadataTreeNode root )
        {
            this.root = root;
        }
    }

    /**
     * creates an instance of MetadataTree. Use this instance to
     * 
    *
  • buildTree - process all the dependencies
  • *
  • resolveConflicts
  • *
      * * @param filters - can veto any artifact before it's added to the tree * @param comparators - used to define selection policies. If null is passed, classic comparators - nearest/newest * first - will be used. * @param repositories - order is very important. Ordering allows m2eclipse, for instance, insert a workspace * repository * @throws RepositoryException */ protected DependencyTreeBuilder( Collection repositories, Collection filters, List> comparators, Map processors ) throws RepositoryException { this._filters = filters; this._comparators = comparators; // if used does not want to bother. // if it's an empty list - user does not want any comparators - so be it if ( _comparators == null ) { _comparators = new ArrayList>( 2 ); _comparators.add( new ClassicDepthComparator() ); _comparators.add( new ClassicVersionComparator() ); } if ( processors != null ) _processors = processors; this._reader = new VirtualRepositoryReader( repositories ); } // ------------------------------------------------------------------------ public MetadataTreeNode buildTree( ArtifactMetadata startMD, ArtifactScopeEnum treeScope ) throws MetadataTreeException { if ( startMD == null ) throw new MetadataTreeException( "null start point" ); try { _reader.setEventManager( _eventManager ); _reader.setProcessors( _processors ); _reader.init(); } catch ( RepositoryException e ) { throw new MetadataTreeException( e ); } _existingNodes = new HashMap( 256 ); GenericEvent treeBuildEvent = null; if ( _eventManager != null ) treeBuildEvent = new GenericEvent( EventTypeEnum.dependencyBuilder, TREE_BUILD_EVENT, startMD.getGAV() ); MetadataTreeNode root = createNode( startMD, null, startMD, treeScope ); if ( _eventManager != null ) treeBuildEvent.stop(); if ( _eventManager != null ) _eventManager.fireEvent( treeBuildEvent ); MetadataTreeNode.reNumber( root, 1 ); return root; } // ------------------------------------------------------------------------ public List resolveConflicts( ArtifactScopeEnum scope , ArtifactQueryList artifacts , ArtifactInclusionList inclusions , ArtifactExclusionList exclusions ) throws MetadataTreeException { TruckLoad tl = resolveConflictsInternally( scope, artifacts, inclusions, exclusions, false ); return tl == null ? null : tl.cp; } // ------------------------------------------------------------------------ public MetadataTreeNode resolveConflictsAsTree( ArtifactScopeEnum scope , ArtifactQueryList artifacts , ArtifactInclusionList inclusions , ArtifactExclusionList exclusions ) throws MetadataTreeException { TruckLoad tl = resolveConflictsInternally( scope, artifacts, inclusions, exclusions, true ); return tl == null ? null : tl.root; } // ------------------------------------------------------------------------ public TruckLoad resolveConflictsInternally( ArtifactScopeEnum scope , ArtifactQueryList artifacts , ArtifactInclusionList inclusions , ArtifactExclusionList exclusions , boolean asTree ) throws MetadataTreeException { if ( artifacts == null ) throw new MetadataTreeException( LANG.getMessage( "empty.md.collection" ) ); List startMDs = artifacts.getMetadataList(); if ( Util.isEmpty( startMDs ) ) throw new MetadataTreeException( LANG.getMessage( "empty.md.collection" ) ); int nodeCount = startMDs.size(); if ( nodeCount == 1 && inclusions == null && exclusions == null ) { ArtifactMetadata bmd = startMDs.get( 0 ); MetadataTreeNode rooty = buildTree( bmd, scope ); TruckLoad tl = null; if( asTree ) { MetadataTreeNode tr = resolveConflictsAsTree( rooty ); tl = new TruckLoad( tr ); } else { List res = resolveConflicts( rooty ); tl = new TruckLoad( res ); if(_dumpDepTree ) _dumper.dump( scope, artifacts, inclusions, exclusions, rooty, res ); } return tl; } DUMMY_ROOT.setDependencies( startMDs ); DUMMY_ROOT.setInclusions( inclusions == null ? null : inclusions.getMetadataList() ); DUMMY_ROOT.setExclusions( exclusions == null ? null : exclusions.getMetadataList() ); MetadataTreeNode root = null; if( _buildIndividualTrees ) { List deps = new ArrayList( nodeCount ); for ( ArtifactMetadata bmd : startMDs ) { if( scope != null && !scope.encloses( bmd.getArtifactScope() ) ) continue; try { if( ! DUMMY_ROOT.allowDependency( bmd ) ) continue; } catch ( VersionException e ) { throw new MetadataTreeException(e); } if( inclusions != null ) { List inc = inclusions.getMetadataList(); if( bmd.hasInclusions() ) bmd.getInclusions().addAll( inc ); else bmd.setInclusions( inc ); } if( exclusions != null ) { List excl = exclusions.getMetadataList(); if( bmd.hasExclusions() ) bmd.getExclusions().addAll( excl ); else bmd.setExclusions( excl ); } MetadataTreeNode rooty = buildTree( bmd, scope ); deps.add( rooty ); } if( Util.isEmpty( deps ) ) // all dependencies are filtered out return null; // combine into one tree root = new MetadataTreeNode( DUMMY_ROOT, null, null ); for ( MetadataTreeNode kid : deps ) root.addChild( kid ); } else { DUMMY_ROOT.setDependencies( startMDs ); root = buildTree( DUMMY_ROOT, scope ); } TruckLoad tl = null; if( asTree ) { MetadataTreeNode tr = resolveConflictsAsTree( root ); tl = new TruckLoad( tr ); } else { List cp = resolveConflicts( root ); if( cp != null ) cp.remove( DUMMY_ROOT ); if(_dumpDepTree ) _dumper.dump( scope, artifacts, inclusions, exclusions, root, cp ); tl = new TruckLoad( cp ); } return tl; } // ----------------------------------------------------- private MetadataTreeNode createNode( ArtifactMetadata nodeMD, MetadataTreeNode parent , ArtifactMetadata nodeQuery, ArtifactScopeEnum globalScope ) throws MetadataTreeException { GenericEvent nodeBuildEvent = null; if ( _eventManager != null ) nodeBuildEvent = new GenericEvent( EventTypeEnum.dependencyBuilder, TREE_NODE_BUILD_EVENT, nodeMD.getGAV() ); try { try { checkForCircularDependency( nodeMD, parent ); } catch ( MetadataTreeCircularDependencyException e ) { if( _allowCircularDependencies ) { String line = LANG.getMessage( "attention.line" ); LOG.info( line + e.getMessage() + line ); return null; } else throw e; } ArtifactMetadata mr; MetadataTreeNode existingNode = _existingNodes.get( nodeQuery.toString() ); if ( existingNode != null ) return MetadataTreeNode.deepCopy( existingNode ); if( DUMMY_ROOT.equals( nodeMD )) mr = DUMMY_ROOT; else mr = _reader.readDependencies( nodeMD ); if ( mr == null ) throw new MetadataTreeException( LANG.getMessage( "artifact.md.not.found", nodeMD.toString() ) ); MetadataTreeNode node = new MetadataTreeNode( mr, parent, nodeQuery ); List allDependencies = mr.getDependencies(); if ( allDependencies == null || allDependencies.size() < 1 ) return node; List dependencies = new ArrayList( allDependencies.size() ); if ( globalScope != null ) for ( ArtifactMetadata md : allDependencies ) { ArtifactScopeEnum mdScope = md.getArtifactScope(); if ( globalScope.encloses( mdScope ) ) dependencies.add( md ); } else dependencies.addAll( allDependencies ); if ( Util.isEmpty( dependencies ) ) return node; MetadataResults res = _reader.readVersions( dependencies ); if( res == null ) throw new MetadataTreeException( LANG.getMessage( "no.versions", dependencies.toString() ) ); Map> expandedDeps = res.getResults(); for ( ArtifactMetadata md : dependencies ) { if ( LOG.isDebugEnabled() ) LOG.debug( "node " + nodeQuery + ", dep " + md ); List versions = expandedDeps.get( md ); if ( versions == null || versions.size() < 1 ) { if ( md.isOptional() ) continue; throw new MetadataTreeException( LANG.getMessage( "not.optional.missing" ) + md + " <== "+ showPath( node ) ); } boolean noVersions = true; boolean noGoodVersions = true; for ( ArtifactMetadata ver : versions ) { if ( veto( ver, _filters ) || vetoInclusionsExclusions( node, ver ) ) { // there were good versions, but this one is filtered out noGoodVersions = false; continue; } MetadataTreeNode kid = createNode( ver, node, md, globalScope ); if( kid != null ) node.addChild( kid ); noVersions = false; noGoodVersions = false; } if ( noVersions && !noGoodVersions ) { // there were good versions, but they were all filtered out continue; } else if ( noGoodVersions ) { if ( md.isOptional() ) continue; throw new MetadataTreeException( LANG.getMessage( "not.optional.missing" ) + md + " <== "+ showPath( node ) ); } else node.addQuery( md ); } _existingNodes.put( nodeQuery.toString(), node ); return node; } catch ( RepositoryException e ) { if ( _eventManager != null ) nodeBuildEvent.setResult( e.getMessage() ); throw new MetadataTreeException( e ); } catch ( VersionException e ) { if ( _eventManager != null ) nodeBuildEvent.setResult( e.getMessage() ); throw new MetadataTreeException( e ); } catch ( MetadataTreeException e ) { if ( _eventManager != null ) nodeBuildEvent.setResult( e.getMessage() ); throw e; } finally { if ( _eventManager != null ) { nodeBuildEvent.stop(); _eventManager.fireEvent( nodeBuildEvent ); } } } // ----------------------------------------------------- private void checkForCircularDependency( ArtifactMetadata md, MetadataTreeNode parent ) throws MetadataTreeCircularDependencyException { MetadataTreeNode p = parent; int count = 0; while ( p != null ) { count++; // System.out.println("circ "+md+" vs "+p.md); if ( md.sameGA( p.getMd() ) ) { p = parent; StringBuilder sb = new StringBuilder( 128 ); sb.append( md.toString() ); while ( p != null ) { sb.append( " <- " + p.getMd().toString() ); if ( md.sameGA( p.getMd() ) ) { throw new MetadataTreeCircularDependencyException( "circular dependency " + count + " levels up. " + sb.toString() + " <= " + ( p.getParent() == null ? "no parent" : p.getParent().getMd() ) ); } p = p.getParent(); } } p = p.getParent(); } } // ----------------------------------------------------- private boolean veto( ArtifactMetadata md, Collection filters ) { if ( filters != null && filters.size() > 1 ) for ( MetadataTreeArtifactFilter filter : filters ) if ( filter.veto( md ) ) return true; return false; } // ----------------------------------------------------- private boolean vetoInclusionsExclusions( MetadataTreeNode node, ArtifactMetadata ver ) throws VersionException { for ( MetadataTreeNode n = node; n != null; n = n.getParent() ) { ArtifactMetadata md = n.getQuery(); if ( !md.allowDependency( ver ) ) // veto it return true; } return false; // allow because all parents are OK with it } // ----------------------------------------------------- public List resolveConflicts( MetadataTreeNode root ) throws MetadataTreeException { if ( root == null ) throw new MetadataTreeException( LANG.getMessage( "empty.tree" ) ); root.createNames( 0, 0 ); try { DefaultSatSolver solver = new DefaultSatSolver( root, _eventManager ); solver.applyPolicies( getComparators() ); List res = solver.solve(); return res; } catch ( SatException e ) { throw new MetadataTreeException( e ); } } // ----------------------------------------------------- public MetadataTreeNode resolveConflictsAsTree( MetadataTreeNode root ) throws MetadataTreeException { if ( root == null ) throw new MetadataTreeException( LANG.getMessage( "empty.tree" ) ); try { DefaultSatSolver solver = new DefaultSatSolver( root, _eventManager ); solver.applyPolicies( getComparators() ); MetadataTreeNode res = solver.solveAsTree(); return res; } catch ( SatException e ) { throw new MetadataTreeException( e ); } } // ----------------------------------------------------- private List> getComparators() { if ( Util.isEmpty( _comparators ) ) _comparators = new ArrayList>( 2 ); if ( _comparators.size() < 1 ) { _comparators.add( new ClassicDepthComparator() ); _comparators.add( new ClassicVersionComparator() ); } return _comparators; } // ----------------------------------------------------- private String showPath( MetadataTreeNode node ) throws MetadataTreeCircularDependencyException { StringBuilder sb = new StringBuilder( 256 ); String comma = ""; MetadataTreeNode p = node; while ( p != null ) { sb.append( comma + p.getMd().toString() ); comma = " <== "; p = p.getParent(); } return sb.toString(); } public void register( MercuryEventListener listener ) { if ( _eventManager == null ) _eventManager = new EventManager(); _eventManager.register( listener ); } public void unRegister( MercuryEventListener listener ) { if ( _eventManager != null ) _eventManager.unRegister( listener ); } public void setEventManager( EventManager eventManager ) { if ( _eventManager == null ) _eventManager = eventManager; else _eventManager.getListeners().addAll( eventManager.getListeners() ); } public void close() { if( _reader != null ) _reader.close(); } @SuppressWarnings("unchecked") public void setOption( String name, Object val ) throws ConfigurationException { if( SYSTEM_PROPERTY_ALLOW_CIRCULAR_DEPENDENCIES.equals( name ) ) _allowCircularDependencies = Boolean.parseBoolean( (String)val ); else if( CONFIGURATION_PROPERTY_VERSION_MAP.equals( name ) ) _versionMap = (Map) val; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy