You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2011/06/04 18:06:07 UTC

svn commit: r1131422 - in /cxf/trunk: api/src/main/java/org/apache/cxf/endpoint/ rt/core/src/main/java/org/apache/cxf/clustering/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ rt/fron...

Author: sergeyb
Date: Sat Jun  4 16:06:06 2011
New Revision: 1131422

URL: http://svn.apache.org/viewvc?rev=1131422&view=rev
Log:
[CXF-3561] Initial support for failover feature

Added:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/features/
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/features/clustering/
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/features/clustering/FailoverFeature.java   (with props)
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/FailoverTest.java   (with props)
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/Server.java   (with props)
Modified:
    cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java
    cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverFeature.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/LoadDistributorFeature.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java

Modified: cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java
URL: http://svn.apache.org/viewvc/cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java (original)
+++ cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java Sat Jun  4 16:06:06 2011
@@ -27,6 +27,7 @@ import org.apache.cxf.common.util.String
 import org.apache.cxf.interceptor.Fault;
 import org.apache.cxf.message.Exchange;
 import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
 import org.apache.cxf.service.model.EndpointInfo;
 import org.apache.cxf.transport.Conduit;
 import org.apache.cxf.transport.ConduitInitiator;
@@ -41,7 +42,8 @@ import org.apache.cxf.ws.addressing.Endp
  * that retreives a Conduit from the ConduitInitiator.
  */
 public abstract class AbstractConduitSelector implements ConduitSelector {
-
+    protected static final String KEEP_CONDUIT_ALIVE = "KeepConduitAlive";
+    
     protected Conduit selectedConduit;
     protected Endpoint endpoint;
 
@@ -126,6 +128,13 @@ public abstract class AbstractConduitSel
      * @param exchange represents the completed MEP
      */
     public void complete(Exchange exchange) {
+        // Clients expecting explicit InputStream responses
+        // will need to keep low level conduits operating on InputStreams open
+        // and will be responsible for closing the streams
+        
+        if (MessageUtils.isTrue(exchange.get(KEEP_CONDUIT_ALIVE))) {
+            return;
+        }
         try {
             if (exchange.getInMessage() != null) {
                 getSelectedConduit(exchange.getInMessage()).close(exchange.getInMessage());

Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverFeature.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverFeature.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverFeature.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/FailoverFeature.java Sat Jun  4 16:06:06 2011
@@ -21,6 +21,8 @@ package org.apache.cxf.clustering;
 import org.apache.cxf.Bus;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
 import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.endpoint.ConduitSelector;
+import org.apache.cxf.endpoint.Endpoint;
 import org.apache.cxf.feature.AbstractFeature;
 
 /**
@@ -35,13 +37,22 @@ public class FailoverFeature extends Abs
     
     @Override
     public void initialize(Client client, Bus bus) {
-        FailoverTargetSelector selector =
-            new FailoverTargetSelector();
-        selector.setEndpoint(client.getEndpoint());
-        selector.setStrategy(getStrategy());
+        ConduitSelector selector = initTargetSelector(client.getConduitSelector().getEndpoint());
         client.setConduitSelector(selector);
     }
 
+    protected ConduitSelector initTargetSelector(Endpoint endpoint) {
+        FailoverTargetSelector selector = getTargetSelector();
+        selector.setEndpoint(endpoint);
+        selector.setStrategy(getStrategy());
+        return selector;
+    }
+    
+    protected FailoverTargetSelector getTargetSelector() {
+        return new FailoverTargetSelector();
+    }
+    
+    
     public void setStrategy(FailoverStrategy strategy) {
         failoverStrategy = strategy;
     }

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=1131422&r1=1131421&r2=1131422&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 Sat Jun  4 16:06:06 2011
@@ -207,9 +207,11 @@ public class FailoverTargetSelector exte
             failover = curr instanceof java.io.IOException;
             curr = curr.getCause();
         }
-        getLogger().log(Level.INFO,
-                        "CHECK_FAILURE_IN_TRANSPORT",
-                        new Object[] {ex, failover});
+        if (ex != null) {
+            getLogger().log(Level.INFO,
+                            "CHECK_FAILURE_IN_TRANSPORT",
+                            new Object[] {ex, failover});
+        }
         return failover;
     }
     

Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/LoadDistributorFeature.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/LoadDistributorFeature.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/LoadDistributorFeature.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/clustering/LoadDistributorFeature.java Sat Jun  4 16:06:06 2011
@@ -18,9 +18,7 @@
  */
 package org.apache.cxf.clustering;
 
-import org.apache.cxf.Bus;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
-import org.apache.cxf.endpoint.Client;
 
 /**
  * This feature may be applied to a Client so as to enable
@@ -32,12 +30,7 @@ import org.apache.cxf.endpoint.Client;
 public class LoadDistributorFeature extends FailoverFeature {
 
     @Override
-    public void initialize(Client client, Bus bus) {
-        LoadDistributorTargetSelector selector =
-            new LoadDistributorTargetSelector();
-        selector.setEndpoint(client.getEndpoint());
-        selector.setStrategy(getStrategy());
-        client.setConduitSelector(selector);
+    protected FailoverTargetSelector getTargetSelector() {
+        return new LoadDistributorTargetSelector();
     }
-
 }

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java Sat Jun  4 16:06:06 2011
@@ -19,6 +19,8 @@
 package org.apache.cxf.jaxrs;
 
 
+import javax.xml.namespace.QName;
+
 import org.apache.cxf.Bus;
 import org.apache.cxf.binding.AbstractBaseBindingFactory;
 import org.apache.cxf.binding.Binding;
@@ -65,7 +67,7 @@ public class JAXRSBindingFactory extends
      */
     public BindingInfo createBindingInfo(Service service, String namespace, Object obj) {
         BindingInfo info = new BindingInfo(null, JAXRSBindingFactory.JAXRS_BINDING_ID);
-
+        info.setName(new QName(JAXRSBindingFactory.JAXRS_BINDING_ID, "binding"));
         return info;
     }
 

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java Sat Jun  4 16:06:06 2011
@@ -24,7 +24,6 @@ import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.net.HttpURLConnection;
 import java.net.URI;
-import java.net.URL;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -53,6 +52,8 @@ import org.apache.cxf.common.i18n.Bundle
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.endpoint.ConduitSelector;
 import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.endpoint.Retryable;
+import org.apache.cxf.helpers.CastUtils;
 import org.apache.cxf.interceptor.Fault;
 import org.apache.cxf.interceptor.Interceptor;
 import org.apache.cxf.jaxrs.impl.MetadataMap;
@@ -65,22 +66,27 @@ import org.apache.cxf.jaxrs.utils.Inject
 import org.apache.cxf.message.Exchange;
 import org.apache.cxf.message.ExchangeImpl;
 import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageContentsList;
 import org.apache.cxf.message.MessageImpl;
 import org.apache.cxf.phase.PhaseChainCache;
 import org.apache.cxf.phase.PhaseInterceptorChain;
 import org.apache.cxf.phase.PhaseManager;
 import org.apache.cxf.service.Service;
+import org.apache.cxf.service.model.BindingOperationInfo;
 import org.apache.cxf.transport.MessageObserver;
+import org.apache.cxf.transport.http.HTTPConduit;
 
 /**
  * Common proxy and http-centric client implementation
  *
  */
-public class AbstractClient implements Client {
+public abstract class AbstractClient implements Client, Retryable {
+    protected static final String REQUEST_CONTEXT = "RequestContext";
+    protected static final String RESPONSE_CONTEXT = "ResponseContext";
+    protected static final String KEEP_CONDUIT_ALIVE = "KeepConduitAlive";
+    
     private static final Logger LOG = LogUtils.getL7dLogger(AbstractClient.class);
     private static final ResourceBundle BUNDLE = BundleUtils.getBundle(AbstractClient.class);
-    private static final String REQUEST_CONTEXT = "RequestContext";
-    private static final String RESPONSE_CONTEXT = "ResponseContext";
     
     protected ClientConfiguration cfg = new ClientConfiguration();
     private ClientState state;
@@ -308,27 +314,15 @@ public class AbstractClient implements C
         return null;
     }
     
-    protected ResponseBuilder setResponseBuilder(HttpURLConnection conn, Exchange exchange) throws Throwable {
-        Message inMessage = exchange.getInMessage();
+    protected ResponseBuilder setResponseBuilder(Message outMessage, Exchange exchange) throws Exception {
+        HttpURLConnection conn = (HttpURLConnection)outMessage.get(HTTPConduit.KEY_HTTP_CONNECTION);
+        
         if (conn == null) {
-            // unlikely to occur
             throw new ClientWebApplicationException("HTTP Connection is null"); 
         }
-        Integer responseCode = (Integer)exchange.get(Message.RESPONSE_CODE);
-        if (responseCode == null) {
-            //Invocation was never made to server, something stopped the outbound 
-            //interceptor chain, we dont have a response code.
-            //Do not call conn.getResponseCode() as that will
-            //result in a call to the server when we have already decided not to.
-            //Throw an exception if we have one
-            Exception ex = exchange.getOutMessage().getContent(Exception.class);
-            if (ex != null) {
-                throw ex; 
-            } else {
-                throw new RuntimeException("Unknown client side exception");
-            }
-        } 
-        int status = responseCode.intValue();
+        checkClientException(exchange.getOutMessage(), exchange.getOutMessage().getContent(Exception.class));
+        
+        int status = (Integer)exchange.get(Message.RESPONSE_CODE);
         ResponseBuilder currentResponseBuilder = Response.status(status);
         
         for (Map.Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
@@ -363,6 +357,8 @@ public class AbstractClient implements C
             }
         }
         InputStream mStream = null;
+        
+        Message inMessage = exchange.getInMessage();
         if (inMessage != null) {
             mStream = inMessage.getContent(InputStream.class);
         }
@@ -450,6 +446,92 @@ public class AbstractClient implements C
         return null;                                                
     }
     
+    protected void completeExchange(Object response, Exchange exchange) {
+        // higher level conduits such as FailoverTargetSelector need to
+        // clear the request state but a fair number of response objects 
+        // depend on InputStream being still open thus lower-level conduits
+        // operating on InputStream don't have to close streams pro-actively
+        exchange.put(KEEP_CONDUIT_ALIVE, true);    
+        getConfiguration().getConduitSelector().complete(exchange);
+    }
+    
+    protected Object[] preProcessResult(Message message) throws Exception {
+        
+        Exchange exchange = message.getExchange(); 
+      
+        Exception ex = null;
+        // Check to see if there is a Fault from the outgoing chain if it's an out Message
+        if (!message.get(Message.INBOUND_MESSAGE).equals(Boolean.TRUE)) {
+            ex = message.getContent(Exception.class);
+        }
+        if (ex != null) {
+            getConfiguration().getConduitSelector().complete(exchange);
+            checkClientException(message, message.getContent(Exception.class));
+        }
+        checkClientException(message, message.getExchange().get(Exception.class));
+        
+        List result = message.getExchange().get(List.class);
+        return result != null ? result.toArray() : null;
+    }
+    
+    protected void checkClientException(Message message, Exception ex) throws Exception {
+        if (message.getExchange().get(Message.RESPONSE_CODE) == null) {
+            if (ex instanceof ClientWebApplicationException) {
+                throw ex;
+            } else if (ex != null) {
+                throw new ClientWebApplicationException(ex);
+            } else {
+                throw new ClientWebApplicationException();
+            }
+        }
+    }
+    
+    protected URI calculateNewRequestURI(Map<String, Object> reqContext) {
+        URI newBaseURI = URI.create(reqContext.get(Message.ENDPOINT_ADDRESS).toString());
+        String baseURIPath = newBaseURI.getRawPath();
+        
+        URI requestURI = URI.create(reqContext.get(Message.REQUEST_URI).toString());
+        String reqURIPath = requestURI.getRawPath();
+        
+        UriBuilder builder = UriBuilder.fromUri(newBaseURI);
+        String basePath = reqURIPath.startsWith(baseURIPath) ? baseURIPath : getBaseURI().getRawPath(); 
+        builder.path(reqURIPath.equals(basePath) ? "" : reqURIPath.substring(basePath.length()));
+        URI newRequestURI = builder.replaceQuery(requestURI.getRawQuery()).build();
+        
+        resetBaseAddress(newBaseURI);
+        resetCurrentBuilder(newRequestURI);
+        
+        return newRequestURI;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public Object[] invoke(BindingOperationInfo oi, Object[] params, Map<String, Object> context,
+                           Exchange exchange) throws Exception {
+        
+        try {
+            Object body = params.length == 0 ? null : params[0];
+            Map<String, Object> reqContext = CastUtils.cast((Map)context.get(REQUEST_CONTEXT));
+            MultivaluedMap<String, String> headers = 
+                (MultivaluedMap<String, String>)reqContext.get(Message.PROTOCOL_HEADERS);
+                        
+            URI newRequestURI = calculateNewRequestURI(reqContext);
+            
+            Object response = retryInvoke(newRequestURI, headers, body, exchange, context);
+            exchange.put(List.class, getContentsList(response));
+            return new Object[]{response};
+        } catch (Throwable t) {
+            Exception ex = t instanceof Exception ? (Exception)t : new Exception(t);
+            exchange.put(Exception.class, ex);
+            return null;
+        }
+    }
+    
+    protected abstract Object retryInvoke(URI newRequestURI, 
+                                 MultivaluedMap<String, String> headers,
+                                 Object body,
+                                 Exchange exchange, 
+                                 Map<String, Object> invContext) throws Throwable;
+    
     // TODO : shall we just do the reflective invocation here ?
     protected static void addParametersToBuilder(UriBuilder ub, String paramName, Object pValue,
                                                  ParameterType pt) {
@@ -509,18 +591,6 @@ public class AbstractClient implements C
         return MediaType.WILDCARD_TYPE;
     }
     
-    protected static HttpURLConnection createHttpConnection(URI uri, String methodName) {
-        try {
-            URL url = uri.toURL();
-            HttpURLConnection connect = (HttpURLConnection)url.openConnection();
-            connect.setDoOutput(true);
-            connect.setRequestMethod(methodName);
-            return connect;
-        } catch (Exception ex) {
-            throw new ClientWebApplicationException("REMOTE_CONNECTION_PROBLEM", ex, null);
-        }
-    }
-    
     protected static void setAllHeaders(MultivaluedMap<String, String> headers, HttpURLConnection conn) {
         for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
             StringBuilder b = new StringBuilder();    
@@ -601,6 +671,7 @@ public class AbstractClient implements C
             LOG.warning("Failure to prepare a message from conduit selector");
         }
         message.getExchange().put(ConduitSelector.class, cfg.getConduitSelector());
+        message.getExchange().put(Service.class, cfg.getConduitSelector().getEndpoint().getService());
     }
     
     protected static PhaseInterceptorChain setupOutInterceptorChain(ClientConfiguration cfg) { 
@@ -626,9 +697,12 @@ public class AbstractClient implements C
         return m;
     }
     
-    protected Message createMessage(String httpMethod, 
+    protected Message createMessage(Object body,
+                                    String httpMethod, 
                                     MultivaluedMap<String, String> headers,
-                                    URI currentURI) {
+                                    URI currentURI,
+                                    Exchange exchange,
+                                    Map<String, Object> invocationContext) {
         Message m = cfg.getConduitSelector().getEndpoint().getBinding().createMessage();
         m.put(Message.REQUESTOR_ROLE, Boolean.TRUE);
         m.put(Message.INBOUND_MESSAGE, Boolean.FALSE);
@@ -640,39 +714,79 @@ public class AbstractClient implements C
         
         m.put(Message.CONTENT_TYPE, headers.getFirst(HttpHeaders.CONTENT_TYPE));
         
-        Exchange exchange = new ExchangeImpl();
-        exchange.setSynchronous(true);
-        exchange.setOutMessage(m);
-        exchange.put(Bus.class, cfg.getBus());
-        exchange.put(MessageObserver.class, new ClientMessageObserver(cfg));
-        exchange.put(Endpoint.class, cfg.getConduitSelector().getEndpoint());
-        exchange.setOneWay("true".equals(headers.getFirst(Message.ONE_WAY_REQUEST)));
-        // no need for the underlying conduit to throw the IO exceptions in case of
-        // client requests returning error HTTP code, it can be overridden if really needed 
-        exchange.put("org.apache.cxf.http.no_io_exceptions", true);
-        m.setExchange(exchange);
+        m.setContent(List.class, getContentsList(body));
+        if (body == null) {
+            setEmptyRequestProperty(m, httpMethod);
+        }
+        
+        m.put(URITemplate.TEMPLATE_PARAMETERS, getState().getTemplates());
         
         PhaseInterceptorChain chain = setupOutInterceptorChain(cfg);
         m.setInterceptorChain(chain);
         
+        exchange = createExchange(m, exchange);
+        exchange.setOneWay("true".equals(headers.getFirst(Message.ONE_WAY_REQUEST)));
+        exchange.put(Retryable.class, this);
+        
         // context
-        if (cfg.getRequestContext().size() > 0 || cfg.getResponseContext().size() > 0) {
-            Map<String, Object> context = new HashMap<String, Object>();
-            context.put(REQUEST_CONTEXT, cfg.getRequestContext());
-            context.put(RESPONSE_CONTEXT, cfg.getResponseContext());
-            m.put(Message.INVOCATION_CONTEXT, context);
-            m.putAll(cfg.getRequestContext());
-            exchange.putAll(cfg.getRequestContext());
-            exchange.putAll(cfg.getResponseContext());
-        }
+        setContexts(m, exchange, invocationContext);
         
         //setup conduit selector
         prepareConduitSelector(m);
-        exchange.put(Service.class, cfg.getConduitSelector().getEndpoint().getService());
         
         return m;
     }
-
+    
+    protected Map<String, Object> getRequestContext(Message outMessage) {
+        Map<String, Object> invContext = CastUtils.cast((Map)outMessage.get(Message.INVOCATION_CONTEXT));
+        return CastUtils.cast((Map)invContext.get(REQUEST_CONTEXT));
+    }
+    
+    protected List getContentsList(Object body) {
+        return body == null ? new MessageContentsList() : new MessageContentsList(body);
+    }
+    
+    protected Exchange createExchange(Message m, Exchange exchange) {
+        if (exchange == null) {
+            exchange = new ExchangeImpl();
+        }
+        exchange.setSynchronous(true);
+        exchange.setOutMessage(m);
+        exchange.put(Bus.class, cfg.getBus());
+        exchange.put(MessageObserver.class, new ClientMessageObserver(cfg));
+        exchange.put(Endpoint.class, cfg.getConduitSelector().getEndpoint());
+        exchange.put("org.apache.cxf.http.no_io_exceptions", true);
+        m.setExchange(exchange);
+        return exchange;
+    }
+    
+    protected void setContexts(Message message, Exchange exchange, 
+                               Map<String, Object> context) {
+        Map<String, Object> reqContext = null;
+        Map<String, Object> resContext = null;
+        if (context == null) {
+            context = new HashMap<String, Object>();
+        }
+        reqContext = CastUtils.cast((Map)context.get(REQUEST_CONTEXT));
+        resContext = CastUtils.cast((Map)context.get(RESPONSE_CONTEXT));
+        if (reqContext == null) { 
+            reqContext = new HashMap<String, Object>(cfg.getRequestContext());
+            context.put(REQUEST_CONTEXT, reqContext);
+        }
+        reqContext.put(Message.PROTOCOL_HEADERS, message.get(Message.PROTOCOL_HEADERS));
+        reqContext.put(Message.REQUEST_URI, message.get(Message.REQUEST_URI));
+        reqContext.put(Message.ENDPOINT_ADDRESS, message.get(Message.ENDPOINT_ADDRESS));
+        
+        if (resContext == null) {
+            resContext = new HashMap<String, Object>();
+            context.put(RESPONSE_CONTEXT, resContext);
+        }
+        
+        message.put(Message.INVOCATION_CONTEXT, context);
+        message.putAll(reqContext);
+        exchange.putAll(reqContext);
+    }
+    
     protected void setEmptyRequestProperty(Message outMessage, String httpMethod) {
         if ("POST".equals(httpMethod)) {
             outMessage.put("org.apache.cxf.post.empty", true);

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java Sat Jun  4 16:06:06 2011
@@ -23,7 +23,6 @@ import java.io.OutputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
-import java.net.HttpURLConnection;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -45,6 +44,7 @@ import javax.xml.stream.XMLStreamWriter;
 import org.apache.cxf.common.i18n.BundleUtils;
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.helpers.CastUtils;
 import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor;
 import org.apache.cxf.interceptor.Fault;
 import org.apache.cxf.interceptor.InterceptorProvider;
@@ -53,20 +53,20 @@ import org.apache.cxf.jaxrs.model.ClassR
 import org.apache.cxf.jaxrs.model.OperationResourceInfo;
 import org.apache.cxf.jaxrs.model.Parameter;
 import org.apache.cxf.jaxrs.model.ParameterType;
-import org.apache.cxf.jaxrs.model.URITemplate;
 import org.apache.cxf.jaxrs.provider.ProviderFactory;
 import org.apache.cxf.jaxrs.utils.FormUtils;
 import org.apache.cxf.jaxrs.utils.InjectionUtils;
+import org.apache.cxf.message.Exchange;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.message.MessageContentsList;
 import org.apache.cxf.phase.Phase;
-import org.apache.cxf.transport.http.HTTPConduit;
 
 /**
  * Proxy-based client implementation
  *
  */
-public class ClientProxyImpl extends AbstractClient implements InvocationHandlerAware, InvocationHandler {
+public class ClientProxyImpl extends AbstractClient implements 
+    InvocationHandlerAware, InvocationHandler {
 
     private static final Logger LOG = LogUtils.getL7dLogger(ClientProxyImpl.class);
     private static final ResourceBundle BUNDLE = BundleUtils.getBundle(ClientProxyImpl.class);
@@ -174,7 +174,14 @@ public class ClientProxyImpl extends Abs
         setRequestHeaders(headers, ori, types.containsKey(ParameterType.FORM), 
             bodyIndex == -1 ? null : params[bodyIndex].getClass(), m.getReturnType());
         
-        return doChainedInvocation(uri, headers, ori, params, bodyIndex, types, pathParams);
+        getState().setTemplates(getTemplateParametersMap(ori.getURITemplate(), pathParams));
+        
+        Object body = null;
+        boolean isForm = types.containsKey(ParameterType.FORM);
+        if (bodyIndex != -1 || isForm) {
+            body = isForm ? handleForm(types, params) : params[bodyIndex];
+        }
+        return doChainedInvocation(uri, headers, ori, body, bodyIndex, null, null);
         
     }
 
@@ -409,47 +416,77 @@ public class ClientProxyImpl extends Abs
         }
     }
     
-    private Object doChainedInvocation(URI uri, MultivaluedMap<String, String> headers, 
-                          OperationResourceInfo ori, Object[] params, int bodyIndex, 
-                          MultivaluedMap<ParameterType, Parameter> types,
-                          List<Object> pathParams) throws Throwable {
-        Message outMessage = createMessage(ori.getHttpMethod(), headers, uri);
-        outMessage.getExchange().setOneWay(ori.isOneway());
+    private Object doChainedInvocation(URI uri, 
+                                       MultivaluedMap<String, String> headers, 
+                                       OperationResourceInfo ori, 
+                                       Object body, 
+                                       int bodyIndex,
+                                       Exchange exchange,
+                                       Map<String, Object> invocationContext) throws Throwable {
         
-        getState().setTemplates(getTemplateParametersMap(ori.getURITemplate(), pathParams));
-        outMessage.put(URITemplate.TEMPLATE_PARAMETERS, getState().getTemplates());
+        Message outMessage = createMessage(body, ori.getHttpMethod(), headers, uri, 
+                                           exchange, invocationContext);
         
+        outMessage.getExchange().setOneWay(ori.isOneway());
         outMessage.setContent(OperationResourceInfo.class, ori);
         setPlainOperationNameProperty(outMessage, ori.getMethodToInvoke().getName());
         outMessage.getExchange().put(Method.class, ori.getMethodToInvoke());
-        boolean isForm = types.containsKey(ParameterType.FORM);
-        if (bodyIndex != -1 || isForm) {
+        
+        if (body != null) {
             outMessage.put("BODY_INDEX", bodyIndex);
-            Object body = isForm ? handleForm(types, params) : params[bodyIndex];
-            MessageContentsList contents = new MessageContentsList(new Object[]{body});
-            outMessage.setContent(List.class, contents);
             outMessage.getInterceptorChain().add(new BodyWriter());
-        } else {
-            setEmptyRequestProperty(outMessage, ori.getHttpMethod());
         }
+
+        Map<String, Object> reqContext = getRequestContext(outMessage);
+        reqContext.put(OperationResourceInfo.class.getName(), ori);
+        reqContext.put("BODY_INDEX", bodyIndex);
         
         // execute chain    
         try {
             outMessage.getInterceptorChain().doIntercept(outMessage);
-        } catch (Throwable ex) {
-            // we'd like a user to get the whole Response anyway if needed
+        } catch (Exception ex) {
+            outMessage.setContent(Exception.class, ex);
+        }
+        
+        Object[] results = preProcessResult(outMessage);
+        if (results != null && results.length == 1) {
+            // this can happen if a connection exception has occurred and
+            // failover feature used this client to invoke on a different address  
+            return results[0];
+        }
+        
+        Object response = null;
+        try {
+            response = handleResponse(outMessage);
+            return response;
+        } catch (Exception ex) {
+            response = ex;
+            throw ex;
+        } finally {
+            completeExchange(response, outMessage.getExchange());
         }
         
-        // TODO : this needs to be done in an inbound chain instead
-        HttpURLConnection connect = (HttpURLConnection)outMessage.get(HTTPConduit.KEY_HTTP_CONNECTION);
-        return handleResponse(connect, outMessage, ori);
+    }
+    
+    @Override
+    protected Object retryInvoke(URI newRequestURI, 
+                                 MultivaluedMap<String, String> headers,
+                                 Object body,
+                                 Exchange exchange, 
+                                 Map<String, Object> invContext) throws Throwable {
         
+        Map<String, Object> reqContext = CastUtils.cast((Map)invContext.get(REQUEST_CONTEXT));
+        int bodyIndex = body != null ? (Integer)reqContext.get("BODY_INDEX") : -1;
+        OperationResourceInfo ori = 
+            (OperationResourceInfo)reqContext.get(OperationResourceInfo.class.getName());
+        return doChainedInvocation(newRequestURI, headers, ori, 
+                                   body, bodyIndex, exchange, invContext);
     }
     
-    protected Object handleResponse(HttpURLConnection connect, Message outMessage, OperationResourceInfo ori) 
+    protected Object handleResponse(Message outMessage) 
         throws Throwable {
-        Response r = setResponseBuilder(connect, outMessage.getExchange()).build();
-        Method method = ori.getMethodToInvoke();
+        Response r = setResponseBuilder(outMessage, outMessage.getExchange()).build();
+        Method method = outMessage.getExchange().get(Method.class);
         checkResponse(method, r, outMessage);
         if (method.getReturnType() == Void.class) { 
             return null;
@@ -526,6 +563,5 @@ public class ClientProxyImpl extends Abs
         }
         
     }
-
     
 }

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java Sat Jun  4 16:06:06 2011
@@ -37,6 +37,7 @@ import org.apache.cxf.jaxrs.JAXRSService
 import org.apache.cxf.jaxrs.impl.MetadataMap;
 import org.apache.cxf.jaxrs.model.ClassResourceInfo;
 import org.apache.cxf.service.Service;
+import org.apache.cxf.service.factory.FactoryBeanListener;
 
 public class JAXRSClientFactoryBean extends AbstractJAXRSFactoryBean {
     
@@ -186,6 +187,8 @@ public class JAXRSClientFactoryBean exte
             WebClient client = actualState == null ? new WebClient(getAddress())
                 : new WebClient(actualState);
             initClient(client, ep, actualState == null);
+    
+            this.getServiceFactory().sendEvent(FactoryBeanListener.Event.CLIENT_CREATED, client, ep);
             
             return client;
         } catch (Exception ex) {
@@ -267,17 +270,20 @@ public class JAXRSClientFactoryBean exte
             }
             initClient(proxyImpl, ep, actualState == null);    
             
+            Client actualClient = null;
             try {
-                return (Client)ProxyHelper.getProxy(cri.getServiceClass().getClassLoader(),
+                actualClient = (Client)ProxyHelper.getProxy(cri.getServiceClass().getClassLoader(),
                                         new Class[]{cri.getServiceClass(), Client.class, 
                                                     InvocationHandlerAware.class}, 
                                         proxyImpl);
             } catch (Exception ex) {
-                return (Client)ProxyHelper.getProxy(Thread.currentThread().getContextClassLoader(),
+                actualClient = (Client)ProxyHelper.getProxy(Thread.currentThread().getContextClassLoader(),
                                                     new Class[]{cri.getServiceClass(), Client.class, 
                                                                 InvocationHandlerAware.class}, 
                                      proxyImpl);
             }
+            this.getServiceFactory().sendEvent(FactoryBeanListener.Event.CLIENT_CREATED, actualClient, ep);
+            return actualClient;
         } catch (IllegalArgumentException ex) {
             String message = ex.getLocalizedMessage();
             if (cri != null) {
@@ -312,7 +318,6 @@ public class JAXRSClientFactoryBean exte
             ep.getEndpointInfo().addExtensor(authPolicy);
         }
         
-        applyFeatures(client);
         client.getConfiguration().setConduitSelector(getConduitSelector(ep));
         client.getConfiguration().setBus(getBus());
         client.getConfiguration().getOutInterceptors().addAll(getOutInterceptors());
@@ -320,6 +325,8 @@ public class JAXRSClientFactoryBean exte
         client.getConfiguration().getInInterceptors().addAll(getInInterceptors());
         client.getConfiguration().getInInterceptors().addAll(ep.getInInterceptors());
 
+        applyFeatures(client);
+        
         if (headers != null && addHeaders) {
             client.headers(headers);
         }

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java Sat Jun  4 16:06:06 2011
@@ -22,7 +22,6 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-import java.net.HttpURLConnection;
 import java.net.URI;
 import java.util.Arrays;
 import java.util.Collection;
@@ -53,10 +52,10 @@ import org.apache.cxf.jaxrs.model.URITem
 import org.apache.cxf.jaxrs.utils.HttpUtils;
 import org.apache.cxf.jaxrs.utils.JAXRSUtils;
 import org.apache.cxf.jaxrs.utils.ParameterizedCollectionType;
+import org.apache.cxf.message.Exchange;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.message.MessageContentsList;
 import org.apache.cxf.phase.Phase;
-import org.apache.cxf.transport.http.HTTPConduit;
 
 
 /**
@@ -65,6 +64,9 @@ import org.apache.cxf.transport.http.HTT
  */
 public class WebClient extends AbstractClient {
     
+    private static final String RESPONSE_CLASS = "response.class";
+    private static final String RESPONSE_TYPE = "response.type";
+    
     protected WebClient(String baseAddress) {
         this(URI.create(baseAddress));
     }
@@ -629,46 +631,80 @@ public class WebClient extends AbstractC
             headers.putSingle(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML_TYPE.toString());
         }
         resetResponse();
-        return doChainedInvocation(httpMethod, headers, body, responseClass, genericType);
+        return doChainedInvocation(httpMethod, headers, body, responseClass, genericType, null, null);
     }
 
+    @Override
+    protected Object retryInvoke(URI newRequestURI, 
+                                 MultivaluedMap<String, String> headers,
+                                 Object body,
+                                 Exchange exchange, 
+                                 Map<String, Object> invContext) throws Throwable {
+        
+        Map<String, Object> reqContext = CastUtils.cast((Map)invContext.get(REQUEST_CONTEXT));
+        String httpMethod = (String)reqContext.get(Message.HTTP_REQUEST_METHOD);
+        Class<?> respClass = (Class)reqContext.get(RESPONSE_CLASS);
+        Type type = (Type)reqContext.get(RESPONSE_TYPE);
+        return doChainedInvocation(httpMethod, headers, body, respClass, type, exchange, invContext);
+    }
+    
     protected Response doChainedInvocation(String httpMethod, 
-        MultivaluedMap<String, String> headers, Object body, Class<?> responseClass, Type genericType) {
-        Throwable primaryError = null;
+                                           MultivaluedMap<String, String> headers, 
+                                           Object body, 
+                                           Class<?> responseClass, 
+                                           Type genericType,
+                                           Exchange exchange,
+                                           Map<String, Object> invContext) {
         
         URI uri = getCurrentURI();
-        Message m = createMessage(httpMethod, headers, uri);
-        m.put(URITemplate.TEMPLATE_PARAMETERS, getState().getTemplates());
+        Message m = createMessage(body, httpMethod, headers, uri, exchange, invContext);
+        
+        Map<String, Object> reqContext = getRequestContext(m);
+        reqContext.put(Message.HTTP_REQUEST_METHOD, httpMethod);
+        reqContext.put(RESPONSE_CLASS, responseClass);
+        reqContext.put(RESPONSE_TYPE, genericType);
+        
         if (body != null) {
-            MessageContentsList contents = new MessageContentsList(body);
-            m.setContent(List.class, contents);
             m.getInterceptorChain().add(new BodyWriter());
-        } else {
-            setEmptyRequestProperty(m, httpMethod);
         }
         setPlainOperationNameProperty(m, httpMethod + ":" + uri.toString());
         
         try {
             m.getInterceptorChain().doIntercept(m);
-            primaryError = m.getExchange().get(Exception.class);
-        } catch (Throwable ex) {
-            primaryError = ex;
+        } catch (Exception ex) {
+            m.setContent(Exception.class, ex);
+        }
+        try {
+            Object[] results = preProcessResult(m);
+            if (results != null && results.length == 1) {
+                // this can happen if a connection exception has occurred and
+                // failover feature used this client to invoke on a different address  
+                return (Response)results[0];
+            }
+        } catch (Exception ex) {
+            throw ex instanceof ServerWebApplicationException 
+                ? (ServerWebApplicationException)ex 
+                : ex instanceof ClientWebApplicationException 
+                ? new ClientWebApplicationException(ex) : new RuntimeException(ex); 
         }
-
         
-        // TODO : this needs to be done in an inbound chain instead
-        HttpURLConnection connect = (HttpURLConnection)m.get(HTTPConduit.KEY_HTTP_CONNECTION);
-        if (connect == null && primaryError != null) {
-            /** do we have a pre-connect error ? */
-            throw new ClientWebApplicationException(primaryError);
+        Response response = null;
+        Object entity = null;
+        try {
+            response = handleResponse(m, responseClass, genericType);
+            entity = response.getEntity();
+            return response;
+        } catch (RuntimeException ex) {
+            entity = ex;
+            throw ex;
+        } finally {
+            completeExchange(entity, m.getExchange());
         }
-        return handleResponse(connect, m, responseClass, genericType);
     }
     
-    protected Response handleResponse(HttpURLConnection conn, Message outMessage, 
-                                      Class<?> responseClass, Type genericType) {
+    protected Response handleResponse(Message outMessage, Class<?> responseClass, Type genericType) {
         try {
-            ResponseBuilder rb = setResponseBuilder(conn, outMessage.getExchange());
+            ResponseBuilder rb = setResponseBuilder(outMessage, outMessage.getExchange());
             Response currentResponse = rb.clone().build();
             
             Object entity = readBody(currentResponse, outMessage, responseClass, genericType,
@@ -678,13 +714,11 @@ public class WebClient extends AbstractC
             
             return rb.build();
         } catch (Throwable ex) {
-            throw new ClientWebApplicationException(ex);
+            throw (ex instanceof ClientWebApplicationException) ? (ClientWebApplicationException)ex
+                                                              : new ClientWebApplicationException(ex);
         }
     }
     
-    protected HttpURLConnection getConnection(String methodName) {
-        return createHttpConnection(getCurrentBuilder().clone().buildFromEncoded(), methodName);
-    }
     
     private class BodyWriter extends AbstractOutDatabindingInterceptor {
 
@@ -725,7 +759,7 @@ public class WebClient extends AbstractC
         newClient.setConfiguration(oldClient.getConfiguration());
     }
     
-    private static AbstractClient toAbstractClient(Client client) {
+    private static AbstractClient toAbstractClient(Object client) {
         if (client instanceof AbstractClient) {
             return (AbstractClient)client;
         } else {

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/features/clustering/FailoverFeature.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/features/clustering/FailoverFeature.java?rev=1131422&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/features/clustering/FailoverFeature.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/features/clustering/FailoverFeature.java Sat Jun  4 16:06:06 2011
@@ -0,0 +1,75 @@
+/**
+ * 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.jaxrs.features.clustering;
+
+import java.util.List;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.clustering.AbstractStaticFailoverStrategy;
+import org.apache.cxf.clustering.FailoverStrategy;
+import org.apache.cxf.clustering.FailoverTargetSelector;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.endpoint.ConduitSelector;
+import org.apache.cxf.interceptor.InterceptorProvider;
+import org.apache.cxf.jaxrs.client.ClientConfiguration;
+
+/**
+ * This feature may be applied to proxy or HTTP-centric clients
+ */
+@NoJSR250Annotations
+public class FailoverFeature extends org.apache.cxf.clustering.FailoverFeature {
+
+    private FailoverTargetSelector customSelector;
+    
+    @Override
+    public void initialize(InterceptorProvider interceptorProvider, Bus bus) {
+        initialize((ClientConfiguration)interceptorProvider, bus);
+    }
+    
+    public void initialize(ClientConfiguration client, Bus bus) {
+        ConduitSelector selector = initTargetSelector(client.getConduitSelector().getEndpoint());
+        client.setConduitSelector(selector);
+    }
+
+    @Override
+    protected FailoverTargetSelector getTargetSelector() {
+        if (customSelector != null) {
+            return customSelector;
+        } else {
+            return super.getTargetSelector();
+        }
+    }
+    
+    @Override
+    public FailoverStrategy getStrategy()  {
+        FailoverStrategy strategy = super.getStrategy();
+        
+        if (strategy == null) {
+            throw new IllegalStateException("Default Strategies are not supported");
+        }
+        
+        if (strategy instanceof AbstractStaticFailoverStrategy) {
+            List<String> altAdresses = ((AbstractStaticFailoverStrategy)strategy).getAlternateAddresses(null);
+            if (altAdresses == null || altAdresses.isEmpty()) {
+                throw new IllegalStateException("Strategy is not initialized");
+            }
+        }
+        return strategy;
+    }
+}

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/features/clustering/FailoverFeature.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/features/clustering/FailoverFeature.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java Sat Jun  4 16:06:06 2011
@@ -47,6 +47,7 @@ import org.apache.commons.httpclient.met
 import org.apache.commons.httpclient.methods.RequestEntity;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.jaxrs.client.ClientWebApplicationException;
 import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
 import org.apache.cxf.jaxrs.client.ResponseReader;
@@ -71,6 +72,18 @@ public class JAXRSClientServerBookTest e
     }
     
     @Test
+    public void testProxyWrongAddress() throws Exception {
+        BookStore store = JAXRSClientFactory.create("http://localhost:8080/wrongaddress", 
+                                                    BookStore.class);
+        try {
+            store.getBook("123");
+            fail("ClientWebApplicationException expected");
+        } catch (ClientWebApplicationException ex) {
+            // expected
+        }
+    }
+    
+    @Test
     public void testGetBookWithCustomHeader() throws Exception {
         
         String endpointAddress =

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java Sat Jun  4 16:06:06 2011
@@ -65,7 +65,7 @@ public class JAXRSMultipartTest extends 
     @BeforeClass
     public static void startServers() throws Exception {
         assertTrue("server did not launch correctly",
-                   launchServer(MultipartServer.class));
+                   launchServer(MultipartServer.class, true));
     }
     
     @Test

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java?rev=1131422&r1=1131421&r2=1131422&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java Sat Jun  4 16:06:06 2011
@@ -89,7 +89,7 @@ public class JAXRSSoapBookTest extends A
     @BeforeClass
     public static void startServers() throws Exception {
         assertTrue("server did not launch correctly", 
-                   launchServer(BookServerRestSoap.class, true));
+                   launchServer(BookServerRestSoap.class));
     }
     
     @Test
@@ -285,7 +285,7 @@ public class JAXRSSoapBookTest extends A
         Book b = new Book("CXF", 1L);
         
         // Just to make sure it is enforced
-        Map<String, Object> props = WebClient.getConfig(client).getResponseContext();
+        Map<String, Object> props = WebClient.getConfig(client).getRequestContext();
         props.put(FIStaxOutInterceptor.FI_ENABLED, Boolean.TRUE);
         
         Book b2 = client.addFastinfoBook(b);
@@ -800,7 +800,8 @@ public class JAXRSSoapBookTest extends A
         } catch (Exception e) {
             assertTrue("Out Interceptor not invoked", testFeature.handleMessageOnOutInterceptorCalled());
             assertTrue("In Interceptor not invoked", !testFeature.handleMessageOnInInterceptorCalled());
-            assertTrue("Wrong exception caught", "fault from bad interceptor".equals(e.getMessage()));
+            assertTrue("Wrong exception caught", 
+                       "fault from bad interceptor".equals(e.getCause().getMessage()));
             assertTrue("Client In Fault In Interceptor was invoked", 
                     !testFeature.faultInInterceptorCalled());
         }

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/FailoverTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/FailoverTest.java?rev=1131422&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/FailoverTest.java (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/FailoverTest.java Sat Jun  4 16:06:06 2011
@@ -0,0 +1,278 @@
+/**
+ * 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.jaxrs.failover;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.clustering.FailoverTargetSelector;
+import org.apache.cxf.clustering.RandomStrategy;
+import org.apache.cxf.clustering.SequentialStrategy;
+import org.apache.cxf.endpoint.ConduitSelector;
+import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.jaxrs.client.ClientWebApplicationException;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.jaxrs.client.ServerWebApplicationException;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.features.clustering.FailoverFeature;
+import org.apache.cxf.systest.jaxrs.Book;
+import org.apache.cxf.systest.jaxrs.BookStore;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+/**
+ * Tests failover within a static cluster.
+ */
+public class FailoverTest extends AbstractBusClientServerTestBase {
+    
+    @BeforeClass
+    public static void startServers() throws Exception {
+        assertTrue("server did not launch correctly",
+                   launchServer(Server.class, true));
+        boolean activeReplica1Started = false;
+        boolean activeReplica2Started = false;
+        for (int i = 0; i < 60; i++) {
+            if (!activeReplica1Started) {
+                activeReplica1Started = checkReplica(Server.ADDRESS2);
+            }
+            if (!activeReplica2Started) {
+                activeReplica2Started = checkReplica(Server.ADDRESS3);
+            }
+            if (activeReplica1Started && activeReplica2Started) {
+                break;
+            }
+            Thread.sleep(1000);    
+        }
+    }
+    private static boolean checkReplica(String address) {
+        try {
+            Response r = WebClient.create(address).query("_wadl").get();
+            return r.getStatus() == 200;
+        } catch (Exception ex) {
+            return false;
+        }
+    }
+    
+    @Test    
+    public void testSequentialStrategy() throws Exception {
+        FailoverFeature feature = 
+            getFeature(false, Server.ADDRESS2, Server.ADDRESS3); 
+        strategyTest(Server.ADDRESS1, feature, Server.ADDRESS2, null, false, false);
+    }
+    
+    @Test
+    public void testSequentialStrategyWebClient() throws Exception {
+        FailoverFeature feature = 
+            getFeature(false, Server.ADDRESS3, Server.ADDRESS2); 
+        strategyTestWebClient(Server.ADDRESS1, feature, Server.ADDRESS3, null, false, false);
+    }
+    
+    @Test    
+    public void testRandomStrategy() throws Exception {
+        FailoverFeature feature = 
+            getFeature(true, Server.ADDRESS2, Server.ADDRESS3); 
+        strategyTest(Server.ADDRESS1, feature, Server.ADDRESS2, Server.ADDRESS3, false, true);
+    }
+    
+    @Test    
+    public void testSequentialStrategyWithDiffBaseAddresses() throws Exception {
+        FailoverFeature feature = 
+            getFeature(false, Server.ADDRESS3, null); 
+        strategyTest(Server.ADDRESS1, feature, Server.ADDRESS3, Server.ADDRESS2, false, false);
+    }
+    
+    @Test(expected = ServerWebApplicationException.class)
+    public void testSequentialStrategyWithServerException() throws Exception {
+        FailoverFeature feature = 
+            getFeature(false, Server.ADDRESS2, Server.ADDRESS3); 
+        strategyTest(Server.ADDRESS1, feature, Server.ADDRESS2, Server.ADDRESS3, true, false);
+    }
+    
+    @Test(expected = ClientWebApplicationException.class)    
+    public void testSequentialStrategyFailure() throws Exception {
+        FailoverFeature feature = 
+            getFeature(false, "http://localhost:8080/non-existent"); 
+        strategyTest(Server.ADDRESS1, feature, null, null, false, false);
+    }
+
+    private FailoverFeature getFeature(boolean random, String ...address) {
+        FailoverFeature feature = new FailoverFeature();
+        List<String> alternateAddresses = new ArrayList<String>();
+        for (String s : address) {
+            alternateAddresses.add(s);
+        }
+        if (!random) {
+            SequentialStrategy strategy = new SequentialStrategy();
+            strategy.setAlternateAddresses(alternateAddresses);
+            feature.setStrategy(strategy);
+        } else {
+            RandomStrategy strategy = new RandomStrategy();
+            strategy.setAlternateAddresses(alternateAddresses);
+            feature.setStrategy(strategy);
+        }
+        return feature;
+    }
+    
+    protected BookStore getBookStore(String address, 
+                                     FailoverFeature feature) throws Exception {
+        JAXRSClientFactoryBean bean = createBean(address, feature);
+        bean.setServiceClass(BookStore.class);
+        return bean.create(BookStore.class);
+    }
+    
+    protected WebClient getWebClient(String address, 
+                                     FailoverFeature feature) throws Exception {
+        JAXRSClientFactoryBean bean = createBean(address, feature);
+        
+        return bean.createWebClient();
+    }
+    
+    protected JAXRSClientFactoryBean createBean(String address, 
+                                                FailoverFeature feature) {
+        JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
+        bean.setAddress(address);
+        List<AbstractFeature> features = new ArrayList<AbstractFeature>();
+        features.add(feature);
+        bean.setFeatures(features);
+        
+        return bean;
+    }
+    
+    protected void strategyTest(String inactiveReplica,
+                                FailoverFeature feature,
+                                String activeReplica1,
+                                String activeReplica2,
+                                boolean expectServerException,
+                                boolean expectRandom) throws Exception {
+        boolean randomized = false;
+        String prevEndpoint = null;
+        for (int i = 0; i < 20; i++) {
+            BookStore bookStore = getBookStore(inactiveReplica, feature);
+            verifyStrategy(bookStore, expectRandom 
+                              ? RandomStrategy.class
+                              : SequentialStrategy.class);
+            String bookId = expectServerException ? "9999" : "123";
+            Exception ex = null;
+            try {
+                Book book = bookStore.getBook(bookId);
+                assertNotNull("expected non-null response", book);
+                assertEquals("unexpected id", 123L, book.getId());
+            } catch (Exception error) {
+                if (!expectServerException) {
+                    //String currEndpoint = getCurrentEndpointAddress(bookStore);
+                    //assertTrue(currEndpoint.equals(inactiveReplica));
+                    throw error;
+                }
+                ex = error;
+            }
+            String currEndpoint = getCurrentEndpointAddress(bookStore);
+            assertFalse(currEndpoint.equals(inactiveReplica));
+            if (expectRandom) {
+                assertTrue(currEndpoint.equals(activeReplica1) || currEndpoint.equals(activeReplica2));
+            } else {
+                assertTrue(currEndpoint.equals(activeReplica1));
+            }
+            if (expectServerException) {
+                assertNotNull(ex);
+                throw ex;
+            }
+            
+            if (!(prevEndpoint == null || currEndpoint.equals(prevEndpoint))) {
+                randomized = true;
+            }
+            prevEndpoint = currEndpoint;
+        }
+        assertEquals("unexpected random/sequential distribution of failovers",
+                     expectRandom,
+                     randomized);
+    }
+    
+    protected void strategyTestWebClient(String inactiveReplica,
+                                FailoverFeature feature,
+                                String activeReplica1,
+                                String activeReplica2,
+                                boolean expectServerException,
+                                boolean expectRandom) throws Exception {
+        boolean randomized = false;
+        String prevEndpoint = null;
+        for (int i = 0; i < 20; i++) {
+            WebClient bookStore = getWebClient(inactiveReplica, feature);
+            verifyStrategy(bookStore, expectRandom 
+                              ? RandomStrategy.class
+                              : SequentialStrategy.class);
+            String bookId = expectServerException ? "9999" : "123";
+            bookStore.path("bookstore/books").path(bookId);
+            Exception ex = null;
+            try {
+                Book book = bookStore.get(Book.class);
+                assertNotNull("expected non-null response", book);
+                assertEquals("unexpected id", 123L, book.getId());
+            } catch (Exception error) {
+                if (!expectServerException) {
+                    throw error;
+                }
+                ex = error;
+            }
+            String currEndpoint = getCurrentEndpointAddress(bookStore);
+            assertFalse(currEndpoint.equals(inactiveReplica));
+            if (expectRandom) {
+                assertTrue(currEndpoint.equals(activeReplica1) || currEndpoint.equals(activeReplica2));
+            } else {
+                assertTrue(currEndpoint.equals(activeReplica1));
+            }
+            if (expectServerException) {
+                assertNotNull(ex);
+                throw ex;
+            }
+            
+            if (!(prevEndpoint == null || currEndpoint.equals(prevEndpoint))) {
+                randomized = true;
+            }
+            prevEndpoint = currEndpoint;
+        }
+        assertEquals("unexpected random/sequential distribution of failovers",
+                     expectRandom,
+                     randomized);
+    }
+
+    
+    protected String getCurrentEndpointAddress(Object client) {
+        return WebClient.client(client).getBaseURI().toString();
+    }
+    
+        
+    protected void verifyStrategy(Object proxy, Class clz) {
+        ConduitSelector conduitSelector =
+            WebClient.getConfig(proxy).getConduitSelector();
+        if (conduitSelector instanceof FailoverTargetSelector) {
+            Object strategy =
+                ((FailoverTargetSelector)conduitSelector).getStrategy();
+            assertTrue("unexpected strategy", clz.isInstance(strategy));
+        } else {
+            fail("unexpected conduit selector: " + conduitSelector);
+        }
+    }
+    
+}

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/FailoverTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/FailoverTest.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/Server.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/Server.java?rev=1131422&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/Server.java (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/Server.java Sat Jun  4 16:06:06 2011
@@ -0,0 +1,62 @@
+/**
+ * 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.jaxrs.failover;
+
+
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.systest.jaxrs.BookStore;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+
+public class Server extends AbstractBusTestServerBase {
+    public static final String PORT1 = allocatePort(Server.class, 0);
+    public static final String PORT2 = allocatePort(Server.class, 1);
+    public static final String PORT3 = allocatePort(Server.class, 3);
+
+    public static final String ADDRESS1 = "http://localhost:" + PORT1 + "/rest";
+    public static final String ADDRESS2 = "http://localhost:" + PORT2 + "/rest";
+    public static final String ADDRESS3 = "http://localhost:" + PORT3 + "/work/rest";
+    
+
+    protected void run()  {
+        createEndpoint(ADDRESS2);
+        createEndpoint(ADDRESS3);
+    }
+    
+    private void createEndpoint(String address) {
+        JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+        sf.setResourceClasses(BookStore.class);
+        sf.setResourceProvider(BookStore.class, new SingletonResourceProvider(new BookStore(), false));
+        sf.setAddress(address);
+        sf.create();
+    }
+
+    public static void main(String[] args) {
+        try { 
+            Server s = new Server(); 
+            s.start();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            System.exit(-1);
+        } finally { 
+            System.out.println("done!");
+        }
+    }
+}

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/Server.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/failover/Server.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date