* move back to using hacked mae-container plexus variant, to avoid the need to maintain all of sisu with component selectors...which isn't all that useful for MAE, really

* Introduce graph support based on Jung project, and dependency grapher api based on the graph support module, to make it easier to build up a dependency graph for a project



git-svn-id: https://svn.apache.org/repos/asf/maven/sandbox/trunk/mae@1161723 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/mae-boms/mae-library-bom/pom.xml b/mae-boms/mae-library-bom/pom.xml
index 29129f8..2970432 100644
--- a/mae-boms/mae-library-bom/pom.xml
+++ b/mae-boms/mae-library-bom/pom.xml
@@ -123,10 +123,14 @@
         <scope>provided</scope>
       </dependency>
       <dependency>
+        <groupId>commons-lang</groupId>
+        <artifactId>commons-lang</artifactId>
+        <version>2.5</version>
+      </dependency>
+      <dependency>
         <groupId>commons-io</groupId>
         <artifactId>commons-io</artifactId>
-        <version>2.0</version>
-        <scope>provided</scope>
+        <version>2.0.1</version>
       </dependency>
       <!-- /END infra requirements -->
       
diff --git a/mae-booter/pom.xml b/mae-booter/pom.xml
index 003a66c..dae9164 100644
--- a/mae-booter/pom.xml
+++ b/mae-booter/pom.xml
@@ -44,7 +44,6 @@
     <dependency>
     	<groupId>org.sonatype.sisu</groupId>
     	<artifactId>sisu-inject-plexus</artifactId>
-    	<version>2.1.1</version>
     </dependency>
     <dependency>
       <groupId>org.apache.maven.mae</groupId>
@@ -79,6 +78,17 @@
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-core</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven.mae</groupId>
+      <artifactId>mae-container</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-all</artifactId>
+      <version>1.1</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   
   <build>
diff --git a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/ComponentSelectionModule.java b/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/ComponentSelectionModule.java
deleted file mode 100644
index 35c3607..0000000
--- a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/ComponentSelectionModule.java
+++ /dev/null
@@ -1,251 +0,0 @@
-package org.apache.maven.mae.boot.embed;
-
-import java.lang.reflect.Field;
-import java.security.PrivilegedAction;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.maven.mae.internal.container.ComponentKey;
-import org.apache.maven.mae.internal.container.ComponentSelector;
-import org.codehaus.plexus.component.annotations.Component;
-import org.codehaus.plexus.component.annotations.Requirement;
-
-import com.google.inject.Binder;
-import com.google.inject.Key;
-import com.google.inject.MembersInjector;
-import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.TypeLiteral;
-import com.google.inject.matcher.Matchers;
-import com.google.inject.spi.Dependency;
-import com.google.inject.spi.InjectionListener;
-import com.google.inject.spi.InjectionPoint;
-import com.google.inject.spi.TypeEncounter;
-import com.google.inject.spi.TypeListener;
-
-// FIXME: Test this! It's definitely not complete.
-public class ComponentSelectionModule
-    implements Module
-{
-
-    private final ComponentSelector selector;
-
-    public ComponentSelectionModule( final ComponentSelector selector )
-    {
-        this.selector = selector;
-    }
-
-    @Override
-    public void configure( final Binder binder )
-    {
-        // binder.bindListener( Matchers.any(), new SysoutTypeListener() );
-        binder.bindListener( Matchers.any(), new ComponentSelectingListener( selector, binder ) );
-    }
-
-    @SuppressWarnings( "unused" )
-    private static final class SysoutTypeListener
-        implements TypeListener
-    {
-
-        @Override
-        public <I> void hear( final TypeLiteral<I> type, final TypeEncounter<I> encounter )
-        {
-            Set<InjectionPoint> injectionPoints = InjectionPoint.forInstanceMethodsAndFields( type.getRawType() );
-            if ( injectionPoints != null && !injectionPoints.isEmpty() )
-            {
-                System.out.println( type.getRawType().getName() );
-
-                for ( InjectionPoint ip : injectionPoints )
-                {
-                    Dependency<?> dep = ip.getDependencies().get( 0 );
-                    System.out.printf( "%s --> %s", ip.getMember(), dep.getKey() );
-                }
-            }
-
-            Component comp = type.getRawType().getAnnotation( Component.class );
-            if ( comp != null )
-            {
-                ComponentKey<I> found = new ComponentKey<I>( comp );
-
-                System.out.printf( "%s [Provider: %s]\n", found, encounter.getProvider( found.componentKey() ) );
-            }
-            else
-            {
-                System.out.printf( "RAW: %s\n", type.getRawType().getName() );
-            }
-        }
-
-    }
-
-    private static final class ComponentSelectingListener
-        implements TypeListener
-    {
-
-        private final ComponentSelector selector;
-
-        private final Binder binder;
-
-        public ComponentSelectingListener( final ComponentSelector selector, final Binder binder )
-        {
-            this.selector = selector;
-            this.binder = binder;
-        }
-
-        @SuppressWarnings( { "unchecked", "rawtypes" } )
-        @Override
-        public <I> void hear( final TypeLiteral<I> type, final TypeEncounter<I> encounter )
-        {
-            Map<Field, Provider<?>> requirementFields = new HashMap<Field, Provider<?>>();
-
-            // Component comp = type.getRawType().getAnnotation( Component.class );
-            // Set<InjectionPoint> injectionPoints = InjectionPoint.forInstanceMethodsAndFields( type.getRawType() );
-            // if ( comp != null || ( injectionPoints != null && !injectionPoints.isEmpty() ) )
-            // {
-            // encounter.register( null )
-            // }
-
-            for ( Field field : type.getRawType().getDeclaredFields() )
-            {
-                Requirement req = field.getAnnotation( Requirement.class );
-                if ( req != null )
-                {
-                    // FIXME: Collections!
-                    if ( !field.getType().equals( req.role() ) )
-                    {
-                        // System.out.printf( "Found collection? %s\n", field );
-                    }
-                    else
-                    {
-                        ComponentKey key = new ComponentKey( req, field );
-
-                        Key componentKey = key.componentKey();
-                        if ( key.isLiteral() )
-                        {
-                            componentKey = key.rawComponentKey();
-                            // System.out.printf( "%s ------> %s\n", field, componentKey );
-                        }
-                        else if ( selector.hasOverride( key ) )
-                        {
-                            binder.bind( key.literalComponentKey() ).toProvider( encounter.getProvider( key.componentKey() ) );
-                            componentKey = selector.getOverride( key ).componentKey();
-                            // System.out.printf( "%s ------> %s\n", field, componentKey );
-                        }
-
-                        requirementFields.put( field, encounter.getProvider( componentKey ) );
-                    }
-                }
-            }
-
-            if ( !requirementFields.isEmpty() )
-            {
-                final ComponentSelectionInjector<I> injector = new ComponentSelectionInjector<I>( requirementFields );
-                encounter.register( new MembersInjector<I>()
-                {
-                    @Override
-                    public void injectMembers( final I instance )
-                    {
-                        injector.inject( instance );
-                    }
-                } );
-
-                encounter.register( new InjectionListener<I>()
-                {
-                    @Override
-                    public void afterInjection( final I instance )
-                    {
-                        injector.inject( instance );
-                    }
-                } );
-            }
-        }
-    }
-
-    private static final class ComponentSelectionInjector<T>
-    {
-        private final Map<Field, Provider<?>> fields;
-
-        public ComponentSelectionInjector( final Map<Field, Provider<?>> fields )
-        {
-            this.fields = fields;
-        }
-
-        public void inject( final T instance )
-        {
-            for ( Map.Entry<Field, Provider<?>> entry : fields.entrySet() )
-            {
-                Field field = entry.getKey();
-                Provider<?> provider = entry.getValue();
-                boolean acc = field.isAccessible();
-                if ( !acc )
-                {
-                    field.setAccessible( true );
-                }
-
-                try
-                {
-                    Object value = provider.get();
-                    System.out.printf( "Setting %s to: %s\n", field, value );
-                    field.set( instance, value );
-                }
-                catch ( IllegalArgumentException e )
-                {
-                    throw new RuntimeException( e );
-                }
-                catch ( IllegalAccessException e )
-                {
-                    throw new RuntimeException( e );
-                }
-                finally
-                {
-                    field.setAccessible( acc );
-                }
-
-                // FieldSetter setter = new FieldSetter( entry.getKey(), entry.getValue(), instance );
-                // AccessController.doPrivileged( setter );
-            }
-        }
-
-    }
-
-    @SuppressWarnings( "unused" )
-    private static final class FieldSetter<T>
-        implements PrivilegedAction<T>
-    {
-
-        private final Field field;
-
-        private final Provider<?> provider;
-
-        private final T instance;
-
-        FieldSetter( final Field field, final Provider<?> provider, final T instance )
-        {
-            this.field = field;
-            this.provider = provider;
-            this.instance = instance;
-
-        }
-
-        @Override
-        public T run()
-        {
-            try
-            {
-                field.set( instance, provider.get() );
-            }
-            catch ( IllegalArgumentException e )
-            {
-                throw new RuntimeException( e );
-            }
-            catch ( IllegalAccessException e )
-            {
-                throw new RuntimeException( e );
-            }
-
-            return null;
-        }
-
-    }
-
-}
diff --git a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/InstanceModule.java b/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/InstanceModule.java
deleted file mode 100644
index 6b88364..0000000
--- a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/InstanceModule.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package org.apache.maven.mae.boot.embed;
-
-import java.util.Map;
-
-import org.apache.maven.mae.internal.container.ComponentKey;
-import org.apache.maven.mae.internal.container.InstanceRegistry;
-import org.apache.maven.mae.internal.container.VirtualInstance;
-import org.codehaus.plexus.component.annotations.Component;
-import org.sonatype.guice.bean.reflect.DeferredClass;
-import org.sonatype.guice.bean.reflect.LoadedClass;
-import org.sonatype.guice.plexus.config.Roles;
-import org.sonatype.guice.plexus.config.Strategies;
-
-import com.google.inject.Binder;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.Scopes;
-import com.google.inject.binder.ScopedBindingBuilder;
-
-public class InstanceModule
-    implements Module
-{
-
-    private final InstanceRegistry registry;
-
-    public InstanceModule( final InstanceRegistry reg )
-    {
-        this.registry = reg;
-    }
-
-    @SuppressWarnings( { "unchecked", "rawtypes" } )
-    @Override
-    public void configure( final Binder binder )
-    {
-        for ( final Map.Entry<ComponentKey<?>, Object> mapping : registry )
-        {
-            final ComponentKey<?> key = mapping.getKey();
-            final Object instance = mapping.getValue();
-
-            if ( instance instanceof VirtualInstance )
-            {
-                final VirtualInstance vi = (VirtualInstance) instance;
-                final Class<?> cls = vi.getVirtualClass();
-
-                final Component comp = cls.getAnnotation( Component.class );
-                if ( comp != null )
-                {
-                    hear( binder, comp, new LoadedClass<Object>( cls ),
-                          "External instance loaded from: " + cls.getClassLoader(), vi );
-                }
-                else
-                {
-                    binder.bind( Roles.componentKey( key.getRoleClass(), key.getHint() ) ).toProvider( (Provider) instance );
-                }
-            }
-            else
-            {
-                InstanceProvider provider = new InstanceProvider( instance );
-
-                final Component comp = instance.getClass().getAnnotation( Component.class );
-                if ( comp != null )
-                {
-                    hear( binder, comp, new LoadedClass<Object>( instance.getClass() ),
-                          "External instance loaded from: " + instance.getClass().getClassLoader(), provider );
-                }
-                else
-                {
-                    binder.bind( Roles.componentKey( key.getRoleClass(), key.getHint() ) ).toProvider( provider );
-                }
-            }
-        }
-    }
-
-    @SuppressWarnings( { "rawtypes", "unchecked" } )
-    private void hear( final Binder binder, final Component component, final DeferredClass<?> clazz,
-                       final String source, final Provider<?> provider )
-    {
-        final Key roleKey = Roles.componentKey( component );
-        final String strategy = component.instantiationStrategy();
-
-        final ScopedBindingBuilder sbb =
-            binder.withSource( source == null ? component.description() : source ).bind( roleKey ).toProvider( provider );
-
-        if ( Strategies.LOAD_ON_START.equals( strategy ) )
-        {
-            sbb.asEagerSingleton();
-        }
-        else if ( !Strategies.PER_LOOKUP.equals( strategy ) )
-        {
-            sbb.in( Scopes.SINGLETON );
-        }
-    }
-
-    private static final class InstanceProvider<T>
-        implements Provider<T>
-    {
-        @Inject
-        private Injector injector;
-
-        private final T instance;
-
-        InstanceProvider( final T instance )
-        {
-            this.instance = instance;
-        }
-
-        @Override
-        public T get()
-        {
-            injector.injectMembers( instance );
-            return instance;
-        }
-    }
-
-}
diff --git a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/MAEEmbedder.java b/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/MAEEmbedder.java
index 6c97f90..3101ca3 100644
--- a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/MAEEmbedder.java
+++ b/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/MAEEmbedder.java
@@ -53,6 +53,7 @@
 import org.apache.maven.mae.conf.mgmt.MAEManagementException;
 import org.apache.maven.mae.conf.mgmt.MAEManagementView;
 import org.apache.maven.mae.internal.container.ComponentKey;
+import org.apache.maven.mae.internal.container.MAEContainer;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.properties.internal.EnvironmentUtils;
 import org.apache.maven.settings.Settings;
@@ -63,7 +64,6 @@
 import org.apache.maven.settings.building.SettingsBuildingResult;
 import org.apache.maven.settings.building.SettingsProblem;
 import org.codehaus.plexus.PlexusConstants;
-import org.codehaus.plexus.PlexusContainer;
 import org.codehaus.plexus.component.annotations.Component;
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 import org.codehaus.plexus.logging.Logger;
@@ -76,6 +76,8 @@
 import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
 import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
 
+import com.google.inject.Injector;
+
 /**
  * The core of the embeddable Maven environment. This class is used as the main interface to the embedded Maven
  * environment for the application developer. The only other interface is component-instance injection, available
@@ -99,7 +101,7 @@
 
     private final boolean showVersion;
 
-    private final PlexusContainer container;
+    private final MAEContainer container;
 
     private final MAEConfiguration embConfiguration;
 
@@ -117,11 +119,13 @@
 
     private boolean stopped = false;
 
-    MAEEmbedder( final Maven maven, final MAEConfiguration embConfiguration, final PlexusContainer container,
-                 final SettingsBuilder settingsBuilder, final MavenExecutionRequestPopulator executionRequestPopulator,
-                 final DefaultSecDispatcher securityDispatcher, final MAEServiceManager serviceManager,
-                 final List<MAELibraryLoader> libraryLoaders, final PrintStream standardOut, final Logger logger,
-                 final boolean shouldShowErrors, final boolean showVersion )
+    MAEEmbedder( final Maven maven, final MAEConfiguration embConfiguration,
+                 final MAEContainer container, final SettingsBuilder settingsBuilder,
+                 final MavenExecutionRequestPopulator executionRequestPopulator,
+                 final DefaultSecDispatcher securityDispatcher,
+                 final MAEServiceManager serviceManager,
+                 final List<MAELibraryLoader> libraryLoaders, final PrintStream standardOut,
+                 final Logger logger, final boolean shouldShowErrors, final boolean showVersion )
     {
         this.maven = maven;
         this.embConfiguration = embConfiguration;
@@ -138,25 +142,23 @@
         this.showVersion = showVersion;
     }
 
-    // public synchronized Injector injector()
-    // throws MAEEmbeddingException
-    // {
-    // printInfo( null );
-    // return container.getInjector();
-    // }
+    public synchronized Injector injector()
+        throws MAEEmbeddingException
+    {
+        return container.getInjector();
+    }
 
-    // /**
-    // * Wire a series of externally managed objects with components from the Maven environment,
-    // * according to component annotations in those instances.
-    // */
-    // public synchronized Map<Object, Throwable> wire( final Object... instances )
-    // throws MAEEmbeddingException
-    // {
-    // checkStopped();
-    //
-    // printInfo( null );
-    // return container.extrudeDependencies( instances );
-    // }
+    /**
+     * Wire a series of externally managed objects with components from the Maven environment, according to component
+     * annotations in those instances.
+     */
+    public synchronized Map<Object, Throwable> wire( final Object... instances )
+        throws MAEEmbeddingException
+    {
+        checkStopped();
+
+        return container.extrudeDependencies( instances );
+    }
 
     protected void checkStopped()
     {
@@ -227,14 +229,17 @@
         {
             final DefaultPlexusCipher cipher = new DefaultPlexusCipher();
 
-            final String result = cipher.encryptAndDecorate( passwd, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION );
+            final String result =
+                cipher.encryptAndDecorate( passwd,
+                                           DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION );
             logger.info( result );
 
             return result;
         }
         catch ( final PlexusCipherException e )
         {
-            throw new MAEEmbeddingException( "Failed to encrypt master password: {0}", e, e.getMessage() );
+            throw new MAEEmbeddingException( "Failed to encrypt master password: {0}", e,
+                                             e.getMessage() );
         }
     }
 
@@ -257,7 +262,9 @@
             configurationFile = System.getProperty( "user.home" ) + configurationFile.substring( 1 );
         }
 
-        final String file = System.getProperty( DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, configurationFile );
+        final String file =
+            System.getProperty( DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION,
+                                configurationFile );
 
         String master = null;
 
@@ -271,7 +278,9 @@
 
             if ( master == null )
             {
-                throw new IllegalStateException( "Master password is not set in the setting security file: " + file );
+                throw new IllegalStateException(
+                                                 "Master password is not set in the setting security file: "
+                                                     + file );
             }
 
             final DefaultPlexusCipher cipher = new DefaultPlexusCipher();
