You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by jd...@apache.org on 2011/05/24 05:32:14 UTC

svn commit: r1126852 - in /maven/maven-3/branches/mirror-group-routing: maven-compat/src/main/java/org/apache/maven/repository/ maven-core/src/main/java/org/apache/maven/repository/mirror/ maven-embedder/src/main/java/org/apache/maven/cli/ maven-routem...

Author: jdcasey
Date: Tue May 24 03:32:13 2011
New Revision: 1126852

URL: http://svn.apache.org/viewvc?rev=1126852&view=rev
Log:
group/mirror routing integrated, up to the point where the group routes need to be queried (from the artifact resolver). Still debating how to override some of this aether code...

Added:
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouter.java   (with props)
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouterException.java   (with props)
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializer.java   (with props)
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouterModelException.java   (with props)
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/io/
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializerTest.java   (with props)
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterOption.java   (with props)
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/FileRouterConfigBuilder.java   (with props)
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DiscoveryResult.java   (with props)
Removed:
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouteSerializer.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouterModelException.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRoutingTables.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/RouterMirrorSerializerTest.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/ArtifactRouter.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/ArtifactRouterException.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/DefaultRouterConfigBuilder.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/FileRouterConfigSource.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/RouterConfigSource.java
Modified:
    maven/maven-3/branches/mirror-group-routing/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java
    maven/maven-3/branches/mirror-group-routing/maven-core/src/main/java/org/apache/maven/repository/mirror/RoutingMirrorSelector.java
    maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
    maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupPattern.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupRoute.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/MirrorRoutingTableTest.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfiguration.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfigurationException.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/RouterConfigBuilder.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/ArtifactRouterDiscoveryStrategy.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DNSDiscoveryStrategy.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/ArtifactRouterLoader.java
    maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/DefaultRouterLoader.java

