]> source.dussan.org Git - archiva.git/commitdiff
[MRM-1510] api to configure NetworkProxy
authorOlivier Lamy <olamy@apache.org>
Thu, 8 Sep 2011 08:05:01 +0000 (08:05 +0000)
committerOlivier Lamy <olamy@apache.org>
Thu, 8 Sep 2011 08:05:01 +0000 (08:05 +0000)
git-svn-id: https://svn.apache.org/repos/asf/archiva/trunk@1166556 13f79535-47bb-0310-9956-ffa450edef68

archiva-modules/archiva-base/archiva-configuration/src/main/mdo/configuration.mdo
archiva-modules/archiva-base/archiva-repository-admin/src/main/java/org/apache/archiva/admin/repository/networkproxy/DefaultNetworkProxyAdmin.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-admin/src/main/java/org/apache/archiva/admin/repository/networkproxy/NetworkProxy.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-admin/src/main/java/org/apache/archiva/admin/repository/networkproxy/NetworkProxyAdmin.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-admin/src/test/java/org/apache/archiva/admin/repository/networkproxy/NetworkProxyAdminTest.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-admin/src/test/java/org/apache/archiva/admin/repository/proxyconnector/ProxyConnectorAdminTest.java
archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditEvent.java
pom.xml

index 96e545328a0ada353d03dbf4f33983b114a5776e..4b3a74b888cf78eafa4e76db016240607bf558bb 100644 (file)
           <type>String</type>
         </field>
       </fields>
