JaxWsClientInitializer.java
/*
* #%L
* wcm.io
* %%
* Copyright (C) 2016 wcm.io
* %%
* Licensed 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.
* #L%
*/
package io.wcm.caravan.jaxws.consumer;
import java.io.IOException;
import java.security.GeneralSecurityException;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.ws.BindingProvider;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientFactoryBean;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.jaxws.JaxWsClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.cxf.transports.http.configuration.ProxyServerType;
import org.apache.cxf.ws.addressing.AddressingProperties;
import org.apache.cxf.ws.addressing.AttributedURIType;
import org.apache.cxf.ws.addressing.JAXWSAConstants;
import org.apache.cxf.ws.addressing.WSAddressingFeature;
import org.osgi.annotation.versioning.ConsumerType;
import io.wcm.caravan.jaxws.consumer.impl.CertificateLoader;
import io.wcm.caravan.jaxws.consumer.impl.OsgiAwareJaxWsClientFactoryBean;
/**
* Default JAX-WS SOAP client initializer
*/
@ConsumerType
public class JaxWsClientInitializer {
// Use to disable jaxb default validation for CXF
private static final String JAXB_VALIDATION = "set-jaxb-validation-event-handler";
private static final String SCHEMA_VALIDATION = "schema-validation-enabled";
/**
* List of properties of this class that contain sensitive information which should not be logged.
*/
public static final String[] SENSITIVE_PROPERTY_NAMES = new String[] {
"proxyPassword",
"certstorePassword",
"trustStorePassword"
};
private int connectTimeout;
private int socketTimeout;
private String httpUser;
private String httpPassword;
private String proxyHost;
private int proxyPort;
private String proxyUser;
private String proxyPassword;
private String sslContextType = CertificateLoader.SSL_CONTEXT_TYPE_DEFAULT;
private String keyManagerType = CertificateLoader.KEY_MANAGER_TYPE_DEFAULT;
private String keyStoreType = CertificateLoader.KEY_STORE_TYPE_DEFAULT;
private String keyStoreProvider;
private String keyStorePath;
private String keyStorePassword;
private String trustManagerType = CertificateLoader.TRUST_MANAGER_TYPE_DEFAULT;
private String trustStoreType = CertificateLoader.TRUST_STORE_TYPE_DEFAULT;
private String trustStoreProvider;
private String trustStorePath;
private String trustStorePassword;
private String wsAddressingToUri;
private boolean ignoreUnexpectedElements;
private boolean allowChunking = true;
private transient TLSClientParameters tlsClientParameters;
/**
* Initialize JAXWS proxy factory bean
* @param factory JAXWS Proxy factory bean
*/
public void initializeFactory(JaxWsProxyFactoryBean factory) {
// set outgoing security (username/password)
if (StringUtils.isNotEmpty(getHttpUser())) {
factory.setUsername(getHttpUser());
factory.setPassword(getHttpPassword());
}
// enable WS-addressing
if (StringUtils.isNotEmpty(getWSAddressingToUri())) {
factory.getFeatures().add(new WSAddressingFeature());
}
}
/**
* Initialize SOAP client pFactory and create SOAP client object instance.
* @param factory JAXWS proxy pFactory bean (has to be already initialized)
* @return SOAP client object
*/
public Object createClient(JaxWsProxyFactoryBean factory) {
try {
// create port object
Object portObject = createPortObject(factory);
// initialize endpiont client
Client endpointClient = ClientProxy.getClient(portObject);
initializeEndpointClient(endpointClient);
// set http settings
HTTPConduit httpConduit = (HTTPConduit)endpointClient.getConduit();
initializeHttpConduit(httpConduit);
// make sure request context is per thread local
// see http://cxf.apache.org/faq.html#FAQ-AreJAXWSclientproxiesthreadsafe%3F
((BindingProvider)portObject).getRequestContext().put(JaxWsClientProxy.THREAD_LOCAL_REQUEST_CONTEXT, Boolean.TRUE);
// ignore unexpected elements and attributes on data binding
if (isIgnoreUnexpectedElements()) {
// disable schema validation to be upward-compatible to future schema changes
((BindingProvider)portObject).getRequestContext().put(JAXB_VALIDATION, false);
((BindingProvider)portObject).getRequestContext().put(SCHEMA_VALIDATION, false);
}
return portObject;
}
catch (Throwable ex) {
throw new JaxWsClientInitializeException("SOAP client initialization failed "
+ "(" + factory.getServiceClass().getName() + ").", ex);
}
}
/**
* Create port object
* @param factory JAXWS proxy factory bean
* @return Port object
*/
protected Object createPortObject(JaxWsProxyFactoryBean factory) {
return factory.create();
}
/**
* Initialize endpoint client
* @param endpointClient Endpoint client
*/
protected void initializeEndpointClient(Client endpointClient) {
// set destination address for WS-addressing
if (StringUtils.isNotEmpty(getWSAddressingToUri())) {
endpointClient.getOutInterceptors().add(new AbstractPhaseInterceptor<Message>(Phase.SETUP) {
@Override
public void handleMessage(Message pMessage) throws Fault {
AttributedURIType uri = new AttributedURIType();
uri.setValue(getWSAddressingToUri());
AddressingProperties maps = new AddressingProperties();
maps.setTo(uri);
pMessage.put(JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES, maps);
}
});
}
// add interceptors
addInterceptors(endpointClient);
}
/**
* Add interceptors (e.g. for request/response logging)
* @param endpointClient Endpoint client
*/
protected void addInterceptors(Client endpointClient) {
// can be overridden by subclasses
}
/**
* Initialize HTTP conduit
* @param httpConduit HTTP conduit
* @throws GeneralSecurityException security exception
* @throws IOException I/O exception
*/
protected void initializeHttpConduit(HTTPConduit httpConduit) throws IOException, GeneralSecurityException {
HTTPClientPolicy clientPolicy = httpConduit.getClient();
clientPolicy.setAllowChunking(isAllowChunking());
if (getConnectTimeout() > 0) {
clientPolicy.setConnectionTimeout(getConnectTimeout());
}
if (getSocketTimeout() > 0) {
clientPolicy.setReceiveTimeout(getSocketTimeout());
}
// optionally enable proxy server
if (StringUtils.isNotEmpty(getProxyHost()) && getProxyPort() > 0) {
clientPolicy.setProxyServerType(ProxyServerType.HTTP);
clientPolicy.setProxyServer(getProxyHost());
clientPolicy.setProxyServerPort(getProxyPort());
// optionally define proxy authentication
if (StringUtils.isNotEmpty(getProxyUser())) {
ProxyAuthorizationPolicy proxyAuthentication = new ProxyAuthorizationPolicy();
proxyAuthentication.setUserName(getProxyUser());
proxyAuthentication.setPassword(getProxyPassword());
httpConduit.setProxyAuthorization(proxyAuthentication);
}
}
// setup TLS - enable certificate for WS access
if (CertificateLoader.isSslKeyManagerEnabled(this) || CertificateLoader.isSslTrustStoreEnbaled(this)) {
httpConduit.setTlsClientParameters(getTLSClientParameters());
}
}
/**
* Get JAX-WS client factory bean instance.
* @return Client factory bean.
*/
public ClientFactoryBean createClientFactoryBean() {
return new OsgiAwareJaxWsClientFactoryBean();
}
/**
* @return Connection timeout in ms.
*/
public final int getConnectTimeout() {
return this.connectTimeout;
}
/**
* @param value Connection timeout in ms.
*/
public final void setConnectTimeout(int value) {
this.connectTimeout = value;
}
/**
* @return Response timeout in ms.
*/
public final int getSocketTimeout() {
return this.socketTimeout;
}
/**
* @param value Response timeout in ms.
*/
public final void setSocketTimeout(int value) {
this.socketTimeout = value;
}
/**
* @return Http basic authentication user.
*/
public final String getHttpUser() {
return this.httpUser;
}
/**
* @param value Http basic authentication user.
*/
public final void setHttpUser(String value) {
this.httpUser = value;
}
/**
* @return Http basic authentication password
*/
public final String getHttpPassword() {
return this.httpPassword;
}
/**
* @param value Http basic authentication password
*/
public final void setHttpPassword(String value) {
this.httpPassword = value;
}
/**
* @return Proxy host name
*/
public final String getProxyHost() {
return this.proxyHost;
}
/**
* @param value Proxy host name
*/
public final void setProxyHost(String value) {
this.proxyHost = value;
}
/**
* @return Proxy port
*/
public final int getProxyPort() {
return this.proxyPort;
}
/**
* @param value Proxy port
*/
public final void setProxyPort(int value) {
this.proxyPort = value;
}
/**
* @return Proxy user name
*/
public final String getProxyUser() {
return this.proxyUser;
}
/**
* @param value Proxy user name
*/
public final void setProxyUser(String value) {
this.proxyUser = value;
}
/**
* @return Proxy password
*/
public final String getProxyPassword() {
return this.proxyPassword;
}
/**
* @param value Proxy password
*/
public final void setProxyPassword(String value) {
this.proxyPassword = value;
}
/**
* @return SSL context type (default: TLS)
*/
public final String getSslContextType() {
return this.sslContextType;
}
/**
* @param value SSL context type (default: TLS)
*/
public final void setSslContextType(String value) {
this.sslContextType = value;
this.tlsClientParameters = null;
}
/**
* @return Key manager type (default: SunX509)
*/
public final String getKeyManagerType() {
return this.keyManagerType;
}
/**
* @param value Key manager type (default: SunX509)
*/
public final void setKeyManagerType(String value) {
this.keyManagerType = value;
this.tlsClientParameters = null;
}
/**
* @return Key store type (default: PKCS12)
*/
public final String getKeyStoreType() {
return this.keyStoreType;
}
/**
* @param value Key store type (default: PKCS12)
*/
public final void setKeyStoreType(String value) {
this.keyStoreType = value;
this.tlsClientParameters = null;
}
/**
* @return Key store provider (default: null = use first matching security provider)
*/
public String getKeyStoreProvider() {
return this.keyStoreProvider;
}
/**
* @param value Key store provider (default: null = use first matching security provider)
*/
public void setKeyStoreProvider(String value) {
this.keyStoreProvider = value;
this.tlsClientParameters = null;
}
/**
* @return Key store file path
*/
public final String getKeyStorePath() {
return this.keyStorePath;
}
/**
* @param value Key store file path
*/
public final void setKeyStorePath(String value) {
this.keyStorePath = value;
this.tlsClientParameters = null;
}
/**
* @return Key store password
*/
public final String getKeyStorePassword() {
return this.keyStorePassword;
}
/**
* @param value Key store password
*/
public final void setKeyStorePassword(String value) {
this.keyStorePassword = value;
this.tlsClientParameters = null;
}
/**
* @return Trust manager type (default: SunX509)
*/
public final String getTrustManagerType() {
return this.trustManagerType;
}
/**
* @param value Trust manager type (default: SunX509)
*/
public final void setTrustManagerType(String value) {
this.trustManagerType = value;
this.tlsClientParameters = null;
}
/**
* @return Trust store type (default: JKS)
*/
public final String getTrustStoreType() {
return this.trustStoreType;
}
/**
* @param value Trust store type (default: JKS)
*/
public final void setTrustStoreType(String value) {
this.trustStoreType = value;
this.tlsClientParameters = null;
}
/**
* @return Trust store provider (default: null = use first matching security provider)
*/
public String getTrustStoreProvider() {
return this.trustStoreProvider;
}
/**
* @param value Trust store provider (default: null = use first matching security provider)
*/
public void setTrustStoreProvider(String value) {
this.trustStoreProvider = value;
this.tlsClientParameters = null;
}
/**
* @return Trust store file path
*/
public final String getTrustStorePath() {
return this.trustStorePath;
}
/**
* @param value Trust store file path
*/
public final void setTrustStorePath(String value) {
this.trustStorePath = value;
this.tlsClientParameters = null;
}
/**
* @return Trust store password
*/
public final String getTrustStorePassword() {
return this.trustStorePassword;
}
/**
* @param vaule Trust store password
*/
public final void setTrustStorePassword(String vaule) {
this.trustStorePassword = vaule;
this.tlsClientParameters = null;
}
/**
* Create TLS client parameters based on given certstore path/password parameters.
* Caches the parameter in member variable of this factory.
* @return TLS client parameters
* @throws IOException I/O exception
* @throws GeneralSecurityException General security exception
*/
public final TLSClientParameters getTLSClientParameters() throws IOException, GeneralSecurityException {
if (tlsClientParameters == null) {
TLSClientParameters tlsCP = new TLSClientParameters();
// initialize certstore
if (CertificateLoader.isSslTrustStoreEnbaled(this)) {
try {
KeyManagerFactory keyManagerFactory = CertificateLoader.getKeyManagerFactory(this);
tlsCP.setKeyManagers(keyManagerFactory.getKeyManagers());
}
catch (Throwable ex) {
throw new RuntimeException("Unable to initialize certificate store for SOAP endpoint.\n"
+ "Please check configuration parameters 'certstorePath' and 'certstorePassword' in this config:\n"
+ this.toString(), ex);
}
}
// initialize trustStore
if (CertificateLoader.isSslTrustStoreEnbaled(this)) {
try {
TrustManagerFactory trustManagerFactory = CertificateLoader.getTrustManagerFactory(this);
tlsCP.setTrustManagers(trustManagerFactory.getTrustManagers());
}
catch (Throwable ex) {
throw new RuntimeException("Unable to initialize trust store for SOAP endpoint.\n"
+ "Please check configuration parameters 'trustStorePath' and 'trustStorePassword' in this config:\n"
+ this.toString(), ex);
}
}
tlsClientParameters = tlsCP;
}
return tlsClientParameters;
}
/**
* @param value CXF TSL client parameters
*/
public final void setTLSClientParameters(TLSClientParameters value) {
this.tlsClientParameters = value;
}
/**
* @return Addressing-To URI to be sent as WS-Adressing header
*/
public final String getWSAddressingToUri() {
return this.wsAddressingToUri;
}
/**
* @param value Addressing-To URI to be sent as WS-Adressing header
*/
public final void setWSAddressingToUri(String value) {
this.wsAddressingToUri = value;
}
/**
* @return If true compatibility mode for WSDL/schema changes is activated.
* If the SOAP server returns unknown XML element they are ignored during validation.
*/
public final boolean isIgnoreUnexpectedElements() {
return this.ignoreUnexpectedElements;
}
/**
* @param value If true compatibility mode for WSDL/schema changes is activated.
* If the SOAP server returns unknown XML element they are ignored during validation.
*/
public final void setIgnoreUnexpectedElements(boolean value) {
this.ignoreUnexpectedElements = value;
}
/**
* @return Allow HTTP 1.1 chunking
*/
public final boolean isAllowChunking() {
return this.allowChunking;
}
/**
* @param value Allow HTTP 1.1 chunking
*/
public final void setAllowChunking(boolean value) {
this.allowChunking = value;
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this, false);
}
@Override
public boolean equals(Object pObj) {
return EqualsBuilder.reflectionEquals(this, pObj, false);
}
}