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 2010/02/12 15:45:23 UTC

svn commit: r909435 - in /cxf/branches/2.2.x-fixes: ./ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/ rt/frontend/jaxrs/src/main/resources/schemas/ systests/jaxrs/src/test/java...

Author: sergeyb
Date: Fri Feb 12 14:45:22 2010
New Revision: 909435

URL: http://svn.apache.org/viewvc?rev=909435&view=rev
Log:
Merged revisions 909102 via svnmerge from 
https://svn.apache.org/repos/asf/cxf/trunk

........
  r909102 | sergeyb | 2010-02-11 18:41:34 +0000 (Thu, 11 Feb 2010) | 1 line
  
  JAXRS : making proxies and webclients optionally thread-safe
........

Added:
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientState.java
      - copied unchanged from r909102, cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientState.java
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/LocalClientState.java
      - copied unchanged from r909102, cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/LocalClientState.java
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ThreadLocalClientState.java
      - copied unchanged from r909102, cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ThreadLocalClientState.java
    cxf/branches/2.2.x-fixes/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultithreadedClientTest.java
      - copied unchanged from r909102, cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultithreadedClientTest.java
Modified:
    cxf/branches/2.2.x-fixes/   (props changed)
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java
    cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd
    cxf/branches/2.2.x-fixes/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java

Propchange: cxf/branches/2.2.x-fixes/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Feb 12 14:45:22 2010
@@ -1 +1 @@
-/cxf/trunk:908451
+/cxf/trunk:908451,909102

Propchange: cxf/branches/2.2.x-fixes/
------------------------------------------------------------------------------
Binary property 'svnmerge-integrated' - no diff available.

Modified: cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java?rev=909435&r1=909434&r2=909435&view=diff
==============================================================================
--- cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java (original)
+++ cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java Fri Feb 12 14:45:22 2010
@@ -83,16 +83,14 @@
     private static final String RESPONSE_CONTEXT = "ResponseContext";
     
     protected ClientConfiguration cfg = new ClientConfiguration();
+    private ClientState state;
     
-    private MultivaluedMap<String, String> requestHeaders = new MetadataMap<String, String>();
-    private ResponseBuilder responseBuilder;
+    protected AbstractClient(URI baseURI) {
+        this.state = new LocalClientState(baseURI);
+    }
     