Modified: maven/maven-3/branches/mirror-group-routing/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java Tue May 24 03:32:13 2011
@@ -75,8 +75,8 @@ public class DefaultMirrorSelector
         {
             final String repoUrl = repository.getUrl();
 
-            ArtifactRouter routingTable = legacySupport.getSession().getMirrorRouter();
-            final MirrorRoute rMirror = routingTable.getMirror( repoUrl );
+            ArtifactRouter router = legacySupport.getSession().getMirrorRouter();
+            final MirrorRoute rMirror = router.getMirror( repoUrl );
 
             if ( rMirror != null )
             {

Modified: maven/maven-3/branches/mirror-group-routing/maven-core/src/main/java/org/apache/maven/repository/mirror/RoutingMirrorSelector.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-core/src/main/java/org/apache/maven/repository/mirror/RoutingMirrorSelector.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-core/src/main/java/org/apache/maven/repository/mirror/RoutingMirrorSelector.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-core/src/main/java/org/apache/maven/repository/mirror/RoutingMirrorSelector.java Tue May 24 03:32:13 2011
@@ -32,15 +32,15 @@ public class RoutingMirrorSelector
     implements MirrorSelector
 {
 
-    private final ArtifactRouter mirrorRouter;
+    private final ArtifactRouter router;
 
     private final DefaultMirrorSelector delegate = new DefaultMirrorSelector();
 
     private final Logger logger;
     
-    public RoutingMirrorSelector( final ArtifactRouter mirrorRouter, final Logger logger )
+    public RoutingMirrorSelector( final ArtifactRouter router, final Logger logger )
     {
-        this.mirrorRouter = mirrorRouter;
+        this.router = router;
         this.logger = logger;
     }
 
@@ -66,7 +66,7 @@ public class RoutingMirrorSelector
         {
             final String repoUrl = repository.getUrl();
 
-            final MirrorRoute route = mirrorRouter.getMirror( repoUrl );
+            final MirrorRoute route = router.getMirror( repoUrl );
 
             if ( route != null )
             {

Modified: maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java Tue May 24 03:32:13 2011
@@ -31,6 +31,7 @@ import org.apache.commons.cli.HelpFormat
 import org.apache.commons.cli.OptionBuilder;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
+import org.apache.maven.artifact.router.conf.ArtifactRouterOption;
 
 /**
  * @author Jason van Zyl
@@ -96,6 +97,8 @@ public class CLIManager
     public static final String ENCRYPT_PASSWORD = "ep";
 
     public static final String THREADS = "T";
+    
+    public static final String ROUTER_OPTIONS = "R";
 
     private Options options;
 
@@ -133,6 +136,9 @@ public class CLIManager
         options.addOption( OptionBuilder.withLongOpt( "encrypt-master-password" ).hasArg().withDescription( "Encrypt master security password" ).create( ENCRYPT_MASTER_PASSWORD ) );
         options.addOption( OptionBuilder.withLongOpt( "encrypt-password" ).hasArg().withDescription( "Encrypt server password" ).create( ENCRYPT_PASSWORD ) );
         options.addOption( OptionBuilder.withLongOpt( "threads" ).hasArg().withDescription( "Thread count, for instance 2.0C where C is core multiplied" ).create( THREADS ) );
+        
+        options.addOption( OptionBuilder.withLongOpt( "router-options" ).withDescription( "Options controlling artifact-router behavior. Available values are: "
+                                                                                              + ArtifactRouterOption.list( "\n\t\t" ) ).create( ROUTER_OPTIONS ) );
 
         // Adding this back in for compatibility with the verifier that hard codes this option.
 

Modified: maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java Tue May 24 03:32:13 2011
@@ -38,8 +38,8 @@ import org.apache.maven.InternalErrorExc
 import org.apache.maven.Maven;
 import org.apache.maven.artifact.router.ArtifactRouter;
 import org.apache.maven.artifact.router.ArtifactRouterException;
-import org.apache.maven.artifact.router.conf.FileRouterConfigSource;
-import org.apache.maven.artifact.router.conf.RouterConfigBuilder;
+import org.apache.maven.artifact.router.conf.ArtifactRouterOption;
+import org.apache.maven.artifact.router.conf.FileRouterConfigBuilder;
 import org.apache.maven.artifact.router.conf.ArtifactRouterConfiguration;
 import org.apache.maven.artifact.router.loader.ArtifactRouterLoader;
 import org.apache.maven.eventspy.internal.EventSpyDispatcher;
@@ -122,8 +122,6 @@ public class MavenCli
 
     private SettingsBuilder settingsBuilder;
 
-    private RouterConfigBuilder routerConfBuilder;
-    
     private ArtifactRouterLoader routerLoader;
 
     private DefaultSecDispatcher dispatcher;
@@ -427,7 +425,6 @@ public class MavenCli
 
         settingsBuilder = container.lookup( SettingsBuilder.class );
 
-        routerConfBuilder = container.lookup( RouterConfigBuilder.class );
         routerLoader = container.lookup( ArtifactRouterLoader.class );
         
         dispatcher = (DefaultSecDispatcher) container.lookup( SecDispatcher.class, "maven" );
@@ -555,7 +552,7 @@ public class MavenCli
 
         try
         {
-            routerLoader.saveSelectedMirrors( cliRequest.request.getArtifactRouter(), routerConfig );
+            routerLoader.save( cliRequest.request.getArtifactRouter(), routerConfig );
         }
         catch ( ArtifactRouterException e )
         {
@@ -940,8 +937,13 @@ public class MavenCli
             userToolchainsFile = MavenCli.DEFAULT_USER_TOOLCHAINS_FILE;
         }
 
-        routerConfig =
-            routerConfBuilder.build( new FileRouterConfigSource( DEFAULT_USER_EXT_CONF_DIR ) );
+        routerConfig = new FileRouterConfigBuilder( DEFAULT_USER_EXT_CONF_DIR ).build();
+        if ( commandLine.hasOption( CLIManager.ROUTER_OPTIONS ) )
+        {
+            routerConfig.setOptions( ArtifactRouterOption.parse( commandLine.getOptionValues( CLIManager.ROUTER_OPTIONS ) ) );
+        }
+        
+        routerConfig.setOffline( request.isOffline() );
         
         ArtifactRouter router = routerLoader.load( routerConfig );
         

Added: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouter.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouter.java?rev=1126852&view=auto
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouter.java (added)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouter.java Tue May 24 03:32:13 2011
@@ -0,0 +1,300 @@
+package org.apache.maven.artifact.router;
+
+/*
+ * 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.
+ */
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+public final class ArtifactRouter
+{
+
+    private List<MirrorRoute> mirrors = new ArrayList<MirrorRoute>();
+
+    private Map<GroupPattern, GroupRoute> groups = new HashMap<GroupPattern, GroupRoute>();
+
+    private final transient Random random = new Random();
+
+    private transient Map<String, int[]> mirrorIndexGrabBags = new HashMap<String, int[]>();
+
+    private Map<String, MirrorRoute> selectedMirrors = new HashMap<String, MirrorRoute>();
+    
+    public synchronized ArtifactRouter clearSelections()
+    {
+        selectedMirrors.clear();
+        return this;
+    }
+
+    public synchronized ArtifactRouter addMirrors( final Collection<MirrorRoute> mirrors )
+    {
+        for ( MirrorRoute route : mirrors )
+        {
+            addMirror( route );
+        }
+
+        return this;
+    }
+
+    public synchronized ArtifactRouter addMirror( final MirrorRoute mirror )
+    {
+        if ( !mirrors.contains( mirror ) )
+        {
+            mirrors.add( mirror );
+            for ( String url : mirror.getMirrorOfUrls() )
+            {
+                mirrorIndexGrabBags.remove( url );
+                selectedMirrors.remove( url );
+            }
+        }
+
+        return this;
+    }
+
+    public synchronized MirrorRoute getMirror( final String canonicalUrl )
+    {
+        MirrorRoute route = selectedMirrors.get( canonicalUrl );
+        if ( route == null )
+        {
+            route = getWeightedRandomMirror( canonicalUrl );
+            selectedMirrors.put( canonicalUrl, route );
+        }
+
+        return route;
+    }
+
+    public synchronized ArtifactRouter addGroups( final Collection<GroupRoute> groups )
+    {
+        for ( GroupRoute route : groups )
+        {
+            addGroup( route );
+        }
+
+        return this;
+    }
+
+    public synchronized ArtifactRouter addGroup( final GroupRoute group )
+    {
+        GroupRoute toInsert = group;
+
+        List<GroupRoute> routes = new ArrayList<GroupRoute>( groups.values() );
+        int idx = routes.indexOf( group );
+        if ( idx > -1 )
+        {
+            GroupRoute existing = routes.get( idx );
+            for ( GroupPattern p : existing.getGroupPatterns() )
+            {
+                groups.remove( p );
+            }
+
+            existing.merge( group.getGroupPatterns() );
+            toInsert = existing;
+        }
+
+        for ( GroupPattern p : toInsert.getGroupPatterns() )
+        {
+            groups.put( p, toInsert );
+        }
+
+        return this;
+    }
+
+    public GroupRoute getGroup( String groupId )
+    {
+        List<GroupPattern> matches = new ArrayList<GroupPattern>();
+        for ( GroupPattern p : this.groups.keySet() )
+        {
+            if ( p.implies( groupId ) )
+            {
+                matches.add( p );
+            }
+        }
+
+        if ( matches.isEmpty() )
+        {
+            return GroupRoute.CENTRAL;
+        }
+        else
+        {
+            Collections.sort( matches );
+            GroupPattern pattern = matches.get( 0 );
+
+            return groups.get( pattern );
+        }
+    }
+
+    // private MirrorRoute getHighestPriorityMirror( final String canonicalUrl )
+    // {
+    // if ( mirrors.isEmpty() )
+    // {
+    // return null;
+    // }
+    //
+    // final List<MirrorRoute> available = new ArrayList<MirrorRoute>( mirrors );
+    //
+    // // sort by weight.
+    // Collections.sort( available );
+    //
+    // for ( final MirrorRoute mirror : available )
+    // {
+    // // return the highest-priority ENABLED mirror.
+    // if ( mirror.isEnabled() && canonicalUrl.equals( mirror.getUrl() ) )
+    // {
+    // return mirror;
+    // }
+    // }
+    //
+    // return null;
+    // }
+
+    private MirrorRoute getWeightedRandomMirror( final String canonicalUrl )
+    {
+        if ( mirrors.isEmpty() )
+        {
+            return null;
+        }
+
+        int[] indexGrabBag = null;
+        synchronized ( canonicalUrl )
+        {
+            indexGrabBag = mirrorIndexGrabBags.get( canonicalUrl );
+            if ( indexGrabBag == null )
+            {
+                final List<Integer> gb = new ArrayList<Integer>();
+                for ( int idx = 0; idx < mirrors.size(); idx++ )
+                {
+                    final MirrorRoute mirror = mirrors.get( idx );
+                    if ( !mirror.isEnabled() || !mirror.isMirrorOf( canonicalUrl ) )
+                    {
+                        // only select from enabled mirrors that actually mirror the given canonical repository URL.
+                        continue;
+                    }
+
+                    if ( mirror.getWeight() < 1 )
+                    {
+                        // make sure this mirror has at least one chance of being picked.
+                        gb.add( idx );
+                    }
+                    else
+                    {
+                        // if weight == 9, give this mirror 9 chances of being picked randomly.
+                        for ( int i = 0; i < mirror.getWeight(); i++ )
+                        {
+                            gb.add( idx );
+                        }
+                    }
+                }
+
+                indexGrabBag = new int[gb.size()];
+                for ( int i = 0; i < indexGrabBag.length; i++ )
+                {
+                    indexGrabBag[i] = gb.get( i );
+                }
+
+                mirrorIndexGrabBags.put( canonicalUrl, indexGrabBag );
+            }
+        }
+
+        if ( indexGrabBag.length == 0 )
+        {
+            return null;
+        }
+
+        // generate a random number that will correspond to an index stored in the index grab bag.
+        int idx = Math.abs( random.nextInt() ) % indexGrabBag.length;
+
+        // use that random number to select the index of the mirror in the mirrors list.
+        idx = indexGrabBag[idx];
+
+        // lookup the mirror instance associated with the index from the grab bag.
+        return mirrors.get( idx );
+    }
+
+    public boolean contains( final MirrorRoute o )
+    {
+        return mirrors.contains( o );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ( ( mirrors == null ) ? 0 : mirrors.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 ArtifactRouter other = (ArtifactRouter) obj;
+        if ( mirrors == null )
+        {
+            if ( other.mirrors != null )
+            {
+                return false;
+            }
+        }
+        else if ( !mirrors.equals( other.mirrors ) )
+        {
+            return false;
+        }
+        return true;
+    }
+
+    public List<MirrorRoute> getMirrors()
+    {
+        return mirrors;
+    }
+
+    void setMirrors( final List<MirrorRoute> mirrors )
+    {
+        this.mirrors = mirrors;
+    }
+
+    public boolean containsMirrorOf( final String canonicalUrl )
+    {
+        for ( final MirrorRoute mirror : mirrors )
+        {
+            if ( mirror.isEnabled() && mirror.isMirrorOf( canonicalUrl ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+}

Propchange: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouterException.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouterException.java?rev=1126852&view=auto
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouterException.java (added)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouterException.java Tue May 24 03:32:13 2011
@@ -0,0 +1,38 @@
+package org.apache.maven.artifact.router;
+
+/*
+ * 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.
+ */
+
+public class ArtifactRouterException
+    extends Exception
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public ArtifactRouterException( final String message, final Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    public ArtifactRouterException( final String message )
+    {
+        super( message );
+    }
+
+}

Propchange: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/ArtifactRouterException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupPattern.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupPattern.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupPattern.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupPattern.java Tue May 24 03:32:13 2011
@@ -25,8 +25,6 @@ public final class GroupPattern
     
     private static final String GROUP_SEPARATOR = "[\\/.]";
 
-    public static final GroupPattern GLOBAL_WILDCARD = new GroupPattern( "*" );
-    
     private final String pattern;
     
     private transient final String[] parts;
@@ -38,7 +36,6 @@ public final class GroupPattern
     public GroupPattern( String pattern )
     {
         this.pattern = pattern;
-        
         if ( pattern.endsWith("*") )
         {
             wildcard = true;

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupRoute.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupRoute.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupRoute.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/GroupRoute.java Tue May 24 03:32:13 2011
@@ -28,7 +28,7 @@ import java.util.List;
 public final class GroupRoute
 {
     
-    public static final GroupRoute CENTRAL = new GroupRoute( "http://repo1.maven.org/maven2", GroupPattern.GLOBAL_WILDCARD );
+    public static final GroupRoute CENTRAL = new GroupRoute( "http://repo1.maven.org/maven2", new GroupPattern( "*" ) );
 
     private final String canonicalUrl;
     

Added: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializer.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializer.java?rev=1126852&view=auto
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializer.java (added)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializer.java Tue May 24 03:32:13 2011
@@ -0,0 +1,338 @@
+package org.apache.maven.artifact.router.io;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.artifact.router.ArtifactRouter;
+import org.apache.maven.artifact.router.GroupPattern;
+import org.apache.maven.artifact.router.GroupRoute;
+import org.apache.maven.artifact.router.MirrorRoute;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.InstanceCreator;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.reflect.TypeToken;
+
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ArtifactRouteSerializer
+{
+
+    // NOTE: Gson is supposed to be threadsafe, so all this static stuff should be fine.
+    private static Gson gson;
+
+    public static void serializeMirrors( final Set<MirrorRoute> mirrors, final Writer writer )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            getGson().toJson( mirrors, writer );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot write mirrors to router format.", e );
+        }
+    }
+
+    public static String serializeMirrorsToString( final Set<MirrorRoute> mirrors )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            return getGson().toJson( mirrors );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot write mirrors to router format.", e );
+        }
+    }
+
+    public static Set<MirrorRoute> deserializeMirrors( final Reader reader )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            return getGson().fromJson( reader, RepositoryMirrorSetCreator.getType() );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot read mirrors from router format.", e );
+        }
+    }
+
+    public static Set<MirrorRoute> deserializeMirrors( final String source )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            return getGson().fromJson( source, RepositoryMirrorSetCreator.getType() );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot read mirrors from router format.", e );
+        }
+    }
+
+    public static void serializeGroups( final Set<GroupRoute> groups, final Writer writer )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            getGson().toJson( groups, writer );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot write groups to router format.", e );
+        }
+    }
+
+    public static String serializeGroupsToString( final Set<GroupRoute> groups )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            return getGson().toJson( groups );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot write groups to router format.", e );
+        }
+    }
+
+    public static Set<GroupRoute> deserializeGroups( final Reader reader )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            return getGson().fromJson( reader, GroupRouteSetCreator.getType() );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot read groups from router format.", e );
+        }
+    }
+
+    public static Set<GroupRoute> deserializeGroups( final String source )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            return getGson().fromJson( source, GroupRouteSetCreator.getType() );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot read groups from router format.", e );
+        }
+    }
+
+    public static void serialize( final ArtifactRouter tables, final Writer writer )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            getGson().toJson( tables, writer );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot write routing tables.", e );
+        }
+    }
+
+    public static String serializeToString( final ArtifactRouter tables )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            return getGson().toJson( tables );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot write routing tables.", e );
+        }
+    }
+
+    public static ArtifactRouter deserialize( final Reader reader )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            return getGson().fromJson( reader, ArtifactRouter.class );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot read routing tables.", e );
+        }
+    }
+
+    public static ArtifactRouter deserialize( final String source )
+        throws ArtifactRouterModelException
+    {
+        try
+        {
+            return getGson().fromJson( source, ArtifactRouter.class );
+        }
+        catch ( final JsonParseException e )
+        {
+            throw new ArtifactRouterModelException( "Cannot read routing tables.", e );
+        }
+    }
+
+    private static Gson getGson()
+    {
+        if ( gson == null )
+        {
+            final GsonBuilder builder = new GsonBuilder();
+            builder.disableHtmlEscaping().disableInnerClassSerialization().setPrettyPrinting();
+            builder.registerTypeAdapter( RepositoryMirrorSetCreator.getType(), new RepositoryMirrorSetCreator() );
+            builder.registerTypeAdapter( RepositoryMirrorListCreator.getType(), new RepositoryMirrorListCreator() );
+            builder.registerTypeAdapter( GroupRouteMapCreator.getType(), new GroupRouteMapCreator() );
+            builder.registerTypeAdapter( GroupRouteSetCreator.getType(), new GroupRouteSetCreator() );
+            builder.registerTypeAdapter( GroupPattern.class, new GroupPatternSerializer() );
+
+            gson = builder.create();
+        }
+
+        return gson;
+    }
+
+    public static final class GroupRouteMapCreator
+        implements InstanceCreator<Map<GroupPattern, GroupRoute>>
+    {
+
+        public Map<GroupPattern, GroupRoute> createInstance( Type type )
+        {
+            return new HashMap<GroupPattern, GroupRoute>();
+        }
+
+        public static Type getType()
+        {
+            return new TypeToken<Map<GroupPattern, GroupRoute>>()
+            {
+            }.getType();
+        }
+
+    }
+
+    public static final class GroupRouteSetCreator
+        implements InstanceCreator<Set<GroupRoute>>
+    {
+
+        public Set<GroupRoute> createInstance( Type type )
+        {
+            return new HashSet<GroupRoute>();
+        }
+
+        public static Type getType()
+        {
+            return new TypeToken<Set<GroupRoute>>()
+            {
+            }.getType();
+        }
+
+    }
+
+    public static final class GroupPatternListCreator
+        implements InstanceCreator<List<GroupPattern>>
+    {
+
+        public List<GroupPattern> createInstance( Type type )
+        {
+            return new ArrayList<GroupPattern>();
+        }
+
+        public static Type getType()
+        {
+            return new TypeToken<List<GroupPattern>>()
+            {
+            }.getType();
+        }
+
+    }
+
+    public static final class RepositoryMirrorListCreator
+        implements InstanceCreator<List<MirrorRoute>>
+    {
+
+        public List<MirrorRoute> createInstance( final Type type )
+        {
+            return new ArrayList<MirrorRoute>();
+        }
+
+        public static Type getType()
+        {
+            return new TypeToken<List<MirrorRoute>>()
+            {
+            }.getType();
+        }
+
+    }
+
+    public static final class RepositoryMirrorSetCreator
+        implements InstanceCreator<Set<MirrorRoute>>
+    {
+
+        public Set<MirrorRoute> createInstance( final Type type )
+        {
+            return new LinkedHashSet<MirrorRoute>();
+        }
+
+        public static Type getType()
+        {
+            return new TypeToken<Set<MirrorRoute>>()
+            {
+            }.getType();
+        }
+
+    }
+
+    public static final class GroupPatternSerializer
+        implements JsonSerializer<GroupPattern>, JsonDeserializer<GroupPattern>
+    {
+
+        public GroupPattern deserialize( JsonElement json, Type typeOfT, JsonDeserializationContext context )
+            throws JsonParseException
+        {
+            String pattern = json.getAsString();
+            return new GroupPattern( pattern );
+        }
+
+        public JsonElement serialize( GroupPattern src, Type typeOfSrc, JsonSerializationContext context )
+        {
+            return new JsonPrimitive( src.getPattern() );
+        }
+
+    }
+
+}

