You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by eg...@apache.org on 2009/11/30 14:46:35 UTC

svn commit: r885426 - in /cxf/trunk: api/src/main/java/org/apache/cxf/clustering/ rt/core/src/main/java/org/apache/cxf/clustering/ systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/

Author: eglynn
Date: Mon Nov 30 13:46:34 2009
New Revision: 885426

URL: http://svn.apache.org/viewvc?rev=885426&view=rev
Log:
Allowing failover addresses to be overridden in configuration

Added:
    cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/FailoverAddressOverrideTest.java
    cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/failover_address_override.xml
Modified:
    cxf/trunk/api/src/main/java/org/apache/cxf/clustering/FailoverStrategy.java
    cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/AbstractStaticFailoverStrategy.java
    cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverTargetSelector.java
    cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/Messages.properties
    cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/RandomStrategy.java
    cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/SequentialStrategy.java

Modified: cxf/trunk/api/src/main/java/org/apache/cxf/clustering/FailoverStrategy.java
URL: http://svn.apache.org/viewvc/cxf/trunk/api/src/main/java/org/apache/cxf/clustering/FailoverStrategy.java?rev=885426&r1=885425&r2=885426&view=diff
==============================================================================
--- cxf/trunk/api/src/main/java/org/apache/cxf/clustering/FailoverStrategy.java (original)
+++ cxf/trunk/api/src/main/java/org/apache/cxf/clustering/FailoverStrategy.java Mon Nov 30 13:46:34 2009
@@ -44,4 +44,21 @@
      * @return the selected endpoint
      */
     Endpoint selectAlternateEndpoint(List<Endpoint> alternates);
+
+    /**
+     * Get the alternate addresses for this invocation.
+     * These addresses over-ride any addresses specified in the WSDL.
+     * 
+     * @param exchange the current Exchange     
+     * @return a failover endpoint if one is available
+     */
+    List<String> getAlternateAddresses(Exchange exchange);
+
+    /**
+     * Select one of the alternate addresses for a retried invocation.
+     * 
+     * @param alternates List of alternate addresses if available
+     * @return the selected address
+     */
+    String selectAlternateAddress(List<String> addresses);
 }

Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/AbstractStaticFailoverStrategy.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/AbstractStaticFailoverStrategy.java?rev=885426&r1=885425&r2=885426&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/AbstractStaticFailoverStrategy.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/AbstractStaticFailoverStrategy.java Mon Nov 30 13:46:34 2009
@@ -38,10 +38,47 @@
  * multiple endpoints associated with the same service instance.
  */
 public abstract class AbstractStaticFailoverStrategy implements FailoverStrategy {
-    
     private static final Logger LOG =
         LogUtils.getL7dLogger(AbstractStaticFailoverStrategy.class);
-    
+
+    private List<String> alternateAddresses;
+
+
+    public void setAlternateAddresses(List<String> alternateAddresses) {
+        this.alternateAddresses = alternateAddresses;
+    }
+   
+    /**
+     * Get the alternate addresses for this invocation.
+     * 
+     * @param exchange the current Exchange
+     * @return a List of alternate addresses if available
+     */
+    public List<String> getAlternateAddresses(Exchange exchange) {
+        return alternateAddresses != null
+               ? new ArrayList<String>(alternateAddresses)
+               : null;
+    }
+
+    /**
+     * Select one of the alternate addresses for a retried invocation.
+     * 
+     * @param a List of alternate addresses if available
+     * @return the selected address
+     */
+    public String selectAlternateAddress(List<String> alternates) {
+        String selected = null;
+        if (alternates != null && alternates.size() > 0) {
+            selected = getNextAlternate(alternates);
+            LOG.log(Level.WARNING,
+                    "FAILING_OVER_TO_ADDRESS_OVERRIDE",
+                    selected);
+        } else {
+            LOG.warning("NO_ALTERNATE_TARGETS_REMAIN");
+        }
+        return selected;
+    }
+
     /**
      * Get the alternate endpoints for this invocation.
      * 
@@ -53,6 +90,26 @@
     }
     
     /**
+     * Select one of the alternate endpoints for a retried invocation.
+     * 
+     * @param a List of alternate endpoints if available
+     * @return the selected endpoint
+     */
+    public Endpoint selectAlternateEndpoint(List<Endpoint> alternates) {
+        Endpoint selected = null;
+        if (alternates != null && alternates.size() > 0) {
+            selected = getNextAlternate(alternates);
+            LOG.log(Level.WARNING,
+                    "FAILING_OVER_TO_ALTERNATE_ENDPOINT",
+                     new Object[] {selected.getEndpointInfo().getName(),
+                                   selected.getEndpointInfo().getAddress()});
+        } else {
+            LOG.warning("NO_ALTERNATE_TARGETS_REMAIN");
+        }
+        return selected;
+    }
+    
+    /**
      * Get the endpoints for this invocation.
      * 
      * @param exchange the current Exchange
@@ -91,30 +148,10 @@
     }
 
     /**
-     * Select one of the alternate endpoints for a retried invocation.
-     * 
-     * @param a List of alternate endpoints if available
-     * @return the selected endpoint
-     */
-    public Endpoint selectAlternateEndpoint(List<Endpoint> alternates) {
-        Endpoint selected = null;
-        if (alternates != null && alternates.size() > 0) {
-            selected = getNextAlternate(alternates);
-            LOG.log(Level.WARNING,
-                    "FAILING_OVER_TO",
-                     new Object[] {selected.getEndpointInfo().getName(),
-                                   selected.getEndpointInfo().getAddress()});
-        } else {
-            LOG.warning("NO_ALTERNATE_TARGETS_REMAIN");
-        }
-        return selected;
-    }
-    
-    /**
      * Get next alternate endpoint.
      * 
      * @param alternates non-empty List of alternate endpoints 
      * @return
      */
-    protected abstract Endpoint getNextAlternate(List<Endpoint> alternates);
+    protected abstract <T> T getNextAlternate(List<T> alternates);
 }

Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverTargetSelector.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverTargetSelector.java?rev=885426&r1=885425&r2=885426&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverTargetSelector.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverTargetSelector.java Mon Nov 30 13:46:34 2009
@@ -102,7 +102,7 @@
     }
 
     /**
-     * Called on completion of the MEP for which the Conduit was required.
+     * Called on completion of the MEP for which the Condit was required.
      * 
      * @param exchange represents the completed MEP
      */