-    private URI baseURI;
-    private UriBuilder currentBuilder;
-
-    protected AbstractClient(URI baseURI, URI currentURI) {
-        this.baseURI = baseURI;
-        this.currentBuilder = new UriBuilderImpl(currentURI);
+    protected AbstractClient(ClientState initialState) {
+        this.state = initialState;
     }
     
     /**
@@ -102,20 +100,25 @@
         if (values == null) {
             throw new IllegalArgumentException();
         }
-        if (HttpHeaders.CONTENT_TYPE.equals(name) && values.length > 1) {
-            throw new WebApplicationException();
-        }
-        for (Object o : values) {
-            requestHeaders.add(name, o.toString());
+        if (HttpHeaders.CONTENT_TYPE.equals(name)) {
+            if (values.length > 1) {
+                throw new IllegalArgumentException("Content-Type can have a single value only");
+            }
+            type(values[0].toString());
+        } else {
+            for (Object o : values) {
+                possiblyAddHeader(name, o.toString());
+            }
         }
         return this;
     }
 
+    
     /**
      * {@inheritDoc}
      */
     public Client headers(MultivaluedMap<String, String> map) {
-        requestHeaders.putAll(map);
+        state.getRequestHeaders().putAll(map);
         return this;
     }
     
@@ -124,7 +127,7 @@
      */
     public Client accept(MediaType... types) {
         for (MediaType mt : types) {
-            requestHeaders.add(HttpHeaders.ACCEPT, mt.toString());
+            possiblyAddHeader(HttpHeaders.ACCEPT, mt.toString());
         }
         return this;
     }
@@ -140,7 +143,7 @@
      * {@inheritDoc}
      */
     public Client type(String type) {
-        requestHeaders.putSingle(HttpHeaders.CONTENT_TYPE, type);
+        state.getRequestHeaders().putSingle(HttpHeaders.CONTENT_TYPE, type);
         return this;
     }
 
@@ -149,7 +152,7 @@
      */
     public Client accept(String... types) {
         for (String type : types) {
-            requestHeaders.add(HttpHeaders.ACCEPT, type);
+            possiblyAddHeader(HttpHeaders.ACCEPT, type);
         }
         return this;
     }
@@ -158,7 +161,7 @@
      * {@inheritDoc}
      */
     public Client cookie(Cookie cookie) {
-        requestHeaders.add(HttpHeaders.COOKIE, cookie.toString());
+        possiblyAddHeader(HttpHeaders.COOKIE, cookie.toString());
         return this;
     }
 
@@ -168,7 +171,7 @@
     public Client modified(Date date, boolean ifNot) {
         SimpleDateFormat dateFormat = HttpUtils.getHttpDateFormat();
         String hName = ifNot ? HttpHeaders.IF_UNMODIFIED_SINCE : HttpHeaders.IF_MODIFIED_SINCE;
-        requestHeaders.putSingle(hName, dateFormat.format(date));
+        state.getRequestHeaders().putSingle(hName, dateFormat.format(date));
         return this;
     }
 
@@ -176,7 +179,7 @@
      * {@inheritDoc}
      */
     public Client language(String language) {
-        requestHeaders.putSingle(HttpHeaders.CONTENT_LANGUAGE, language);
+        state.getRequestHeaders().putSingle(HttpHeaders.CONTENT_LANGUAGE, language);
         return this;
     }
 
@@ -185,7 +188,7 @@
      */
     public Client match(EntityTag tag, boolean ifNot) {
         String hName = ifNot ? HttpHeaders.IF_NONE_MATCH : HttpHeaders.IF_MATCH; 
-        requestHeaders.putSingle(hName, tag.toString());
+        state.getRequestHeaders().putSingle(hName, tag.toString());
         return this;
     }
 
@@ -194,7 +197,7 @@
      */
     public Client acceptLanguage(String... languages) {
         for (String s : languages) {
-            requestHeaders.add(HttpHeaders.ACCEPT_LANGUAGE, s);
+            possiblyAddHeader(HttpHeaders.ACCEPT_LANGUAGE, s);
         }
         return this;
     }
@@ -204,7 +207,7 @@
      */
     public Client acceptEncoding(String... encs) {
         for (String s : encs) {
-            requestHeaders.add(HttpHeaders.ACCEPT_ENCODING, s);
+            possiblyAddHeader(HttpHeaders.ACCEPT_ENCODING, s);
         }
         return this;
     }
@@ -213,7 +216,7 @@
      * {@inheritDoc}
      */
     public Client encoding(String enc) {
-        requestHeaders.putSingle(HttpHeaders.CONTENT_ENCODING, enc);
+        state.getRequestHeaders().putSingle(HttpHeaders.CONTENT_ENCODING, enc);
         return this;
     }
 
@@ -222,7 +225,7 @@
      */
     public MultivaluedMap<String, String> getHeaders() {
         MultivaluedMap<String, String> map = new MetadataMap<String, String>();
-        map.putAll(requestHeaders);
+        map.putAll(state.getRequestHeaders());
         return map;
     }
     
@@ -230,7 +233,7 @@
      * {@inheritDoc}
      */
     public URI getBaseURI() {
-        return baseURI;
+        return state.getBaseURI();
     }
     
     /**
@@ -244,37 +247,50 @@
      * {@inheritDoc}
      */
     public Response getResponse() {
-        if (responseBuilder == null) {
+        if (state.getResponseBuilder() == null) {
             return null;
         }
-        return responseBuilder.build();
+        return state.getResponseBuilder().build();
     }
     
     /**
      * {@inheritDoc}
      */
     public Client reset() {
-        requestHeaders.clear();
-        resetResponse();
+        state.reset();
         return this;
     }
 
+    private void possiblyAddHeader(String name, String value) {
+        if (!isDuplicate(name, value)) {
+            state.getRequestHeaders().add(name, value);
+        }
+    }
+    
+    private boolean isDuplicate(String name, String value) {
+        List<String> values = state.getRequestHeaders().get(name);
+        return values != null && values.contains(value) ? true : false;
+    }
+    
+    protected ClientState getState() {
+        return state;
+    }
     
     protected UriBuilder getCurrentBuilder() {
-        return currentBuilder;
+        return state.getCurrentBuilder();
     }
 
     protected void resetResponse() {
-        responseBuilder = null;
+        state.setResponseBuilder(null);
     }
     
     protected void resetBaseAddress(URI uri) {
-        baseURI = uri;
+        state.setBaseURI(uri);
         resetCurrentBuilder(uri);
     }
     
     protected void resetCurrentBuilder(URI uri) {
-        currentBuilder = new UriBuilderImpl(uri);
+        state.setCurrentBuilder(new UriBuilderImpl(uri));
     }
     
     protected ResponseBuilder setResponseBuilder(HttpURLConnection conn, Exchange exchange) throws Throwable {
@@ -297,19 +313,20 @@
             }
         } 
         int status = responseCode.intValue();
-        responseBuilder = Response.status(status);
+        ResponseBuilder currentResponseBuilder = Response.status(status);
+        
         for (Map.Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
             if (null == entry.getKey()) {
                 continue;
             }
             if (HttpUtils.isDateRelatedHeader(entry.getKey())) {
-                responseBuilder.header(entry.getKey(), entry.getValue());
+                currentResponseBuilder.header(entry.getKey(), entry.getValue());
             } else if (entry.getValue().size() > 0) {
                 String[] values = entry.getValue().get(0).split(",");
                 for (String s : values) {
                     String theValue = s.trim();
                     if (theValue.length() > 0) {
-                        responseBuilder.header(entry.getKey(), theValue);
+                        currentResponseBuilder.header(entry.getKey(), theValue);
                     }
                 }
             }
@@ -321,19 +338,21 @@
         if (status >= 400) {
             try {
                 InputStream errorStream = mStream == null ? conn.getErrorStream() : mStream;
-                responseBuilder.entity(errorStream);
+                currentResponseBuilder.entity(errorStream);
             } catch (Exception ex) {
                 // nothing we can do really
             }
         } else {
             try {
                 InputStream stream = mStream == null ? conn.getInputStream() : mStream;
-                responseBuilder.entity(stream);
+                currentResponseBuilder.entity(stream);
             } catch (Exception ex) {
                 // it may that the successful response has no response body
             }
         }
-        return responseBuilder;
+        ResponseBuilder rb = currentResponseBuilder.clone();
+        state.setResponseBuilder(currentResponseBuilder);
+        return rb;
     }
 
     @SuppressWarnings("unchecked")

Modified: cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java?rev=909435&r1=909434&r2=909435&view=diff
==============================================================================
--- cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java (original)
+++ cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java Fri Feb 12 14:45:22 2010
@@ -76,9 +76,18 @@
     private boolean isRoot;
     private Map<String, Object> valuesMap;
     
-    public ClientProxyImpl(URI baseURI, URI currentURI, ClassResourceInfo cri, boolean isRoot, 
+    public ClientProxyImpl(URI baseURI, ClassResourceInfo cri, boolean isRoot, 
                            boolean inheritHeaders, Object... varValues) {
-        super(baseURI, currentURI);
+        super(baseURI);
+        this.cri = cri;
+        this.isRoot = isRoot;
+        this.inheritHeaders = inheritHeaders;
+        initValuesMap(varValues);
+    }
+    
+    public ClientProxyImpl(ClientState initialState, ClassResourceInfo cri, boolean isRoot, 
+                           boolean inheritHeaders, Object... varValues) {
+        super(initialState);
         this.cri = cri;
         this.isRoot = isRoot;
         this.inheritHeaders = inheritHeaders;
@@ -148,15 +157,16 @@
             if (subCri == null) {
                 reportInvalidResourceMethod(m, "INVALID_SUBRESOURCE");
             }
-            ClientProxyImpl proxyImpl = new ClientProxyImpl(getBaseURI(), uri, subCri, false, inheritHeaders);
-            proxyImpl.setConfiguration(getConfiguration());
             
-            Object proxy = JAXRSClientFactory.create(m.getReturnType(), proxyImpl);
+            MultivaluedMap<String, String> subHeaders = paramHeaders;
             if (inheritHeaders) {
-                WebClient.client(proxy).headers(headers);
+                subHeaders.putAll(headers);    
             }
-            WebClient.client(proxy).headers(paramHeaders);
-            return proxy;
+            
+            ClientState newState = getState().newState(uri, headers);
+            ClientProxyImpl proxyImpl = new ClientProxyImpl(newState, subCri, false, inheritHeaders);
+            proxyImpl.setConfiguration(getConfiguration());
+            return JAXRSClientFactory.create(m.getReturnType(), proxyImpl);
         } 
         
         headers.putAll(paramHeaders);
@@ -443,7 +453,7 @@
     
     protected Object handleResponse(HttpURLConnection connect, Message outMessage, OperationResourceInfo ori) 
         throws Throwable {
-        Response r = setResponseBuilder(connect, outMessage.getExchange()).clone().build();
+        Response r = setResponseBuilder(connect, outMessage.getExchange()).build();
         Method method = ori.getMethodToInvoke();
         checkResponse(method, r, outMessage);
         if (method.getReturnType() == Void.class) { 

Modified: cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java?rev=909435&r1=909434&r2=909435&view=diff
==============================================================================
--- cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java (original)
+++ cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java Fri Feb 12 14:45:22 2010
@@ -23,6 +23,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import javax.ws.rs.core.MultivaluedMap;
+
 import org.apache.cxf.common.util.ProxyHelper;
 import org.apache.cxf.jaxrs.model.UserResource;
 
@@ -113,6 +115,23 @@
     }
     
     /**
+     * Creates a thread safe proxy
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param providers list of providers
+     * @param threadSafe if true then a thread-safe proxy will be created
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, List<?> providers, boolean threadSafe) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, null);
+        bean.setProviders(providers);
+        if (threadSafe) {
+            bean.setInitialState(new ThreadLocalClientState(baseAddress));
+        }
+        return bean.create(cls);
+    }
+    
+    /**
      * Creates a proxy
      * @param baseAddress baseAddress
      * @param cls proxy class, if not interface then a CGLIB proxy will be created
@@ -172,6 +191,26 @@
     }
     
     /**
+     * Creates a thread safe proxy using user resource model
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param modelRef model location
+     * @param providers list of providers
+     * @param threadSafe if true then thread-safe proxy will be created 
+     * @return typed proxy
+     */
+    public static <T> T createFromModel(String baseAddress, Class<T> cls, String modelRef, 
+                                        List<?> providers, boolean threadSafe) {
+        JAXRSClientFactoryBean bean = WebClient.getBean(baseAddress, null);
+        bean.setProviders(providers);
+        bean.setModelRef(modelRef);
+        if (threadSafe) {
+            bean.setInitialState(new ThreadLocalClientState(baseAddress));
+        }
+        return bean.create(cls);
+    }
+    
+    /**
      * Creates a proxy using user resource model
      * @param baseAddress baseAddress
      * @param cls proxy class, if not interface then a CGLIB proxy will be created
@@ -219,10 +258,21 @@
      * @return typed proxy
      */
     public static <T> T fromClient(Client client, Class<T> cls, boolean inheritHeaders) {
+        JAXRSClientFactoryBean bean = getBean(client.getCurrentURI().toString(), cls, null);
+        bean.setInheritHeaders(inheritHeaders);
+        
+        ClientState clientState = WebClient.getClientState(client);
         
-        T proxy = create(client.getCurrentURI(), cls, inheritHeaders);
-        if (inheritHeaders) {
-            WebClient.client(proxy).headers(client.getHeaders());
+        T proxy = null;
+        if (clientState == null) {
+            proxy = bean.create(cls);
+            if (inheritHeaders) {
+                WebClient.client(proxy).headers(client.getHeaders());
+            }
+        } else {
+            MultivaluedMap<String, String> headers = inheritHeaders ? client.getHeaders() : null;
+            bean.setInitialState(clientState.newState(client.getCurrentURI(), headers));
+            proxy = bean.create(cls);
         }
         WebClient.copyProperties(WebClient.client(proxy), client);
         return proxy;

Modified: cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java?rev=909435&r1=909434&r2=909435&view=diff
==============================================================================
--- cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java (original)
+++ cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java Fri Feb 12 14:45:22 2010
@@ -46,6 +46,8 @@
     private String password;
     private boolean inheritHeaders; 
     private MultivaluedMap<String, String> headers;
+    private ClientState initialState;
+    private boolean threadSafe; 
     
     public JAXRSClientFactoryBean() {
         this(new JAXRSServiceFactoryBean());
@@ -57,6 +59,10 @@
         
     }
     
+    public void setThreadSafe(boolean threadSafe) {
+        this.threadSafe = threadSafe;
+    }
+    
     public String getUsername() {
         return username;
     }
@@ -108,8 +114,10 @@
         
         try {
             Endpoint ep = createEndpoint();
-            WebClient client = new WebClient(getAddress());
-            initClient(client, ep);
+            ClientState actualState = getActualState();
+            WebClient client = actualState == null ? new WebClient(getAddress())
+                : new WebClient(actualState);
+            initClient(client, ep, actualState == null);
             
             return client;
         } catch (Exception ex) {
@@ -118,6 +126,18 @@
         }
     }
     
+    private ClientState getActualState() {
+        if (threadSafe) {
+            initialState = new ThreadLocalClientState(getAddress());
+        }
+        if (initialState != null) {
+            return headers != null
+                ? initialState.newState(URI.create(getAddress()), headers) : initialState;
+        } else {
+            return null;
+        }
+    }
+    
     public <T> T create(Class<T> cls, Object... varValues) {
         return cls.cast(createWithValues(varValues));
     }
@@ -131,12 +151,18 @@
         ClassResourceInfo cri = null;
         try {
             Endpoint ep = createEndpoint();
-            URI baseURI = URI.create(getAddress());
             cri = serviceFactory.getClassResourceInfo().get(0);
             boolean isRoot = cri.getURITemplate() != null;
-            ClientProxyImpl proxyImpl = new ClientProxyImpl(baseURI, baseURI, cri, isRoot, inheritHeaders,
-                                                            varValues);
-            initClient(proxyImpl, ep);    
+            ClientProxyImpl proxyImpl = null;
+            ClientState actualState = getActualState();
+            if (actualState == null) {
+                proxyImpl = 
+                    new ClientProxyImpl(URI.create(getAddress()), cri, isRoot, inheritHeaders, varValues);
+            } else {
+                proxyImpl = 
+                    new ClientProxyImpl(actualState, cri, isRoot, inheritHeaders, varValues);
+            }
+            initClient(proxyImpl, ep, actualState == null);    
             
             try {
                 return (Client)ProxyHelper.getProxy(cri.getServiceClass().getClassLoader(),
@@ -174,7 +200,7 @@
         return cs;
     }
     
-    protected void initClient(AbstractClient client, Endpoint ep) {
+    protected void initClient(AbstractClient client, Endpoint ep, boolean addHeaders) {
         
         if (username != null) {
             AuthorizationPolicy authPolicy = new AuthorizationPolicy();
@@ -188,9 +214,11 @@
         client.getConfiguration().setBus(getBus());
         client.getConfiguration().getOutInterceptors().addAll(getOutInterceptors());
         client.getConfiguration().getInInterceptors().addAll(getInInterceptors());
-        if (headers != null) {
+
+        if (headers != null && addHeaders) {
             client.headers(headers);
         }
+        
         setupFactory(ep);
     }
     
@@ -201,4 +229,10 @@
             }
         }
     }
+
+    public void setInitialState(ClientState initialState) {
+        this.initialState = initialState;
+    }
+
+    
 } 

Modified: cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java?rev=909435&r1=909434&r2=909435&view=diff
==============================================================================
--- cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java (original)
+++ cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java Fri Feb 12 14:45:22 2010
@@ -25,6 +25,7 @@
 import java.net.HttpURLConnection;
 import java.net.URI;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -40,7 +41,6 @@
 import javax.ws.rs.core.Response.ResponseBuilder;
 import javax.ws.rs.core.UriBuilder;
 
-
 import org.apache.cxf.Bus;
 import org.apache.cxf.bus.spring.SpringBusFactory;
 import org.apache.cxf.helpers.CastUtils;
@@ -71,7 +71,11 @@
     }
     
     protected WebClient(URI baseAddress) {
-        super(baseAddress, baseAddress);
+        super(baseAddress);
+    }
+    
+    protected WebClient(ClientState state) {
+        super(state);
     }
     
     /**
@@ -95,6 +99,14 @@
     /**
      * Creates WebClient
      * @param baseURI baseURI
+     */
+    public static WebClient create(String baseURI, boolean threadSafe) {
+        return create(baseURI, Collections.emptyList(), threadSafe);
+    }
+    
+    /**
+     * Creates WebClient
+     * @param baseURI baseURI
      * @param providers list of providers
      */
     public static WebClient create(String baseAddress, List<?> providers) {
@@ -102,6 +114,20 @@
     }
     
     /**
+     * Creates WebClient
+     * @param baseURI baseURI
+     * @param providers list of providers
+     */
+    public static WebClient create(String baseAddress, List<?> providers, boolean threadSafe) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, null);
+        bean.setProviders(providers);
+        if (threadSafe) {
+            bean.setInitialState(new ThreadLocalClientState(baseAddress));
+        }
+        return bean.createWebClient();        
+    }
+    
+    /**
      * Creates a Spring-configuration aware WebClient
      * @param baseAddress baseAddress
      * @param providers list of providers
@@ -159,9 +185,18 @@
      *        and subresource proxies if any 
      */
     public static WebClient fromClient(Client client, boolean inheritHeaders) {
-        WebClient webClient = create(client.getCurrentURI());
-        if (inheritHeaders) {
-            webClient.headers(client.getHeaders());
+        
+        WebClient webClient = null;
+        
+        ClientState clientState = getClientState(client);
+        if (clientState == null) {
+            webClient = create(client.getCurrentURI());
+            if (inheritHeaders) {
+                webClient.headers(client.getHeaders());
+            }
+        } else {
+            MultivaluedMap<String, String> headers = inheritHeaders ? client.getHeaders() : null;
+            webClient = new WebClient(clientState.newState(client.getCurrentURI(), headers));
         }
         copyProperties(webClient, client);
         return webClient;
@@ -586,7 +621,7 @@
     protected Response handleResponse(HttpURLConnection conn, Message outMessage, 
                                       Class<?> responseClass, Type genericType) {
         try {
-            ResponseBuilder rb = setResponseBuilder(conn, outMessage.getExchange()).clone();
+            ResponseBuilder rb = setResponseBuilder(conn, outMessage.getExchange());
             Response currentResponse = rb.clone().build();
             
             Object entity = readBody(currentResponse, conn, outMessage, responseClass, genericType,
@@ -665,4 +700,15 @@
         bean.setAddress(baseAddress);
         return bean;
     }
+    
+    static ClientState getClientState(Client client) {
+        ClientState clientState = null;
+        if (client instanceof WebClient) { 
+            clientState = ((AbstractClient)client).getState();
+        } else if (client instanceof InvocationHandlerAware) {
+            Object handler = ((InvocationHandlerAware)client).getInvocationHandler();
+            clientState = ((AbstractClient)handler).getState();
+        }
+        return clientState;
+    }
 }

Modified: cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java?rev=909435&r1=909434&r2=909435&view=diff
==============================================================================
--- cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java (original)
+++ cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java Fri Feb 12 14:45:22 2010
@@ -134,6 +134,18 @@
         return value == null ? null : URI.create(value);
     }
     
+    public URI[] getLinks(String expression, Map<String, String> namespaces) {
+        String[] values = getValues(expression, namespaces);
+        if (values == null) {
+            return null;
+        }
+        URI[] uris = new URI[values.length];
+        for (int i = 0; i < values.length; i++) {
+            uris[i] = URI.create(values[i]);
+        }
+        return uris;
+    }
+    
     public URI getBaseURI() {
         Map<String, String> map = new LinkedHashMap<String, String>();
         map.put("xml", XML_NAMESPACE);

Modified: cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd
URL: http://svn.apache.org/viewvc/cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd?rev=909435&r1=909434&r2=909435&view=diff
==============================================================================
--- cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd (original)
+++ cxf/branches/2.2.x-fixes/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd Fri Feb 12 14:45:22 2010
@@ -101,6 +101,7 @@
           <xsd:attribute name="username" type="xsd:string"/>
           <xsd:attribute name="password" type="xsd:string"/>
           <xsd:attribute name="serviceName" type="xsd:QName"/>
+          <xsd:attribute name="threadSafe" type="xsd:boolean"/>
         </xsd:extension>
       </xsd:complexContent>
     </xsd:complexType>

Modified: cxf/branches/2.2.x-fixes/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.2.x-fixes/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java?rev=909435&r1=909434&r2=909435&view=diff
==============================================================================
--- cxf/branches/2.2.x-fixes/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java (original)
+++ cxf/branches/2.2.x-fixes/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java Fri Feb 12 14:45:22 2010
@@ -633,6 +633,27 @@
         return new Long(theBookId);
     }
     
+    @POST
+    @Path("/booksecho")
+    @Consumes("text/plain")
+    @Produces("text/plain")
+    public Response echoBookNameAndHeader(@HeaderParam("CustomHeader") String headerValue, String name) {
+        return Response.ok().entity(name).header("CustomHeader", headerValue).build();
+    }
+    
+    @Path("/bookstoresub")
+    public BookStore echoThroughBookStoreSub() {
+        return this;
+    }
+    
+    @POST
+    @Path("/booksecho2")
+    @Consumes("text/plain")
+    @Produces("text/plain")
+    public Response echoBookNameAndHeader2(String name) {
+        return echoBookNameAndHeader(httpHeaders.getRequestHeader("CustomHeader").get(0), name);
+    }
+    
     @GET
     @Path("/cd/{CDId}/")
     public CD getCD() {