+      <codeSegments>
+        <codeSegment>
+          <version>1.0.0+</version>
+          <code><![CDATA[
+            public int hashCode()
+            {
+                int result = 17;
+                result = 37 * result + ( id != null ? id.hashCode() : 0 );
+                return result;
+            }
+
+            public boolean equals( Object other )
+            {
+                if ( this == other )
+                {
+                    return true;
+                }
+
+                if ( !( other instanceof NetworkProxyConfiguration ) )
+                {
+                    return false;
+                }
+
+                NetworkProxyConfiguration that = (NetworkProxyConfiguration) other;
+                boolean result = true;
+                result = result && ( getId() == null ? that.getId() == null : getId().equals( that.getId() ) );
+                return result;
+            }
+          ]]></code>
+        </codeSegment>
+      </codeSegments>
+
+
+
     </class>
 
     <!--
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/src/main/java/org/apache/archiva/admin/repository/networkproxy/DefaultNetworkProxyAdmin.java b/archiva-modules/archiva-base/archiva-repository-admin/src/main/java/org/apache/archiva/admin/repository/networkproxy/DefaultNetworkProxyAdmin.java
new file mode 100644 (file)
index 0000000..b9f87fb
--- /dev/null
@@ -0,0 +1,142 @@
+package org.apache.archiva.admin.repository.networkproxy;
+/*
+ * 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 net.sf.beanlib.provider.replicator.BeanReplicator;
+import org.apache.archiva.admin.AuditInformation;
+import org.apache.archiva.admin.repository.AbstractRepositoryAdmin;
+import org.apache.archiva.admin.repository.RepositoryAdminException;
+import org.apache.archiva.audit.AuditEvent;
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.archiva.configuration.Configuration;
+import org.apache.maven.archiva.configuration.NetworkProxyConfiguration;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Olivier Lamy
+ */
+@Service( "networkProxyAdmin#default" )
+public class DefaultNetworkProxyAdmin
+    extends AbstractRepositoryAdmin
+    implements NetworkProxyAdmin
+{
+
+    public List<NetworkProxy> getNetworkProxies()
+        throws RepositoryAdminException
+    {
+        List<NetworkProxy> networkProxies = new ArrayList<NetworkProxy>();
+        for ( NetworkProxyConfiguration networkProxyConfiguration : getArchivaConfiguration().getConfiguration().getNetworkProxies() )
+        {
+            networkProxies.add( getNetworkProxy( networkProxyConfiguration ) );
+        }
+        return networkProxies;
+    }
+
+    public NetworkProxy getNetworkProxy( String networkProxyId )
+        throws RepositoryAdminException
+    {
+        for ( NetworkProxy networkProxy : getNetworkProxies() )
+        {
+            if ( StringUtils.equals( networkProxyId, networkProxy.getId() ) )
+            {
+                return networkProxy;
+            }
+        }
+
+        return null;
+    }
+
+    public void addNetworkProxy( NetworkProxy networkProxy, AuditInformation auditInformation )
+        throws RepositoryAdminException
+    {
+        if ( networkProxy == null )
+        {
+            return;
+        }
+        if ( getNetworkProxy( networkProxy.getId() ) != null )
+        {
+            throw new RepositoryAdminException(
+                "cannot add NetworkProxy with id " + networkProxy.getId() + " already exist" );
+        }
+        Configuration configuration = getArchivaConfiguration().getConfiguration();
+        configuration.addNetworkProxy( getNetworkProxyConfiguration( networkProxy ) );
+
+        triggerAuditEvent( networkProxy.getId(), null, AuditEvent.ADD_NETWORK_PROXY, auditInformation );
+
+        saveConfiguration( configuration );
+    }
+
+    public void updateNetworkProxy( NetworkProxy networkProxy, AuditInformation auditInformation )
+        throws RepositoryAdminException
+    {
+        if ( networkProxy == null )
+        {
+            return;
+        }
+        if ( getNetworkProxy( networkProxy.getId() ) == null )
+        {
+            throw new RepositoryAdminException(
+                "cannot update NetworkProxy with id " + networkProxy.getId() + " as not exist" );
+        }
+        Configuration configuration = getArchivaConfiguration().getConfiguration();
+        NetworkProxyConfiguration networkProxyConfiguration = getNetworkProxyConfiguration( networkProxy );
+        configuration.removeNetworkProxy( networkProxyConfiguration );
+        configuration.addNetworkProxy( networkProxyConfiguration );
+
+        triggerAuditEvent( networkProxy.getId(), null, AuditEvent.MODIFY_NETWORK_PROXY, auditInformation );
+
+        saveConfiguration( configuration );
+    }
+
+    public void deleteNetworkProxy( String networkProxyId, AuditInformation auditInformation )
+        throws RepositoryAdminException
+    {
+
+        NetworkProxy networkProxy = getNetworkProxy( networkProxyId );
+        if ( networkProxy == null )
+        {
+            throw new RepositoryAdminException(
+                "cannot delete NetworkProxy with id " + networkProxyId + " as not exist" );
+        }
+        Configuration configuration = getArchivaConfiguration().getConfiguration();
+        NetworkProxyConfiguration networkProxyConfiguration = getNetworkProxyConfiguration( networkProxy );
+        configuration.removeNetworkProxy( networkProxyConfiguration );
+
+        triggerAuditEvent( networkProxy.getId(), null, AuditEvent.DELETE_NETWORK_PROXY, auditInformation );
+
+        saveConfiguration( configuration );
+    }
+
+    protected NetworkProxy getNetworkProxy( NetworkProxyConfiguration networkProxyConfiguration )
+    {
+        return networkProxyConfiguration == null
+            ? null
+            : new BeanReplicator().replicateBean( networkProxyConfiguration, NetworkProxy.class );
+    }
+
+    protected NetworkProxyConfiguration getNetworkProxyConfiguration( NetworkProxy networkProxy )
+    {
+        return networkProxy == null
+            ? null
+            : new BeanReplicator().replicateBean( networkProxy, NetworkProxyConfiguration.class );
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/src/main/java/org/apache/archiva/admin/repository/networkproxy/NetworkProxy.java b/archiva-modules/archiva-base/archiva-repository-admin/src/main/java/org/apache/archiva/admin/repository/networkproxy/NetworkProxy.java
new file mode 100644 (file)
index 0000000..ec5250b
--- /dev/null
@@ -0,0 +1,177 @@
+package org.apache.archiva.admin.repository.networkproxy;
+/*
+ * 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.io.Serializable;
+
+/**
+ * @author Olivier Lamy
+ * @since 1.4
+ */
+public class NetworkProxy
+    implements Serializable
+{
+    private String id;
+
+    /**
+     * The network protocol to use with this proxy: "http", "socks-4"
+     * .
+     */
+    private String protocol = "http";
+
+    /**
+     * The proxy host.
+     */
+    private String host;
+
+    /**
+     * The proxy port.
+     */
+    private int port = 8080;
+
+    /**
+     * The proxy user.
+     */
+    private String username;
+
+    /**
+     * The proxy password.
+     */
+    private String password;
+
+    public NetworkProxy()
+    {
+        // no op
+    }
+
+    public NetworkProxy( String id, String protocol, String host, int port, String username, String password )
+    {
+        this.id = id;
+        this.protocol = protocol;
+        this.host = host;
+        this.port = port;
+        this.username = username;
+        this.password = password;
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+
+    public String getProtocol()
+    {
+        return protocol;
+    }
+
+    public void setProtocol( String protocol )
+    {
+        this.protocol = protocol;
+    }
+
+    public String getHost()
+    {
+        return host;
+    }
+
+    public void setHost( String host )
+    {
+        this.host = host;
+    }
+
+    public int getPort()
+    {
+        return port;
+    }
+
+    public void setPort( int port )
+    {
+        this.port = port;
+    }
+
+    public String getUsername()
+    {
+        return username;
+    }
+
+    public void setUsername( String username )
+    {
+        this.username = username;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setPassword( String password )
+    {
+        this.password = password;
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        NetworkProxy that = (NetworkProxy) o;
+
+        if ( id != null ? !id.equals( that.id ) : that.id != null )
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int result = 17;
+        result = 37 * result + ( id != null ? id.hashCode() : 0 );
+        return result;
+    }
+
+    @Override
+    public String toString()
+    {
+        final StringBuilder sb = new StringBuilder();
+        sb.append( "NetworkProxy" );
+        sb.append( "{id='" ).append( id ).append( '\'' );
+        sb.append( ", protocol='" ).append( protocol ).append( '\'' );
+        sb.append( ", host='" ).append( host ).append( '\'' );
+        sb.append( ", port=" ).append( port );
+        sb.append( ", username='" ).append( username ).append( '\'' );
+        sb.append( ", password='" ).append( password ).append( '\'' );
+        sb.append( '}' );
+        return sb.toString();
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/src/main/java/org/apache/archiva/admin/repository/networkproxy/NetworkProxyAdmin.java b/archiva-modules/archiva-base/archiva-repository-admin/src/main/java/org/apache/archiva/admin/repository/networkproxy/NetworkProxyAdmin.java
new file mode 100644 (file)
index 0000000..e8c0a01
--- /dev/null
@@ -0,0 +1,46 @@
+package org.apache.archiva.admin.repository.networkproxy;
+/*
+ * 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.archiva.admin.AuditInformation;
+import org.apache.archiva.admin.repository.RepositoryAdminException;
+
+import java.util.List;
+
+/**
+ * @author Olivier Lamy
+ * @since 1.4
+ */
+public interface NetworkProxyAdmin
+{
+    List<NetworkProxy> getNetworkProxies()
+        throws RepositoryAdminException;
+
+    NetworkProxy getNetworkProxy( String networkProxyId )
+        throws RepositoryAdminException;
+
+    void addNetworkProxy( NetworkProxy networkProxy, AuditInformation auditInformation )
+        throws RepositoryAdminException;
+
+    void updateNetworkProxy( NetworkProxy networkProxy, AuditInformation auditInformation )
+        throws RepositoryAdminException;
+
+    void deleteNetworkProxy( String networkProxyId, AuditInformation auditInformation )
+        throws RepositoryAdminException;
+}
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/src/test/java/org/apache/archiva/admin/repository/networkproxy/NetworkProxyAdminTest.java b/archiva-modules/archiva-base/archiva-repository-admin/src/test/java/org/apache/archiva/admin/repository/networkproxy/NetworkProxyAdminTest.java
new file mode 100644 (file)
index 0000000..840ba19
--- /dev/null
@@ -0,0 +1,142 @@
+package org.apache.archiva.admin.repository.networkproxy;
+/*
+ * 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.archiva.admin.repository.AbstractRepositoryAdminTest;
+import org.apache.archiva.audit.AuditEvent;
+import org.junit.Test;
+
+import javax.inject.Inject;
+
+/**
+ * @author Olivier Lamy
+ */
+public class NetworkProxyAdminTest
+    extends AbstractRepositoryAdminTest
+{
+
+    @Inject
+    private NetworkProxyAdmin networkProxyAdmin;
+
+    @Test
+    public void getAllEmpty()
+        throws Exception
+    {
+        assertNotNull( networkProxyAdmin.getNetworkProxies() );
+    }
+
+    @Test
+    public void addAndDelete()
+        throws Exception
+    {
+        mockAuditListener.clearEvents();
+        int initialSize = networkProxyAdmin.getNetworkProxies().size();
+        NetworkProxy networkProxy = getNetworkProxyTest( "foo" );
+
+        networkProxyAdmin.addNetworkProxy( networkProxy, getFakeAuditInformation() );
+
+        assertEquals( initialSize + 1, networkProxyAdmin.getNetworkProxies().size() );
+
+        networkProxy = networkProxyAdmin.getNetworkProxy( "foo" );
+
+        assertNotNull( networkProxy );
+        assertEquals( getNetworkProxyTest( "foo" ).getId(), networkProxy.getId() );
+        assertEquals( getNetworkProxyTest( "foo" ).getHost(), networkProxy.getHost() );
+        assertEquals( getNetworkProxyTest( "foo" ).getPassword(), networkProxy.getPassword() );
+        assertEquals( getNetworkProxyTest( "foo" ).getPort(), networkProxy.getPort() );
+        assertEquals( getNetworkProxyTest( "foo" ).getUsername(), networkProxy.getUsername() );
+        assertEquals( getNetworkProxyTest( "foo" ).getProtocol(), networkProxy.getProtocol() );
+
+        networkProxyAdmin.deleteNetworkProxy( "foo", getFakeAuditInformation() );
+
+        assertNull( networkProxyAdmin.getNetworkProxy( "foo" ) );
+
+        assertEquals( 2, mockAuditListener.getAuditEvents().size() );
+
+        assertEquals( AuditEvent.ADD_NETWORK_PROXY, mockAuditListener.getAuditEvents().get( 0 ).getAction() );
+        assertEquals( AuditEvent.DELETE_NETWORK_PROXY, mockAuditListener.getAuditEvents().get( 1 ).getAction() );
+
+        mockAuditListener.clearEvents();
+    }
+
+    @Test
+    public void addAndUpdateAndDelete()
+        throws Exception
+    {
+        mockAuditListener.clearEvents();
+        int initialSize = networkProxyAdmin.getNetworkProxies().size();
+        NetworkProxy networkProxy = getNetworkProxyTest( "foo" );
+
+        networkProxyAdmin.addNetworkProxy( networkProxy, getFakeAuditInformation() );
+
+        assertEquals( initialSize + 1, networkProxyAdmin.getNetworkProxies().size() );
+
+        networkProxy = networkProxyAdmin.getNetworkProxy( "foo" );
+
+        assertNotNull( networkProxy );
+        assertEquals( getNetworkProxyTest( "foo" ).getId(), networkProxy.getId() );
+        assertEquals( getNetworkProxyTest( "foo" ).getHost(), networkProxy.getHost() );
+        assertEquals( getNetworkProxyTest( "foo" ).getPassword(), networkProxy.getPassword() );
+        assertEquals( getNetworkProxyTest( "foo" ).getPort(), networkProxy.getPort() );
+        assertEquals( getNetworkProxyTest( "foo" ).getUsername(), networkProxy.getUsername() );
+        assertEquals( getNetworkProxyTest( "foo" ).getProtocol(), networkProxy.getProtocol() );
+
+        networkProxy.setHost( "https://toto.com" );
+        networkProxy.setPassword( "newpasswd" );
+        networkProxy.setPort( 9191 );
+        networkProxy.setProtocol( "http" );
+        networkProxy.setUsername( "newusername" );
+
+        networkProxyAdmin.updateNetworkProxy( networkProxy, getFakeAuditInformation() );
+
+        NetworkProxy updatedNetworkProxy = networkProxyAdmin.getNetworkProxy( "foo" );
+
+        assertNotNull( updatedNetworkProxy );
+        assertEquals( networkProxy.getId(), updatedNetworkProxy.getId() );
+        assertEquals( networkProxy.getHost(), updatedNetworkProxy.getHost() );
+        assertEquals( networkProxy.getPassword(), updatedNetworkProxy.getPassword() );
+        assertEquals( networkProxy.getPort(), updatedNetworkProxy.getPort() );
+        assertEquals( networkProxy.getUsername(), updatedNetworkProxy.getUsername() );
+        assertEquals( networkProxy.getProtocol(), updatedNetworkProxy.getProtocol() );
+
+        networkProxyAdmin.deleteNetworkProxy( "foo", getFakeAuditInformation() );
+
+        assertEquals( 3, mockAuditListener.getAuditEvents().size() );
+
+        assertEquals( AuditEvent.ADD_NETWORK_PROXY, mockAuditListener.getAuditEvents().get( 0 ).getAction() );
+        assertEquals( AuditEvent.MODIFY_NETWORK_PROXY, mockAuditListener.getAuditEvents().get( 1 ).getAction() );
+        assertEquals( AuditEvent.DELETE_NETWORK_PROXY, mockAuditListener.getAuditEvents().get( 2 ).getAction() );
+
+        mockAuditListener.clearEvents();
+    }
+
+
+    protected NetworkProxy getNetworkProxyTest( String id )
+    {
+        NetworkProxy networkProxy = new NetworkProxy();
+        networkProxy.setId( "foo" );
+        networkProxy.setHost( "http://foo.com" );
+        networkProxy.setPassword( "passwd" );
+        networkProxy.setPort( 9090 );
+        networkProxy.setUsername( "root" );
+        networkProxy.setProtocol( "https" );
+        return networkProxy;
+    }
+
+}
index 4240f2fd98803358798d12be0a0eec3c4821f371..8b1eaafa6fe0c359bf42ff0affb6f16540fa3a7d 100644 (file)
@@ -36,6 +36,7 @@ public class ProxyConnectorAdminTest
     public void addAndDelete()
         throws Exception
     {
+        mockAuditListener.clearEvents();
         assertEquals( "not proxyConnectors 2 " + proxyConnectorAdmin.getProxyConnectors(), 2,
                       proxyConnectorAdmin.getProxyConnectors().size() );
         assertFalse( proxyConnectorAdmin.getProxyConnectors().isEmpty() );
@@ -72,7 +73,7 @@ public class ProxyConnectorAdminTest
     public void addAndUpdateAndDelete()
         throws Exception
     {
-
+        mockAuditListener.clearEvents();
         RemoteRepository remoteRepository = getRemoteRepository( "test-new-one" );
 
         remoteRepositoryAdmin.addRemoteRepository( remoteRepository, getFakeAuditInformation() );
@@ -140,6 +141,7 @@ public class ProxyConnectorAdminTest
     public void updateProxyConnector()
         throws Exception
     {
+        mockAuditListener.clearEvents();
         ProxyConnector proxyConnector = proxyConnectorAdmin.getProxyConnector( "internal", "central" );
         assertNotNull( proxyConnector );
         proxyConnector.setDisabled( false );
@@ -156,6 +158,7 @@ public class ProxyConnectorAdminTest
         proxyConnectorAdmin.updateProxyConnector( proxyConnector, getFakeAuditInformation() );
         proxyConnector = proxyConnectorAdmin.getProxyConnector( "internal", "central" );
         assertEquals( 4, proxyConnector.getOrder() );
+        mockAuditListener.clearEvents();
 
     }
 
index 99129dcfb6c1124923250b632e6c3fc2fab4f5a0..d39059e7c31487c4cda830188241c590caac0d05 100644 (file)
@@ -110,6 +110,12 @@ public class AuditEvent
     public static final String DELETE_PROXY_CONNECTOR = "Deleted Proxy Connector";
 
     public static final String MODIFY_PROXY_CONNECTOR = "Updated Proxy Connector";
+
+    public static final String ADD_NETWORK_PROXY = "Added Network Proxy";
+
+    public static final String DELETE_NETWORK_PROXY = "Deleted Network Proxy";
+
+    public static final String MODIFY_NETWORK_PROXY = "Updated Network Proxy";
     
     private String repositoryId;
 
diff --git a/pom.xml b/pom.xml
index f4327e5575f3a70a3a1abc2270d53c39e9be3ea8..5301a874dcdd5ac01b0d691e6dba3fcc33262d80 100644 (file)
--- a/pom.xml
+++ b/pom.xml
           <version>2.9</version>
           <configuration>
             <redirectTestOutputToFile>${surefire.redirectTestOutputToFile}</redirectTestOutputToFile>
+            <runOrder>alphabetical</runOrder>
             <systemPropertyVariables>
               <java.io.tmpdir>${project.build.outputDirectory}</java.io.tmpdir>
             </systemPropertyVariables>