@@ -300,15 +309,18 @@
 
         for ( final MAELibrary library : embConfiguration.getLibraries() )
         {
-            final Set<ComponentKey<?>> components = library.getManagementComponents( LoadOnStart.class );
+            final Set<ComponentKey<?>> components =
+                library.getManagementComponents( LoadOnStart.class );
             if ( components != null && !components.isEmpty() )
             {
-                final MAEManagementView mgmtView = new EmbedderManagementView( container, embConfiguration );
+                final MAEManagementView mgmtView =
+                    new EmbedderManagementView( container, embConfiguration );
                 for ( final ComponentKey<?> key : components )
                 {
                     try
                     {
-                        final LoadOnStart los = (LoadOnStart) container.lookup( key.getRole(), key.getHint() );
+                        final LoadOnStart los =
+                            (LoadOnStart) container.lookup( key.getRole(), key.getHint() );
                         los.executionStarting( mgmtView );
                     }
                     catch ( final ComponentLookupException e )
@@ -336,15 +348,18 @@
         stopped = true;
         for ( final MAELibrary library : embConfiguration.getLibraries() )
         {
-            final Set<ComponentKey<?>> components = library.getManagementComponents( LoadOnFinish.class );
+            final Set<ComponentKey<?>> components =
+                library.getManagementComponents( LoadOnFinish.class );
             if ( components != null && !components.isEmpty() )
             {
-                final MAEManagementView mgmtView = new EmbedderManagementView( container, embConfiguration );
+                final MAEManagementView mgmtView =
+                    new EmbedderManagementView( container, embConfiguration );
                 for ( final ComponentKey<?> key : components )
                 {
                     try
                     {
-                        final LoadOnFinish lof = (LoadOnFinish) container.lookup( key.getRole(), key.getHint() );
+                        final LoadOnFinish lof =
+                            (LoadOnFinish) container.lookup( key.getRole(), key.getHint() );
                         lof.executionFinished( mgmtView );
                     }
                     catch ( final ComponentLookupException e )
@@ -399,11 +414,13 @@
     {
         checkStopped();
 
-        String localRepoProperty = request.getUserProperties().getProperty( MAEMain.LOCAL_REPO_PROPERTY );
+        String localRepoProperty =
+            request.getUserProperties().getProperty( MAEMain.LOCAL_REPO_PROPERTY );
 
         if ( localRepoProperty == null )
         {
-            localRepoProperty = request.getSystemProperties().getProperty( MAEMain.LOCAL_REPO_PROPERTY );
+            localRepoProperty =
+                request.getSystemProperties().getProperty( MAEMain.LOCAL_REPO_PROPERTY );
         }
 
         if ( localRepoProperty != null )
@@ -435,8 +452,7 @@
             container.lookup( LoggerManager.class ).setThresholds( request.getLoggingLevel() );
         }
         catch ( ComponentLookupException e )
-        {
-        }
+        {}
 
         // final Configurator log4jConfigurator = new Configurator()
         // {
@@ -501,7 +517,8 @@
             {
                 throw new MAEEmbeddingException(
                                                  "Failed to build settings; {0}\nGlobal settings: {1}\nUser settings: {2}",
-                                                 e, e.getMessage(), request.getGlobalSettingsFile(),
+                                                 e, e.getMessage(),
+                                                 request.getGlobalSettingsFile(),
                                                  request.getUserSettingsFile() );
             }
 
@@ -510,11 +527,13 @@
 
         try
         {
-            executionRequestPopulator.populateFromSettings( request.asMavenExecutionRequest(), settings );
+            executionRequestPopulator.populateFromSettings( request.asMavenExecutionRequest(),
+                                                            settings );
         }
         catch ( final MavenExecutionRequestPopulationException e )
         {
-            throw new MAEEmbeddingException( "Failed to populate request from settings; {0}", e, e.getMessage() );
+            throw new MAEEmbeddingException( "Failed to populate request from settings; {0}", e,
+                                             e.getMessage() );
         }
 
         if ( !settingsResult.getProblems().isEmpty() && logger.isWarnEnabled() )
@@ -535,8 +554,8 @@
      * Print information about the {@link MAELibrary} instances loaded into this environment to the {@link PrintStream}
      * parameter.
      */
-    public static void showInfo( final MAEConfiguration config, final List<MAELibraryLoader> loaders,
-                                 final PrintStream standardOut )
+    public static void showInfo( final MAEConfiguration config,
+                                 final List<MAELibraryLoader> loaders, final PrintStream standardOut )
         throws IOException
     {
         if ( infoShown )
@@ -551,7 +570,8 @@
         final Collection<MAELibrary> libraries = loadLibraries( config, loaders );
         for ( final MAELibrary ext : libraries )
         {
-            standardOut.println( "+" + ext.getLabel() + " (Log handle: '" + ext.getLogHandle() + "')" );
+            standardOut.println( "+" + ext.getLabel() + " (Log handle: '" + ext.getLogHandle()
+                + "')" );
         }
 
         standardOut.println();
@@ -565,7 +585,8 @@
      * Print the information about {@link MAELibrary} instances loaded, along with version information about this Maven
      * environment in general, to the provided {@link PrintStream} parameter.
      */
-    public static void showVersion( final MAEConfiguration config, final List<MAELibraryLoader> loaders,
+    public static void showVersion( final MAEConfiguration config,
+                                    final List<MAELibraryLoader> loaders,
                                     final PrintStream standardOut )
         throws IOException
     {
@@ -615,7 +636,8 @@
      * Print error output from a Maven execution request, in the familiar format, to the {@link Logger} instance used by
      * this embedder.
      */
-    public int formatErrorOutput( final MAEExecutionRequest request, final MavenExecutionResult result )
+    public int formatErrorOutput( final MAEExecutionRequest request,
+                                  final MavenExecutionResult result )
     {
         if ( result.hasExceptions() )
         {
@@ -660,7 +682,8 @@
                 }
             }
 
-            if ( project != null && !project.equals( result.getTopologicallySortedProjects().get( 0 ) ) )
+            if ( project != null
+                && !project.equals( result.getTopologicallySortedProjects().get( 0 ) ) )
             {
                 logger.error( "" );
                 logger.error( "After correcting the problems, you can resume the build with the command" );
@@ -684,7 +707,8 @@
         }
     }
 
-    protected void logSummary( final ExceptionSummary summary, final Map<String, String> references, String indent,
+    protected void logSummary( final ExceptionSummary summary,
+                               final Map<String, String> references, String indent,
                                final boolean showErrors )
     {
         String referenceKey = "";
@@ -734,11 +758,11 @@
         implements MAEManagementView
     {
 
-        private final PlexusContainer container;
+        private final MAEContainer container;
 
         private final MAEConfiguration configuration;
 
-        EmbedderManagementView( final PlexusContainer container, final MAEConfiguration configuration )
+        EmbedderManagementView( final MAEContainer container, final MAEConfiguration configuration )
         {
             this.container = container;
             this.configuration = configuration;
@@ -772,7 +796,8 @@
             {
                 throw new MAEManagementException(
                                                   "Failed to lookup component for managed component.\nRole: %s\nHint: %s\nReason: %s",
-                                                  e, role, PlexusConstants.PLEXUS_DEFAULT_HINT, e.getMessage() );
+                                                  e, role, PlexusConstants.PLEXUS_DEFAULT_HINT,
+                                                  e.getMessage() );
             }
         }
 
diff --git a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/MAEEmbedderBuilder.java b/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/MAEEmbedderBuilder.java
index 7aa2dba..e9a0727 100644
--- a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/MAEEmbedderBuilder.java
+++ b/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/MAEEmbedderBuilder.java
@@ -51,13 +51,12 @@
 import org.apache.maven.mae.internal.container.ComponentKey;
 import org.apache.maven.mae.internal.container.ComponentSelector;
 import org.apache.maven.mae.internal.container.InstanceRegistry;
+import org.apache.maven.mae.internal.container.MAEContainer;
 import org.apache.maven.mae.internal.container.VirtualInstance;
 import org.apache.maven.model.building.ModelProcessor;
 import org.apache.maven.settings.building.SettingsBuilder;
 import org.codehaus.plexus.ContainerConfiguration;
 import org.codehaus.plexus.DefaultContainerConfiguration;
-import org.codehaus.plexus.DefaultPlexusContainer;
-import org.codehaus.plexus.PlexusContainer;
 import org.codehaus.plexus.PlexusContainerException;
 import org.codehaus.plexus.classworlds.ClassWorld;
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
@@ -65,8 +64,6 @@
 import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
 
-import com.google.inject.Module;
-
 public class MAEEmbedderBuilder
 {
 
@@ -108,7 +105,7 @@
 
     private ModelProcessor modelProcessor;
 
-    private PlexusContainer container;
+    private MAEContainer container;
 
     private MavenExecutionRequestPopulator executionRequestPopulator;
 
@@ -148,7 +145,8 @@
 
     private List<MAELibraryLoader> libraryLoaders;
 
-    private final VirtualInstance<MAEEmbedder> embedderVirtual = new VirtualInstance<MAEEmbedder>( MAEEmbedder.class );
+    private final VirtualInstance<MAEEmbedder> embedderVirtual =
+        new VirtualInstance<MAEEmbedder>( MAEEmbedder.class );
 
     public synchronized MAEEmbedderBuilder withSettingsBuilder( final SettingsBuilder settingsBuilder )
     {
@@ -229,7 +227,8 @@
         return this;
     }
 
-    public synchronized MAEEmbedderBuilder withCoreClassLoader( final ClassLoader root, final Object... constituents )
+    public synchronized MAEEmbedderBuilder withCoreClassLoader( final ClassLoader root,
+                                                                final Object... constituents )
         throws MalformedURLException
     {
         if ( constituents != null && constituents.length > 0 )
@@ -269,8 +268,9 @@
                     final URL resource = cloader.getResource( fname );
                     if ( resource == null )
                     {
-                        throw new IllegalStateException( "Class doesn't appear in its own classloader! ["
-                            + object.getClass().getName() + "]" );
+                        throw new IllegalStateException(
+                                                         "Class doesn't appear in its own classloader! ["
+                                                             + object.getClass().getName() + "]" );
                     }
 
                     String path = resource.toExternalForm();
@@ -400,8 +400,8 @@
         }
         catch ( final ComponentLookupException e )
         {
-            throw new MAEEmbeddingException( "Failed to lookup component: %s. Reason: %s", e, cls.getName(),
-                                             e.getMessage() );
+            throw new MAEEmbeddingException( "Failed to lookup component: %s. Reason: %s", e,
+                                             cls.getName(), e.getMessage() );
         }
     }
 
@@ -414,12 +414,13 @@
         }
         catch ( final ComponentLookupException e )
         {
-            throw new MAEEmbeddingException( "Failed to lookup component: {0} with hint: {1}. Reason: {2}", e,
-                                             cls.getName(), hint, e.getMessage() );
+            throw new MAEEmbeddingException(
+                                             "Failed to lookup component: {0} with hint: {1}. Reason: {2}",
+                                             e, cls.getName(), hint, e.getMessage() );
         }
     }
 
-    public synchronized MAEEmbedderBuilder withContainer( final PlexusContainer container )
+    public synchronized MAEEmbedderBuilder withContainer( final MAEContainer container )
     {
         this.container = container;
         resetContainer();
@@ -463,7 +464,7 @@
         }
     }
 
-    public synchronized PlexusContainer container()
+    public synchronized MAEContainer container()
         throws MAEEmbeddingException
     {
         // Need to switch to using: org.codehaus.plexus.MutablePlexusContainer.addPlexusInjector(List<PlexusBeanModule>,
@@ -475,16 +476,15 @@
             final InstanceRegistry reg = new InstanceRegistry( instanceRegistry() );
             reg.addVirtual( new ComponentKey<MAEEmbedder>( MAEEmbedder.class ), embedderVirtual );
 
-            Module[] mods = { new ComponentSelectionModule( selector() ), new InstanceModule( reg ) };
-
-            DefaultPlexusContainer c;
+            MAEContainer c;
             try
             {
-                c = new DefaultPlexusContainer( cc, mods );
+                c = new MAEContainer( cc, selector(), reg );
             }
             catch ( final PlexusContainerException e )
             {
-                throw new MAEEmbeddingException( "Failed to initialize component container: {0}", e, e.getMessage() );
+                throw new MAEEmbeddingException( "Failed to initialize component container: {0}",
+                                                 e, e.getMessage() );
             }
 
             c.setLoggerManager( new MavenLoggerManager( logger ) );
@@ -519,7 +519,8 @@
         {
             for ( final String logHandle : debugLogHandles )
             {
-                final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger( logHandle );
+                final org.apache.log4j.Logger logger =
+                    org.apache.log4j.Logger.getLogger( logHandle );
                 logger.setLevel( Level.DEBUG );
             }
 
@@ -546,7 +547,8 @@
                 config.withLibraries( libraries );
 
                 if ( debugLogHandles != null
-                    && Arrays.binarySearch( debugLogHandles, MAEConfiguration.STANDARD_LOG_HANDLE_CORE ) > -1 )
+                    && Arrays.binarySearch( debugLogHandles,
+                                            MAEConfiguration.STANDARD_LOG_HANDLE_CORE ) > -1 )
                 {
                     MAEEmbedder.showInfo( config, loaders, standardOut() );
                 }
@@ -630,7 +632,9 @@
     {
         if ( libraryLoaders == null )
         {
-            libraryLoaders = new ArrayList<MAELibraryLoader>( Collections.singletonList( new ServiceLibraryLoader() ) );
+            libraryLoaders =
+                new ArrayList<MAELibraryLoader>(
+                                                 Collections.singletonList( new ServiceLibraryLoader() ) );
         }
 
         return libraryLoaders;
@@ -796,8 +800,7 @@
                 withStandardOut( newOut );
             }
             catch ( final FileNotFoundException e )
-            {
-            }
+            {}
         }
 
         logger();
@@ -807,9 +810,10 @@
         throws MAEEmbeddingException
     {
         final MAEEmbedder embedder =
-            new MAEEmbedder( maven(), configuration(), container(), settingsBuilder(), executionRequestPopulator(),
-                             securityDispatcher(), serviceManager(), libraryLoaders(), standardOut(), logger(),
-                             shouldShowErrors(), showVersion() );
+            new MAEEmbedder( maven(), configuration(), container(), settingsBuilder(),
+                             executionRequestPopulator(), securityDispatcher(), serviceManager(),
+                             libraryLoaders(), standardOut(), logger(), shouldShowErrors(),
+                             showVersion() );
 
         embedderVirtual.setInstance( embedder );
 
diff --git a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/MAEApplicationTest.java b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/MAEApplicationTest.java
index 8e1a66b..1964ed2 100644
--- a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/MAEApplicationTest.java
+++ b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/MAEApplicationTest.java
@@ -19,21 +19,28 @@
 
 package org.apache.maven.mae.internal.container;
 
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertThat;
 
+import java.util.Map;
+
 import org.apache.maven.mae.MAEException;
 import org.apache.maven.mae.app.AbstractMAEApplication;
 import org.apache.maven.mae.boot.embed.MAEEmbedderBuilder;
 import org.apache.maven.mae.internal.container.fixture.ContainerOwner;
 import org.apache.maven.mae.internal.container.fixture.DefaultSingletonOwner;
+import org.apache.maven.mae.internal.container.fixture.InitializedUsingRequirement;
+import org.apache.maven.mae.internal.container.fixture.MapOwner;
 import org.apache.maven.mae.internal.container.fixture.NonSimplePart;
 import org.apache.maven.mae.internal.container.fixture.Part;
 import org.apache.maven.mae.internal.container.fixture.SimplePart;
 import org.apache.maven.mae.internal.container.fixture.SingletonLiteralOwner;
 import org.apache.maven.mae.internal.container.fixture.SingletonOwner;
+import org.codehaus.plexus.ContainerConfiguration;
+import org.codehaus.plexus.DefaultContainerConfiguration;
 import org.junit.Test;
 
 public class MAEApplicationTest
@@ -74,21 +81,23 @@
                     owner.container, notNullValue() );
     }
 
-    // @Test
-    // public void mappedRequirementContainsNoLiteralIds()
-    // throws Throwable
-    // {
-    // final ContainerConfiguration config = new DefaultContainerConfiguration().setClassPathScanning( "ON" );
-    //
-    // final MAEContainer container = new MAEContainer( config, new ComponentSelector(), new InstanceRegistry() );
-    //
-    // final MapOwner mapOwner = container.lookup( MapOwner.class );
-    // final Map<String, Child> members = mapOwner.members();
-    //
-    // System.out.println( members );
-    //
-    // assertNull( members.get( "simple" + ComponentKey.LITERAL_SUFFIX ) );
-    // }
+    @Test
+    public void mappedRequirementContainsNoLiteralIds()
+        throws Throwable
+    {
+        final ContainerConfiguration config =
+            new DefaultContainerConfiguration().setClassPathScanning( "ON" );
+
+        final MAEContainer container =
+            new MAEContainer( config, new ComponentSelector(), new InstanceRegistry() );
+
+        final MapOwner mapOwner = container.lookup( MapOwner.class );
+        final Map<String, Part> members = mapOwner.members();
+
+        assertThat( members.size(), equalTo( 2 ) );
+
+        assertThat( members.get( "simple" + ComponentKey.LITERAL_SUFFIX ), nullValue() );
+    }
 
     @Test
     public void singletonImpliedRequirementOnComponentWithImpliedHint()
@@ -123,7 +132,8 @@
         throws Throwable
     {
         ContainerOwner owner = new ContainerOwner();
-        new TestApplication().withInstance( owner ).withComponentSelection( new ComponentKey<Part>( Part.class,
+        new TestApplication().withInstance( owner ).withComponentSelection( new ComponentKey<Part>(
+                                                                                                    Part.class,
                                                                                                     "simple" ),
                                                                             "non-simple" ).load();
 
@@ -140,7 +150,8 @@
         throws Throwable
     {
         ContainerOwner owner = new ContainerOwner();
-        new TestApplication().withInstance( owner ).withComponentSelection( new ComponentKey<Part>( Part.class,
+        new TestApplication().withInstance( owner ).withComponentSelection( new ComponentKey<Part>(
+                                                                                                    Part.class,
                                                                                                     "simple" ),
                                                                             "non-simple" ).load();
 
@@ -152,17 +163,18 @@
         assertThat( single.singletonLiteral(), instanceOf( SimplePart.class ) );
     }
 
-    //
-    // @Test
-    // public void initializableUsingRequirement()
-    // throws Throwable
-    // {
-    // final ContainerConfiguration config = new DefaultContainerConfiguration().setClassPathScanning( "ON" );
-    //
-    // final MAEContainer container = new MAEContainer( config, new ComponentSelector(), new InstanceRegistry() );
-    //
-    // container.lookup( InitializedUsingRequirement.class );
-    // }
+    @Test
+    public void initializableUsingRequirement()
+        throws Throwable
+    {
+        final ContainerConfiguration config =
+            new DefaultContainerConfiguration().setClassPathScanning( "ON" );
+
+        final MAEContainer container =
+            new MAEContainer( config, new ComponentSelector(), new InstanceRegistry() );
+
+        container.lookup( InitializedUsingRequirement.class );
+    }
 
     private static final class TestApplication
         extends AbstractMAEApplication
diff --git a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/MapOwner.java b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/MapOwner.java
index 141b979..21d6a35 100644
--- a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/MapOwner.java
+++ b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/MapOwner.java
@@ -19,19 +19,19 @@
 
 package org.apache.maven.mae.internal.container.fixture;
 
+import java.util.Map;
+
 import org.codehaus.plexus.component.annotations.Component;
 import org.codehaus.plexus.component.annotations.Requirement;
 
-import java.util.Map;
-
 @Component( role = MapOwner.class )
 public class MapOwner
 {
 
-    @Requirement( role = SimplePart.class )
-    private Map<String, SimplePart> members;
+    @Requirement( role = Part.class )
+    private Map<String, Part> members;
 
-    public Map<String, SimplePart> members()
+    public Map<String, Part> members()
     {
         return members;
     }
diff --git a/mae-components/mae-dependency-grapher/pom.xml b/mae-components/mae-dependency-grapher/pom.xml
new file mode 100644
index 0000000..385e61c
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<!--
+  Copyright 2010 Red Hat, Inc.
+  
+  Licensed 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.maven.mae.components</groupId>
+    <artifactId>mae-components</artifactId>
+    <version>1.0-alpha-1-SNAPSHOT</version>
+  </parent>
+  
+  <artifactId>mae-dependency-grapher</artifactId>
+  <name>Maven App Engine: Dependency-Graphing Tools</name>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+  
+  <dependencies>
+    <dependency>
+    	<groupId>commons-io</groupId>
+    	<artifactId>commons-io</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.maven.mae</groupId>
+    	<artifactId>mae-booter</artifactId>
+    	<version>${project.version}</version>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.maven.wagon</groupId>
+    	<artifactId>wagon-http-lightweight</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.maven.wagon</groupId>
+    	<artifactId>wagon-file</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>org.sonatype.aether</groupId>
+    	<artifactId>aether-connector-wagon</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.maven</groupId>
+    	<artifactId>maven-aether-provider</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.maven.mae.components</groupId>
+    	<artifactId>mae-graph-support</artifactId>
+    	<version>${project.version}</version>
+    </dependency>
+    <dependency>
+    	<groupId>commons-lang</groupId>
+    	<artifactId>commons-lang</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.maven.mae.components</groupId>
+    	<artifactId>mae-project-tools</artifactId>
+    	<version>${maeVersion}</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphException.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphException.java
new file mode 100644
index 0000000..d708664
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphException.java
@@ -0,0 +1,50 @@
+/*
+ * 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.mae.depgraph;
+
+import org.apache.maven.mae.MAEException;
+
+public class DepGraphException
+    extends MAEException
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public DepGraphException( final String message, final Object... params )
+    {
+        super( message, params );
+    }
+
+    public DepGraphException( final String message, final Throwable cause, final Object... params )
+    {
+        super( message, cause, params );
+    }
+
+    public DepGraphException( final String message, final Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    public DepGraphException( final String message )
+    {
+        super( message );
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphLoader.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphLoader.java
new file mode 100644
index 0000000..8961d77
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphLoader.java
@@ -0,0 +1,40 @@
+/*
+ * 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.mae.depgraph;
+
+import java.io.File;
+
+import org.apache.maven.mae.MAEException;
+import org.apache.maven.mae.project.session.ProjectToolsSession;
+
+public interface DepGraphLoader
+{
+
+    DependencyGraph loadProjectDependencyGraph( final File rootPom,
+                                                final ProjectToolsSession session,
+                                                final boolean includeModuleProjects )
+        throws MAEException;
+
+    DependencyGraph resolveProjectDependencies( final File rootPom,
+                                                final ProjectToolsSession session,
+                                                final boolean includeModuleProjects )
+        throws MAEException;
+
+}
\ No newline at end of file
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphNode.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphNode.java
new file mode 100644
index 0000000..3d0d151
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphNode.java
@@ -0,0 +1,255 @@
+/*
+ * 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.mae.depgraph;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.maven.artifact.ArtifactUtils;
+import org.apache.maven.mae.depgraph.impl.ArtifactOnlyDependencyNode;
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.resolution.ArtifactResult;
+
+public class DepGraphNode
+    implements Iterable<Throwable>
+{
+
+    private Artifact latestArtifact;
+
+    private ArtifactResult latestResult;
+
+    private final LinkedHashSet<RemoteRepository> remoteRepositories =
+        new LinkedHashSet<RemoteRepository>();
+
+    private String key;
+
+    private final boolean preResolved;
+
+    private final Set<Throwable> errors = new HashSet<Throwable>();
+
+    private DependencyNode latestDependencyNode;
+
+    public DepGraphNode( final DependencyNode node )
+    {
+        this( node, null, false );
+    }
+
+    protected DepGraphNode( final DependencyNode node, final String key, final boolean preResolved )
+    {
+        merge( node );
+
+        if ( key == null )
+        {
+            if ( latestArtifact != null )
+            {
+                this.key = key( latestArtifact );
+            }
+            else
+            {
+                throw new NullPointerException(
+                                                "Cannot calculate node key. DependencyNode parameter does not contain a valid artifact!" );
+            }
+        }
+        else
+        {
+            this.key = key;
+        }
+
+        this.preResolved = preResolved;
+    }
+
+    public DepGraphNode( final Artifact artifact, final boolean preResolved )
+    {
+        key = key( artifact );
+        latestArtifact = artifact;
+        this.preResolved = preResolved;
+    }
+
+    static String key( final Artifact a )
+    {
+        return ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getBaseVersion() );
+    }
+
+    public boolean isPreResolved()
+    {
+        return preResolved;
+    }
+
+    public synchronized void merge( final DependencyNode node )
+    {
+        latestDependencyNode = node;
+
+        if ( node.getRepositories() != null )
+        {
+            remoteRepositories.addAll( node.getRepositories() );
+        }
+
+        if ( latestArtifact == null && node.getDependency() != null
+            && node.getDependency().getArtifact() != null )
+        {
+            latestArtifact = node.getDependency().getArtifact();
+        }
+    }
+
+    public synchronized void merge( final ArtifactResult result )
+    {
+        if ( result.getArtifact() != null && result.getArtifact().getFile() != null )
+        {
+            result.getExceptions().clear();
+        }
+
+        latestResult = result;
+    }
+
+    public DependencyNode getLatestDependencyNode()
+    {
+        return latestDependencyNode;
+    }
+
+    public ArtifactResult getLatestResult()
+    {
+        return latestResult;
+    }
+
+    public Artifact getLatestArtifact()
+    {
+        return latestArtifact;
+    }
+
+    public LinkedHashSet<RemoteRepository> getRemoteRepositories()
+    {
+        return remoteRepositories;
+    }
+
+    public String getKey()
+    {
+        return key;
+    }
+
+    public synchronized boolean hasErrors()
+    {
+        return !errors.isEmpty();
+    }
+
+    private String renderErrors()
+    {
+        final StringBuilder sb = new StringBuilder();
+
+        sb.append( "Failed to resolve: " ).append( getKey() );
+        sb.append( "\n\n" ).append( errors.size() ).append( " Resolution errors:\n" );
+
+        for ( final Throwable error : errors )
+        {
+            final StringWriter sWriter = new StringWriter();
+            error.printStackTrace( new PrintWriter( sWriter ) );
+
+            sb.append( "\n\n" ).append( sWriter.toString() );
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    public Iterator<Throwable> iterator()
+    {
+        return getErrors().iterator();
+    }
+
+    public List<Throwable> getErrors()
+    {
+        return new ArrayList<Throwable>( errors );
+    }
+
+    public void logErrors( final PrintWriter writer )
+    {
+        writer.println( renderErrors() );
+    }
+
+    @Override
+    public String toString()
+    {
+        final StringBuilder builder = new StringBuilder();
+        builder.append( "DependencyTrackingState (\n    latestArtifact=" );
+        builder.append( latestArtifact );
+        builder.append( "\n    latestResult=" );
+        builder.append( latestResult );
+        builder.append( "\n    projectId=" );
+        builder.append( key );
+        builder.append( "\n)" );
+        return builder.toString();
+    }
+
+    public void removeLatestResult()
+    {
+        latestResult = null;
+    }
+
+    public void merge( final Artifact child )
+    {
+        merge( new ArtifactOnlyDependencyNode( child ) );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ( ( key == null ) ? 0 : key.hashCode() );
+        return result;
+    }
+
+    @Override
+    public boolean equals( final Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+        if ( obj == null )
+        {
+            return false;
+        }
+        if ( getClass() != obj.getClass() )
+        {
+            return false;
+        }
+        final DepGraphNode other = (DepGraphNode) obj;
+        if ( key == null )
+        {
+            if ( other.key != null )
+            {
+                return false;
+            }
+        }
+        else if ( !key.equals( other.key ) )
+        {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphRootNode.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphRootNode.java
new file mode 100644
index 0000000..2cf16fe
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DepGraphRootNode.java
@@ -0,0 +1,44 @@
+/*
+ * 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.mae.depgraph;
+
+import org.apache.maven.artifact.ArtifactUtils;
+import org.apache.maven.project.MavenProject;
+import org.sonatype.aether.graph.DependencyNode;
+
+public class DepGraphRootNode
+    extends DepGraphNode
+{
+
+    private final MavenProject project;
+
+    public DepGraphRootNode( final DependencyNode node, final MavenProject project )
+    {
+        super( node, ArtifactUtils.key( project.getGroupId(), project.getArtifactId(),
+                                        project.getVersion() ), true );
+        this.project = project;
+    }
+
+    public MavenProject getProject()
+    {
+        return project;
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DependencyGraph.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DependencyGraph.java
new file mode 100644
index 0000000..9e1512c
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/DependencyGraph.java
@@ -0,0 +1,217 @@
+/*
+ * 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.mae.depgraph;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.maven.mae.graph.DirectedGraph;
+import org.apache.maven.mae.graph.DirectionalEdge;
+import org.apache.maven.mae.graph.SimpleDirectedGraph;
+import org.apache.maven.project.MavenProject;
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.resolution.ArtifactResult;
+
+public class DependencyGraph
+    implements Iterable<DepGraphNode>
+{
+
+    private final Set<DepGraphRootNode> roots = new LinkedHashSet<DepGraphRootNode>();
+
+    private final DepGraph graph = new DepGraph();
+
+    public DepGraphRootNode addRoot( final DependencyNode root )
+    {
+        return addRoot( root, null );
+    }
+
+    public DepGraphRootNode addRoot( final DependencyNode root, final MavenProject project )
+    {
+        final DepGraphRootNode newRoot = new DepGraphRootNode( root, project );
+        final DepGraphRootNode rootNode = (DepGraphRootNode) findOrAdd( newRoot );
+
+        if ( rootNode != newRoot )
+        {
+            rootNode.merge( root );
+        }
+
+        roots.add( rootNode );
+
+        return rootNode;
+    }
+
+    public DepGraphNode addNodeResult( final Artifact artifact, final ArtifactResult result )
+    {
+        final DepGraphNode node = findOrAdd( new DepGraphNode( artifact, false ) );
+        node.merge( result );
+
+        return node;
+    }
+
+    private DepGraphNode findOrAdd( final DepGraphNode node )
+    {
+        DepGraphNode result = find( node );
+
+        if ( result == null )
+        {
+            graph.addVertex( node );
+            result = node;
+        }
+
+        return result;
+    }
+
+    private DepGraphNode find( final DepGraphNode node )
+    {
+        final List<DepGraphNode> nodes = new ArrayList<DepGraphNode>( graph.vertices() );
+        final int idx = nodes.indexOf( node );
+        if ( idx > -1 )
+        {
+            return nodes.get( idx );
+        }
+
+        return null;
+    }
+
+    public DepGraphNode[] addDependency( final DepGraphNode parentNode, final DependencyNode child )
+    {
+        final DepGraphNode newChildNode = new DepGraphNode( child );
+        final DepGraphNode childNode = findOrAdd( newChildNode );
+
+        // if we're reusing an existing node, merge the new info from the child.
+        if ( childNode != newChildNode )
+        {
+            childNode.merge( child );
+        }
+
+        if ( parentNode != null )
+        {
+            graph.connect( parentNode, childNode );
+        }
+
+        return new DepGraphNode[] { parentNode, childNode };
+    }
+
+    /**
+     * Add a dependency edge between the nodes representing the two given {@link DependencyNode} instances.
+     * 
+     * @param parent The parent, which has the dependency on the child.
+     * @param child The child, which is depended upon by the parent.
+     * @return An array of graph nodes, with the parent node in index 0, and the child node in index 1. <br/>
+     *         <b>NOTE:</b> If the parent parameter is null, the node at index 0 will be null as well.
+     */
+    public DepGraphNode[] addDependency( final DependencyNode parent, final DependencyNode child )
+    {
+        DepGraphNode parentNode = null;
+        if ( parent != null )
+        {
+            parentNode = findOrAdd( new DepGraphNode( parent ) );
+        }
+
+        return addDependency( parentNode, child );
+    }
+
+    public DepGraphNode[] addDependency( final Artifact parent, final Artifact child,
+                                         final boolean parentPreResolved,
+                                         final boolean childPreResolved )
+    {
+        final DepGraphNode from = findOrAdd( new DepGraphNode( parent, parentPreResolved ) );
+
+        final DepGraphNode newTo = new DepGraphNode( child, childPreResolved );
+        final DepGraphNode to = findOrAdd( newTo );
+
+        // if we're reusing an existing node, merge the new info from the child.
+        if ( to != newTo )
+        {
+            to.merge( child );
+        }
+
+        graph.connect( from, to );
+
+        return new DepGraphNode[] { from, to };
+    }
+
+    public Set<DepGraphRootNode> getRoots()
+    {
+        return new LinkedHashSet<DepGraphRootNode>( roots );
+    }
+
+    @Override
+    public Iterator<DepGraphNode> iterator()
+    {
+        return new LinkedHashSet<DepGraphNode>( graph.vertices() ).iterator();
+    }
+
+    public int size()
+    {
+        return graph.vertices().size();
+    }
+
+    public DirectedGraph<DepGraphNode, DirectionalEdge<DepGraphNode>> getGraph()
+    {
+        return graph;
+    }
+
+    public boolean contains( final DependencyNode dep )
+    {
+        return find( new DepGraphNode( dep ) ) != null;
+    }
+
+    private static final class DepGraph
+        extends SimpleDirectedGraph<DepGraphNode>
+    {
+
+        public void addVertex( final DepGraphNode node )
+        {
+            getNakedGraph().addVertex( node );
+        }
+
+        public Collection<? extends DepGraphNode> vertices()
+        {
+            return getNakedGraph().getVertices();
+        }
+
+    }
+
+    public static DependencyGraph constructFromRoot( final DependencyNode rootNode,
+                                                     final MavenProject rootProject )
+    {
+        final DependencyGraph graph = new DependencyGraph();
+        graph.addRoot( rootNode, rootProject );
+        constructChildren( graph, rootNode );
+
+        return graph;
+    }
+
+    private static void constructChildren( final DependencyGraph graph, final DependencyNode node )
+    {
+        for ( final DependencyNode child : node.getChildren() )
+        {
+            graph.addDependency( node, child );
+            constructChildren( graph, child );
+        }
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/ArtifactOnlyDependencyNode.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/ArtifactOnlyDependencyNode.java
new file mode 100644
index 0000000..2fe1fe4
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/ArtifactOnlyDependencyNode.java
@@ -0,0 +1,170 @@
+/*
+ * 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.mae.depgraph.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.graph.Dependency;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.graph.DependencyVisitor;
+import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.version.Version;
+import org.sonatype.aether.version.VersionConstraint;
+
+public final class ArtifactOnlyDependencyNode
+    implements DependencyNode
+{
+
+    private final Dependency dep;
+
+    private String preVersion;
+
+    private String preScope;
+
+    private final Map<Object, Object> data = new LinkedHashMap<Object, Object>();
+
+    private String requestContext = "project";
+
+    public ArtifactOnlyDependencyNode( final Artifact artifact )
+    {
+        dep = new Dependency( artifact, null );
+    }
+
+    @Override
+    public List<DependencyNode> getChildren()
+    {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Dependency getDependency()
+    {
+        return dep;
+    }
+
+    @Override
+    public synchronized void setArtifact( final Artifact artifact )
+    {
+        if ( artifact == null )
+        {
+            return;
+        }
+        else if ( preVersion == null )
+        {
+            preVersion = dep.getArtifact().getVersion();
+        }
+
+        dep.setArtifact( artifact );
+    }
+
+    @Override
+    public List<Artifact> getRelocations()
+    {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Collection<Artifact> getAliases()
+    {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public VersionConstraint getVersionConstraint()
+    {
+        return null;
+    }
+
+    @Override
+    public Version getVersion()
+    {
+        return null;
+    }
+
+    @Override
+    public synchronized void setScope( final String scope )
+    {
+        if ( scope == null )
+        {
+            return;
+        }
+
+        if ( preScope == null )
+        {
+            preScope = dep.getScope();
+        }
+
+        dep.setScope( scope );
+    }
+
+    @Override
+    public synchronized String getPremanagedVersion()
+    {
+        return preVersion == null ? dep.getArtifact().getVersion() : preVersion;
+    }
+
+    @Override
+    public synchronized String getPremanagedScope()
+    {
+        return preScope == null ? dep.getScope() : preScope;
+    }
+
+    @Override
+    public List<RemoteRepository> getRepositories()
+    {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public String getRequestContext()
+    {
+        return requestContext;
+    }
+
+    @Override
+    public void setRequestContext( final String requestContext )
+    {
+        this.requestContext = requestContext;
+    }
+
+    @Override
+    public Map<Object, Object> getData()
+    {
+        return data;
+    }
+
+    @Override
+    public void setData( final Object key, final Object value )
+    {
+        data.put( key, value );
+    }
+
+    @Override
+    public boolean accept( final DependencyVisitor visitor )
+    {
+        return false;
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/DefaultDepGraphLoader.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/DefaultDepGraphLoader.java
new file mode 100644
index 0000000..9462882
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/DefaultDepGraphLoader.java
@@ -0,0 +1,113 @@
+/*
+ * 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.mae.depgraph.impl;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.apache.maven.mae.MAEException;
+import org.apache.maven.mae.depgraph.DepGraphLoader;
+import org.apache.maven.mae.depgraph.DependencyGraph;
+import org.apache.maven.mae.project.ProjectLoader;
+import org.apache.maven.mae.project.session.ProjectToolsSession;
+import org.apache.maven.mae.project.session.SessionInitializer;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.sonatype.aether.RepositorySystemSession;
+
+@Component( role = DepGraphLoader.class )
+public class DefaultDepGraphLoader
+    implements DepGraphLoader
+{
+
+    @SuppressWarnings( "unused" )
+    private static final Logger LOGGER = Logger.getLogger( DefaultDepGraphLoader.class );
+
+    @Requirement
+    private DependencyGraphResolver dependencyGraphResolver;
+
+    @Requirement
+    private SessionInitializer sessionInitializer;
+
+    @Requirement
+    private ProjectLoader projectLoader;
+
+    @Override
+    public DependencyGraph loadProjectDependencyGraph( final File rootPom,
+                                                       final ProjectToolsSession session,
+                                                       final boolean includeModuleProjects )
+        throws MAEException
+    {
+        sessionInitializer.initializeSessionComponents( session );
+
+        List<MavenProject> projects;
+        if ( includeModuleProjects )
+        {
+            projects =
+                projectLoader.buildReactorProjectInstances( session, includeModuleProjects, rootPom );
+        }
+        else
+        {
+            projects =
+                Collections.singletonList( projectLoader.buildProjectInstance( rootPom, session ) );
+        }
+
+        final DependencyGraph depGraph =
+            dependencyGraphResolver.accumulateGraph( projects,
+                                                     session.getRepositorySystemSession(), session );
+        session.setState( depGraph );
+
+        return depGraph;
+    }
+
+    @Override
+    public DependencyGraph resolveProjectDependencies( final File rootPom,
+                                                       final ProjectToolsSession session,
+                                                       final boolean includeModuleProjects )
+        throws MAEException
+    {
+        sessionInitializer.initializeSessionComponents( session );
+
+        List<MavenProject> projects;
+        if ( includeModuleProjects )
+        {
+            projects =
+                projectLoader.buildReactorProjectInstances( session, includeModuleProjects, rootPom );
+        }
+        else
+        {
+            projects =
+                Collections.singletonList( projectLoader.buildProjectInstance( rootPom, session ) );
+        }
+
+        final RepositorySystemSession rss = session.getRepositorySystemSession();
+        final DependencyGraph depGraph =
+            dependencyGraphResolver.accumulateGraph( projects, rss, session );
+        dependencyGraphResolver.resolveGraph( depGraph, projects, rss, session );
+
+        session.setState( depGraph );
+
+        return depGraph;
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/DependencyGraphResolver.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/DependencyGraphResolver.java
new file mode 100644
index 0000000..a16327c
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/DependencyGraphResolver.java
@@ -0,0 +1,763 @@
+/*
+ * 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.mae.depgraph.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.log4j.Logger;
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.mae.depgraph.DepGraphNode;
+import org.apache.maven.mae.depgraph.DepGraphRootNode;
+import org.apache.maven.mae.depgraph.DependencyGraph;
+import org.apache.maven.mae.project.session.ProjectToolsSession;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.DependencyManagement;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.sonatype.aether.RepositorySystem;
+import org.sonatype.aether.RepositorySystemSession;
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.artifact.ArtifactTypeRegistry;
+import org.sonatype.aether.collection.CollectRequest;
+import org.sonatype.aether.collection.CollectResult;
+import org.sonatype.aether.collection.DependencyCollectionException;
+import org.sonatype.aether.graph.DependencyFilter;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.graph.DependencyVisitor;
+import org.sonatype.aether.graph.Exclusion;
+import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.resolution.ArtifactRequest;
+import org.sonatype.aether.resolution.ArtifactResolutionException;
+import org.sonatype.aether.resolution.ArtifactResult;
+import org.sonatype.aether.util.DefaultRepositorySystemSession;
+import org.sonatype.aether.util.artifact.JavaScopes;
+
+@Component( role = DependencyGraphResolver.class )
+public class DependencyGraphResolver
+{
+
+    private static final Logger LOGGER = Logger.getLogger( DependencyGraphResolver.class );
+
+    @Requirement
+    private RepositorySystem repositorySystem;
+
+    public DependencyGraph accumulateGraph( final Collection<MavenProject> rootProjects,
+                                            RepositorySystemSession rss,
+                                            final ProjectToolsSession session )
+    {
+        // if ( LOGGER.isDebugEnabled() )
+        {
+            if ( LOGGER.isDebugEnabled() )
+            {
+                LOGGER.debug( "Preparing for dependency-graph accumulation..." );
+            }
+        }
+        rss = prepareForGraphResolution( rss, session );
+
+        // if ( LOGGER.isDebugEnabled() )
+        {
+            if ( LOGGER.isDebugEnabled() )
+            {
+                LOGGER.debug( "Accumulating dependency graph..." );
+            }
+        }
+
+        return accumulate( session, rss, rootProjects, session.getRemoteRepositoriesArray() );
+    }
+
+    public DependencyGraph resolveGraph( final DependencyGraph depGraph,
+                                         final Collection<MavenProject> rootProjects,
+                                         final RepositorySystemSession rss,
+                                         final ProjectToolsSession session )
+    {
+
+        // if ( LOGGER.isDebugEnabled() )
+        {
+            if ( LOGGER.isDebugEnabled() )
+            {
+                LOGGER.debug( "Resolving dependencies in graph..." );
+            }
+        }
+        resolve( rss, rootProjects, depGraph, session );
+
+        // if ( LOGGER.isDebugEnabled() )
+        {
+            if ( LOGGER.isDebugEnabled() )
+            {
+                LOGGER.debug( "Graph state contains: " + depGraph.size() + " nodes." );
+            }
+        }
+
+        return depGraph;
+    }
+
+    // TODO: Allow fine-tuning of scopes resolved...
+    private RepositorySystemSession prepareForGraphResolution( final RepositorySystemSession s,
+                                                               final ProjectToolsSession session )
+    {
+        final DefaultRepositorySystemSession result = new DefaultRepositorySystemSession( s );
+        result.setDependencySelector( session.getDependencySelector() );
+
+        return result;
+    }
+
+    private void resolve( final RepositorySystemSession session,
+                          final Collection<MavenProject> rootProjects,
+                          final DependencyGraph depGraph, final ProjectToolsSession toolsSession )
+    {
+        final Set<DependencyResolveWorker> workers = new HashSet<DependencyResolveWorker>();
+        for ( final DepGraphNode node : depGraph )
+        {
+            if ( node == null || node.hasErrors() || node.isPreResolved() )
+            {
+                continue;
+            }
+
+            // if ( LOGGER.isDebugEnabled() )
+            {
+                if ( LOGGER.isDebugEnabled() )
+                {
+                    LOGGER.debug( "Resolving: " + node.getLatestArtifact() );
+                }
+            }
+            workers.add( new DependencyResolveWorker( node, session, repositorySystem ) );
+        }
+
+        runResolve( workers, toolsSession );
+        // for ( final DependencyResolveWorker worker : workers )
+        // {
+        // worker.run();
+        // }
+
+        // if ( LOGGER.isDebugEnabled() )
+        {
+            if ( LOGGER.isDebugEnabled() )
+            {
+                LOGGER.debug( "Dependency-graph resolution complete." );
+            }
+        }
+    }
+
+    private void runResolve( final Set<DependencyResolveWorker> workers,
+                             final ProjectToolsSession session )
+    {
+        final ExecutorService executorService =
+            Executors.newFixedThreadPool( session.getResolveThreads() );
+
+        final CountDownLatch latch = new CountDownLatch( workers.size() );
+        for ( final DependencyResolveWorker worker : workers )
+        {
+            worker.setLatch( latch );
+            executorService.execute( worker );
+        }
+
+        synchronized ( latch )
+        {
+            long count = 0;
+            while ( ( count = latch.getCount() ) > 0 )
+            {
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( count + " resolution workers remaining. Waiting 3s..." );
+                    }
+                }
+                try
+                {
+                    latch.await( 3, TimeUnit.SECONDS );
+                }
+                catch ( final InterruptedException e )
+                {
+                    break;
+                }
+            }
+        }
+
+        boolean terminated = false;
+        int count = 1;
+        while ( !terminated )
+        {
+            try
+            {
+                executorService.shutdown();
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Attempt " + count
+                            + " to shutdown graph-resolver. Waiting 3s..." );
+                    }
+                }
+
+                count++;
+                terminated = executorService.awaitTermination( 3, TimeUnit.SECONDS );
+            }
+            catch ( final InterruptedException e )
+            {
+                break;
+            }
+        }
+    }
+
+    private DependencyGraph accumulate( final ProjectToolsSession session,
+                                        final RepositorySystemSession rss,
+                                        final Collection<MavenProject> projects,
+                                        final RemoteRepository... remoteRepositories )
+    {
+        final ArtifactTypeRegistry stereotypes = rss.getArtifactTypeRegistry();
+
+        DependencyGraph depGraph;
+        synchronized ( session )
+        {
+            depGraph = session.getState( DependencyGraph.class );
+            if ( depGraph == null )
+            {
+                depGraph = new DependencyGraph();
+            }
+        }
+
+        final GraphAccumulator accumulator =
+            new GraphAccumulator( depGraph, session.getDependencyFilter() );
+
+        for ( final MavenProject project : projects )
+        {
+            // if ( LOGGER.isDebugEnabled() )
+            {
+                if ( LOGGER.isDebugEnabled() )
+                {
+                    LOGGER.debug( "Collecting dependencies for: " + project );
+                }
+            }
+            final CollectRequest request = new CollectRequest();
+            request.setRequestContext( "project" );
+            request.setRepositories( Arrays.asList( remoteRepositories ) );
+
+            if ( project.getDependencyArtifacts() == null )
+            {
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Adding dependencies to collection request..." );
+                    }
+                }
+                for ( final Dependency dependency : project.getDependencies() )
+                {
+                    request.addDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
+                }
+            }
+            else
+            {
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Mapping project dependencies by management key..." );
+                    }
+                }
+                final Map<String, Dependency> dependencies = new HashMap<String, Dependency>();
+                for ( final Dependency dependency : project.getDependencies() )
+                {
+                    final String key = dependency.getManagementKey();
+                    dependencies.put( key, dependency );
+                }
+
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Adding dependencies to collection request..." );
+                    }
+                }
+                for ( final org.apache.maven.artifact.Artifact artifact : project.getDependencyArtifacts() )
+                {
+                    final String key = artifact.getDependencyConflictId();
+                    final Dependency dependency = dependencies.get( key );
+                    final Collection<org.apache.maven.model.Exclusion> exclusions =
+                        dependency != null ? dependency.getExclusions() : null;
+
+                    org.sonatype.aether.graph.Dependency dep =
+                        RepositoryUtils.toDependency( artifact, exclusions );
+                    if ( !JavaScopes.SYSTEM.equals( dep.getScope() )
+                        && dep.getArtifact().getFile() != null )
+                    {
+                        // enable re-resolution
+                        org.sonatype.aether.artifact.Artifact art = dep.getArtifact();
+                        art = art.setFile( null ).setVersion( art.getBaseVersion() );
+                        dep = dep.setArtifact( art );
+                    }
+                    request.addDependency( dep );
+                }
+            }
+
+            final DependencyManagement depMngt = project.getDependencyManagement();
+            if ( depMngt != null )
+            {
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Adding managed dependencies to collection request..." );
+                    }
+                }
+                for ( final Dependency dependency : depMngt.getDependencies() )
+                {
+                    request.addManagedDependency( RepositoryUtils.toDependency( dependency,
+                                                                                stereotypes ) );
+                }
+            }
+
+            // if ( LOGGER.isDebugEnabled() )
+            {
+                if ( LOGGER.isDebugEnabled() )
+                {
+                    LOGGER.debug( "Collecting dependencies..." );
+                }
+            }
+            CollectResult result;
+            final Object old = rss.getData().get( ProjectToolsSession.SESSION_KEY );
+            try
+            {
+                rss.getData().set( ProjectToolsSession.SESSION_KEY, session );
+                result = repositorySystem.collectDependencies( rss, request );
+            }
+            catch ( final DependencyCollectionException e )
+            {
+                // TODO: Handle problem resolving POMs...
+                result = e.getResult();
+
+                // result.setDependencyGraph( e.getResult().getRoot() );
+                // result.setCollectionErrors( e.getResult().getExceptions() );
+                //
+                // throw new DependencyResolutionException( result, "Could not resolve dependencies for project "
+                // + project.getId() + ": " + e.getMessage(), e );
+            }
+            finally
+            {
+                rss.getData().set( ProjectToolsSession.SESSION_KEY, old );
+            }
+
+            final DependencyNode root = result.getRoot();
+            final DepGraphRootNode rootNode = depGraph.addRoot( root, project );
+
+            accumulator.resetForNextRun( root, rootNode );
+
+            // if ( LOGGER.isDebugEnabled() )
+            {
+                if ( LOGGER.isDebugEnabled() )
+                {
+                    LOGGER.debug( "Adding collected dependencies to consolidated dependency graph..." );
+                }
+            }
+            result.getRoot().accept( accumulator );
+
+        }
+
+        return depGraph;
+    }
+
+    private static final class GraphAccumulator
+        implements DependencyVisitor
+    {
+        private final LinkedList<DependencyNode> parents = new LinkedList<DependencyNode>();
+
+        private final Set<Exclusion> exclusions = new HashSet<Exclusion>();
+
+        private final Set<Exclusion> lastExclusions = new HashSet<Exclusion>();
+
+        private final DependencyGraph depGraph;
+
+        private DependencyNode root;
+
+        private DepGraphRootNode rootNode;
+
+        private final DependencyFilter filter;
+
+        GraphAccumulator( final DependencyGraph depGraph, final DependencyFilter filter )
+        {
+            this.depGraph = depGraph;
+            this.filter = filter;
+        }
+
+        void resetForNextRun( final DependencyNode root, final DepGraphRootNode rootNode )
+        {
+            parents.clear();
+            this.root = root;
+            this.rootNode = rootNode;
+            exclusions.clear();
+            lastExclusions.clear();
+        }
+
+        @Override
+        public boolean visitEnter( final DependencyNode node )
+        {
+            if ( filter != null && !filter.accept( node, parents ) )
+            {
+                return false;
+            }
+
+            if ( node == root )
+            {
+                parents.addFirst( root );
+                return true;
+            }
+            else if ( node == null || node.getDependency() == null
+                || node.getDependency().getArtifact() == null )
+            {
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Invalid node: " + node );
+                    }
+                }
+                return true;
+            }
+
+            // if ( LOGGER.isDebugEnabled() )
+            {
+                if ( LOGGER.isDebugEnabled() )
+                {
+                    LOGGER.debug( "START: dependency-processing for: " + node );
+                }
+            }
+
+            boolean result = false;
+            final Artifact artifact = node.getDependency().getArtifact();
+            if ( !excluded( artifact ) )
+            {
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Enabling resolution for: " + node );
+                    }
+                }
+
+                final DependencyNode parent = parents.getFirst();
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Adding dependency from: " + parent + " to: " + node );
+                    }
+                }
+
+                // TODO: don't traverse beyond this node if it's already been considered...though we still need to
+                // connect it
+                // to the parent node (see below).
+                result = !depGraph.contains( node );
+
+                // result = true;
+
+                if ( parent == root )
+                {
+                    depGraph.addDependency( rootNode, node );
+                }
+                else
+                {
+                    depGraph.addDependency( parent, node );
+                }
+
+                if ( node.getDependency().getExclusions() != null )
+                {
+                    for ( final Exclusion exclusion : node.getDependency().getExclusions() )
+                    {
+                        if ( exclusions.add( exclusion ) )
+                        {
+                            lastExclusions.add( exclusion );
+                        }
+                    }
+                }
+
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Pushing node: " + node + " onto parents stack." );
+                    }
+                }
+                parents.addFirst( node );
+
+                final StringBuilder builder = new StringBuilder();
+                for ( int i = 0; i < parents.size(); i++ )
+                {
+                    builder.append( "  " );
+                }
+                builder.append( ">>>" );
+                builder.append( node );
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( builder.toString() );
+                    }
+                }
+            }
+            else
+            {
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "DISABLING resolution for: " + node );
+                    }
+                }
+            }
+
+            if ( node != null && !node.getRelocations().isEmpty() )
+            {
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "The artifact " + node.getRelocations().get( 0 )
+                            + " has been relocated to " + node.getDependency().getArtifact() );
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        private boolean excluded( final Artifact artifact )
+        {
+            for ( final Exclusion exclusion : exclusions )
+            {
+                if ( match( exclusion.getGroupId(), artifact.getGroupId() )
+                    && match( exclusion.getArtifactId(), artifact.getArtifactId() )
+                    && match( exclusion.getExtension(), artifact.getExtension() )
+                    && match( exclusion.getClassifier(), artifact.getClassifier() ) )
+                {
+                    // if ( LOGGER.isDebugEnabled() )
+                    {
+                        if ( LOGGER.isDebugEnabled() )
+                        {
+                            LOGGER.debug( "EXCLUDED: " + artifact );
+                        }
+                    }
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private boolean match( final String excluded, final String check )
+        {
+            return "*".equals( excluded ) || excluded.equals( check );
+        }
+
+        @Override
+        public boolean visitLeave( final DependencyNode node )
+        {
+            if ( node == null || parents.isEmpty() )
+            {
+                return true;
+            }
+
+            if ( node == parents.getFirst() )
+            {
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( "Removing exclusions from last node: " + node );
+                    }
+                }
+
+                for ( final Exclusion exclusion : lastExclusions )
+                {
+                    exclusions.remove( exclusion );
+                }
+
+                lastExclusions.clear();
+
+                final StringBuilder builder = new StringBuilder();
+                for ( int i = 0; i < parents.size(); i++ )
+                {
+                    builder.append( "  " );
+                }
+                builder.append( "<<<" );
+                builder.append( node );
+                // if ( LOGGER.isDebugEnabled() )
+                {
+                    if ( LOGGER.isDebugEnabled() )
+                    {
+                        LOGGER.debug( builder.toString() );
+                    }
+                }
+
+                parents.removeFirst();
+            }
+            else
+            {
+                final int idx = parents.indexOf( node );
+                if ( idx > -1 )
+                {
+                    // if ( LOGGER.isDebugEnabled() )
+                    {
+                        if ( LOGGER.isDebugEnabled() )
+                        {
+                            LOGGER.debug( "TRAVERSAL LEAK. Removing " + ( idx + 1 )
+                                + " unaccounted-for parents that have finished traversal." );
+                        }
+                    }
+
+                    for ( int i = 0; i <= idx; i++ )
+                    {
+                        parents.removeFirst();
+                    }
+                }
+            }
+
+            // if ( LOGGER.isDebugEnabled() )
+            {
+                if ( LOGGER.isDebugEnabled() )
+                {
+                    LOGGER.debug( "END: dependency-processing for: " + node );
+                }
+            }
+
+            return true;
+        }
+
+    }
+
+    private static final class DependencyResolveWorker
+        implements Runnable
+    {
+
+        private final DepGraphNode depState;
+
+        private final RepositorySystemSession session;
+
+        private final RepositorySystem repositorySystem;
+
+        private ArtifactResult result;
+
+        private CountDownLatch latch;
+
+        DependencyResolveWorker( final DepGraphNode depState,
+                                 final RepositorySystemSession session,
+                                 final RepositorySystem repositorySystem )
+        {
+            this.depState = depState;
+            this.session = session;
+            this.repositorySystem = repositorySystem;
+        }
+
+        void setLatch( final CountDownLatch latch )
+        {
+            this.latch = latch;
+        }
+
+        @Override
+        public void run()
+        {
+            final Artifact artifact = depState.getLatestArtifact();
+
+            try
+            {
+                final ArtifactRequest request =
+                    new ArtifactRequest(
+                                         artifact,
+                                         new ArrayList<RemoteRepository>(
+                                                                          depState.getRemoteRepositories() ),
+                                         "project" );
+
+                result = new ArtifactResult( request );
+                if ( validateForResolution() )
+                {
+                    try
+                    {
+                        // if ( LOGGER.isDebugEnabled() )
+                        {
+                            if ( LOGGER.isDebugEnabled() )
+                            {
+                                LOGGER.debug( "RESOLVE: " + artifact );
+                            }
+                        }
+
+                        result = repositorySystem.resolveArtifact( session, request );
+                    }
+                    catch ( final ArtifactResolutionException e )
+                    {
+                        result.addException( e );
+                    }
+                }
+            }
+            finally
+            {
+                // final Runtime r = Runtime.getRuntime();
+                //
+                // final long MB = 1024 * 1024;
+                //
+                // System.out.println( "Memory status: " + ( r.totalMemory() - r.freeMemory() ) / MB + "M/"
+                // + r.totalMemory() / MB + "M" );
+
+                // FIXME: Do we need to detect whether resolution already happened for this artifact before we try to
+                // resolve it
+                // again??
+                depState.merge( result );
+                if ( latch != null )
+                {
+                    latch.countDown();
+                }
+            }
+        }
+
+        private boolean validateForResolution()
+        {
+            boolean valid = true;
+            if ( session == null )
+            {
+                result.addException( new IllegalArgumentException( "Cannot resolve dependency: "
+                    + depState.getLatestArtifact() + ", RepositorySystemSession has not been set!" ) );
+
+                valid = false;
+            }
+
+            if ( repositorySystem == null )
+            {
+                result.addException( new IllegalArgumentException( "Cannot resolve dependency: "
+                    + depState.getLatestArtifact() + ", RepositorySystem has not been set!" ) );
+
+                valid = false;
+            }
+
+            return valid;
+        }
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/DisconnectedDepNode.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/DisconnectedDepNode.java
new file mode 100644
index 0000000..59f81f1
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/DisconnectedDepNode.java
@@ -0,0 +1,142 @@
+/*
+ * 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.mae.depgraph.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.graph.Dependency;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.graph.DependencyVisitor;
+import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.version.Version;
+import org.sonatype.aether.version.VersionConstraint;
+
+final class DisconnectedDepNode
+    implements DependencyNode
+{
+
+    private final DependencyNode delegate;
+
+    DisconnectedDepNode( final DependencyNode delegate )
+    {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public List<DependencyNode> getChildren()
+    {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean accept( final DependencyVisitor visitor )
+    {
+        return false;
+    }
+
+    @Override
+    public Dependency getDependency()
+    {
+        return delegate.getDependency();
+    }
+
+    @Override
+    public void setArtifact( final Artifact artifact )
+    {
+        delegate.setArtifact( artifact );
+    }
+
+    @Override
+    public List<Artifact> getRelocations()
+    {
+        return delegate.getRelocations();
+    }
+
+    @Override
+    public Collection<Artifact> getAliases()
+    {
+        return delegate.getAliases();
+    }
+
+    @Override
+    public VersionConstraint getVersionConstraint()
+    {
+        return delegate.getVersionConstraint();
+    }
+
+    @Override
+    public Version getVersion()
+    {
+        return delegate.getVersion();
+    }
+
+    @Override
+    public void setScope( final String scope )
+    {
+        delegate.setScope( scope );
+    }
+
+    @Override
+    public String getPremanagedVersion()
+    {
+        return delegate.getPremanagedVersion();
+    }
+
+    @Override
+    public String getPremanagedScope()
+    {
+        return delegate.getPremanagedScope();
+    }
+
+    @Override
+    public List<RemoteRepository> getRepositories()
+    {
+        return delegate.getRepositories();
+    }
+
+    @Override
+    public String getRequestContext()
+    {
+        return delegate.getRequestContext();
+    }
+
+    @Override
+    public void setRequestContext( final String context )
+    {
+        delegate.setRequestContext( context );
+    }
+
+    @Override
+    public Map<Object, Object> getData()
+    {
+        return delegate.getData();
+    }
+
+    @Override
+    public void setData( final Object key, final Object value )
+    {
+        delegate.setData( key, value );
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/FlexibleScopeDependencySelector.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/FlexibleScopeDependencySelector.java
new file mode 100644
index 0000000..4581191
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/FlexibleScopeDependencySelector.java
@@ -0,0 +1,166 @@
+package org.apache.maven.mae.depgraph.impl;
+
+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.sonatype.aether.collection.DependencyCollectionContext;
+import org.sonatype.aether.collection.DependencySelector;
+import org.sonatype.aether.graph.Dependency;
+
+/**
+ * A dependency selector that filters transitive dependencies based on their scope. Direct dependencies are always
+ * included regardless of their scope. <em>Note:</em> This filter does not assume any relationships between the scopes.
+ * In particular, the filter is not aware of scopes that logically include other scopes.
+ * 
+ * @author Benjamin Bentmann
+ * @see Dependency#getScope()
+ */
+public class FlexibleScopeDependencySelector
+    implements DependencySelector
+{
+
+    private boolean transitive;
+
+    private final Set<String> included;
+
+    private final Set<String> excluded;
+
+    public FlexibleScopeDependencySelector()
+    {
+        included = new HashSet<String>();
+        excluded = new HashSet<String>();
+    }
+
+    /**
+     * Creates a new selector using the specified includes and excludes.
+     * 
+     * @param included The set of scopes to include, may be {@code null} or empty to include any scope.
+     * @param excluded The set of scopes to exclude, may be {@code null} or empty to exclude no scope.
+     */
+    public FlexibleScopeDependencySelector( final Collection<String> included,
+                                            final Collection<String> excluded )
+    {
+        transitive = false;
+        if ( included != null )
+        {
+            this.included = new HashSet<String>();
+            this.included.addAll( included );
+        }
+        else
+        {
+            this.included = Collections.emptySet();
+        }
+        if ( excluded != null )
+        {
+            this.excluded = new HashSet<String>();
+            this.excluded.addAll( excluded );
+        }
+        else
+        {
+            this.excluded = Collections.emptySet();
+        }
+    }
+
+    /**
+     * Creates a new selector using the specified excludes.
+     * 
+     * @param excluded The set of scopes to exclude, may be {@code null} or empty to exclude no scope.
+     */
+    public FlexibleScopeDependencySelector( final String... excluded )
+    {
+        this( null, Arrays.asList( excluded ) );
+    }
+
+    private FlexibleScopeDependencySelector( final boolean transitive,
+                                             final Collection<String> included,
+                                             final Collection<String> excluded )
+    {
+        this.transitive = transitive;
+        this.included = included == null ? null : new HashSet<String>( included );
+        this.excluded = excluded == null ? null : new HashSet<String>( excluded );
+    }
+
+    @Override
+    public boolean selectDependency( final Dependency dependency )
+    {
+        if ( !transitive )
+        {
+            return true;
+        }
+
+        String scope = dependency.getScope();
+        return ( included.isEmpty() || included.contains( scope ) )
+            && ( excluded.isEmpty() || !excluded.contains( scope ) );
+    }
+
+    @Override
+    public DependencySelector deriveChildSelector( final DependencyCollectionContext context )
+    {
+        if ( this.transitive || context.getDependency() == null )
+        {
+            return this;
+        }
+
+        return new FlexibleScopeDependencySelector( true, included, excluded );
+    }
+
+    @Override
+    public boolean equals( final Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+        else if ( null == obj || !getClass().equals( obj.getClass() ) )
+        {
+            return false;
+        }
+
+        FlexibleScopeDependencySelector that = (FlexibleScopeDependencySelector) obj;
+        return transitive == that.transitive && included.equals( that.included )
+            && excluded.equals( that.excluded );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int hash = 17;
+        hash = hash * 31 + ( transitive ? 1 : 0 );
+        hash = hash * 31 + included.hashCode();
+        hash = hash * 31 + excluded.hashCode();
+        return hash;
+    }
+
+    public FlexibleScopeDependencySelector includeScope( final String scope )
+    {
+        included.add( scope );
+        return this;
+    }
+
+    public FlexibleScopeDependencySelector excludeScope( final String scope )
+    {
+        excluded.add( scope );
+        return this;
+    }
+
+    public FlexibleScopeDependencySelector setTransitive( final boolean transitive )
+    {
+        this.transitive = transitive;
+        return this;
+    }
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/BareBonesDependencyCollector.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/BareBonesDependencyCollector.java
new file mode 100644
index 0000000..4cace35
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/BareBonesDependencyCollector.java
@@ -0,0 +1,713 @@
+package org.apache.maven.mae.depgraph.impl.collect;
+
+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+
+import static org.sonatype.aether.util.artifact.ArtifacIdUtils.toId;
+
+import org.apache.log4j.Logger;
+import org.apache.maven.mae.project.session.ProjectToolsSession;
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.sonatype.aether.RepositoryException;
+import org.sonatype.aether.RepositorySystemSession;
+import org.sonatype.aether.RequestTrace;
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.artifact.ArtifactType;
+import org.sonatype.aether.artifact.ArtifactTypeRegistry;
+import org.sonatype.aether.collection.CollectRequest;
+import org.sonatype.aether.collection.CollectResult;
+import org.sonatype.aether.collection.DependencyCollectionContext;
+import org.sonatype.aether.collection.DependencyCollectionException;
+import org.sonatype.aether.collection.DependencyGraphTransformationContext;
+import org.sonatype.aether.collection.DependencyGraphTransformer;
+import org.sonatype.aether.collection.DependencyManagement;
+import org.sonatype.aether.collection.DependencyManager;
+import org.sonatype.aether.collection.DependencySelector;
+import org.sonatype.aether.collection.DependencyTraverser;
+import org.sonatype.aether.graph.Dependency;
+import org.sonatype.aether.graph.DependencyFilter;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.impl.ArtifactDescriptorReader;
+import org.sonatype.aether.impl.DependencyCollector;
+import org.sonatype.aether.impl.RemoteRepositoryManager;
+import org.sonatype.aether.impl.VersionRangeResolver;
+import org.sonatype.aether.impl.internal.DefaultDependencyCollector;
+import org.sonatype.aether.repository.ArtifactRepository;
+import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.resolution.ArtifactDescriptorException;
+import org.sonatype.aether.resolution.ArtifactDescriptorRequest;
+import org.sonatype.aether.resolution.ArtifactDescriptorResult;
+import org.sonatype.aether.resolution.VersionRangeRequest;
+import org.sonatype.aether.resolution.VersionRangeResolutionException;
+import org.sonatype.aether.resolution.VersionRangeResult;
+import org.sonatype.aether.spi.locator.Service;
+import org.sonatype.aether.spi.locator.ServiceLocator;
+import org.sonatype.aether.util.DefaultRepositorySystemSession;
+import org.sonatype.aether.util.DefaultRequestTrace;
+import org.sonatype.aether.util.artifact.ArtifactProperties;
+import org.sonatype.aether.version.Version;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * {@link DependencyCollector} implementation based on {@link DefaultDependencyCollector}, but which uses a more
+ * aggressively graph-oriented approach. This centralizes and de-dupes references to {@link Artifact} and
+ * {@link RemoteRepository} instances, in order to reduce the memory consumption.
+ * 
+ * @author Benjamin Bentmann
+ * @author John Casey
+ */
+@Component( role = DependencyCollector.class, hint = BareBonesDependencyCollector.HINT )
+public class BareBonesDependencyCollector
+    implements DependencyCollector, Service
+{
+
+    public static final String HINT = "bare-bones";
+
+    private static final Logger logger = Logger.getLogger( BareBonesDependencyCollector.class );
+
+    @Requirement
+    private RemoteRepositoryManager remoteRepositoryManager;
+
+    @Requirement
+    private ArtifactDescriptorReader descriptorReader;
+
+    @Requirement
+    private VersionRangeResolver versionRangeResolver;
+
+    public BareBonesDependencyCollector()
+    {
+        // enables default constructor
+    }
+
+    public BareBonesDependencyCollector( final Logger logger, final RemoteRepositoryManager remoteRepositoryManager,
+                                         final ArtifactDescriptorReader artifactDescriptorReader,
+                                         final VersionRangeResolver versionRangeResolver )
+    {
+        setRemoteRepositoryManager( remoteRepositoryManager );
+        setArtifactDescriptorReader( artifactDescriptorReader );
+        setVersionRangeResolver( versionRangeResolver );
+    }
+
+    @Override
+    public void initService( final ServiceLocator locator )
+    {
+        setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
+        setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) );
+        setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
+    }
+
+    public BareBonesDependencyCollector setRemoteRepositoryManager( final RemoteRepositoryManager remoteRepositoryManager )
+    {
+        if ( remoteRepositoryManager == null )
+        {
+            throw new IllegalArgumentException( "remote repository manager has not been specified" );
+        }
+        this.remoteRepositoryManager = remoteRepositoryManager;
+        return this;
+    }
+
+    public BareBonesDependencyCollector setArtifactDescriptorReader( final ArtifactDescriptorReader artifactDescriptorReader )
+    {
+        if ( artifactDescriptorReader == null )
+        {
+            throw new IllegalArgumentException( "artifact descriptor reader has not been specified" );
+        }
+        descriptorReader = artifactDescriptorReader;
+        return this;
+    }
+
+    public BareBonesDependencyCollector setVersionRangeResolver( final VersionRangeResolver versionRangeResolver )
+    {
+        if ( versionRangeResolver == null )
+        {
+            throw new IllegalArgumentException( "version range resolver has not been specified" );
+        }
+        this.versionRangeResolver = versionRangeResolver;
+        return this;
+    }
+
+    @Override
+    public CollectResult collectDependencies( RepositorySystemSession session, final CollectRequest request )
+        throws DependencyCollectionException
+    {
+        session = optimizeSession( session );
+
+        final RequestTrace trace = DefaultRequestTrace.newChild( request.getTrace(), request );
+
+        final CollectResult result = new CollectResult( request );
+
+        final ProjectToolsSession toolsSession =
+            (ProjectToolsSession) session.getData().get( ProjectToolsSession.SESSION_KEY );
+        
+        DependencySelector depSelector = toolsSession == null ? null : toolsSession.getDependencySelector();
+        if ( depSelector == null )
+        {
+            depSelector = session.getDependencySelector();
+        }
+        
+        final DependencyManager depManager = session.getDependencyManager();
+        final DependencyTraverser depTraverser = session.getDependencyTraverser();
+
+        Dependency root = request.getRoot();
+        List<RemoteRepository> repositories = request.getRepositories();
+        List<Dependency> dependencies = request.getDependencies();
+        List<Dependency> managedDependencies = request.getManagedDependencies();
+
+        final SlimDepGraph graph = new SlimDepGraph( session );
+        SlimDependencyEdge edge = null;
+        if ( root != null )
+        {
+            VersionRangeResult rangeResult;
+            try
+            {
+                final VersionRangeRequest rangeRequest =
+                    new VersionRangeRequest( root.getArtifact(), request.getRepositories(), request.getRequestContext() );
+                rangeRequest.setTrace( trace );
+                rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest );
+
+                if ( rangeResult.getVersions().isEmpty() )
+                {
+                    throw new VersionRangeResolutionException( rangeResult, "No versions available for "
+                        + root.getArtifact() + " within specified range" );
+                }
+            }
+            catch ( final VersionRangeResolutionException e )
+            {
+                result.addException( e );
+                throw new DependencyCollectionException( result );
+            }
+
+            final Version version = rangeResult.getVersions().get( rangeResult.getVersions().size() - 1 );
+            root = root.setArtifact( root.getArtifact().setVersion( version.toString() ) );
+
+            ArtifactDescriptorResult descriptorResult;
+            try
+            {
+                final ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
+                descriptorRequest.setArtifact( root.getArtifact() );
+                descriptorRequest.setRepositories( request.getRepositories() );
+                descriptorRequest.setRequestContext( request.getRequestContext() );
+                descriptorRequest.setTrace( trace );
+                if ( isLackingDescriptor( root.getArtifact() ) )
+                {
+                    descriptorResult = new ArtifactDescriptorResult( descriptorRequest );
+                }
+                else
+                {
+                    descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
+                }
+            }
+            catch ( final ArtifactDescriptorException e )
+            {
+                result.addException( e );
+                throw new DependencyCollectionException( result );
+            }
+
+            root = root.setArtifact( descriptorResult.getArtifact() );
+
+            repositories =
+                remoteRepositoryManager.aggregateRepositories( session, repositories,
+                                                               descriptorResult.getRepositories(), true );
+            dependencies = mergeDeps( dependencies, descriptorResult.getDependencies() );
+            managedDependencies = mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() );
+
+            final SlimDependencyNode node = new SlimDependencyNode( toId( descriptorResult.getArtifact() ), graph );
+            node.setAliases( descriptorResult.getAliases() );
+            node.setRepositories( request.getRepositories() );
+
+            edge = new SlimDependencyEdge( node, graph );
+            edge.setDependency( root );
+            edge.setRequestContext( request.getRequestContext() );
+            edge.setRelocations( descriptorResult.getRelocations() );
+            edge.setVersionConstraint( rangeResult.getVersionConstraint() );
+            edge.setVersion( version );
+        }
+        else
+        {
+            edge = new SlimDependencyEdge( new SlimDependencyNode( SlimDependencyNode.UNKNOWN_ROOT_ID, graph ), graph );
+        }
+
+        result.setRoot( edge );
+
+        final boolean traverse = ( root == null ) || depTraverser.traverseDependency( root );
+
+        if ( traverse && !dependencies.isEmpty() )
+        {
+            final LinkedList<SlimDependencyEdge> edges = new LinkedList<SlimDependencyEdge>();
+            edges.addFirst( edge );
+
+            final DependencyCollectionContext context = new CollectionContext( session, root, managedDependencies );
+
+            final DepGraphCache pool = new DepGraphCache( session );
+            process( session, toolsSession, trace, result, edges, dependencies, repositories,
+                     depSelector.deriveChildSelector( context ), depManager.deriveChildManager( context ),
+                     depTraverser.deriveChildTraverser( context ), pool, graph );
+        }
+
+        final DependencyGraphTransformer transformer = session.getDependencyGraphTransformer();
+        try
+        {
+            final DependencyGraphTransformationContext context = new GraphTransformationContext( session );
+            result.setRoot( transformer.transformGraph( edge, context ) );
+        }
+        catch ( final RepositoryException e )
+        {
+            result.addException( e );
+        }
+
+        if ( !result.getExceptions().isEmpty() )
+        {
+            throw new DependencyCollectionException( result );
+        }
+
+        return result;
+    }
+
+    private RepositorySystemSession optimizeSession( final RepositorySystemSession session )
+    {
+        final DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session );
+        optimized.setArtifactTypeRegistry( new TypeRegistry( session ) );
+        return optimized;
+    }
+
+    private List<Dependency> mergeDeps( final List<Dependency> dominant, final List<Dependency> recessive )
+    {
+        List<Dependency> result;
+        if ( dominant == null || dominant.isEmpty() )
+        {
+            result = recessive;
+        }
+        else if ( recessive == null || recessive.isEmpty() )
+        {
+            result = dominant;
+        }
+        else
+        {
+            result = new ArrayList<Dependency>( dominant.size() + recessive.size() );
+            final Collection<String> ids = new HashSet<String>();
+            for ( final Dependency dependency : dominant )
+            {
+                ids.add( getId( dependency.getArtifact() ) );
+                result.add( dependency );
+            }
+            for ( final Dependency dependency : recessive )
+            {
+                if ( !ids.contains( getId( dependency.getArtifact() ) ) )
+                {
+                    result.add( dependency );
+                }
+            }
+        }
+        return result;
+    }
+
+    private String getId( final Artifact a )
+    {
+        return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension();
+    }
+
+    private boolean process( final RepositorySystemSession session, final ProjectToolsSession toolsSession,
+                             final RequestTrace trace, final CollectResult result,
+                             final LinkedList<SlimDependencyEdge> edges, final List<Dependency> dependencies,
+                             final List<RemoteRepository> repositories, final DependencySelector depSelector,
+                             final DependencyManager depManager, final DependencyTraverser depTraverser,
+                             final DepGraphCache pool, final SlimDepGraph graph )
+        throws DependencyCollectionException
+    {
+        boolean cycle = false;
+
+        final DependencyFilter filter = toolsSession == null ? null : toolsSession.getDependencyFilter();
+
+        nextDependency: for ( Dependency dependency : dependencies )
+        {
+            boolean disableVersionManagement = false;
+
+            List<Artifact> relocations = Collections.emptyList();
+
+            thisDependency: while ( true )
+            {
+                if ( !depSelector.selectDependency( dependency ) )
+                {
+                    logger.warn( "SELECT - Excluding from dependency graph: " + dependency.getArtifact() );
+                    continue nextDependency;
+                }
+
+                final DependencyManagement depMngt = depManager.manageDependency( dependency );
+                String premanagedVersion = null;
+                String premanagedScope = null;
+
+                if ( depMngt != null )
+                {
+                    if ( depMngt.getVersion() != null && !disableVersionManagement )
+                    {
+                        final Artifact artifact = dependency.getArtifact();
+                        premanagedVersion = artifact.getVersion();
+                        dependency = dependency.setArtifact( artifact.setVersion( depMngt.getVersion() ) );
+                    }
+                    if ( depMngt.getProperties() != null )
+                    {
+                        final Artifact artifact = dependency.getArtifact();
+                        dependency = dependency.setArtifact( artifact.setProperties( depMngt.getProperties() ) );
+                    }
+                    if ( depMngt.getScope() != null )
+                    {
+                        premanagedScope = dependency.getScope();
+                        dependency = dependency.setScope( depMngt.getScope() );
+                    }
+                    if ( depMngt.getExclusions() != null )
+                    {
+                        dependency = dependency.setExclusions( depMngt.getExclusions() );
+                    }
+                }
+                disableVersionManagement = false;
+
+                final boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() );
+
+                final boolean traverse = !noDescriptor && depTraverser.traverseDependency( dependency );
+
+                VersionRangeResult rangeResult;
+                try
+                {
+                    final VersionRangeRequest rangeRequest = new VersionRangeRequest();
+                    rangeRequest.setArtifact( dependency.getArtifact() );
+                    rangeRequest.setRepositories( repositories );
+                    rangeRequest.setRequestContext( result.getRequest().getRequestContext() );
+                    rangeRequest.setTrace( trace );
+
+                    final Object key = pool.toKey( rangeRequest );
+                    rangeResult = pool.getConstraint( key, rangeRequest );
+                    if ( rangeResult == null )
+                    {
+                        rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest );
+                        pool.putConstraint( key, rangeResult );
+                    }
+
+                    if ( rangeResult.getVersions().isEmpty() )
+                    {
+                        throw new VersionRangeResolutionException( rangeResult, "No versions available for "
+                            + dependency.getArtifact() + " within specified range" );
+                    }
+                }
+                catch ( final VersionRangeResolutionException e )
+                {
+                    result.addException( e );
+                    continue nextDependency;
+                }
+
+                final List<Version> versions = new ArrayList<Version>( rangeResult.getVersions() );
+                Collections.reverse( versions );
+
+                for ( final Version version : versions )
+                {
+                    final Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
+                    Dependency d = dependency.setArtifact( originalArtifact );
+
+                    ArtifactDescriptorResult descriptorResult;
+                    try
+                    {
+                        final ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
+                        descriptorRequest.setArtifact( d.getArtifact() );
+                        descriptorRequest.setRepositories( repositories );
+                        descriptorRequest.setRequestContext( result.getRequest().getRequestContext() );
+                        descriptorRequest.setTrace( trace );
+
+                        if ( noDescriptor )
+                        {
+                            descriptorResult = new ArtifactDescriptorResult( descriptorRequest );
+                        }
+                        else
+                        {
+                            final Object key = pool.toKey( descriptorRequest );
+                            descriptorResult = pool.getDescriptor( key, descriptorRequest );
+                            if ( descriptorResult == null )
+                            {
+                                descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
+                                pool.putDescriptor( key, descriptorResult );
+                            }
+                        }
+                    }
+                    catch ( final ArtifactDescriptorException e )
+                    {
+                        result.addException( e );
+                        continue;
+                    }
+
+                    d = d.setArtifact( descriptorResult.getArtifact() );
+
+                    if ( findDuplicate( edges, d.getArtifact() ) != null )
+                    {
+                        cycle = true;
+                        continue nextDependency;
+                    }
+
+                    if ( !descriptorResult.getRelocations().isEmpty() )
+                    {
+                        relocations = descriptorResult.getRelocations();
+
+                        disableVersionManagement =
+                            originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
+                                && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
+
+                        dependency = d;
+                        continue thisDependency;
+                    }
+
+                    d = pool.intern( d.setArtifact( graph.intern( d.getArtifact() ) ) );
+
+                    DependencySelector childSelector = null;
+                    DependencyManager childManager = null;
+                    DependencyTraverser childTraverser = null;
+                    List<RemoteRepository> childRepos = null;
+                    final String key = toId( d.getArtifact() );
+
+                    boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
+                    if ( recurse )
+                    {
+                        final DependencyCollectionContext context =
+                            new CollectionContext( session, d, descriptorResult.getManagedDependencies() );
+
+                        childSelector = depSelector.deriveChildSelector( context );
+                        childManager = depManager.deriveChildManager( context );
+                        childTraverser = depTraverser.deriveChildTraverser( context );
+
+                        childRepos =
+                            remoteRepositoryManager.aggregateRepositories( session, repositories,
+                                                                           descriptorResult.getRepositories(), true );
+                    }
+
+                    List<RemoteRepository> repos;
+                    final ArtifactRepository repo = rangeResult.getRepository( version );
+                    if ( repo instanceof RemoteRepository )
+                    {
+                        repos = Collections.singletonList( (RemoteRepository) repo );
+                    }
+                    else if ( repo == null )
+                    {
+                        repos = repositories;
+                    }
+                    else
+                    {
+                        repos = Collections.emptyList();
+                    }
+
+                    SlimDependencyNode child = graph.getNode( key );
+                    if ( child == null )
+                    {
+                        child = new SlimDependencyNode( key, graph );
+                        child.setAliases( descriptorResult.getAliases() );
+                        child.setRepositories( repos );
+                    }
+                    else
+                    {
+                        recurse = false;
+
+                        if ( repos.size() < child.getRepositories().size() )
+                        {
+                            child.setRepositories( repos );
+                        }
+                    }
+
+                    final SlimDependencyNode node = edges.getFirst().getTo();
+
+                    final SlimDependencyEdge edge = new SlimDependencyEdge( node, child, graph );
+                    edge.setDependency( d );
+                    edge.setScope( d.getScope() );
+                    edge.setPremanagedScope( premanagedScope );
+                    edge.setPremanagedVersion( premanagedVersion );
+                    edge.setRelocations( relocations );
+                    edge.setVersionConstraint( rangeResult.getVersionConstraint() );
+                    edge.setVersion( version );
+                    edge.setRequestContext( result.getRequest().getRequestContext() );
+
+                    final List<DependencyNode> parents = new ArrayList<DependencyNode>();
+                    parents.add( edges.getFirst() );
+
+                    if ( filter != null && !filter.accept( edge, parents ) )
+                    {
+                        logger.warn( "FILTER - Excluding from dependency graph: " + edge.getDependency().getArtifact() );
+                        graph.removeEdge( edge );
+                        continue nextDependency;
+                    }
+
+                    if ( recurse )
+                    {
+                        edges.addFirst( edge );
+
+                        if ( process( session, toolsSession, trace, result, edges, descriptorResult.getDependencies(), childRepos,
+                                      childSelector, childManager, childTraverser, pool, graph ) )
+                        {
+                            cycle = true;
+                            continue nextDependency;
+                        }
+
+                        edges.removeFirst();
+                    }
+                }
+
+                break;
+            }
+        }
+
+        return cycle;
+    }
+
+    private SlimDependencyEdge findDuplicate( final List<SlimDependencyEdge> edges, final Artifact artifact )
+    {
+        for ( final SlimDependencyEdge edge : edges )
+        {
+            final Dependency dependency = edge.getDependency();
+            if ( dependency == null )
+            {
+                break;
+            }
+
+            final Artifact a = dependency.getArtifact();
+            if ( !a.getArtifactId().equals( artifact.getArtifactId() ) )
+            {
+                continue;
+            }
+            if ( !a.getGroupId().equals( artifact.getGroupId() ) )
+            {
+                continue;
+            }
+            if ( !a.getBaseVersion().equals( artifact.getBaseVersion() ) )
+            {
+                continue;
+            }
+            if ( !a.getExtension().equals( artifact.getExtension() ) )
+            {
+                continue;
+            }
+            if ( !a.getClassifier().equals( artifact.getClassifier() ) )
+            {
+                continue;
+            }
+
+            return edge;
+        }
+
+        return null;
+    }
+
+    private boolean isLackingDescriptor( final Artifact artifact )
+    {
+        return artifact.getProperty( ArtifactProperties.LOCAL_PATH, null ) != null;
+    }
+
+    private static final class CollectionContext
+        implements DependencyCollectionContext
+    {
+
+        private final RepositorySystemSession session;
+
+        private final Dependency dependency;
+
+        private final List<Dependency> managedDependencies;
+
+        CollectionContext( final RepositorySystemSession session, final Dependency dependency,
+                           final List<Dependency> managedDependencies )
+        {
+            this.session = session;
+            this.dependency = dependency;
+            this.managedDependencies = managedDependencies;
+        }
+
+        @Override
+        public RepositorySystemSession getSession()
+        {
+            return session;
+        }
+
+        @Override
+        public Dependency getDependency()
+        {
+            return dependency;
+        }
+
+        @Override
+        public List<Dependency> getManagedDependencies()
+        {
+            return managedDependencies;
+        }
+
+    }
+
+    private static final class GraphTransformationContext
+        implements DependencyGraphTransformationContext
+    {
+
+        private final RepositorySystemSession session;
+
+        private final Map<Object, Object> ctx = new HashMap<Object, Object>();
+
+        GraphTransformationContext( final RepositorySystemSession session )
+        {
+            this.session = session;
+        }
+
+        @Override
+        public RepositorySystemSession getSession()
+        {
+            return session;
+        }
+
+        @Override
+        public Object get( final Object key )
+        {
+            return ctx.get( key );
+        }
+
+        @Override
+        public Object put( final Object key, final Object value )
+        {
+            return ctx.put( key, value );
+        }
+
+    }
+
+    private static final class TypeRegistry
+        implements ArtifactTypeRegistry
+    {
+        private final ArtifactTypeRegistry delegate;
+
+        private final Map<String, ArtifactType> types = new HashMap<String, ArtifactType>();
+
+        TypeRegistry( final RepositorySystemSession session )
+        {
+            delegate = session.getArtifactTypeRegistry();
+        }
+
+        @Override
+        public ArtifactType get( final String typeId )
+        {
+            ArtifactType type = types.get( typeId );
+
+            if ( type == null )
+            {
+                type = delegate.get( typeId );
+                types.put( typeId, type );
+            }
+
+            return type;
+        }
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/DepGraphCache.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/DepGraphCache.java
new file mode 100644
index 0000000..7bbf9e8
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/DepGraphCache.java
@@ -0,0 +1,511 @@
+package org.apache.maven.mae.depgraph.impl.collect;
+
+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+
+import static org.sonatype.aether.util.artifact.ArtifacIdUtils.toId;
+
+import org.sonatype.aether.RepositoryCache;
+import org.sonatype.aether.RepositorySystemSession;
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.collection.DependencyManager;
+import org.sonatype.aether.collection.DependencySelector;
+import org.sonatype.aether.collection.DependencyTraverser;
+import org.sonatype.aether.graph.Dependency;
+import org.sonatype.aether.repository.ArtifactRepository;
+import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.resolution.ArtifactDescriptorRequest;
+import org.sonatype.aether.resolution.ArtifactDescriptorResult;
+import org.sonatype.aether.resolution.VersionRangeRequest;
+import org.sonatype.aether.resolution.VersionRangeResult;
+import org.sonatype.aether.version.Version;
+import org.sonatype.aether.version.VersionConstraint;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * Based on DataPool in aether-impl, this cache provides the ability to clear the cache in the
+ * {@link RepositorySystemSession}, along with tracking for {@link RemoteRepository} instances, and NOT including
+ * tracking for nodes.
+ * 
+ * @author Benjamin Bentmann
+ * @author John Casey
+ */
+public final class DepGraphCache
+{
+
+    private static final String ARTIFACT_POOL = DepGraphCache.class.getName() + "$Artifact";
+
+    private static final String DEPENDENCY_POOL = DepGraphCache.class.getName() + "$Dependency";
+
+    private static final String REPOSITORY_POOL = DepGraphCache.class.getName() + "$Repository";
+
+    private static final String DESCRIPTORS = DepGraphCache.class.getName() + "$Descriptors";
+
+    private ObjectPool<String, Artifact> artifacts;
+
+    private ObjectPool<Dependency, Dependency> dependencies;
+
+    private ObjectPool<RemoteRepository, RemoteRepository> repositories;
+
+    private Map<Object, Descriptor> descriptors;
+
+    private final Map<Object, Constraint> constraints = new WeakHashMap<Object, Constraint>();
+
+    @SuppressWarnings( "unchecked" )
+    DepGraphCache( final RepositorySystemSession session )
+    {
+        final RepositoryCache cache = session.getCache();
+
+        if ( cache != null )
+        {
+            artifacts = (ObjectPool<String, Artifact>) cache.get( session, ARTIFACT_POOL );
+            dependencies = (ObjectPool<Dependency, Dependency>) cache.get( session, DEPENDENCY_POOL );
+            repositories = (ObjectPool<RemoteRepository, RemoteRepository>) cache.get( session, REPOSITORY_POOL );
+            descriptors = (Map<Object, Descriptor>) cache.get( session, DESCRIPTORS );
+        }
+
+        if ( artifacts == null )
+        {
+            artifacts = new ObjectPool<String, Artifact>();
+            if ( cache != null )
+            {
+                cache.put( session, ARTIFACT_POOL, artifacts );
+            }
+        }
+
+        if ( dependencies == null )
+        {
+            dependencies = new ObjectPool<Dependency, Dependency>();
+            if ( cache != null )
+            {
+                cache.put( session, DEPENDENCY_POOL, dependencies );
+            }
+        }
+
+        if ( repositories == null )
+        {
+            repositories = new ObjectPool<RemoteRepository, RemoteRepository>();
+            if ( cache != null )
+            {
+                cache.put( session, DEPENDENCY_POOL, repositories );
+            }
+        }
+
+        if ( descriptors == null )
+        {
+            descriptors = Collections.synchronizedMap( new WeakHashMap<Object, Descriptor>( 256 ) );
+            if ( cache != null )
+            {
+                cache.put( session, DESCRIPTORS, descriptors );
+            }
+        }
+    }
+
+    public static void clear( final RepositorySystemSession session )
+    {
+        synchronized ( session )
+        {
+            final RepositoryCache cache = session.getCache();
+
+            if ( cache != null )
+            {
+                cache.put( session, ARTIFACT_POOL, null );
+                cache.put( session, DEPENDENCY_POOL, null );
+                cache.put( session, REPOSITORY_POOL, null );
+                cache.put( session, DESCRIPTORS, null );
+            }
+        }
+    }
+
+    synchronized void setArtifact( final Artifact artifact )
+    {
+        final String id = toId( artifact );
+        artifacts.set( id, artifact );
+    }
+
+    Artifact getArtifact( final String key )
+    {
+        return artifacts.get( key );
+    }
+
+    Artifact intern( final Artifact artifact )
+    {
+        return artifacts.intern( toId( artifact ), artifact );
+    }
+
+    Dependency intern( final Dependency dependency )
+    {
+        return dependencies.intern( dependency, dependency );
+    }
+
+    RemoteRepository intern( final RemoteRepository repo )
+    {
+        return repositories.intern( repo, repo );
+    }
+
+    Object toKey( final ArtifactDescriptorRequest request )
+    {
+        return request.getArtifact();
+    }
+
+    ArtifactDescriptorResult getDescriptor( final Object key, final ArtifactDescriptorRequest request )
+    {
+        final Descriptor descriptor = descriptors.get( key );
+        if ( descriptor != null )
+        {
+            return descriptor.toResult( request );
+        }
+        return null;
+    }
+
+    void putDescriptor( final Object key, final ArtifactDescriptorResult result )
+    {
+        descriptors.put( key, new Descriptor( result ) );
+    }
+
+    Object toKey( final VersionRangeRequest request )
+    {
+        return new ConstraintKey( request );
+    }
+
+    VersionRangeResult getConstraint( final Object key, final VersionRangeRequest request )
+    {
+        final Constraint constraint = constraints.get( key );
+        if ( constraint != null )
+        {
+            return constraint.toResult( request );
+        }
+        return null;
+    }
+
+    void putConstraint( final Object key, final VersionRangeResult result )
+    {
+        constraints.put( key, new Constraint( result ) );
+    }
+
+    Object toKey( final Artifact artifact, final List<RemoteRepository> repositories )
+    {
+        return new NodeKey( artifact, repositories );
+    }
+
+    Object toKey( final Artifact artifact, final List<RemoteRepository> repositories,
+                  final DependencySelector selector, final DependencyManager manager,
+                  final DependencyTraverser traverser )
+    {
+        return new GraphKey( artifact, repositories, selector, manager, traverser );
+    }
+
+    static class Descriptor
+    {
+
+        final Artifact artifact;
+
+        final Map<String, Object> properties;
+
+        final List<Artifact> relocations;
+
+        final List<RemoteRepository> repositories;
+
+        final List<Dependency> dependencies;
+
+        final List<Dependency> managedDependencies;
+
+        public Descriptor( final ArtifactDescriptorResult result )
+        {
+            artifact = result.getArtifact();
+            properties = result.getProperties();
+            relocations = result.getRelocations();
+            dependencies = result.getDependencies();
+            managedDependencies = result.getManagedDependencies();
+            repositories = clone( result.getRepositories() );
+        }
+
+        public ArtifactDescriptorResult toResult( final ArtifactDescriptorRequest request )
+        {
+            final ArtifactDescriptorResult result = new ArtifactDescriptorResult( request );
+            result.setArtifact( artifact );
+            result.setProperties( properties );
+            result.setRelocations( relocations );
+            result.setDependencies( dependencies );
+            result.setManagedDependencies( dependencies );
+            result.setRepositories( clone( repositories ) );
+            return result;
+        }
+
+        private static List<RemoteRepository> clone( final List<RemoteRepository> repositories )
+        {
+            final List<RemoteRepository> clones = new ArrayList<RemoteRepository>( repositories.size() );
+            for ( final RemoteRepository repository : repositories )
+            {
+                final RemoteRepository clone = new RemoteRepository( repository );
+                clone.setMirroredRepositories( new ArrayList<RemoteRepository>( repository.getMirroredRepositories() ) );
+                clones.add( clone );
+            }
+            return clones;
+        }
+
+    }
+
+    static class Constraint
+    {
+
+        final Map<Version, ArtifactRepository> repositories;
+
+        final VersionConstraint versionConstraint;
+
+        public Constraint( final VersionRangeResult result )
+        {
+            versionConstraint = result.getVersionConstraint();
+            repositories = new LinkedHashMap<Version, ArtifactRepository>();
+            for ( final Version version : result.getVersions() )
+            {
+                repositories.put( version, result.getRepository( version ) );
+            }
+        }
+
+        public VersionRangeResult toResult( final VersionRangeRequest request )
+        {
+            final VersionRangeResult result = new VersionRangeResult( request );
+            for ( final Map.Entry<Version, ArtifactRepository> entry : repositories.entrySet() )
+            {
+                result.addVersion( entry.getKey() );
+                result.setRepository( entry.getKey(), entry.getValue() );
+            }
+            result.setVersionConstraint( versionConstraint );
+            return result;
+        }
+
+    }
+
+    static class ConstraintKey
+    {
+
+        private final Artifact artifact;
+
+        private final List<RemoteRepository> repositories;
+
+        private final int hashCode;
+
+        public ConstraintKey( final VersionRangeRequest request )
+        {
+            artifact = request.getArtifact();
+            repositories = request.getRepositories();
+            hashCode = artifact.hashCode();
+        }
+
+        @Override
+        public boolean equals( final Object obj )
+        {
+            if ( obj == this )
+            {
+                return true;
+            }
+            else if ( !( obj instanceof ConstraintKey ) )
+            {
+                return false;
+            }
+            final ConstraintKey that = (ConstraintKey) obj;
+            return artifact.equals( that.artifact ) && equals( repositories, that.repositories );
+        }
+
+        private static boolean equals( final Collection<RemoteRepository> repos1,
+                                       final Collection<RemoteRepository> repos2 )
+        {
+            if ( repos1.size() != repos2.size() )
+            {
+                return false;
+            }
+            for ( Iterator<RemoteRepository> it1 = repos1.iterator(), it2 = repos2.iterator(); it1.hasNext(); )
+            {
+                final RemoteRepository repo1 = it1.next();
+                final RemoteRepository repo2 = it2.next();
+                if ( repo1.isRepositoryManager() != repo2.isRepositoryManager() )
+                {
+                    return false;
+                }
+                if ( repo1.isRepositoryManager() )
+                {
+                    if ( !equals( repo1.getMirroredRepositories(), repo2.getMirroredRepositories() ) )
+                    {
+                        return false;
+                    }
+                }
+                else if ( !repo1.getUrl().equals( repo2.getUrl() ) )
+                {
+                    return false;
+                }
+                else if ( repo1.getPolicy( true ).isEnabled() != repo2.getPolicy( true ).isEnabled() )
+                {
+                    return false;
+                }
+                else if ( repo1.getPolicy( false ).isEnabled() != repo2.getPolicy( false ).isEnabled() )
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return hashCode;
+        }
+
+    }
+
+    static class NodeKey
+    {
+
+        private final Artifact artifact;
+
+        private final List<RemoteRepository> repositories;
+
+        private final int hashCode;
+
+        public NodeKey( final Artifact artifact, final List<RemoteRepository> repositories )
+        {
+            this.artifact = artifact;
+            this.repositories = repositories;
+
+            int hash = 17;
+            hash = hash * 31 + artifact.hashCode();
+            hash = hash * 31 + repositories.hashCode();
+            hashCode = hash;
+        }
+
+        @Override
+        public boolean equals( final Object obj )
+        {
+            if ( obj == this )
+            {
+                return true;
+            }
+            else if ( !( obj instanceof NodeKey ) )
+            {
+                return false;
+            }
+            final NodeKey that = (NodeKey) obj;
+            return artifact.equals( that.artifact ) && repositories.equals( that.repositories );
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return hashCode;
+        }
+
+    }
+
+    static class GraphKey
+    {
+
+        private final Artifact artifact;
+
+        private final List<RemoteRepository> repositories;
+
+        private final DependencySelector selector;
+
+        private final DependencyManager manager;
+
+        private final DependencyTraverser traverser;
+
+        private final int hashCode;
+
+        public GraphKey( final Artifact artifact, final List<RemoteRepository> repositories,
+                         final DependencySelector selector, final DependencyManager manager,
+                         final DependencyTraverser traverser )
+        {
+            this.artifact = artifact;
+            this.repositories = repositories;
+            this.selector = selector;
+            this.manager = manager;
+            this.traverser = traverser;
+
+            int hash = 17;
+            hash = hash * 31 + artifact.hashCode();
+            hash = hash * 31 + repositories.hashCode();
+            hash = hash * 31 + selector.hashCode();
+            hash = hash * 31 + manager.hashCode();
+            hash = hash * 31 + traverser.hashCode();
+            hashCode = hash;
+        }
+
+        @Override
+        public boolean equals( final Object obj )
+        {
+            if ( obj == this )
+            {
+                return true;
+            }
+            else if ( !( obj instanceof GraphKey ) )
+            {
+                return false;
+            }
+            final GraphKey that = (GraphKey) obj;
+            return artifact.equals( that.artifact ) && repositories.equals( that.repositories )
+                && selector.equals( that.selector ) && manager.equals( that.manager )
+                && traverser.equals( that.traverser );
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return hashCode;
+        }
+
+    }
+
+    static final class ObjectPool<K, V>
+    {
+
+        private final Map<K, WeakReference<V>> objects = new WeakHashMap<K, WeakReference<V>>( 256 );
+
+        synchronized V intern( final K id, final V object )
+        {
+            final WeakReference<V> pooledRef = objects.get( id );
+            if ( pooledRef != null )
+            {
+                final V pooled = pooledRef.get();
+                if ( pooled != null )
+                {
+                    return pooled;
+                }
+            }
+
+            objects.put( id, new WeakReference<V>( object ) );
+            return object;
+        }
+
+        void set( final K id, final V object )
+        {
+            objects.put( id, new WeakReference<V>( object ) );
+        }
+
+        V get( final K id )
+        {
+            final WeakReference<V> ref = objects.get( id );
+            return ref == null ? null : ref.get();
+        }
+
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/SlimDepGraph.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/SlimDepGraph.java
new file mode 100644
index 0000000..d32d627
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/SlimDepGraph.java
@@ -0,0 +1,275 @@
+/*
+ * 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.mae.depgraph.impl.collect;
+
+import static org.sonatype.aether.util.artifact.ArtifacIdUtils.toId;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.mae.graph.DirectedGraph;
+import org.sonatype.aether.RepositorySystemSession;
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.repository.RemoteRepository;
+
+class SlimDepGraph
+{
+
+    private static final List<Artifact> NO_ARTIFACTS = Collections.emptyList();
+
+    private final DepGraph graph;
+
+    private final Map<String, Set<Artifact>> relocations = new HashMap<String, Set<Artifact>>();
+
+    private final Map<String, Set<Artifact>> aliases = new HashMap<String, Set<Artifact>>();
+
+    private final Map<String, Set<RemoteRepository>> repositoryMap =
+        new HashMap<String, Set<RemoteRepository>>();
+
+    private final DepGraphCache cache;
+
+    SlimDepGraph( final RepositorySystemSession session )
+    {
+        graph = new DepGraph( this );
+        cache = new DepGraphCache( session );
+    }
+
+    synchronized List<DependencyNode> childrenOf( final SlimDependencyNode node )
+    {
+        final Collection<SlimDependencyEdge> allEdges = graph.edgesFrom( node );
+        final List<DependencyNode> children = new ArrayList<DependencyNode>( allEdges );
+        for ( final SlimDependencyEdge edge : allEdges )
+        {
+            if ( edge.getFrom() == node && edge.getTo() == node )
+            {
+                children.remove( edge );
+            }
+        }
+
+        return children;
+    }
+
+    synchronized void setArtifact( final Artifact artifact )
+    {
+        cache.setArtifact( artifact );
+    }
+
+    synchronized Artifact intern( final Artifact artifact )
+    {
+        return cache.intern( artifact );
+    }
+
+    synchronized RemoteRepository intern( final RemoteRepository repo )
+    {
+        return cache.intern( repo );
+    }
+
+    synchronized List<Artifact> getArtifacts( final List<String> ids )
+    {
+        if ( ids == null )
+        {
+            return NO_ARTIFACTS;
+        }
+
+        final List<Artifact> result = new ArrayList<Artifact>( ids.size() );
+        for ( final String id : ids )
+        {
+            result.add( cache.getArtifact( id ) );
+        }
+
+        return result;
+    }
+
+    Artifact getArtifact( final String id )
+    {
+        return cache.getArtifact( id );
+    }
+
+    synchronized void addEdge( final SlimDependencyEdge edge )
+    {
+        graph.addEdge( edge );
+    }
+
+    SlimDependencyNode getNode( final String id )
+    {
+        final SlimDependencyNode node = new SlimDependencyNode( id, this );
+        if ( graph.containsVertex( node ) )
+        {
+            return node;
+        }
+
+        return null;
+    }
+
+    SlimDependencyNode getNode( final Artifact artifact )
+    {
+        return getNode( toId( artifact ) );
+    }
+
+    List<Artifact> getRelocations( final String id )
+    {
+        final Set<Artifact> r = relocations.get( id );
+        return r == null ? null : new ArrayList<Artifact>( r );
+    }
+
+    synchronized void addRelocation( final String id, final Artifact relocation )
+    {
+        Set<Artifact> r = relocations.get( id );
+        if ( r == null )
+        {
+            r = new LinkedHashSet<Artifact>();
+            relocations.put( id, r );
+        }
+
+        r.add( intern( relocation ) );
+    }
+
+    synchronized void setRelocations( final String id, final List<Artifact> relocations )
+    {
+        if ( relocations == null )
+        {
+            // this.relocations.remove( id );
+            return;
+        }
+
+        final Set<Artifact> r = new LinkedHashSet<Artifact>();
+        for ( final Artifact artifact : relocations )
+        {
+            r.add( intern( artifact ) );
+        }
+
+        this.relocations.put( id, r );
+    }
+
+    Collection<Artifact> getAliases( final String id )
+    {
+        return aliases.get( id );
+    }
+
+    synchronized void addAlias( final String id, final Artifact alias )
+    {
+        Set<Artifact> a = aliases.get( id );
+        if ( a == null )
+        {
+            a = new LinkedHashSet<Artifact>();
+            aliases.put( id, a );
+        }
+
+        a.add( intern( alias ) );
+    }
+
+    synchronized void setAliases( final String id, final Collection<Artifact> aliases )
+    {
+        if ( aliases == null )
+        {
+            // this.aliases.remove( id );
+            return;
+        }
+
+        final Set<Artifact> a = new LinkedHashSet<Artifact>();
+        for ( final Artifact artifact : aliases )
+        {
+            a.add( intern( artifact ) );
+        }
+
+        this.aliases.put( id, a );
+    }
+
+    List<RemoteRepository> getRepositories( final String id )
+    {
+        final Set<RemoteRepository> repos = repositoryMap.get( id );
+        return repos == null ? null : new ArrayList<RemoteRepository>( repos );
+    }
+
+    synchronized void setRepositories( final String id, final List<RemoteRepository> repositories )
+    {
+        if ( repositories == null )
+        {
+            // repositoryMap.remove( id );
+            return;
+        }
+
+        final Set<RemoteRepository> repos = new LinkedHashSet<RemoteRepository>();
+        for ( final RemoteRepository repo : repositories )
+        {
+            repos.add( intern( repo ) );
+        }
+
+        repositoryMap.put( id, repos );
+    }
+
+    private static final class DepGraph
+        extends DirectedGraph<SlimDependencyNode, SlimDependencyEdge>
+    {
+        DepGraph( final SlimDepGraph owner )
+        {
+            super( new SlimDependencyEdge.Factory( owner ) );
+        }
+
+        Collection<SlimDependencyEdge> edgesFrom( final SlimDependencyNode from )
+        {
+            return getNakedGraph().getOutEdges( from );
+        }
+
+        void addEdge( final SlimDependencyEdge edge )
+        {
+            if ( !getNakedGraph().containsVertex( edge.getFrom() ) )
+            {
+                getNakedGraph().addVertex( edge.getFrom() );
+            }
+
+            if ( !getNakedGraph().containsVertex( edge.getTo() ) )
+            {
+                getNakedGraph().addVertex( edge.getTo() );
+            }
+
+            getNakedGraph().addEdge( edge, edge.getFrom(), edge.getTo() );
+        }
+
+        boolean containsVertex( final SlimDependencyNode vertex )
+        {
+            return getNakedGraph().containsVertex( vertex );
+        }
+
+        void removeEdge( final SlimDependencyEdge edge )
+        {
+            getNakedGraph().removeEdge( edge );
+            final SlimDependencyNode to = edge.getTo();
+
+            final Collection<SlimDependencyEdge> in = getNakedGraph().getInEdges( to );
+            if ( in == null || in.isEmpty() )
+            {
+                getNakedGraph().removeVertex( to );
+            }
+        }
+    }
+
+    public void removeEdge( final SlimDependencyEdge edge )
+    {
+        graph.removeEdge( edge );
+    }
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/SlimDependencyEdge.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/SlimDependencyEdge.java
new file mode 100644
index 0000000..ea02bbf
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/SlimDependencyEdge.java
@@ -0,0 +1,360 @@
+/*
+ * 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.mae.depgraph.impl.collect;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.mae.graph.DirectionalEdge;
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.graph.Dependency;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.graph.DependencyVisitor;
+import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.version.Version;
+import org.sonatype.aether.version.VersionConstraint;
+
+class SlimDependencyEdge
+    extends DirectionalEdge<SlimDependencyNode>
+    implements DependencyNode
+{
+
+    private final SlimDepGraph graph;
+
+    private Dependency dependency;
+
+    private String key;
+
+    private VersionConstraint versionConstraint;
+
+    private Version version;
+
+    private String scope;
+
+    private String preManagedVersion;
+
+    private String preManagedScope;
+
+    private String requestContext;
+
+    private Map<Object, Object> data;
+
+    SlimDependencyEdge( final SlimDependencyNode from, final SlimDependencyNode to,
+                        final SlimDepGraph graph )
+    {
+        super( from, to );
+        graph.addEdge( this );
+        this.graph = graph;
+    }
+
+    SlimDependencyEdge( final SlimDependencyNode root, final SlimDepGraph graph )
+    {
+        super( root, root );
+        graph.addEdge( this );
+        this.graph = graph;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getChildren()
+     */
+    @Override
+    public List<DependencyNode> getChildren()
+    {
+        return graph.childrenOf( getTo() );
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getDependency()
+     */
+    @Override
+    public Dependency getDependency()
+    {
+        return dependency == null ? null
+                        : dependency.setArtifact( graph.intern( dependency.getArtifact() ) );
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#setArtifact(org.sonatype.aether.artifact.Artifact)
+     */
+    @Override
+    public void setArtifact( final Artifact artifact )
+    {
+        graph.setArtifact( artifact );
+        if ( dependency != null )
+        {
+            dependency = dependency.setArtifact( artifact );
+        }
+    }
+
+    Artifact getArtifact()
+    {
+        return graph.getArtifact( key );
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getRelocations()
+     */
+    @Override
+    public List<Artifact> getRelocations()
+    {
+        return graph.getRelocations( key );
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getVersionConstraint()
+     */
+    @Override
+    public VersionConstraint getVersionConstraint()
+    {
+        return versionConstraint;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getVersion()
+     */
+    @Override
+    public Version getVersion()
+    {
+        return version;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#setScope(java.lang.String)
+     */
+    @Override
+    public void setScope( final String scope )
+    {
+        this.scope = scope.intern();
+    }
+
+    String getScope()
+    {
+        return scope;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getPremanagedVersion()
+     */
+    @Override
+    public String getPremanagedVersion()
+    {
+        return preManagedVersion;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getPremanagedScope()
+     */
+    @Override
+    public String getPremanagedScope()
+    {
+        return preManagedScope;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getRepositories()
+     */
+    @Override
+    public List<RemoteRepository> getRepositories()
+    {
+        return getTo().getRepositories();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getRequestContext()
+     */
+    @Override
+    public String getRequestContext()
+    {
+        return requestContext;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#setRequestContext(java.lang.String)
+     */
+    @Override
+    public void setRequestContext( final String requestContext )
+    {
+        this.requestContext = requestContext.intern();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getData()
+     */
+    @Override
+    public Map<Object, Object> getData()
+    {
+        return Collections.unmodifiableMap( data );
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#setData(java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public synchronized void setData( final Object key, final Object value )
+    {
+        if ( data == null )
+        {
+            data = new HashMap<Object, Object>();
+        }
+
+        if ( key != null )
+        {
+            data.put( key, value );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#accept(org.sonatype.aether.graph.DependencyVisitor)
+     */
+    @Override
+    public boolean accept( final DependencyVisitor visitor )
+    {
+        if ( visitor.visitEnter( this ) )
+        {
+            for ( final DependencyNode child : getChildren() )
+            {
+                if ( !child.accept( visitor ) )
+                {
+                    break;
+                }
+            }
+        }
+
+        return visitor.visitLeave( this );
+    }
+
+    void setDependency( final Dependency dependency )
+    {
+        this.dependency =
+            dependency == null ? null
+                            : dependency.setArtifact( graph.intern( dependency.getArtifact() ) );
+    }
+
+    void setRelocations( final List<Artifact> relocations )
+    {
+        graph.setRelocations( key, relocations );
+    }
+
+    void addRelocation( final Artifact relocation )
+    {
+        graph.addRelocation( key, relocation );
+    }
+
+    void setVersionConstraint( final VersionConstraint versionConstraint )
+    {
+        this.versionConstraint = versionConstraint;
+    }
+
+    void setVersion( final Version version )
+    {
+        this.version = version;
+    }
+
+    void setPreManagedVersion( final String preManagedVersion )
+    {
+        this.preManagedVersion = preManagedVersion;
+    }
+
+    void setPreManagedScope( final String preManagedScope )
+    {
+        this.preManagedScope = preManagedScope;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.sonatype.aether.graph.DependencyNode#getAliases()
+     */
+    @Override
+    public Collection<Artifact> getAliases()
+    {
+        return getTo().getAliases();
+    }
+
+    static final class Factory
+        implements DirectionalEdgeFactory<SlimDependencyNode, SlimDependencyEdge>
+    {
+
+        private final SlimDepGraph graph;
+
+        Factory( final SlimDepGraph graph )
+        {
+            this.graph = graph;
+        }
+
+        @Override
+        public SlimDependencyEdge createEdge( final SlimDependencyNode from,
+                                              final SlimDependencyNode to )
+        {
+            return new SlimDependencyEdge( from, to, graph );
+        }
+
+    }
+
+    void setPremanagedScope( final String premanagedScope )
+    {
+        preManagedScope = premanagedScope;
+    }
+
+    void setPremanagedVersion( final String premanagedVersion )
+    {
+        preManagedVersion = premanagedVersion;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "Edge:\n\tFrom: " + getFrom() + "\n\tTo: " + getTo();
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/SlimDependencyNode.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/SlimDependencyNode.java
new file mode 100644
index 0000000..eba351c
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/collect/SlimDependencyNode.java
@@ -0,0 +1,112 @@
+/*
+ * 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.mae.depgraph.impl.collect;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.repository.RemoteRepository;
+
+class SlimDependencyNode
+{
+
+    public static final String UNKNOWN_ROOT_ID = "anonymous-root".intern();
+
+    private final SlimDepGraph graph;
+
+    private final String key;
+
+    SlimDependencyNode( final String key, final SlimDepGraph graph )
+    {
+        this.key = key;
+        this.graph = graph;
+    }
+
+    Collection<Artifact> getAliases()
+    {
+        return graph.getAliases( key );
+    }
+
+    void setAliases( final Collection<Artifact> aliases )
+    {
+        graph.setAliases( key, aliases );
+    }
+
+    void addAlias( final Artifact artifact )
+    {
+        graph.addAlias( key, artifact );
+    }
+
+    List<RemoteRepository> getRepositories()
+    {
+        return graph.getRepositories( key );
+    }
+
+    void setRepositories( final List<RemoteRepository> repositories )
+    {
+        graph.setRepositories( key, repositories );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ( ( key == null ) ? 0 : key.hashCode() );
+        return result;
+    }
+
+    @Override
+    public boolean equals( final Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+        if ( obj == null )
+        {
+            return false;
+        }
+        if ( getClass() != obj.getClass() )
+        {
+            return false;
+        }
+        final SlimDependencyNode other = (SlimDependencyNode) obj;
+        if ( key == null )
+        {
+            if ( other.key != null )
+            {
+                return false;
+            }
+        }
+        else if ( !key.equals( other.key ) )
+        {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString()
+    {
+        return key;
+    }
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/session/DepGraphProjectToolsSession.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/session/DepGraphProjectToolsSession.java
new file mode 100644
index 0000000..7d09378
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/impl/session/DepGraphProjectToolsSession.java
@@ -0,0 +1,49 @@
+/*
+ * 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.mae.depgraph.impl.session;
+
+import org.apache.maven.mae.depgraph.DependencyGraph;
+import org.apache.maven.mae.project.session.ProjectToolsSession;
+import org.apache.maven.mae.project.session.SimpleProjectToolsSession;
+import org.sonatype.aether.artifact.Artifact;
+
+public class DepGraphProjectToolsSession
+    extends SimpleProjectToolsSession
+{
+
+    @Override
+    public synchronized ProjectToolsSession connectProjectHierarchy( final Artifact parent,
+                                                                     final boolean parentPreResolved,
+                                                                     final Artifact child,
+                                                                     final boolean childPreResolved )
+    {
+        DependencyGraph graph = getState( DependencyGraph.class );
+        if ( graph == null )
+        {
+            graph = new DependencyGraph();
+            setState( graph );
+        }
+
+        graph.addDependency( parent, child, parentPreResolved, childPreResolved );
+
+        return this;
+    }
+
+}
diff --git a/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/lib/DependencyGrapherLibrary.java b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/lib/DependencyGrapherLibrary.java
new file mode 100644
index 0000000..8372087
--- /dev/null
+++ b/mae-components/mae-dependency-grapher/src/main/java/org/apache/maven/mae/depgraph/lib/DependencyGrapherLibrary.java
@@ -0,0 +1,39 @@
+/*
+ * 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.mae.depgraph.lib;
+
+import org.apache.maven.mae.conf.AbstractMAELibrary;
+import org.apache.maven.mae.conf.MAELibrary;
+import org.apache.maven.mae.conf.MavenPomVersionProvider;
+import org.commonjava.atservice.annotation.Service;
+
+@Service( MAELibrary.class )
+public class DependencyGrapherLibrary
+    extends AbstractMAELibrary
+{
+
+    public DependencyGrapherLibrary()
+    {
+        super( "depgraph", "Dependency-Grapher",
+               new MavenPomVersionProvider( "org.commonjava.emb.components",
+                                            "emb-dependency-grapher" ) );
+    }
+
+}
diff --git a/mae-components/mae-graph-support/pom.xml b/mae-components/mae-graph-support/pom.xml
new file mode 100644
index 0000000..3fe6561
--- /dev/null
+++ b/mae-components/mae-graph-support/pom.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.maven.mae.components</groupId>
+    <artifactId>mae-components</artifactId>
+    <version>1.0-alpha-1-SNAPSHOT</version>
+  </parent>
+  
+  <artifactId>mae-graph-support</artifactId>
+  <name>Maven App Engine: Graph Support</name>
+
+  <dependencies>
+    <dependency>
+    	<groupId>net.sf.jung</groupId>
+    	<artifactId>jung-api</artifactId>
+    	<version>2.0.1</version>
+    </dependency>
+    <dependency>
+    	<groupId>net.sf.jung</groupId>
+    	<artifactId>jung-graph-impl</artifactId>
+    	<version>2.0.1</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/DirectedGraph.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/DirectedGraph.java
new file mode 100644
index 0000000..0fd89d9
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/DirectedGraph.java
@@ -0,0 +1,122 @@
+/*
+ * 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.mae.graph;
+
+import java.io.PrintWriter;
+
+import org.apache.maven.mae.graph.DirectionalEdge.DirectionalEdgeFactory;
+import org.apache.maven.mae.graph.output.EdgePrinter;
+import org.apache.maven.mae.graph.output.GraphPrinter;
+import org.apache.maven.mae.graph.output.VertexPrinter;
+import org.apache.maven.mae.graph.traverse.GraphVisitor;
+
+import edu.uci.ics.jung.graph.DirectedSparseGraph;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.graph.util.Graphs;
+
+public class DirectedGraph<V, E extends DirectionalEdge<V>>
+    implements GraphManager<V, E>
+{
+
+    private final DirectedSparseGraph<V, E> graph = new DirectedSparseGraph<V, E>();
+
+    private final DirectionalEdgeFactory<V, E> edgeFactory;
+
+    public DirectedGraph( final DirectionalEdgeFactory<V, E> edgeFactory )
+    {
+        this.edgeFactory = edgeFactory;
+    }
+
+    public DirectedGraph<V, E> connect( final V from, final V to )
+    {
+        final E edge = edgeFactory.createEdge( from, to );
+
+        if ( graph.containsEdge( edge ) )
+        {
+            return this;
+        }
+
+        if ( !graph.containsVertex( from ) )
+        {
+            graph.addVertex( from );
+        }
+
+        if ( !graph.containsVertex( to ) )
+        {
+            graph.addVertex( to );
+        }
+
+        graph.addEdge( edge, from, to );
+
+        return this;
+    }
+
+    public abstract static class Visitor<T>
+        implements GraphVisitor<T, DirectionalEdge<T>>
+    {
+
+    }
+
+    public static final class Printer<T>
+        extends GraphPrinter<T, DirectionalEdge<T>>
+    {
+
+        public Printer( final PrintWriter printWriter )
+        {
+            super( printWriter );
+        }
+
+        public Printer( final boolean printEdges, final PrintWriter printWriter )
+        {
+            super( printEdges, printWriter );
+        }
+
+        public Printer( final VertexPrinter<T> vPrinter, final PrintWriter printWriter )
+        {
+            super( vPrinter, printWriter );
+        }
+
+        public Printer( final EdgePrinter<DirectionalEdge<T>> ePrinter,
+                        final PrintWriter printWriter )
+        {
+            super( ePrinter, printWriter );
+        }
+
+        public Printer( final String indent, final VertexPrinter<T> vPrinter,
+                        final EdgePrinter<DirectionalEdge<T>> ePrinter,
+                        final PrintWriter printWriter )
+        {
+            super( indent, vPrinter, ePrinter, printWriter );
+        }
+
+    }
+
+    @Override
+    public Graph<V, E> getManagedGraph()
+    {
+        return Graphs.unmodifiableDirectedGraph( graph );
+    }
+
+    protected Graph<V, E> getNakedGraph()
+    {
+        return graph;
+    }
+
+}
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/DirectionalEdge.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/DirectionalEdge.java
new file mode 100644
index 0000000..06ba322
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/DirectionalEdge.java
@@ -0,0 +1,120 @@
+/*
+ * 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.mae.graph;
+
+public class DirectionalEdge<V>
+{
+
+    private final V from;
+
+    private final V to;
+
+    public DirectionalEdge( final V from, final V to )
+    {
+        this.from = from;
+        this.to = to;
+    }
+
+    public V getFrom()
+    {
+        return from;
+    }
+
+    public V getTo()
+    {
+        return to;
+    }
+
+    public interface DirectionalEdgeFactory<V, E extends DirectionalEdge<V>>
+    {
+        E createEdge( V from, V to );
+    }
+
+    public static final class SimpleDirectionalEdgeFactory<V>
+        implements DirectionalEdgeFactory<V, DirectionalEdge<V>>
+    {
+        @Override
+        public DirectionalEdge<V> createEdge( final V from, final V to )
+        {
+            return new DirectionalEdge<V>( from, to );
+        }
+
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 37;
+        int result = 1;
+        result = prime * result + ( ( from == null ) ? 0 : from.hashCode() );
+        result = prime * result + ( ( to == null ) ? 0 : to.hashCode() );
+        return result;
+    }
+
+    @Override
+    public boolean equals( final Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+        if ( obj == null )
+        {
+            return false;
+        }
+        if ( getClass() != obj.getClass() )
+        {
+            return false;
+        }
+        final DirectionalEdge<?> other = (DirectionalEdge<?>) obj;
+        if ( from == null )
+        {
+            if ( other.from != null )
+            {
+                return false;
+            }
+        }
+        else if ( !from.equals( other.from ) )
+        {
+            return false;
+        }
+        if ( to == null )
+        {
+            if ( other.to != null )
+            {
+                return false;
+            }
+        }
+        else if ( !to.equals( other.to ) )
+        {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString()
+    {
+        final StringBuilder builder = new StringBuilder();
+        builder.append( "DirectionalEdge [" ).append( from ).append( " --> " ).append( to ).append( "]" );
+        return builder.toString();
+    }
+
+}
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/GraphManager.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/GraphManager.java
new file mode 100644
index 0000000..c4f95dd
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/GraphManager.java
@@ -0,0 +1,29 @@
+/*
+ * 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.mae.graph;
+
+import edu.uci.ics.jung.graph.Graph;
+
+public interface GraphManager<V, E>
+{
+
+    Graph<V, E> getManagedGraph();
+
+}
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/SimpleDirectedGraph.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/SimpleDirectedGraph.java
new file mode 100644
index 0000000..6ffba12
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/SimpleDirectedGraph.java
@@ -0,0 +1,31 @@
+/*
+ * 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.mae.graph;
+
+public class SimpleDirectedGraph<V>
+    extends DirectedGraph<V, DirectionalEdge<V>>
+{
+
+    public SimpleDirectedGraph()
+    {
+        super( new DirectionalEdge.SimpleDirectionalEdgeFactory<V>() );
+    }
+
+}
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/output/EdgePrinter.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/output/EdgePrinter.java
new file mode 100644
index 0000000..33ead1e
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/output/EdgePrinter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.mae.graph.output;
+
+public interface EdgePrinter<E>
+{
+    public static final class ToStringPrinter<T>
+        implements EdgePrinter<T>
+    {
+        @Override
+        public String printEdge( final T edge )
+        {
+            return edge == null ? "-null-" : edge.toString();
+        }
+    };
+
+    String printEdge( E edge );
+
+}
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/output/GraphPrinter.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/output/GraphPrinter.java
new file mode 100644
index 0000000..b18db9d
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/output/GraphPrinter.java
@@ -0,0 +1,219 @@
+/*
+ * 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.mae.graph.output;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.mae.graph.traverse.GraphVisitor;
+
+import edu.uci.ics.jung.graph.Graph;
+
+public class GraphPrinter<V, E>
+    implements GraphVisitor<V, E>
+{
+    private PrintWriter printWriter;
+
+    private final String indent;
+
+    private int indentCounter = 0;
+
+    private final List<Object> lines = new ArrayList<Object>();
+
+    private final VertexPrinter<V> vPrinter;
+
+    private final EdgePrinter<E> ePrinter;
+
+    public GraphPrinter( final PrintWriter printWriter )
+    {
+        this( null, new VertexPrinter.ToStringPrinter<V>(), null, printWriter );
+    }
+
+    public GraphPrinter( final boolean printEdges, final PrintWriter printWriter )
+    {
+        this( null, new VertexPrinter.ToStringPrinter<V>(),
+              printEdges ? new EdgePrinter.ToStringPrinter<E>() : null, printWriter );
+    }
+
+    public GraphPrinter( final VertexPrinter<V> vPrinter, final PrintWriter printWriter )
+    {
+        this( null, vPrinter, null, printWriter );
+    }
+
+    public GraphPrinter( final EdgePrinter<E> ePrinter, final PrintWriter printWriter )
+    {
+        this( null, null, ePrinter, printWriter );
+    }
+
+    public GraphPrinter( final String indent, final VertexPrinter<V> vPrinter,
+                         final EdgePrinter<E> ePrinter, final PrintWriter printWriter )
+    {
+        this.vPrinter = vPrinter;
+        this.ePrinter = ePrinter;
+        this.printWriter = printWriter;
+        this.indent = indent == null ? "  " : indent;
+    }
+
+    public GraphPrinter<V, E> reset( final PrintWriter printWriter )
+    {
+        this.printWriter = printWriter;
+        indentCounter = 0;
+        lines.clear();
+
+        return this;
+    }
+
+    private void indentLine()
+    {
+        for ( int i = 0; i < indentCounter; i++ )
+        {
+            printWriter.append( indent );
+        }
+    }
+
+    private void newLine()
+    {
+        printWriter.append( '\n' );
+
+        final int sz = lines.size() + 1;
+        printWriter.append( Integer.toString( sz ) ).append( ":" );
+        if ( sz < 100 )
+        {
+            printWriter.append( ' ' );
+            if ( sz < 10 )
+            {
+                printWriter.append( ' ' );
+            }
+        }
+        printWriter.append( ">" ).append( Integer.toString( indentCounter ) ).append( ' ' );
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#traversedEdge(edu.uci.ics.jung.graph.Graph, java.lang.Object)
+     */
+    @Override
+    public boolean traversedEdge( final Graph<V, E> graph, final E edge )
+    {
+        if ( ePrinter != null )
+        {
+            newLine();
+            indentLine();
+
+            printWriter.append( '(' ).append( ePrinter.printEdge( edge ) ).append( ')' );
+            lines.add( edge );
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#startedVertexVisit(edu.uci.ics.jung.graph.Graph,
+     *      java.lang.Object)
+     */
+    @Override
+    public boolean startedVertexVisit( final Graph<V, E> graph, final V vertex )
+    {
+        if ( vPrinter != null )
+        {
+            newLine();
+            indentLine();
+
+            printWriter.append( vPrinter.vertexStarted( vertex ) );
+            lines.add( vertex );
+        }
+
+        indentCounter++;
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#finishedVertexVisit(edu.uci.ics.jung.graph.Graph,
+     *      java.lang.Object)
+     */
+    @Override
+    public boolean finishedVertexVisit( final Graph<V, E> graph, final V vertex )
+    {
+        if ( vPrinter != null )
+        {
+            final String ending = vPrinter.vertexFinished( vertex );
+            if ( ending != null )
+            {
+                newLine();
+                indentLine();
+
+                printWriter.append( ending );
+                lines.add( "end" );
+            }
+        }
+
+        indentCounter--;
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#skippedVertexVisit(edu.uci.ics.jung.graph.Graph,
+     *      java.lang.Object)
+     */
+    @Override
+    public void skippedVertexVisit( final Graph<V, E> graph, final V vertex )
+    {
+        newLine();
+        indentLine();
+
+        final int idx = lines.indexOf( vertex );
+
+        // builder.append( vertex );
+        if ( vPrinter != null )
+        {
+            final String skip = vPrinter.vertexSkipped( vertex );
+            if ( skip != null )
+            {
+                printWriter.append( skip ).append( ' ' );
+            }
+        }
+        printWriter.append( "-DUPLICATE- (see line: " ).append( Integer.toString( idx + 1 ) ).append( ")" );
+
+        // we need some placeholder here, without screwing up indexOf() operations for the vertex...
+        lines.add( idx );
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#skippedEdgeTraversal(edu.uci.ics.jung.graph.Graph,
+     *      java.lang.Object)
+     */
+    @Override
+    public void skippedEdgeTraversal( final Graph<V, E> graph, final E edge )
+    {
+        // NOP.
+    }
+}
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/output/VertexPrinter.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/output/VertexPrinter.java
new file mode 100644
index 0000000..e0f6679
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/output/VertexPrinter.java
@@ -0,0 +1,60 @@
+/*
+ * 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.mae.graph.output;
+
+public interface VertexPrinter<V>
+{
+
+    public static final class ToStringPrinter<T>
+        extends AbstractVertexPrinter<T>
+    {
+        @Override
+        public String vertexStarted( final T vertex )
+        {
+            return vertex == null ? "-null-" : vertex.toString();
+        }
+    };
+
+    public abstract static class AbstractVertexPrinter<T>
+        implements VertexPrinter<T>
+    {
+        @Override
+        public String vertexFinished( final T vertex )
+        {
+            return null;
+        }
+
+        @Override
+        public String vertexSkipped( final T vertex )
+        {
+            return null;
+        }
+    }
+
+    /**
+     * Write the header to signal the start of a new vertex traversal. <b>NEVER</b> <code>null</code>.
+     */
+    String vertexStarted( V vertex );
+
+    String vertexFinished( V vertex );
+
+    String vertexSkipped( V vertex );
+
+}
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/traverse/CompoundGraphVisitor.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/traverse/CompoundGraphVisitor.java
new file mode 100644
index 0000000..56633d3
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/traverse/CompoundGraphVisitor.java
@@ -0,0 +1,132 @@
+/*
+ * 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.mae.graph.traverse;
+
+import edu.uci.ics.jung.graph.Graph;
+
+public final class CompoundGraphVisitor<V, E>
+    implements GraphVisitor<V, E>
+{
+    private final GraphVisitor<V, E>[] visitors;
+
+    public CompoundGraphVisitor( final GraphVisitor<V, E>... visitors )
+    {
+        this.visitors = visitors;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#traversedEdge(edu.uci.ics.jung.graph.Graph, java.lang.Object)
+     */
+    @Override
+    public boolean traversedEdge( final Graph<V, E> graph, final E edge )
+    {
+        boolean doContinue = true;
+        for ( final GraphVisitor<V, E> visitor : visitors )
+        {
+            doContinue = doContinue && visitor.traversedEdge( graph, edge );
+
+            if ( !doContinue )
+            {
+                break;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#startedVertexVisit(edu.uci.ics.jung.graph.Graph,
+     *      java.lang.Object)
+     */
+    @Override
+    public boolean startedVertexVisit( final Graph<V, E> graph, final V vertex )
+    {
+        boolean doContinue = true;
+        for ( final GraphVisitor<V, E> visitor : visitors )
+        {
+            doContinue = doContinue && visitor.startedVertexVisit( graph, vertex );
+
+            if ( !doContinue )
+            {
+                break;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#finishedVertexVisit(edu.uci.ics.jung.graph.Graph,
+     *      java.lang.Object)
+     */
+    @Override
+    public boolean finishedVertexVisit( final Graph<V, E> graph, final V vertex )
+    {
+        boolean doContinue = true;
+        for ( final GraphVisitor<V, E> visitor : visitors )
+        {
+            doContinue = doContinue && visitor.finishedVertexVisit( graph, vertex );
+
+            if ( !doContinue )
+            {
+                break;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#skippedVertexVisit(edu.uci.ics.jung.graph.Graph,
+     *      java.lang.Object)
+     */
+    @Override
+    public void skippedVertexVisit( final Graph<V, E> graph, final V vertex )
+    {
+        for ( final GraphVisitor<V, E> visitor : visitors )
+        {
+            visitor.skippedVertexVisit( graph, vertex );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.maven.mae.graph.traverse.GraphVisitor#skippedEdgeTraversal(edu.uci.ics.jung.graph.Graph,
+     *      java.lang.Object)
+     */
+    @Override
+    public void skippedEdgeTraversal( final Graph<V, E> graph, final E edge )
+    {
+        for ( final GraphVisitor<V, E> visitor : visitors )
+        {
+            visitor.skippedEdgeTraversal( graph, edge );
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/traverse/GraphVisitor.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/traverse/GraphVisitor.java
new file mode 100644
index 0000000..9d81d06
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/traverse/GraphVisitor.java
@@ -0,0 +1,36 @@
+/*
+ * 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.mae.graph.traverse;
+
+import edu.uci.ics.jung.graph.Graph;
+
+public interface GraphVisitor<V, E>
+{
+
+    boolean traversedEdge( Graph<V, E> graph, E edge );
+
+    boolean startedVertexVisit( Graph<V, E> graph, V vertex );
+
+    boolean finishedVertexVisit( Graph<V, E> graph, V vertex );
+
+    void skippedVertexVisit( Graph<V, E> graph, V vertex );
+
+    void skippedEdgeTraversal( Graph<V, E> graph, E edge );
+}
\ No newline at end of file
diff --git a/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/traverse/GraphWalker.java b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/traverse/GraphWalker.java
new file mode 100644
index 0000000..2099ef0
--- /dev/null
+++ b/mae-components/mae-graph-support/src/main/java/org/apache/maven/mae/graph/traverse/GraphWalker.java
@@ -0,0 +1,89 @@
+/*
+ * 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.mae.graph.traverse;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.maven.mae.graph.GraphManager;
+
+import edu.uci.ics.jung.graph.Graph;
+
+public final class GraphWalker
+{
+
+    public static <V, E> List<V> walkDepthFirst( final GraphManager<V, E> graphManager,
+                                                 final V start, final GraphVisitor<V, E> visitor )
+    {
+        return walkDepthFirst( graphManager.getManagedGraph(), start, visitor );
+    }
+
+    public static <V, E> List<V> walkDepthFirst( final Graph<V, E> graph, final V start,
+                                                 final GraphVisitor<V, E> visitor )
+    {
+        final LinkedHashSet<V> encounters = new LinkedHashSet<V>();
+        encounters.add( start );
+
+        walkDepthFirst( graph, start, visitor, encounters );
+
+        return new ArrayList<V>( encounters );
+    }
+
+    private static <V, E> void walkDepthFirst( final Graph<V, E> graph, final V from,
+                                               final GraphVisitor<V, E> visitor,
+                                               final Set<V> progress )
+    {
+        if ( !visitor.startedVertexVisit( graph, from ) )
+        {
+            return;
+        }
+
+        for ( final E edge : graph.getOutEdges( from ) )
+        {
+            final V to = graph.getOpposite( from, edge );
+
+            if ( to.equals( from ) )
+            {
+                visitor.skippedVertexVisit( graph, to );
+                continue;
+            }
+
+            if ( !visitor.traversedEdge( graph, edge ) )
+            {
+                visitor.skippedEdgeTraversal( graph, edge );
+                continue;
+            }
+
+            if ( !progress.contains( to ) )
+            {
+                progress.add( to );
+                walkDepthFirst( graph, to, visitor, progress );
+            }
+            else
+            {
+                visitor.skippedVertexVisit( graph, to );
+            }
+        }
+
+        visitor.finishedVertexVisit( graph, from );
+    }
+}
diff --git a/mae-components/mae-graph-support/src/test/java/org/apache/maven/mae/graph/SimpleDirectedGraphTest.java b/mae-components/mae-graph-support/src/test/java/org/apache/maven/mae/graph/SimpleDirectedGraphTest.java
new file mode 100644
index 0000000..e04ce74
--- /dev/null
+++ b/mae-components/mae-graph-support/src/test/java/org/apache/maven/mae/graph/SimpleDirectedGraphTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.mae.graph;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.maven.mae.graph.DirectionalEdge;
+import org.apache.maven.mae.graph.SimpleDirectedGraph;
+import org.apache.maven.mae.graph.DirectedGraph.Printer;
+import org.apache.maven.mae.graph.output.EdgePrinter;
+import org.apache.maven.mae.graph.traverse.GraphWalker;
+import org.junit.Test;
+
+public class SimpleDirectedGraphTest
+{
+
+    @Test
+    public void printSimpleStringDiGraph()
+    {
+        final SimpleDirectedGraph<String> graph = new SimpleDirectedGraph<String>();
+        graph.connect( "from", "to" ).connect( "to", "onward" );
+
+        final StringWriter sw = new StringWriter();
+
+        final Printer<String> printer =
+            new Printer<String>( new EdgePrinter.ToStringPrinter<DirectionalEdge<String>>(),
+                                 new PrintWriter( sw ) );
+
+        GraphWalker.walkDepthFirst( graph, "from", printer );
+
+        System.out.println( sw );
+    }
+
+}
diff --git a/mae-components/mae-project-tools/pom.xml b/mae-components/mae-project-tools/pom.xml
index 6b8182a..4b80857 100644
--- a/mae-components/mae-project-tools/pom.xml
+++ b/mae-components/mae-project-tools/pom.xml
@@ -32,38 +32,31 @@
     <dependency>
       <groupId>commons-io</groupId>
       <artifactId>commons-io</artifactId>
-      <version>2.0.1</version>
     </dependency>
     <dependency>
       <groupId>org.apache.maven.mae</groupId>
       <artifactId>mae-booter</artifactId>
-      <version>${maeVersion}</version>
-      <scope>compile</scope>
+      <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.apache.maven.wagon</groupId>
       <artifactId>wagon-http-lightweight</artifactId>
-      <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.maven.wagon</groupId>
       <artifactId>wagon-file</artifactId>
-      <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.sonatype.aether</groupId>
       <artifactId>aether-connector-wagon</artifactId>
-      <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-aether-provider</artifactId>
-      <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>commons-lang</groupId>
       <artifactId>commons-lang</artifactId>
-      <version>2.5</version>
     </dependency>
     <dependency>
     	<groupId>org.sonatype.sisu</groupId>
diff --git a/mae-components/pom.xml b/mae-components/pom.xml
index 8b843fe..85e1014 100644
--- a/mae-components/pom.xml
+++ b/mae-components/pom.xml
@@ -66,5 +66,7 @@
   
   <modules>
     <module>mae-project-tools</module>
+    <module>mae-graph-support</module>
+    <module>mae-dependency-grapher</module>
   </modules>
 </project>
diff --git a/mae-container/pom.xml b/mae-container/pom.xml
index ca9487f..9f0889c 100644
--- a/mae-container/pom.xml
+++ b/mae-container/pom.xml
@@ -49,18 +49,6 @@
       <groupId>org.sonatype.sisu</groupId>
       <artifactId>sisu-inject-plexus</artifactId>
     </dependency>
-    <!-- <dependency>
-      <groupId>org.sonatype.spice.inject</groupId>
-      <artifactId>guice-plexus-shim</artifactId>
-      <version>1.3.4</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.sonatype.spice.inject</groupId>
-      <artifactId>guice-plexus-metadata</artifactId>
-      <version>1.3.4</version>
-      <scope>provided</scope>
-    </dependency> -->
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
@@ -70,7 +58,6 @@
     <dependency>
     	<groupId>org.apache.maven.mae</groupId>
     	<artifactId>mae-api</artifactId>
-    	<version>${project.version}</version>
     </dependency>
   </dependencies>
   
diff --git a/pom.xml b/pom.xml
index 02e0ca0..28f715e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -79,6 +79,7 @@
     <module>mae-booter</module>
     <module>mae-prompter-cli</module>
     <module>mae-components</module>
+    <module>mae-container</module>
   </modules>
   
   <scm>