blob: 5efb0b10e66219076793161dafb1a42b5f654d55 [file] [log] [blame]
/**
* 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.spec;
import java.net.URI;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Configurable;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import org.apache.cxf.common.util.ClassHelper;
import org.apache.cxf.common.util.SystemPropertyAction;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.jaxrs.client.ClientConfiguration;
import org.apache.cxf.jaxrs.client.ClientProviderFactory;
import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.jaxrs.model.FilterProviderInfo;
import org.apache.cxf.message.Message;
import org.apache.cxf.transport.https.SSLUtils;
public class ClientImpl implements Client {
private static final String HTTP_CONNECTION_TIMEOUT_PROP = "http.connection.timeout";
private static final String HTTP_RECEIVE_TIMEOUT_PROP = "http.receive.timeout";
private static final String HTTP_PROXY_SERVER_PROP = "http.proxy.server.uri";
private static final String HTTP_PROXY_SERVER_PORT_PROP = "http.proxy.server.port";
private static final String HTTP_AUTOREDIRECT_PROP = "http.autoredirect";
private static final String HTTP_MAINTAIN_SESSION_PROP = "http.maintain.session";
private static final String HTTP_RESPONSE_AUTOCLOSE_PROP = "http.response.stream.auto.close";
private static final String THREAD_SAFE_CLIENT_PROP = "thread.safe.client";
private static final String THREAD_SAFE_CLIENT_STATE_CLEANUP_PROP = "thread.safe.client.state.cleanup.period";
private static final Boolean DEFAULT_THREAD_SAFETY_CLIENT_STATUS;
private static final Integer THREAD_SAFE_CLIENT_STATE_CLEANUP_PERIOD;
static {
DEFAULT_THREAD_SAFETY_CLIENT_STATUS =
Boolean.parseBoolean(SystemPropertyAction.getPropertyOrNull(THREAD_SAFE_CLIENT_PROP));
THREAD_SAFE_CLIENT_STATE_CLEANUP_PERIOD =
getIntValue(SystemPropertyAction.getPropertyOrNull(THREAD_SAFE_CLIENT_STATE_CLEANUP_PROP));
}
private Configurable<Client> configImpl;
private TLSConfiguration secConfig;
private boolean closed;
private Set<WebClient> baseClients =
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<WebClient, Boolean>()));
public ClientImpl(Configuration config,
TLSConfiguration secConfig) {
configImpl = new ClientConfigurableImpl<Client>(this, config);
this.secConfig = secConfig;
}
@Override
public void close() {
if (!closed) {
synchronized (baseClients) {
for (WebClient wc : baseClients) {
wc.close();
}
}
baseClients = null;
closed = true;
}
}
@Override
public Builder invocation(Link link) {
checkNull(link);
checkClosed();
Builder builder = target(link.getUriBuilder()).request();
String type = link.getType();
if (type != null) {
builder.accept(type);
}
return builder;
}
@Override
public WebTarget target(UriBuilder builder) {
checkNull(builder);
checkClosed();
return new WebTargetImpl(builder, getConfiguration());
}
@Override
public WebTarget target(String address) {
checkNull(address);
if (address.isEmpty()) {
address = "/";
}
return target(UriBuilder.fromUri(address));
}
@Override
public WebTarget target(Link link) {
checkNull(link);
return target(link.getUriBuilder());
}
@Override
public WebTarget target(URI uri) {
checkNull(uri);
return target(UriBuilder.fromUri(uri));
}
private void checkNull(Object... target) {
for (Object o : target) {
if (o == null) {
throw new NullPointerException("Value is null");
}
}
}
@Override
public HostnameVerifier getHostnameVerifier() {
checkClosed();
return secConfig.getTlsClientParams().getHostnameVerifier();
}
@Override
public SSLContext getSslContext() {
checkClosed();
if (secConfig.getSslContext() != null) {
return secConfig.getSslContext();
} else if (secConfig.getTlsClientParams().getTrustManagers() != null) {
try {
return SSLUtils.getSSLContext(secConfig.getTlsClientParams());
} catch (Exception ex) {
throw new ProcessingException(ex);
}
} else {
return null;
}
}
private void checkClosed() {
if (closed) {
throw new IllegalStateException("client is closed");
}
}
@Override
public Configuration getConfiguration() {
checkClosed();
return configImpl.getConfiguration();
}
@Override
public Client property(String name, Object value) {
checkClosed();
return configImpl.property(name, value);
}
@Override
public Client register(Class<?> cls) {
checkClosed();
return configImpl.register(cls);
}
@Override
public Client register(Object object) {
checkClosed();
return configImpl.register(object);
}
@Override
public Client register(Class<?> cls, int index) {
checkClosed();
return configImpl.register(cls, index);
}
@Override
public Client register(Class<?> cls, Class<?>... contracts) {
checkClosed();
return configImpl.register(cls, contracts);
}
@Override
public Client register(Class<?> cls, Map<Class<?>, Integer> map) {
checkClosed();
return configImpl.register(cls, map);
}
@Override
public Client register(Object object, int index) {
checkClosed();
return configImpl.register(object, index);
}
@Override
public Client register(Object object, Class<?>... contracts) {
checkClosed();
return configImpl.register(object, contracts);
}
@Override
public Client register(Object object, Map<Class<?>, Integer> map) {
checkClosed();
return configImpl.register(object, map);
}
public class WebTargetImpl implements WebTarget {
private Configurable<WebTarget> configImpl;
private UriBuilder uriBuilder;
private WebClient targetClient;
public WebTargetImpl(UriBuilder uriBuilder,
Configuration config) {
this(uriBuilder, config, null);
}
public WebTargetImpl(UriBuilder uriBuilder,
Configuration config,
WebClient targetClient) {
this.configImpl = new ClientConfigurableImpl<WebTarget>(this, config);
this.uriBuilder = uriBuilder.clone();
this.targetClient = targetClient;
}
public WebClient getWebClient() {
return this.targetClient;
}
@Override
public Builder request() {
checkClosed();
Map<String, Object> configProps = getConfiguration().getProperties();
initTargetClientIfNeeded(configProps);
ClientProviderFactory pf =
ClientProviderFactory.getInstance(WebClient.getConfig(targetClient).getEndpoint());
List<Object> providers = new LinkedList<Object>();
List<org.apache.cxf.feature.Feature> cxfFeatures =
new LinkedList<org.apache.cxf.feature.Feature>();
Configuration cfg = configImpl.getConfiguration();
for (Object p : cfg.getInstances()) {
if (p instanceof org.apache.cxf.feature.Feature) {
cxfFeatures.add((org.apache.cxf.feature.Feature)p);
} else if (!(p instanceof Feature)) {
Map<Class<?>, Integer> contracts = cfg.getContracts(p.getClass());
if (contracts == null || contracts.isEmpty()) {
providers.add(p);
} else {
final Class<?> providerCls = ClassHelper.getRealClass(pf.getBus(), p);
providers.add(new FilterProviderInfo<Object>(p.getClass(),
providerCls, p, pf.getBus(), contracts));
}
}
}
pf.setUserProviders(providers);
ClientConfiguration clientCfg = WebClient.getConfig(targetClient);
clientCfg.getRequestContext().putAll(configProps);
clientCfg.getRequestContext().put(Client.class.getName(), ClientImpl.this);
clientCfg.getRequestContext().put(Configuration.class.getName(),
getConfiguration());
// Response auto-close
Boolean responseAutoClose = getBooleanValue(configProps.get(HTTP_RESPONSE_AUTOCLOSE_PROP));
if (responseAutoClose != null) {
clientCfg.getResponseContext().put("response.stream.auto.close", responseAutoClose);
}
// TLS
TLSClientParameters tlsParams = secConfig.getTlsClientParams();
if (tlsParams.getSSLSocketFactory() != null
|| tlsParams.getTrustManagers() != null
|| tlsParams.getHostnameVerifier() != null) {
clientCfg.getHttpConduit().setTlsClientParameters(tlsParams);
}
setConnectionProperties(configProps, clientCfg);
// CXF Features
for (org.apache.cxf.feature.Feature cxfFeature : cxfFeatures) {
cxfFeature.initialize(clientCfg, clientCfg.getBus());
}
// Start building the invocation
return new InvocationBuilderImpl(WebClient.fromClient(targetClient));
}
private void setConnectionProperties(Map<String, Object> configProps, ClientConfiguration clientCfg) {
Long connTimeOutValue = getLongValue(configProps.get(HTTP_CONNECTION_TIMEOUT_PROP));
if (connTimeOutValue != null) {
clientCfg.getHttpConduit().getClient().setConnectionTimeout(connTimeOutValue);
}
Long recTimeOutValue = getLongValue(configProps.get(HTTP_RECEIVE_TIMEOUT_PROP));
if (recTimeOutValue != null) {
clientCfg.getHttpConduit().getClient().setReceiveTimeout(recTimeOutValue);
}
Object proxyServerValue = configProps.get(HTTP_PROXY_SERVER_PROP);
if (proxyServerValue != null) {
clientCfg.getHttpConduit().getClient().setProxyServer((String)proxyServerValue);
}
Integer proxyServerPortValue = getIntValue(configProps.get(HTTP_PROXY_SERVER_PORT_PROP));
if (proxyServerPortValue != null) {
clientCfg.getHttpConduit().getClient().setProxyServerPort(proxyServerPortValue);
}
Boolean autoRedirectValue = getBooleanValue(configProps.get(HTTP_AUTOREDIRECT_PROP));
if (autoRedirectValue != null) {
clientCfg.getHttpConduit().getClient().setAutoRedirect(autoRedirectValue);
}
Boolean mantainSessionValue = getBooleanValue(configProps.get(HTTP_MAINTAIN_SESSION_PROP));
if (mantainSessionValue != null) {
clientCfg.getRequestContext().put(Message.MAINTAIN_SESSION, mantainSessionValue);
}
}
private void initTargetClientIfNeeded(Map<String, Object> configProps) {
URI uri = uriBuilder.build();
if (targetClient == null) {
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(uri.toString());
bean.setProperties(configProps);
Boolean threadSafe = getBooleanValue(configProps.get(THREAD_SAFE_CLIENT_PROP));
if (threadSafe == null) {
threadSafe = DEFAULT_THREAD_SAFETY_CLIENT_STATUS;
}
bean.setThreadSafe(threadSafe);
if (threadSafe) {
Integer cleanupPeriod = getIntValue(configProps.get(THREAD_SAFE_CLIENT_STATE_CLEANUP_PROP));
if (cleanupPeriod == null) {
cleanupPeriod = THREAD_SAFE_CLIENT_STATE_CLEANUP_PERIOD;
}
if (cleanupPeriod != null) {
bean.setSecondsToKeepState(cleanupPeriod);
}
}
targetClient = bean.createWebClient();
ClientImpl.this.baseClients.add(targetClient);
} else if (!targetClient.getCurrentURI().equals(uri)) {
targetClient.to(uri.toString(), false);
}
}
@Override
public Builder request(String... accept) {
return request().accept(accept);
}
@Override
public Builder request(MediaType... accept) {
return request().accept(accept);
}
@Override
public URI getUri() {
checkClosed();
return uriBuilder.build();
}
@Override
public UriBuilder getUriBuilder() {
checkClosed();
return uriBuilder.clone();
}
@Override
public WebTarget path(String path) {
checkClosed();
checkNull(path);
return newWebTarget(getUriBuilder().path(path));
}
@Override
public WebTarget queryParam(String name, Object... values) {
checkClosed();
checkNullValues(name, values);
UriBuilder thebuilder = getUriBuilder();
if (values == null || values.length == 1 && values[0] == null) {
thebuilder.replaceQueryParam(name, (Object[])null);
} else {
thebuilder.queryParam(name, values);
}
return newWebTarget(thebuilder);
}
@Override
public WebTarget matrixParam(String name, Object... values) {
checkClosed();
checkNullValues(name, values);
UriBuilder thebuilder = getUriBuilder();
if (values == null || values.length == 1 && values[0] == null) {
thebuilder.replaceMatrixParam(name, (Object[])null);
} else {
thebuilder.matrixParam(name, values);
}
return newWebTarget(thebuilder);
}
@Override
public WebTarget resolveTemplate(String name, Object value) {
return resolveTemplate(name, value, true);
}
@Override
public WebTarget resolveTemplate(String name, Object value, boolean encodeSlash) {
checkClosed();
checkNull(name, value);
return newWebTarget(getUriBuilder().resolveTemplate(name, value, encodeSlash));
}
@Override
public WebTarget resolveTemplateFromEncoded(String name, Object value) {
checkNull(name, value);
return newWebTarget(getUriBuilder().resolveTemplateFromEncoded(name, value));
}
@Override
public WebTarget resolveTemplates(Map<String, Object> templatesMap) {
return resolveTemplates(templatesMap, true);
}
@Override
public WebTarget resolveTemplates(Map<String, Object> templatesMap, boolean encodeSlash) {
checkClosed();
checkNullMap(templatesMap);
if (templatesMap.isEmpty()) {
return this;
}
return newWebTarget(getUriBuilder().resolveTemplates(templatesMap, encodeSlash));
}
@Override
public WebTarget resolveTemplatesFromEncoded(Map<String, Object> templatesMap) {
checkClosed();
checkNullMap(templatesMap);
if (templatesMap.isEmpty()) {
return this;
}
return newWebTarget(getUriBuilder().resolveTemplatesFromEncoded(templatesMap));
}
private WebTarget newWebTarget(UriBuilder newBuilder) {
WebClient newClient;
if (targetClient != null) {
newClient = WebClient.fromClient(targetClient);
} else {
newClient = null;
}
return new WebTargetImpl(newBuilder, getConfiguration(), newClient);
}
@Override
public Configuration getConfiguration() {
checkClosed();
return configImpl.getConfiguration();
}
@Override
public WebTarget property(String name, Object value) {
checkClosed();
return configImpl.property(name, value);
}
@Override
public WebTarget register(Class<?> cls) {
checkClosed();
return configImpl.register(cls);
}
@Override
public WebTarget register(Object object) {
checkClosed();
return configImpl.register(object);
}
@Override
public WebTarget register(Class<?> cls, int index) {
checkClosed();
return configImpl.register(cls, index);
}
@Override
public WebTarget register(Class<?> cls, Class<?>... contracts) {
checkClosed();
return configImpl.register(cls, contracts);
}
@Override
public WebTarget register(Class<?> cls, Map<Class<?>, Integer> map) {
checkClosed();
return configImpl.register(cls, map);
}
@Override
public WebTarget register(Object object, int index) {
checkClosed();
return configImpl.register(object, index);
}
@Override
public WebTarget register(Object object, Class<?>... contracts) {
checkClosed();
return configImpl.register(object, contracts);
}
@Override
public WebTarget register(Object object, Map<Class<?>, Integer> map) {
checkClosed();
return configImpl.register(object, map);
}
private void checkNullValues(Object name, Object... values) {
checkNull(name);
if (values != null && values.length > 1) {
checkNull(values);
}
}
private void checkNullMap(Map<String, Object> templatesMap) {
checkNull(templatesMap);
checkNull(templatesMap.keySet().toArray());
checkNull(templatesMap.values().toArray());
}
}
private static Long getLongValue(Object o) {
return o instanceof Long ? (Long)o
: o instanceof String ? Long.valueOf(o.toString())
: o instanceof Integer ? ((Integer)o).longValue() : null;
}
private static Integer getIntValue(Object o) {
return o instanceof Integer ? (Integer)o : o instanceof String ? Integer.valueOf(o.toString()) : null;
}
private static Boolean getBooleanValue(Object o) {
return o instanceof Boolean ? (Boolean)o : o instanceof String ? Boolean.valueOf(o.toString()) : null;
}
}