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/11 19:41:37 UTC
svn commit: r909102 - in /cxf/trunk:
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/org/apache/cxf/sy...
Author: sergeyb
Date: Thu Feb 11 18:41:34 2010
New Revision: 909102
URL: http://svn.apache.org/viewvc?rev=909102&view=rev
Log:
JAXRS : making proxies and webclients optionally thread-safe
Added:
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientState.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/LocalClientState.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ThreadLocalClientState.java (with props)
cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultithreadedClientTest.java (with props)
Modified:
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/JAXRSClientFactory.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/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java
cxf/trunk/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd
cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
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=909102&r1=909101&r2=909102&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 Thu Feb 11 18:41:34 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/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=909102&r1=909101&r2=909102&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 Thu Feb 11 18:41:34 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) {
Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientState.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientState.java?rev=909102&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientState.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientState.java Thu Feb 11 18:41:34 2010
@@ -0,0 +1,101 @@
+/**
+ * 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.client;
+
+import java.net.URI;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriBuilder;
+
+
+/**
+ * Represents the client state :
+ * - baseURI
+ * - current uri builder
+ * - current requestHeaders,
+ * - current response
+ */
+public interface ClientState {
+
+ /**
+ * Sets the current builder
+ * @param currentBuilder the builder
+ */
+ void setCurrentBuilder(UriBuilder currentBuilder);
+
+ /**
+ * Gets the current builder
+ * @return
+ */
+ UriBuilder getCurrentBuilder();
+
+ /**
+ * Sets the base URI
+ * @param baseURI baseURI
+ */
+ void setBaseURI(URI baseURI);
+
+ /**
+ * Gets the base URI
+ * @return baseURI
+ */
+ URI getBaseURI();
+
+ /**
+ * Sets the responseBuilder
+ * @param responseBuilder responseBuilder
+ */
+ void setResponseBuilder(ResponseBuilder responseBuilder);
+
+ /**
+ * Gets the responseBuilder
+ * @return responseBuilder
+ */
+ ResponseBuilder getResponseBuilder();
+
+ /**
+ * Sets the request headers
+ * @param requestHeaders request headers
+ */
+ void setRequestHeaders(MultivaluedMap<String, String> requestHeaders);
+
+ /**
+ * Gets the request headers
+ * @return request headers, may be immutable
+ */
+ MultivaluedMap<String, String> getRequestHeaders();
+
+ /**
+ * Resets the current state to the baseURI
+ *
+ */
+ void reset();
+
+ /**
+ * The factory method for creating a new state.
+ * Example, proxy and WebClient.fromClient will use this method when creating
+ * subresource proxies and new web clients respectively to ensure thet stay
+ * thread-local if needed
+ * @param baseURI baseURI
+ * @param headers request headers
+ * @return client state
+ */
+ ClientState newState(URI baseURI, MultivaluedMap<String, String> headers);
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientState.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientState.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java?rev=909102&r1=909101&r2=909102&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java Thu Feb 11 18:41:34 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/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=909102&r1=909101&r2=909102&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 Thu Feb 11 18:41:34 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;
+ }
+
+
}
Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/LocalClientState.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/LocalClientState.java?rev=909102&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/LocalClientState.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/LocalClientState.java Thu Feb 11 18:41:34 2010
@@ -0,0 +1,104 @@
+/**
+ * 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.client;
+
+import java.net.URI;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+
+/**
+ * Keeps the client state such as the baseURI, currentURI, requestHeaders, current response
+ *
+ */
+public class LocalClientState implements ClientState {
+
+ private MultivaluedMap<String, String> requestHeaders = new MetadataMap<String, String>();
+ private ResponseBuilder responseBuilder;
+ private URI baseURI;
+ private UriBuilder currentBuilder;
+
+ public LocalClientState() {
+
+ }
+
+ public LocalClientState(URI baseURI) {
+ this.baseURI = baseURI;
+ this.currentBuilder = UriBuilder.fromUri(baseURI);
+ }
+
+ public LocalClientState(LocalClientState cs) {
+ this.requestHeaders = new MetadataMap<String, String>(cs.requestHeaders);
+ this.responseBuilder = cs.responseBuilder != null ? cs.responseBuilder.clone() : null;
+ this.baseURI = cs.baseURI;
+ this.currentBuilder = cs.currentBuilder != null ? cs.currentBuilder.clone() : null;
+
+ }
+
+
+
+ public void setCurrentBuilder(UriBuilder currentBuilder) {
+ this.currentBuilder = currentBuilder;
+ }
+
+ public UriBuilder getCurrentBuilder() {
+ return currentBuilder;
+ }
+
+ public void setBaseURI(URI baseURI) {
+ this.baseURI = baseURI;
+ }
+
+ public URI getBaseURI() {
+ return baseURI;
+ }
+
+ public void setResponseBuilder(ResponseBuilder responseBuilder) {
+ this.responseBuilder = responseBuilder;
+ }
+
+ public ResponseBuilder getResponseBuilder() {
+ return responseBuilder;
+ }
+
+ public void setRequestHeaders(MultivaluedMap<String, String> requestHeaders) {
+ this.requestHeaders = requestHeaders;
+ }
+
+ public MultivaluedMap<String, String> getRequestHeaders() {
+ return requestHeaders;
+ }
+
+ public void reset() {
+ requestHeaders.clear();
+ responseBuilder = null;
+ currentBuilder = UriBuilder.fromUri(baseURI);
+ }
+
+ public ClientState newState(URI newBaseURI, MultivaluedMap<String, String> headers) {
+ ClientState state = new LocalClientState(newBaseURI);
+ if (headers != null) {
+ state.setRequestHeaders(headers);
+ }
+ return state;
+ }
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/LocalClientState.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/LocalClientState.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ThreadLocalClientState.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ThreadLocalClientState.java?rev=909102&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ThreadLocalClientState.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ThreadLocalClientState.java Thu Feb 11 18:41:34 2010
@@ -0,0 +1,151 @@
+/**
+ * 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.client;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriBuilder;
+
+/**
+ * Keeps the client state such as the baseURI, currentURI, requestHeaders, current response
+ * in a thread local storage
+ *
+ */
+public class ThreadLocalClientState implements ClientState {
+
+ private Map<Thread, LocalClientState> state =
+ Collections.synchronizedMap(new WeakHashMap<Thread, LocalClientState>());
+
+ private LocalClientState initialState;
+
+ private Map<Thread, Long> checkpointMap;
+ private long secondsToKeepState;
+
+ public ThreadLocalClientState(String baseURI) {
+ this.initialState = new LocalClientState(URI.create(baseURI));
+ }
+
+ public ThreadLocalClientState(LocalClientState initialState) {
+ this.initialState = initialState;
+ }
+
+ public void setCurrentBuilder(UriBuilder currentBuilder) {
+ getState().setCurrentBuilder(currentBuilder);
+ }
+
+ public UriBuilder getCurrentBuilder() {
+ return getState().getCurrentBuilder();
+ }
+
+ public void setBaseURI(URI baseURI) {
+ getState().setBaseURI(baseURI);
+ }
+
+ public URI getBaseURI() {
+ return getState().getBaseURI();
+ }
+
+ public void setResponseBuilder(ResponseBuilder responseBuilder) {
+ getState().setResponseBuilder(responseBuilder);
+ }
+
+ public ResponseBuilder getResponseBuilder() {
+ return getState().getResponseBuilder();
+ }
+
+ public void setRequestHeaders(MultivaluedMap<String, String> requestHeaders) {
+ getState().setRequestHeaders(requestHeaders);
+ }
+
+ public MultivaluedMap<String, String> getRequestHeaders() {
+ return getState().getRequestHeaders();
+ }
+
+ public void reset() {
+ removeThreadLocalState(Thread.currentThread());
+ }
+
+ public ClientState newState(URI baseURI, MultivaluedMap<String, String> headers) {
+ LocalClientState ls = new LocalClientState(baseURI);
+ if (headers != null) {
+ ls.setRequestHeaders(headers);
+ }
+ return new ThreadLocalClientState(ls);
+ }
+
+ private void removeThreadLocalState(Thread t) {
+ state.remove(t);
+ if (checkpointMap != null) {
+ checkpointMap.remove(t);
+ }
+ }
+
+ protected ClientState getState() {
+ LocalClientState cs = state.get(Thread.currentThread());
+ if (cs == null) {
+ cs = new LocalClientState(initialState);
+ state.put(Thread.currentThread(), cs);
+ if (secondsToKeepState > 0) {
+ long currentTime = System.currentTimeMillis();
+ checkpointMap.put(Thread.currentThread(), currentTime);
+ new CleanupThread(Thread.currentThread(), currentTime).start();
+ }
+ }
+ return cs;
+ }
+
+ public void setSecondsToKeepState(long secondsToKeepState) {
+ this.secondsToKeepState = secondsToKeepState;
+ if (secondsToKeepState > 0) {
+ checkpointMap = new ConcurrentHashMap<Thread, Long>();
+ }
+ }
+
+ private class CleanupThread extends Thread {
+ private Thread thread;
+ private long originalTime;
+
+ public CleanupThread(Thread thread, long originalTime) {
+ this.thread = thread;
+ this.originalTime = originalTime;
+ }
+
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(secondsToKeepState);
+ long actualTime = checkpointMap.get(thread);
+ // if times do not match then the original worker thread
+ // has called reset() but came back again to create new local state
+ // hence there's another cleanup thread nearby which will clean the state
+ if (actualTime == originalTime) {
+ removeThreadLocalState(thread);
+ }
+ } catch (InterruptedException ex) {
+ // ignore
+ }
+ }
+ }
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ThreadLocalClientState.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ThreadLocalClientState.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
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=909102&r1=909101&r2=909102&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 Thu Feb 11 18:41:34 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;
@@ -41,7 +42,6 @@
import javax.ws.rs.core.UriBuilder;
import javax.xml.stream.XMLStreamWriter;
-
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBusFactory;
import org.apache.cxf.helpers.CastUtils;
@@ -72,7 +72,11 @@
}
protected WebClient(URI baseAddress) {
- super(baseAddress, baseAddress);
+ super(baseAddress);
+ }
+
+ protected WebClient(ClientState state) {
+ super(state);
}
/**
@@ -96,6 +100,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) {
@@ -103,6 +115,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
@@ -160,9 +186,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;
@@ -587,7 +622,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,
@@ -669,4 +704,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/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java?rev=909102&r1=909101&r2=909102&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java Thu Feb 11 18:41:34 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/trunk/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd?rev=909102&r1=909101&r2=909102&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd Thu Feb 11 18:41:34 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/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java?rev=909102&r1=909101&r2=909102&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java Thu Feb 11 18:41:34 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() {
Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultithreadedClientTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultithreadedClientTest.java?rev=909102&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultithreadedClientTest.java (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultithreadedClientTest.java Thu Feb 11 18:41:34 2010
@@ -0,0 +1,287 @@
+/**
+ * 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;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.client.Client;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class JAXRSMultithreadedClientTest extends AbstractBusClientServerTestBase {
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ assertTrue("server did not launch correctly",
+ launchServer(BookServer.class));
+ }
+
+ @Test
+ public void testStatefulWebClientWithCopy() throws Exception {
+ runWebClients(WebClient.create("http://localhost:9080/bookstore"), 10, false, true);
+ }
+
+ @Test
+ public void testStatefulWebClientThreadLocal() throws Exception {
+ runWebClients(WebClient.create("http://localhost:9080/bookstore", true), 10, true, true);
+ }
+
+ @Test
+ public void testStatefulWebClientThreadLocalWithCopy() throws Exception {
+ runWebClients(WebClient.create("http://localhost:9080/bookstore", true), 10, false, true);
+ }
+
+ @Test
+ public void testSimpleWebClient() throws Exception {
+ WebClient client = WebClient.create("http://localhost:9080/bookstore/booksecho");
+ client.type("text/plain").accept("text/plain").header("CustomHeader", "CustomValue");
+ runWebClients(client, 10, true, false);
+ }
+
+ @Test
+ public void testSimpleProxy() throws Exception {
+ BookStore proxy = JAXRSClientFactory.create("http://localhost:9080", BookStore.class);
+ runProxies(proxy, 10, true, false);
+ }
+
+ @Test
+ public void testThreadSafeProxy() throws Exception {
+ BookStore proxy = JAXRSClientFactory.create("http://localhost:9080", BookStore.class,
+ Collections.emptyList(), true);
+ runProxies(proxy, 10, true, true);
+ }
+
+ @Test
+ public void testThreadSafeProxyWithCopy() throws Exception {
+ BookStore proxy = JAXRSClientFactory.create("http://localhost:9080", BookStore.class,
+ Collections.emptyList(), true);
+ runProxies(proxy, 10, false, true);
+ }
+
+ @Test
+ public void testThreadSafeSubProxy() throws Exception {
+ BookStore proxy = JAXRSClientFactory.create("http://localhost:9080", BookStore.class,
+ Collections.emptyList(), true);
+
+ runProxies(proxy.echoThroughBookStoreSub(), 10, true, true);
+ }
+
+ private void runWebClients(WebClient client, int numberOfClients,
+ boolean threadSafe, boolean stateCanBeChanged) throws Exception {
+ ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ CountDownLatch startSignal = new CountDownLatch(1);
+ CountDownLatch doneSignal = new CountDownLatch(numberOfClients);
+
+ for (int i = 1; i <= numberOfClients; i++) {
+ WebClient wc = !threadSafe ? WebClient.fromClient(client) : client;
+ String bookName = stateCanBeChanged ? Integer.toString(i) : "TheBook";
+ String bookHeader = stateCanBeChanged ? "value" + i : "CustomValue";
+
+ executor.execute(new WebClientWorker(wc, bookName, bookHeader,
+ startSignal, doneSignal, stateCanBeChanged));
+ }
+ startSignal.countDown();
+ doneSignal.await(60, TimeUnit.SECONDS);
+ executor.shutdownNow();
+ assertEquals("Not all invocations have completed", 0, doneSignal.getCount());
+ }
+
+ private void runProxies(BookStore proxy, int numberOfClients,
+ boolean threadSafe, boolean stateCanBeChanged) throws Exception {
+ ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ CountDownLatch startSignal = new CountDownLatch(1);
+ CountDownLatch doneSignal = new CountDownLatch(numberOfClients);
+
+ for (int i = 1; i <= numberOfClients; i++) {
+ // here we do a double copy : from proxy to web client and back to proxy
+ BookStore bs = !threadSafe ? JAXRSClientFactory.fromClient(
+ WebClient.fromClient(WebClient.client(proxy)), BookStore.class) : proxy;
+ String bookName = stateCanBeChanged ? Integer.toString(i) : "TheBook";
+ String bookHeader = stateCanBeChanged ? "value" + i : "CustomValue";
+
+ executor.execute(new RootProxyWorker(bs, bookName, bookHeader,
+ startSignal, doneSignal, stateCanBeChanged));
+ }
+ startSignal.countDown();
+ doneSignal.await(60, TimeUnit.SECONDS);
+ executor.shutdownNow();
+ assertEquals("Not all invocations have completed", 0, doneSignal.getCount());
+ }
+
+ @Ignore
+ private class WebClientWorker implements Runnable {
+
+ private WebClient client;
+ private String bookName;
+ private String bookHeader;
+ private CountDownLatch startSignal;
+ private CountDownLatch doneSignal;
+ private boolean stateCanBeChanged;
+
+ public WebClientWorker(WebClient client,
+ String bookName,
+ String bookHeader,
+ CountDownLatch startSignal,
+ CountDownLatch doneSignal,
+ boolean stateCanBeChanged) {
+ this.client = client;
+ this.bookName = bookName;
+ this.bookHeader = bookHeader;
+ this.startSignal = startSignal;
+ this.doneSignal = doneSignal;
+ this.stateCanBeChanged = stateCanBeChanged;
+ }
+
+ public void run() {
+
+ try {
+ startSignal.await();
+
+ for (int i = 0; i < 5; i++) {
+ if (stateCanBeChanged) {
+ invoke(i);
+ } else {
+ doInvoke(bookName, bookHeader);
+ }
+ }
+
+ doneSignal.countDown();
+ } catch (InterruptedException ex) {
+ // ignore
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("WebClientWorker thread failed for " + bookName + "," + bookHeader);
+ }
+
+ }
+
+ private void invoke(int ind) throws Exception {
+ client.type("text/plain").accept("text/plain");
+
+ String actualHeaderName = bookHeader + ind;
+ String actualBookName = bookName + ind;
+
+ MultivaluedMap<String, String> map = client.getHeaders();
+ map.putSingle("CustomHeader", actualHeaderName);
+ client.headers(map).path("booksecho");
+
+ doInvoke(actualBookName, actualHeaderName);
+
+ // reset current path
+ client.back(true);
+ }
+
+ private void doInvoke(String actualBookName, String actualHeaderName) throws Exception {
+ Response response = client.post(actualBookName);
+
+ assertEquals(actualHeaderName,
+ response.getMetadata().getFirst("CustomHeader").toString());
+ String responseValue = IOUtils.readStringFromStream((InputStream)response.getEntity());
+ assertEquals(actualBookName, responseValue);
+ }
+ }
+
+ @Ignore
+ private class RootProxyWorker implements Runnable {
+
+ private BookStore proxy;
+ private String bookName;
+ private String bookHeader;
+ private CountDownLatch startSignal;
+ private CountDownLatch doneSignal;
+ private boolean stateCanBeChanged;
+
+ public RootProxyWorker(BookStore proxy,
+ String bookName,
+ String bookHeader,
+ CountDownLatch startSignal,
+ CountDownLatch doneSignal,
+ boolean stateCanBeChanged) {
+ this.proxy = proxy;
+ this.bookName = bookName;
+ this.bookHeader = bookHeader;
+ this.startSignal = startSignal;
+ this.doneSignal = doneSignal;
+ this.stateCanBeChanged = stateCanBeChanged;
+ }
+
+ public void run() {
+
+ try {
+ startSignal.await();
+
+ for (int i = 0; i < 5; i++) {
+ invoke(i);
+ }
+
+ doneSignal.countDown();
+ } catch (InterruptedException ex) {
+ // ignore
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("WebClientWorker thread failed for " + bookName + "," + bookHeader);
+ }
+
+ }
+
+ private void invoke(int ind) throws Exception {
+
+ String actualHeaderName = bookHeader + ind;
+ String actualBookName = bookName + ind;
+
+ if (stateCanBeChanged) {
+ Client c = WebClient.client(proxy);
+ MultivaluedMap<String, String> map = c.getHeaders();
+ map.putSingle("CustomHeader", actualHeaderName);
+ c.headers(map);
+ proxy.echoBookNameAndHeader2(actualBookName);
+ verifyResponse(c.getResponse(), actualBookName, actualHeaderName);
+ } else {
+ verifyResponse(proxy.echoBookNameAndHeader(actualHeaderName, actualBookName),
+ actualBookName, actualHeaderName);
+ }
+ }
+
+ private void verifyResponse(Response response, String actualBookName, String actualHeaderName)
+ throws Exception {
+ assertEquals(actualHeaderName,
+ response.getMetadata().getFirst("CustomHeader").toString());
+ String responseValue = IOUtils.readStringFromStream((InputStream)response.getEntity());
+ assertEquals(actualBookName, responseValue);
+ }
+ }
+}
Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultithreadedClientTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultithreadedClientTest.java
------------------------------------------------------------------------------
svn:keywords = Rev Date