@@ -116,7 +116,7 @@
         if (requiresFailover(exchange)) {
             Endpoint failoverTarget = getFailoverTarget(exchange, invocation);
             if (failoverTarget != null) {
-                endpoint = failoverTarget;
+                setEndpoint(failoverTarget);
                 selectedConduit.close();
                 selectedConduit = null;
                 Exception prevExchangeFault =
@@ -146,12 +146,7 @@
                     }
                 }
             } else {
-                if (endpoint != invocation.getOriginalEndpoint()) {
-                    endpoint = invocation.getOriginalEndpoint();
-                    getLogger().log(Level.INFO,
-                                    "REVERT_TO_ORIGINAL_TARGET",
-                                    endpoint.getEndpointInfo().getName());
-                }
+                setEndpoint(invocation.retrieveOriginalEndpoint(endpoint));
             }
         }
         if (!failover) {
@@ -225,16 +220,39 @@
      * @return a failover endpoint if one is available
      */
     private Endpoint getFailoverTarget(Exchange exchange,
-                                       InvocationContext invocation) {        
-        if (invocation.getAlternateTargets() == null) {
+                                       InvocationContext invocation) {
+        List<String> alternateAddresses = null;
+        if (!invocation.hasAlternates()) {
             // no previous failover attempt on this invocation
             //
-            invocation.setAlternateTargets(
-                getStrategy().getAlternateEndpoints(exchange));
-        } 
+            alternateAddresses = 
+                getStrategy().getAlternateAddresses(exchange);
+            if (alternateAddresses != null) {
+                invocation.setAlternateAddresses(alternateAddresses);
+            } else {
+                invocation.setAlternateEndpoints(
+                    getStrategy().getAlternateEndpoints(exchange));
+            }
+        } else {
+            alternateAddresses = invocation.getAlternateAddresses();
+        }
 
-        return getStrategy().selectAlternateEndpoint(
-                   invocation.getAlternateTargets());
+        Endpoint failoverTarget = null;
+        if (alternateAddresses != null) {
+            String alternateAddress = 
+                getStrategy().selectAlternateAddress(alternateAddresses);
+            if (alternateAddress != null) {
+                // re-use current endpoint
+                //
+                failoverTarget = getEndpoint();
+
+                failoverTarget.getEndpointInfo().setAddress(alternateAddress);
+            }
+        } else {
+            failoverTarget = getStrategy().selectAlternateEndpoint(
+                                 invocation.getAlternateEndpoints());
+        }
+        return failoverTarget;
     }
     
     /**
@@ -282,24 +300,38 @@
     /**
      * Records the context of an invocation.
      */
-    private static class InvocationContext {
+    private class InvocationContext {
         private Endpoint originalEndpoint;
+        private String originalAddress;
         private BindingOperationInfo bindingOperationInfo;
         private Object[] params; 
         private Map<String, Object> context;
-        private List<Endpoint> alternateTargets;
+        private List<Endpoint> alternateEndpoints;
+        private List<String> alternateAddresses;
         
         InvocationContext(Endpoint endpoint,
                           BindingOperationInfo boi,
                           Object[] prms, 
                           Map<String, Object> ctx) {
             originalEndpoint = endpoint;
+            originalAddress = endpoint.getEndpointInfo().getAddress();
             bindingOperationInfo = boi;
             params = prms;
             context = ctx;
         }
 
-        Endpoint getOriginalEndpoint() {
+        Endpoint retrieveOriginalEndpoint(Endpoint endpoint) {
+            if (endpoint != originalEndpoint) {
+                getLogger().log(Level.INFO,
+                                "REVERT_TO_ORIGINAL_TARGET",
+                                endpoint.getEndpointInfo().getName());
+            }
+            if (!endpoint.getEndpointInfo().getAddress().equals(originalAddress)) {
+                endpoint.getEndpointInfo().setAddress(originalAddress);
+                getLogger().log(Level.INFO,
+                                "REVERT_TO_ORIGINAL_ADDRESS",
+                                endpoint.getEndpointInfo().getAddress());
+            }
             return originalEndpoint;
         }
         
@@ -315,12 +347,24 @@
             return context;
         }
         
-        List<Endpoint> getAlternateTargets() {
-            return alternateTargets;
+        List<Endpoint> getAlternateEndpoints() {
+            return alternateEndpoints;
+        }
+
+        List<String> getAlternateAddresses() {
+            return alternateAddresses;
+        }
+
+        void setAlternateEndpoints(List<Endpoint> alternates) {
+            alternateEndpoints = alternates;
+        }
+
+        void setAlternateAddresses(List<String> alternates) {
+            alternateAddresses = alternates;
         }
 
-        void setAlternateTargets(List<Endpoint> alternates) {
-            alternateTargets = alternates;
+        boolean hasAlternates() {
+            return !(alternateEndpoints == null && alternateAddresses == null);
         }
     }    
 }

Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/Messages.properties
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/Messages.properties?rev=885426&r1=885425&r2=885426&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/Messages.properties (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/Messages.properties Mon Nov 30 13:46:34 2009
@@ -20,10 +20,12 @@
 #
 USING_STRATEGY = Using failover strategy {0}
 REVERT_TO_ORIGINAL_TARGET = reverted to original endpoint {0}
+REVERT_TO_ORIGINAL_ADDRESS = reverted to original address {0}
 FAILOVER_NOT_REQUIRED = failover not required
 NO_ALTERNATE_TARGETS_REMAIN = no alternate targets remain => giving up on failover
 CHECK_LAST_INVOKE_FAILED = last invocation raised fault?: {0}
 CHECK_FAILURE_IN_TRANSPORT = failure {0} caused at transport level?: {1}
 FAILOVER_CANDIDATE_ACCEPTED = failover candidate {0} accepted
 FAILOVER_CANDIDATE_REJECTED = failover candidate {0} rejected on binding mismatch
-FAILING_OVER_TO = failing over to alternate target {0}
\ No newline at end of file
+FAILING_OVER_TO_ALTERNATE_ENDPOINT = failing over to alternate target {0}
+FAILING_OVER_TO_ADRESS_OVERRIDE = failing over to alternate address {0}

Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/RandomStrategy.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/RandomStrategy.java?rev=885426&r1=885425&r2=885426&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/RandomStrategy.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/RandomStrategy.java Mon Nov 30 13:46:34 2009
@@ -22,8 +22,6 @@
 import java.util.List;
 import java.util.Random;
 
-import org.apache.cxf.endpoint.Endpoint;
-
 /**
  * Failover strategy based on a randomized walk through the
  * static cluster represented by multiple endpoints associated 
@@ -46,7 +44,7 @@
      * @param alternates non-empty List of alternate endpoints 
      * @return
      */
-    protected Endpoint getNextAlternate(List<Endpoint> alternates) {
+    protected <T> T getNextAlternate(List<T> alternates) {
         return alternates.remove(random.nextInt(alternates.size()));
     }
 }
\ No newline at end of file

Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/SequentialStrategy.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/SequentialStrategy.java?rev=885426&r1=885425&r2=885426&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/SequentialStrategy.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/SequentialStrategy.java Mon Nov 30 13:46:34 2009
@@ -21,8 +21,6 @@
 
 import java.util.List;
 
-import org.apache.cxf.endpoint.Endpoint;
-
 /**
  * Failover strategy based on a sequential walk through the
  * static cluster represented by multiple endpoints associated 
@@ -36,7 +34,7 @@
      * @param alternates non-empty List of alternate endpoints 
      * @return
      */
-    protected Endpoint getNextAlternate(List<Endpoint> alternates) {
+    protected <T> T getNextAlternate(List<T> alternates) {
         return alternates.remove(0);
     }
 }

Added: cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/FailoverAddressOverrideTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/FailoverAddressOverrideTest.java?rev=885426&view=auto
==============================================================================
--- cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/FailoverAddressOverrideTest.java (added)
+++ cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/FailoverAddressOverrideTest.java Mon Nov 30 13:46:34 2009
@@ -0,0 +1,252 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.clustering;
+
+import java.net.ConnectException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.clustering.AbstractStaticFailoverStrategy;
+import org.apache.cxf.clustering.FailoverTargetSelector;
+import org.apache.cxf.clustering.RandomStrategy;
+import org.apache.cxf.clustering.SequentialStrategy;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.endpoint.ConduitSelector;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.frontend.ClientProxy;
+import org.apache.cxf.greeter_control.ClusteredGreeterService;
+import org.apache.cxf.greeter_control.Control;
+import org.apache.cxf.greeter_control.ControlService;
+import org.apache.cxf.greeter_control.Greeter;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.ws.addressing.MAPAggregator;
+import org.apache.cxf.ws.addressing.soap.MAPCodec;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+/**
+ * Tests failover within a static cluster.
+ */
+public class FailoverAddressOverrideTest extends AbstractBusClientServerTestBase {
+
+    protected static final String REPLICA_A =
+        "http://localhost:9051/SoapContext/ReplicatedPortA";
+    protected static final String REPLICA_B =
+        "http://localhost:9052/SoapContext/ReplicatedPortB"; 
+    protected static final String REPLICA_C =
+        "http://localhost:9053/SoapContext/ReplicatedPortC"; 
+    protected static final String REPLICA_D =
+        "http://localhost:9054/SoapContext/ReplicatedPortD"; 
+    private static final Logger LOG =
+        LogUtils.getLogger(FailoverTest.class);
+    private static final String FAILOVER_CONFIG =
+        "org/apache/cxf/systest/clustering/failover_address_override.xml";
+
+    private Bus bus;
+    private Control control;
+    private Greeter greeter;
+    private List<String> targets;
+    private MAPAggregator mapAggregator;
+    private MAPCodec mapCodec;
+
+
+    @BeforeClass
+    public static void startServers() throws Exception {
+        assertTrue("server did not launch correctly",
+                   launchServer(Server.class));
+    }
+            
+    @Before
+    public void setUp() {
+        targets = new ArrayList<String>();
+        SpringBusFactory bf = new SpringBusFactory();    
+        bus = bf.createBus(FAILOVER_CONFIG);
+        BusFactory.setDefaultBus(bus);
+    }
+    
+    @After
+    public void tearDown() {
+        if (null != control) {
+            for (String address : targets) {
+                assertTrue("Failed to stop greeter",
+                           control.stopGreeter(address));
+            }
+        }
+        targets = null;
+        if (bus != null) {
+            bus.shutdown(true);
+        }
+    }
+
+
+    //@Test
+    public void testOverriddenSequentialStrategy() throws Exception {
+        startTarget(REPLICA_C);
+        setupGreeterA();
+        verifyStrategy(greeter, SequentialStrategy.class, 3);
+        String response = greeter.greetMe("fred");
+        assertNotNull("expected non-null response", response);
+        assertTrue("response from unexpected target: " + response,
+                   response.endsWith(REPLICA_C));
+        verifyCurrentEndpoint(REPLICA_C);
+        stopTarget(REPLICA_C);
+    }
+    
+    //@Test
+    public void testOverriddenRandomStrategy() throws Exception {
+        startTarget(REPLICA_B);
+        setupGreeterC();
+        verifyStrategy(greeter, RandomStrategy.class, 3);
+        String response = greeter.greetMe("fred");
+        assertNotNull("expected non-null response", response);
+        assertTrue("response from unexpected target: " + response,
+                   response.endsWith(REPLICA_B));
+        verifyCurrentEndpoint(REPLICA_B);
+        stopTarget(REPLICA_B);
+    }
+
+    @Test
+    public void testUnreachableAddresses() throws Exception {
+        startTarget(REPLICA_A);
+        setupGreeterB();
+        verifyStrategy(greeter, SequentialStrategy.class, 2);
+        try {
+            greeter.greetMe("fred");
+            fail("expected exception");
+        } catch (Exception e) {
+            Throwable cause = e;
+            while (cause.getCause() != null) {
+                cause = cause.getCause();
+            }
+            assertTrue("should revert to original exception when no failover: " 
+                       + cause,
+                       cause instanceof ConnectException);
+            verifyCurrentEndpoint(REPLICA_B.replaceFirst("9052", "15555"));
+        }
+        stopTarget(REPLICA_A);
+    }
+    
+
+    private void startTarget(String address) {
+        ControlService cs = new ControlService();
+        control = cs.getControlPort();
+
+        LOG.info("starting replicated target: " + address);
+        System.out.println("starting replicated target: " + address);
+        assertTrue("Failed to start greeter", control.startGreeter(address));
+        targets.add(address);
+    }
+    
+    private void stopTarget(String address) {
+        if (control != null
+            && targets.contains(address)) {
+            LOG.info("starting replicated target: " + address);
+            assertTrue("Failed to start greeter", control.stopGreeter(address));
+            targets.remove(address);
+        }
+    }
+
+    private void verifyCurrentEndpoint(String replica) {
+        assertEquals("unexpected current endpoint",
+                     replica,
+                     getCurrentEndpoint(greeter));
+    }
+    
+    private String getCurrentEndpoint(Object proxy) {
+        return ClientProxy.getClient(proxy).getEndpoint().getEndpointInfo().getAddress();
+    }
+    
+    private void setupGreeterA() {
+        greeter = new ClusteredGreeterService().getReplicatedPortA();
+        verifyConduitSelector(greeter);
+    }
+
+    private void setupGreeterB() {
+        greeter = new ClusteredGreeterService().getReplicatedPortB();
+        verifyConduitSelector(greeter);
+    }
+
+    private void setupGreeterC() {
+        greeter = new ClusteredGreeterService().getReplicatedPortC();
+        verifyConduitSelector(greeter);
+    }
+        
+    private void verifyConduitSelector(Greeter g) {
+        assertTrue("unexpected conduit slector",
+                   ClientProxy.getClient(g).getConduitSelector()
+                   instanceof FailoverTargetSelector);
+    }
+
+    private void verifyStrategy(Object proxy, Class clz, int count) {
+        ConduitSelector conduitSelector =
+            ClientProxy.getClient(proxy).getConduitSelector();
+        if (conduitSelector instanceof FailoverTargetSelector) {
+            AbstractStaticFailoverStrategy strategy = 
+                (AbstractStaticFailoverStrategy)
+                    ((FailoverTargetSelector)conduitSelector).getStrategy();
+            assertTrue("unexpected strategy", clz.isInstance(strategy));
+            List<String> alternates = strategy.getAlternateAddresses(null);
+            assertNotNull("expected alternate addresses", alternates);
+            assertEquals("unexpected alternate addresses", count, alternates.size());
+        } else {
+            fail("unexpected conduit selector: " + conduitSelector);
+        }
+    }
+
+    protected void enableWSAForCurrentEndpoint() {
+        Endpoint provider = ClientProxy.getClient(greeter).getEndpoint();
+        mapAggregator = new MAPAggregator();
+        mapCodec = new MAPCodec();
+        provider.getInInterceptors().add(mapAggregator);
+        provider.getInInterceptors().add(mapCodec);
+        
+        provider.getOutInterceptors().add(mapAggregator);
+        provider.getOutInterceptors().add(mapCodec);
+        
+        provider.getInFaultInterceptors().add(mapAggregator);
+        provider.getInFaultInterceptors().add(mapCodec);
+        
+        provider.getOutFaultInterceptors().add(mapAggregator);
+        provider.getOutFaultInterceptors().add(mapCodec);
+    }
+    
+    protected boolean isWSAEnabledForCurrentEndpoint() {
+        Endpoint provider = ClientProxy.getClient(greeter).getEndpoint();
+        boolean enabledIn = 
+            provider.getInInterceptors().contains(mapAggregator)
+            && provider.getInInterceptors().contains(mapCodec)
+            && provider.getInFaultInterceptors().contains(mapAggregator)
+            && provider.getInFaultInterceptors().contains(mapCodec);
+        boolean enabledOut = 
+            provider.getOutInterceptors().contains(mapAggregator)
+            && provider.getOutInterceptors().contains(mapCodec)
+            && provider.getOutFaultInterceptors().contains(mapAggregator)
+            && provider.getOutFaultInterceptors().contains(mapCodec);
+        return enabledIn && enabledOut;
+    }
+}

Added: cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/failover_address_override.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/failover_address_override.xml?rev=885426&view=auto
==============================================================================
--- cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/failover_address_override.xml (added)
+++ cxf/trunk/systests/uncategorized/src/test/java/org/apache/cxf/systest/clustering/failover_address_override.xml Mon Nov 30 13:46:34 2009
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:jaxws="http://cxf.apache.org/jaxws"
+       xmlns:clustering="http://cxf.apache.org/clustering"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
+http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
+    
+    <util:list id="addressList">
+        <value>http://localhost:9051/SoapContext/ReplicatedPortA</value>
+        <value>http://localhost:9052/SoapContext/ReplicatedPortB</value>
+        <value>http://localhost:9053/SoapContext/ReplicatedPortC</value>
+    </util:list>
+
+    <bean id="SequentialAddresses" class="org.apache.cxf.clustering.SequentialStrategy">
+        <property name="alternateAddresses">
+            <ref bean="addressList"/>
+        </property>
+    </bean>
+
+    <bean id="RandomAddresses" class="org.apache.cxf.clustering.RandomStrategy">
+        <property name="alternateAddresses">
+            <ref bean="addressList"/>
+        </property>
+    </bean>
+
+    <bean id="UnreachableAddresses" class="org.apache.cxf.clustering.SequentialStrategy">
+        <property name="alternateAddresses">
+            <list>        
+                <value>http://localhost:9050/SoapContext/ReplicatedPortA</value>
+                <value>http://localhost:9053/SoapContext/ReplicatedPortC</value>
+            </list>
+        </property>
+    </bean>
+
+    <jaxws:client name="{http://cxf.apache.org/greeter_control}ReplicatedPortA"
+                  createdFromAPI="true">
+        <jaxws:features>
+            <clustering:failover>
+                <clustering:strategy>
+                    <ref bean="SequentialAddresses"/>
+                </clustering:strategy>
+            </clustering:failover>
+        </jaxws:features>
+    </jaxws:client>
+
+    <jaxws:client name="{http://cxf.apache.org/greeter_control}ReplicatedPortC"
+                  createdFromAPI="true">
+        <jaxws:features>
+            <clustering:failover>
+                <clustering:strategy>
+                    <ref bean="RandomAddresses"/>
+                </clustering:strategy>
+            </clustering:failover>
+        </jaxws:features>
+    </jaxws:client>
+
+    <jaxws:client name="{http://cxf.apache.org/greeter_control}ReplicatedPortB"
+                  address="http://localhost:15555/SoapContext/ReplicatedPortB"
+                  createdFromAPI="true">
+        <jaxws:features>
+            <clustering:failover>
+                <clustering:strategy>
+                    <ref bean="UnreachableAddresses"/>
+                </clustering:strategy>
+            </clustering:failover>
+        </jaxws:features>
+    </jaxws:client>
+    
+</beans>