Propchange: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouterModelException.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouterModelException.java?rev=1126852&view=auto
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouterModelException.java (added)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouterModelException.java Tue May 24 03:32:13 2011
@@ -0,0 +1,40 @@
+package org.apache.maven.artifact.router.io;
+
+import org.apache.maven.artifact.router.ArtifactRouterException;
+
+/*
+ * 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.
+ */
+
+public class ArtifactRouterModelException
+    extends ArtifactRouterException
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public ArtifactRouterModelException( final String message, final Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    public ArtifactRouterModelException( final String message )
+    {
+        super( message );
+    }
+
+}

Propchange: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/main/java/org/apache/maven/artifact/router/io/ArtifactRouterModelException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/MirrorRoutingTableTest.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/MirrorRoutingTableTest.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/MirrorRoutingTableTest.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/MirrorRoutingTableTest.java Tue May 24 03:32:13 2011
@@ -18,7 +18,7 @@
 package org.apache.maven.artifact.router;
 
 import org.apache.maven.artifact.router.MirrorRoute;
-import org.apache.maven.artifact.router.ArtifactRoutingTables;
+import org.apache.maven.artifact.router.ArtifactRouter;
 
 import junit.framework.TestCase;
 
@@ -30,7 +30,7 @@ public class MirrorRoutingTableTest
     {
         String canonical = "http://repo1.maven.org/maven2";
         
-        ArtifactRoutingTables table = new ArtifactRoutingTables();
+        ArtifactRouter table = new ArtifactRouter();
         MirrorRoute route = new MirrorRoute( "test", "http://nowhere.com/mirror", 10, true, canonical );
         table.addMirror( route );
         
@@ -43,7 +43,7 @@ public class MirrorRoutingTableTest
     {
         String canonical = "http://repo1.maven.org/maven3";
         
-        ArtifactRoutingTables table = new ArtifactRoutingTables();
+        ArtifactRouter table = new ArtifactRouter();
         MirrorRoute route = new MirrorRoute( "test", "http://nowhere.com/mirror", 10, true, "http://repo1.maven.org/maven2" );
         table.addMirror( route );
         

Added: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializerTest.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializerTest.java?rev=1126852&view=auto
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializerTest.java (added)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializerTest.java Tue May 24 03:32:13 2011
@@ -0,0 +1,71 @@
+package org.apache.maven.artifact.router.io;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.artifact.router.MirrorRoute;
+import org.apache.maven.artifact.router.ArtifactRouter;
+import org.apache.maven.artifact.router.io.ArtifactRouteSerializer;
+
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+public class ArtifactRouteSerializerTest
+    extends TestCase
+{
+
+    public void testSerializeOneMirror()
+        throws Exception
+    {
+        final ArtifactRouter mirrorMap =
+            new ArtifactRouter().addMirror( new MirrorRoute( "central",
+                                                             "http://localhost:8081/nexus", 99, true, "http://repo1.maven.org/maven2" ) );
+
+        final StringWriter sw = new StringWriter();
+        ArtifactRouteSerializer.serialize( mirrorMap, sw );
+
+        System.out.println( sw );
+    }
+
+    public void testSerializeToStringOneMirror()
+        throws Exception
+    {
+        final ArtifactRouter mirrorMap =
+            new ArtifactRouter().addMirror( new MirrorRoute( "central",
+                                                             "http://localhost:8081/nexus", 99, true, "http://repo1.maven.org/maven2" ) );
+
+        System.out.println( ArtifactRouteSerializer.serializeToString( mirrorMap ) );
+    }
+
+    public void testRoundTripOneMirror()
+        throws Exception
+    {
+        final ArtifactRouter mirrorMap =
+            new ArtifactRouter().addMirror( new MirrorRoute( "central",
+                                                             "http://localhost:8081/nexus", 99, true, "http://repo1.maven.org/maven2" ) );
+
+        final String ser = ArtifactRouteSerializer.serializeToString( mirrorMap );
+        final ArtifactRouter result = ArtifactRouteSerializer.deserialize( ser );
+
+        assertEquals( mirrorMap, result );
+        assertTrue( result.getMirror( "http://repo1.maven.org/maven2" ).isEnabled() );
+    }
+
+}

Propchange: maven/maven-3/branches/mirror-group-routing/maven-routem-api/src/test/java/org/apache/maven/artifact/router/io/ArtifactRouteSerializerTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfiguration.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfiguration.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfiguration.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfiguration.java Tue May 24 03:32:13 2011
@@ -22,6 +22,8 @@ package org.apache.maven.artifact.router
 import org.apache.http.auth.UsernamePasswordCredentials;
 
 import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
 
 public final class ArtifactRouterConfiguration
 {
@@ -34,7 +36,7 @@ public final class ArtifactRouterConfigu
 
     public static final String NO_DISCOVERY_STRATEGIES = "none";
     
-    private File selectedRoutesFile;
+    private File routesFile;
 
     private String routerMirrorsUrl;
 
@@ -46,6 +48,13 @@ public final class ArtifactRouterConfigu
 
     private String routerGroupsUrl;
 
+    private final Set<ArtifactRouterOption> routerOptions = new HashSet<ArtifactRouterOption>();
+    
+    public ArtifactRouterConfiguration()
+    {
+        setUpdate( true );
+    }
+
     public ArtifactRouterConfiguration setRouterCredentials( final String user, final String password )
     {
         routerCredentials = new UsernamePasswordCredentials( user, password );
@@ -110,14 +119,70 @@ public final class ArtifactRouterConfigu
         return this;
     }
     
-    public File getSelectedRoutesFile()
+    public File getRoutesFile()
+    {
+        return routesFile;
+    }
+    
+    public ArtifactRouterConfiguration setRoutesFile( File routesFile )
+    {
+        this.routesFile = routesFile;
+        return this;
+    }
+    
+    public ArtifactRouterConfiguration setOffline( boolean offline )
+    {
+        return setOption( ArtifactRouterOption.offline, offline );
+    }
+
+    private ArtifactRouterConfiguration setOption( ArtifactRouterOption option, boolean enabled )
+    {
+        if ( enabled )
+        {
+            routerOptions.add( option );
+        }
+        else
+        {
+            routerOptions.remove( option );
+        }
+        
+        return this;
+    }
+
+    public boolean isOffline()
+    {
+        return this.routerOptions.contains( ArtifactRouterOption.offline );
+    }
+    
+    public ArtifactRouterConfiguration setClear( boolean clear )
     {
-        return selectedRoutesFile;
+        return setOption( ArtifactRouterOption.clear, clear );
     }
     
-    public ArtifactRouterConfiguration setSelectedRoutesFile( File selectedRoutesFile )
+    public boolean isClear()
+    {
+        return routerOptions.contains( ArtifactRouterOption.clear );
+    }
+
+    public ArtifactRouterConfiguration setUpdate( boolean update )
+    {
+        return setOption( ArtifactRouterOption.update, update );
+    }
+    
+    public boolean isUpdate()
+    {
+        return routerOptions.contains( ArtifactRouterOption.update );
+    }
+
+    public ArtifactRouterConfiguration setOptions( Set<ArtifactRouterOption> routerOptions )
     {
-        this.selectedRoutesFile = selectedRoutesFile;
+        if ( routerOptions.remove( ArtifactRouterOption.unset ) )
+        {
+            this.routerOptions.clear();
+        }
+        
+        this.routerOptions.addAll( routerOptions );
+        
         return this;
     }
 

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfigurationException.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfigurationException.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfigurationException.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterConfigurationException.java Tue May 24 03:32:13 2011
@@ -1,5 +1,7 @@
 package org.apache.maven.artifact.router.conf;
 
+import org.apache.maven.artifact.router.ArtifactRouterException;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -20,7 +22,7 @@ package org.apache.maven.artifact.router
  */
 
 public class ArtifactRouterConfigurationException
-    extends Exception
+    extends ArtifactRouterException
 {
 
     private static final long serialVersionUID = 1L;

Added: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterOption.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterOption.java?rev=1126852&view=auto
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterOption.java (added)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterOption.java Tue May 24 03:32:13 2011
@@ -0,0 +1,82 @@
+/*
+ *  Copyright (C) 2011 John Casey.
+ *  
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Affero General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Affero General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.apache.maven.artifact.router.conf;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public enum ArtifactRouterOption
+{
+
+    update( "Update routes" ), 
+    offline( "Offline. Use only stored routes." ), 
+    clear( "Clear stored routes." ), 
+    unset( "Unset defaults." );
+
+    private String description;
+
+    private ArtifactRouterOption( String description )
+    {
+        this.description = description;
+    }
+
+    public static String list( String separator )
+    {
+        StringBuilder sb = new StringBuilder();
+        for ( ArtifactRouterOption opt : values() )
+        {
+            sb.append( separator ).append( opt.name().toLowerCase() ).append( " - " ).append( opt.description );
+        }
+
+        return sb.toString();
+    }
+
+    public static Set<ArtifactRouterOption> parse( String... values )
+    {
+        Set<ArtifactRouterOption> options = new HashSet<ArtifactRouterOption>();
+
+        Map<String, ArtifactRouterOption> allOptions = new HashMap<String, ArtifactRouterOption>();
+        for ( ArtifactRouterOption value : values() )
+        {
+            allOptions.put( value.name(), value );
+        }
+
+        all: for ( String value : values )
+        {
+            String[] parts = value.split( "[|,]" );
+            for ( String part : parts )
+            {
+                ArtifactRouterOption opt = allOptions.remove( part.toLowerCase().trim() );
+                if ( opt != null )
+                {
+                    options.add( opt );
+                }
+
+                if ( allOptions.isEmpty() )
+                {
+                    break all;
+                }
+            }
+        }
+
+        return options;
+    }
+
+}

Propchange: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/ArtifactRouterOption.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/FileRouterConfigBuilder.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/FileRouterConfigBuilder.java?rev=1126852&view=auto
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/FileRouterConfigBuilder.java (added)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/FileRouterConfigBuilder.java Tue May 24 03:32:13 2011
@@ -0,0 +1,140 @@
+package org.apache.maven.artifact.router.conf;
+
+/*
+ * 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.
+ */
+
+import static org.codehaus.plexus.util.IOUtil.close;
+import static org.codehaus.plexus.util.StringUtils.isNotBlank;
+
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.codehaus.plexus.logging.Logger;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+@Component( role = RouterConfigBuilder.class )
+public class FileRouterConfigBuilder
+    implements RouterConfigBuilder
+{
+
+    private static final String KEY_ROUTER_MIRRORS_URL = "router-mirrors-url";
+
+    private static final String KEY_ROUTER_GROUPS_URL = "router-groups-url";
+    
+    private static final String KEY_ROUTES_FILE = "routing-tables-file";
+
+    private static final String KEY_ROUTER_USER = "router-user";
+
+    private static final String KEY_ROUTER_PASSWORD = "router-password";
+
+    private static final String KEY_DISABLED = "disabled";
+
+    private static final String KEY_DISCOVERY_STRATEGIES = "discovery-strategies";
+
+    private static final String ROUTES_FILE = "artifact-routes.json";
+    
+    private static final String CONFIG_FILENAME = "router.properties";
+    
+    @Requirement
+    private Logger logger;
+    
+    private File confDir;
+    
+    public FileRouterConfigBuilder( File confDir )
+    {
+        this.confDir = confDir;
+    }
+
+    public ArtifactRouterConfiguration build()
+        throws ArtifactRouterConfigurationException
+    {
+        final ArtifactRouterConfiguration config = new ArtifactRouterConfiguration();
+        File routerConfig = new File( confDir, CONFIG_FILENAME );
+
+        if ( logger.isDebugEnabled() )
+        {
+            logger.debug( "Loading mirror configuration from file: " + routerConfig );
+        }
+
+        if ( routerConfig.canRead() )
+        {
+            InputStream stream = null;
+            try
+            {
+                stream = new FileInputStream( routerConfig );
+                final Properties p = new Properties();
+                p.load( stream );
+
+                config.setRouterMirrorsUrl( p.getProperty( KEY_ROUTER_MIRRORS_URL ) );
+                config.setRouterGroupsUrl( p.getProperty( KEY_ROUTER_GROUPS_URL ) );
+                
+                String path = p.getProperty( KEY_ROUTES_FILE );
+                if ( path != null )
+                {
+                    config.setRoutesFile( new File( path ) );
+                }
+
+                config.setDisabled( Boolean.parseBoolean( p.getProperty( KEY_DISABLED, "false" ) ) );
+
+                final String user = p.getProperty( KEY_ROUTER_USER );
+                final String pass = p.getProperty( KEY_ROUTER_PASSWORD );
+
+                final String[] strat =
+                    p.getProperty( KEY_DISCOVERY_STRATEGIES, ArtifactRouterConfiguration.ALL_DISCOVERY_STRATEGIES )
+                     .split( "\\s*,\\s*" );
+
+                config.setDiscoveryStrategies( strat );
+
+                if ( isNotBlank( user ) && isNotBlank( pass ) )
+                {
+                    config.setRouterCredentials( user, pass );
+                }
+            }
+            catch ( final IOException e )
+            {
+                throw new ArtifactRouterConfigurationException( "Failed to read router config properties from: '"
+                                + routerConfig + "'.", e );
+            }
+            finally
+            {
+                close( stream );
+            }
+        }
+        else
+        {
+            if ( logger.isDebugEnabled() )
+            {
+                logger.debug( "Cannot read router configuration from: " + routerConfig
+                                + ". Using defaults." );
+            }
+        }
+        
+        if ( config.getRoutesFile() == null )
+        {
+            config.setRoutesFile( new File( confDir, ROUTES_FILE ) );
+        }
+
+        return config;
+    }
+
+}

Propchange: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/FileRouterConfigBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/RouterConfigBuilder.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/RouterConfigBuilder.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/RouterConfigBuilder.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/conf/RouterConfigBuilder.java Tue May 24 03:32:13 2011
@@ -22,7 +22,7 @@ package org.apache.maven.artifact.router
 public interface RouterConfigBuilder
 {
 
-    ArtifactRouterConfiguration build( final RouterConfigSource source )
+    ArtifactRouterConfiguration build()
         throws ArtifactRouterConfigurationException;
 
 }
\ No newline at end of file

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/ArtifactRouterDiscoveryStrategy.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/ArtifactRouterDiscoveryStrategy.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/ArtifactRouterDiscoveryStrategy.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/ArtifactRouterDiscoveryStrategy.java Tue May 24 03:32:13 2011
@@ -24,7 +24,7 @@ import org.apache.maven.artifact.router.
 public interface ArtifactRouterDiscoveryStrategy
 {
 
-    String findRouter()
+    DiscoveryResult findRouter()
         throws ArtifactRouterException;
 
 }

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DNSDiscoveryStrategy.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DNSDiscoveryStrategy.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DNSDiscoveryStrategy.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DNSDiscoveryStrategy.java Tue May 24 03:32:13 2011
@@ -38,7 +38,7 @@ final class DNSDiscoveryStrategy
     implements ArtifactRouterDiscoveryStrategy
 {
 
-    public String findRouter()
+    public DiscoveryResult findRouter()
         throws ArtifactRouterException
     {
         final Map<String, String> env = new HashMap<String, String>();
@@ -96,7 +96,7 @@ final class DNSDiscoveryStrategy
 
                 if ( txtRecord != null )
                 {
-                    return txtRecord;
+                    return parse( txtRecord );
                 }
             }
         }
@@ -104,4 +104,9 @@ final class DNSDiscoveryStrategy
         return null;
     }
 
+    private DiscoveryResult parse( String record )
+    {
+        return null;
+    }
+
 }

Added: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DiscoveryResult.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DiscoveryResult.java?rev=1126852&view=auto
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DiscoveryResult.java (added)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DiscoveryResult.java Tue May 24 03:32:13 2011
@@ -0,0 +1,51 @@
+package org.apache.maven.artifact.router.discovery;
+
+/*
+ * 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.
+ */
+
+public final class DiscoveryResult
+{
+    
+    private String mirrorsUrl;
+    
+    private String groupsUrl;
+
+    public String getMirrorsUrl()
+    {
+        return mirrorsUrl;
+    }
+
+    public DiscoveryResult setMirrorsUrl( String mirrorsUrl )
+    {
+        this.mirrorsUrl = mirrorsUrl;
+        return this;
+    }
+
+    public String getGroupsUrl()
+    {
+        return groupsUrl;
+    }
+
+    public DiscoveryResult setGroupsUrl( String groupsUrl )
+    {
+        this.groupsUrl = groupsUrl;
+        return this;
+    }
+    
+}

Propchange: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/discovery/DiscoveryResult.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/ArtifactRouterLoader.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/ArtifactRouterLoader.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/ArtifactRouterLoader.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/ArtifactRouterLoader.java Tue May 24 03:32:13 2011
@@ -29,7 +29,7 @@ public interface ArtifactRouterLoader
     ArtifactRouter load( ArtifactRouterConfiguration config )
         throws ArtifactRouterException;
     
-    void saveSelectedMirrors( ArtifactRouter router, ArtifactRouterConfiguration config )
+    void save( ArtifactRouter router, ArtifactRouterConfiguration config )
         throws ArtifactRouterException;
 
 }

Modified: maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/DefaultRouterLoader.java
URL: http://svn.apache.org/viewvc/maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/DefaultRouterLoader.java?rev=1126852&r1=1126851&r2=1126852&view=diff
==============================================================================
--- maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/DefaultRouterLoader.java (original)
+++ maven/maven-3/branches/mirror-group-routing/maven-routem-impl/src/main/java/org/apache/maven/artifact/router/loader/DefaultRouterLoader.java Tue May 24 03:32:13 2011
@@ -34,21 +34,20 @@ import org.apache.http.client.HttpClient
 import org.apache.http.client.ResponseHandler;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.maven.artifact.router.MirrorRoute;
-import org.apache.maven.artifact.router.ArtifactRouteSerializer;
-import org.apache.maven.artifact.router.ArtifactRouterModelException;
-import org.apache.maven.artifact.router.ArtifactRoutingTables;
 import org.apache.maven.artifact.router.ArtifactRouter;
 import org.apache.maven.artifact.router.ArtifactRouterException;
+import org.apache.maven.artifact.router.GroupRoute;
+import org.apache.maven.artifact.router.MirrorRoute;
 import org.apache.maven.artifact.router.conf.ArtifactRouterConfiguration;
 import org.apache.maven.artifact.router.discovery.ArtifactRouterDiscoveryStrategy;
+import org.apache.maven.artifact.router.discovery.DiscoveryResult;
+import org.apache.maven.artifact.router.io.ArtifactRouteSerializer;
+import org.apache.maven.artifact.router.io.ArtifactRouterModelException;
 import org.codehaus.plexus.component.annotations.Component;
 import org.codehaus.plexus.component.annotations.Requirement;
 import org.codehaus.plexus.logging.Logger;
 import org.codehaus.plexus.util.IOUtil;
 
-import javax.inject.Named;
-
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileReader;
@@ -56,15 +55,11 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-//@Component( role = ArtifactRouterLoader.class )
-@Named( "default" )
+@Component( role = ArtifactRouterLoader.class )
 public class DefaultRouterLoader
     implements ArtifactRouterLoader
 {
@@ -78,18 +73,15 @@ public class DefaultRouterLoader
     public ArtifactRouter load( final ArtifactRouterConfiguration config )
         throws ArtifactRouterException
     {
-        if ( config == null )
+        if ( config == null || config.isDisabled() )
         {
             return new ArtifactRouter();
         }
         
-        ArtifactRoutingTables routingTable = loadRoutingTable( config );
-        Map<String, MirrorRoute> selectedRoutes = loadSelectedRoutes( config );
-
-        return new ArtifactRouter( routingTable, selectedRoutes );
+        return loadRoutes( config );
     }
 
-    public void saveSelectedMirrors( final ArtifactRouter router, final ArtifactRouterConfiguration config )
+    public void save( final ArtifactRouter router, final ArtifactRouterConfiguration config )
         throws ArtifactRouterException
     {
         if ( config == null )
@@ -97,29 +89,28 @@ public class DefaultRouterLoader
             return;
         }
         
-        Map<String, MirrorRoute> selectedRoutes = router.getSelectedRoutes();
-        File selectedRoutesFile = config.getSelectedRoutesFile();
-        if ( selectedRoutesFile != null )
+        File routingTablesFile = config.getRoutesFile();
+        if ( routingTablesFile != null )
         {
             FileWriter writer = null;
             try
             {
-                File dir = selectedRoutesFile.getParentFile();
+                File dir = routingTablesFile.getParentFile();
                 if ( dir != null && !dir.exists() )
                 {
                     dir.mkdirs();
                 }
                 
-                writer = new FileWriter( selectedRoutesFile );
-                ArtifactRouteSerializer.serializeLoose( new LinkedHashSet<MirrorRoute>( selectedRoutes.values() ), writer );
+                writer = new FileWriter( routingTablesFile );
+                ArtifactRouteSerializer.serialize( router, writer );
             }
             catch ( IOException e )
             {
-                throw new ArtifactRouterException( "Cannot write selected mirrors to: " + selectedRoutesFile, e );
+                throw new ArtifactRouterException( "Cannot write artifact router to: " + routingTablesFile, e );
             }
             catch ( ArtifactRouterModelException e )
             {
-                throw new ArtifactRouterException( "Cannot write selected mirrors to: " + selectedRoutesFile, e );
+                throw new ArtifactRouterException( "Cannot write artifact router to: " + routingTablesFile, e );
             }
             finally
             {
@@ -128,36 +119,25 @@ public class DefaultRouterLoader
         }
     }
 
-    protected Map<String, MirrorRoute> loadSelectedRoutes( ArtifactRouterConfiguration config )
+    protected ArtifactRouter loadSaved( ArtifactRouterConfiguration config )
         throws ArtifactRouterException
     {
-        File selectedRoutesFile = config.getSelectedRoutesFile();
-        if ( selectedRoutesFile != null && selectedRoutesFile.exists() && selectedRoutesFile.canRead() )
+        File routesFile = config.getRoutesFile();
+        if ( routesFile != null && routesFile.exists() && routesFile.canRead() )
         {
             FileReader reader = null;
             try
             {
-                reader = new FileReader( selectedRoutesFile );
-                Set<MirrorRoute> routes = ArtifactRouteSerializer.deserializeLoose( reader );
-                
-                Map<String, MirrorRoute> result = new LinkedHashMap<String, MirrorRoute>();
-                for ( MirrorRoute route : routes )
-                {
-                    for ( String mirrorOf : route.getMirrorOfUrls() )
-                    {
-                        result.put( mirrorOf, route );
-                    }
-                }
-                
-                return result;
+                reader = new FileReader( routesFile );
+                return ArtifactRouteSerializer.deserialize( reader );
             }
             catch ( IOException e )
             {
-                throw new ArtifactRouterException( "Cannot read selected mirrors from: " + selectedRoutesFile, e );
+                throw new ArtifactRouterException( "Cannot read saved route information from: " + routesFile, e );
             }
             catch ( ArtifactRouterModelException e )
             {
-                throw new ArtifactRouterException( "Cannot read selected mirrors from: " + selectedRoutesFile, e );
+                throw new ArtifactRouterException( "Cannot read saved route information from: " + routesFile, e );
             }
             finally
             {
@@ -165,16 +145,27 @@ public class DefaultRouterLoader
             }
         }
         
-        return Collections.emptyMap();
+        return new ArtifactRouter();
     }
 
-    protected ArtifactRoutingTables loadRoutingTable( final ArtifactRouterConfiguration config )
+    protected ArtifactRouter loadRoutes( final ArtifactRouterConfiguration config )
         throws ArtifactRouterException
     {
-        ArtifactRoutingTables routingTable = null;
+        ArtifactRouter routes = null;
+        
+        if ( config.isClear() )
+        {
+            routes = new ArtifactRouter();
+            save( routes, config );
+        }
+        else
+        {
+            routes = loadSaved( config );
+        }
 
-        if ( !config.isDisabled() )
+        if ( config.isUpdate() && !config.isOffline() )
         {
+            // FIXME: What about proxies?! Should be using settings.xml proxy defs.
             final DefaultHttpClient client = new DefaultHttpClient();
             if ( config.getRouterCredentials() != null )
             {
@@ -200,11 +191,18 @@ public class DefaultRouterLoader
             {
                 if ( config.getRouterMirrorsUrl() != null )
                 {
-                    routingTable = getMirrorMapping( config.getRouterMirrorsUrl(), config, client );
+                    routes.addMirrors( getMirrorMapping( config.getRouterMirrorsUrl(), config, client ) );
+                }
+                
+                if ( config.getRouterGroupsUrl() != null )
+                {
+                    routes.addGroups( getGroupMapping( config.getRouterGroupsUrl(), config, client ) );
                 }
-                else
+                
+                final String[] discoStrategies = config.getDiscoveryStrategies();
+                if ( discoStrategies != null && discoStrategies.length > 0
+                    && !"none".equalsIgnoreCase( discoStrategies[0].toLowerCase() ) )
                 {
-                    final String[] discoStrategies = config.getDiscoveryStrategies();
                     final List<ArtifactRouterDiscoveryStrategy> strats = new ArrayList<ArtifactRouterDiscoveryStrategy>();
                     if ( discoStrategies.length == 1 )
                     {
@@ -238,42 +236,52 @@ public class DefaultRouterLoader
                         }
                     }
 
-                    String routerUrl = null;
+                    DiscoveryResult discoveryResult = null;
                     for ( final ArtifactRouterDiscoveryStrategy strategy : strats )
                     {
-                        routerUrl = strategy.findRouter();
-                        if ( routerUrl != null && routerUrl.trim().length() > 0 )
+                        discoveryResult = strategy.findRouter();
+                        if ( discoveryResult != null )
                         {
-                            routingTable = getMirrorMapping( routerUrl, config, client );
-                            if ( routingTable != null )
+                            if ( discoveryResult.getMirrorsUrl() != null )
+                            {
+                                routes.addMirrors( getMirrorMapping( discoveryResult.getMirrorsUrl(), config, client ) );
+                            }
+                            
+                            if ( discoveryResult.getGroupsUrl() != null )
                             {
-                                break;
+                                routes.addGroups( getGroupMapping( discoveryResult.getGroupsUrl(), config, client ) );
                             }
                         }
                     }
                 }
 
-                final String centralRouterUrl = config.getCanonicalMirrorsUrl();
-                if ( routingTable == null && centralRouterUrl != null && centralRouterUrl.trim().length() > 0 )
+                final String centralMirrorsUrl = config.getCanonicalMirrorsUrl();
+                if ( centralMirrorsUrl != null && centralMirrorsUrl.trim().length() > 0 )
                 {
-                    routingTable = getMirrorMapping( centralRouterUrl, config, client );
+                    routes.addMirrors( getMirrorMapping( centralMirrorsUrl, config, client ) );
+                }
+                
+                final String centralGroupsUrl = config.getCanonicalGroupsUrl();
+                if ( centralGroupsUrl != null && centralGroupsUrl.trim().length() > 0 )
+                {
+                    routes.addGroups( getGroupMapping( centralGroupsUrl, config, client ) );
                 }
             }
             catch ( final ArtifactRouterException e )
             {
                 if ( logger.isDebugEnabled() )
                 {
-                    logger.error( "Failed to auto-detect mirrors: " + e.getMessage(), e );
+                    logger.error( "Failed to auto-detect routing information: " + e.getMessage(), e );
                 }
             }
         }
 
-        if ( routingTable == null )
+        if ( routes == null )
         {
-            routingTable = new ArtifactRoutingTables();
+            routes = new ArtifactRouter();
         }
 
-        return routingTable;
+        return routes;
     }
 
     private ArtifactRouterDiscoveryStrategy getDiscoveryStrategy( final String key )
@@ -292,14 +300,9 @@ public class DefaultRouterLoader
         return strat;
     }
 
-    private ArtifactRoutingTables getMirrorMapping( final String routerUrl, final ArtifactRouterConfiguration config,
+    private Set<MirrorRoute> getMirrorMapping( final String routerUrl, final ArtifactRouterConfiguration config,
                                                  final HttpClient client )
     {
-        if ( config.isDisabled() )
-        {
-            return new ArtifactRoutingTables();
-        }
-
         if ( routerUrl != null && routerUrl.trim().length() > 0 )
         {
             if ( logger.isDebugEnabled() )
@@ -313,9 +316,9 @@ public class DefaultRouterLoader
 
             try
             {
-                return client.execute( get, new ResponseHandler<ArtifactRoutingTables>()
+                return client.execute( get, new ResponseHandler<Set<MirrorRoute>>()
                 {
-                    public ArtifactRoutingTables handleResponse( final HttpResponse response )
+                    public Set<MirrorRoute> handleResponse( final HttpResponse response )
                         throws /* ClientProtocolException, */IOException
                     {
                         final int statusCode = response.getStatusLine().getStatusCode();
@@ -359,7 +362,7 @@ public class DefaultRouterLoader
                                         + content );
                                 }
 
-                                return ArtifactRouteSerializer.deserialize( content );
+                                return ArtifactRouteSerializer.deserializeMirrors( content );
                             }
                             catch ( final ArtifactRouterModelException e )
                             {
@@ -401,4 +404,108 @@ public class DefaultRouterLoader
         return null;
     }
 
+    private Set<GroupRoute> getGroupMapping( final String routerUrl, final ArtifactRouterConfiguration config,
+                                               final HttpClient client )
+  {
+      if ( routerUrl != null && routerUrl.trim().length() > 0 )
+      {
+          if ( logger.isDebugEnabled() )
+          {
+              logger.debug( "Grabbing mirror mappings from: " + routerUrl.toString() );
+          }
+          System.out.println( "Grabbing mirror mappings from: " + routerUrl );
+
+          final HttpGet get = new HttpGet( routerUrl );
+          get.addHeader( "Accept", "application/json;q=0.9,*/*;q=0.8" );
+
+          try
+          {
+              return client.execute( get, new ResponseHandler<Set<GroupRoute>>()
+              {
+                  public Set<GroupRoute> handleResponse( final HttpResponse response )
+                      throws /* ClientProtocolException, */IOException
+                  {
+                      final int statusCode = response.getStatusLine().getStatusCode();
+                      if ( statusCode == 200 )
+                      {
+                          InputStream stream = null;
+                          try
+                          {
+                              stream = response.getEntity().getContent();
+                              final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                              IOUtil.copy( stream, baos );
+
+                              String content = null;
+                              final Header contentType = response.getFirstHeader( "Content-Type" );
+                              if ( contentType != null )
+                              {
+                                  final HeaderElement[] contentTypeElts = contentType.getElements();
+
+                                  if ( contentTypeElts != null )
+                                  {
+                                      for ( final HeaderElement elt : contentTypeElts )
+                                      {
+                                          final NameValuePair nv = elt.getParameterByName( "charset" );
+                                          if ( nv != null )
+                                          {
+                                              content = new String( baos.toByteArray(), nv.getValue() );
+                                          }
+                                      }
+                                  }
+                              }
+
+                              if ( content == null )
+                              {
+                                  content = new String( baos.toByteArray() );
+                              }
+
+                              if ( logger.isDebugEnabled() )
+                              {
+                                  logger.debug( "Response code/message: '" + response.getStatusLine().getStatusCode()
+                                      + " " + response.getStatusLine().getReasonPhrase() + "'\nContent is:\n\n"
+                                      + content );
+                              }
+
+                              return ArtifactRouteSerializer.deserializeGroups( content );
+                          }
+                          catch ( final ArtifactRouterModelException e )
+                          {
+                              logger.error( "Failed to retrieve mirror mapping from: " + routerUrl, e );
+                          }
+                          finally
+                          {
+                              close( stream );
+                          }
+                      }
+                      else if ( logger.isDebugEnabled() )
+                      {
+                          logger.debug( "Response: " + response.getStatusLine().getStatusCode() + " "
+                              + response.getStatusLine().getReasonPhrase() );
+                      }
+
+                      return null;
+                  }
+              } );
+          }
+          catch ( final ClientProtocolException e )
+          {
+              if ( logger.isDebugEnabled() )
+              {
+                  logger.debug( "Failed to read proxied repositories from: '" + routerUrl + "'. Reason: "
+                                    + e.getMessage(), e );
+              }
+          }
+          catch ( final IOException e )
+          {
+              if ( logger.isDebugEnabled() )
+              {
+                  logger.debug( "Failed to read proxied repositories from: '" + routerUrl + "'. Reason: "
+                                    + e.getMessage(), e );
+              }
+          }
+      }
+
+      return null;
+  }
+
 }