I was looking at various ways how to build a SOAP client with CXF into a Spring Boot application. In particular I'm interested in configuring request/response logging.
Precondition: the CXF Maven plugin is used to generate Java stubs from the WSDL files.
I studied a number of tutorials [1][2][3] and they all do that slightly differently. The question is whether there's an "officially endorsed" way of integrating a CXF client with Spring Boot. The CXF documentation doesn't seem to say.
Option 1: BindingProvider
The existing code I'm working with (not mine) does it like that:
@Bean
public PartnerServicePortType partnerServicePortType() {
PartnerServicePortType partnerServicePortType = new PartnerServiceV0().getPartnerService();
configureService((BindingProvider) partnerServicePortType, Services.PARTNER_SERVICE.serviceName());
return partnerServicePortType;
}
private void configureService(BindingProvider bindingProvider, String path) {
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUrl + path);
if (!StringUtils.isEmpty(username)) {
bindingProvider.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
bindingProvider.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
}
/* add logging interceptors
Client client = ClientProxy.getClient(bindingProvider);
Endpoint cxfEndpoint = client.getEndpoint();
cxfEndpoint.getInInterceptors().add(new PayloadExtractingLoggingInInterceptor());
cxfEndpoint.getInFaultInterceptors().add(new PayloadExtractingLoggingInInterceptor());
cxfEndpoint.getOutInterceptors().add(new PayloadExtractingLoggingOutInterceptor());
cxfEndpoint.getOutFaultInterceptors().add(new PayloadExtractingLoggingOutInterceptor());
*/
}
PartnerServiceV0
is generated class that extends javax.xml.ws.Service
and looks like this:
@WebServiceClient(name = "PartnerService_v0",
wsdlLocation = "classpath:some.wsdl",
targetNamespace = "urn:some-namespace")
public class PartnerServiceV0 extends Service {
As you can see I added the code to enable request/response logging to the configureServcie
method. While this does work it feels somewhat odd that we'd have to go through all of that for every service.
Option 2: JaxWsProxyFactoryBean
For verification purposes I ported the above code to using JaxWsProxyFactoryBeans
:
@Bean(name = "partnerService")
public PartnerServicePortType generateProxy() {
JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
proxyFactory.setServiceClass(PartnerServicePortType.class);
proxyFactory.setAddress(baseUrl + Services.PARTNER_SERVICE.serviceName());
if (!StringUtils.isEmpty(username)) {
proxyFactory.setUsername(username);
proxyFactory.setPassword(password);
}
return (PartnerServicePortType) proxyFactory.create();
}
Adding logging config is something I'd still have to figure out with this strategy.
Tutorials I checked
Apache CXF™ is an open source services framework. CXF helps you build and develop services using frontend programming APIs, like JAX-WS and JAX-RS. These services can speak a variety of protocols such as SOAP, XML/HTTP, RESTful HTTP, or CORBA and work over a variety of transports such as HTTP, JMS or JBI.
In Apache Camel, the Camel CXF component is the key to integrating routes with Web services. You can use the Camel CXF component to create a CXF endpoint, which can be used in either of the following ways: Consumer — (at the start of a route) represents a Web service instance, which integrates with the route.
JAX-RS: Java API for RESTful Web Services is a Java programming language API that provides support in creating web services according to the Representational State Transfer (REST) architectural style. CXF supports JAX-RS 2.1 (JSR-370), 2.0 (JSR-339) and 1.1 (JSR-311).
You can directly use JaxWsProxyFactoryBean
to set logging interceptors or directly use logging features as shown below.
@Bean
public HelloWorld bus(SpringBus bus) {
JaxWsProxyFactoryBean bean = new JaxWsProxyFactoryBean();
bean.setServiceClass(HelloWorld.class);
bean.setAddress(serverUrl);
bean.setBus(bus);
//bean.setInInterceptors(Collections.singletonList(new LoggingInInterceptor()));
bean.setFeatures(Collections.singletonList(new LoggingFeature()));
return bean.create(HelloWorld.class);
}
Here is complete example.
I using some jax-ws web service with cxf like this code
package org.roshan.framework.config;
import java.io.IOException;
import java.net.MalformedURLException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import javax.inject.Inject;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;
import org.roshan.framework.config.PaymentProperties.WebServiceDetail.SSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import com.tosan.modern.epayment.webservice.merchant.paymentservice.TosanIPGWS;
import com.tosan.modern.yaghut.service.SoapServices;
@Configuration
public class WebServiceClient {
@Inject
private PaymentProperties paymentProperties;
@Autowired
private ResourceLoader resourceLoader;
@Bean(name = "PaymentWebService")
public TosanIPGWS PaymentWebServiceCLient() throws MalformedURLException {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(TosanIPGWS.class);
factory.setAddress(paymentProperties.getShaparak().getWsPublicUrl());
TosanIPGWS service = (TosanIPGWS) factory.create();
try {
final Client client = ClientProxy.getClient(service);
// client.getInInterceptors().add(new LoggingInInterceptor());
// client.getOutInterceptors().add(new LoggingOutInterceptor());
setupSsl((HTTPConduit) client.getConduit(), paymentProperties.getShaparak().getSsl());
} catch (Exception e) {
// System.out.println("PaymentWebServiceCLient v9898989898");
System.out.println(e.getMessage());
}
return service;
}
private void setupSsl(HTTPConduit httpConduit, SSL ssl) throws Exception {
final String keyStoreLoc = ssl.getKeyStore();
final String keyPassword = ssl.getKeyStorePassword();
final String keystoreType = ssl.getKeyStoreType();
final String trustStoreLoc = ssl.getTrustStore();
final String trustStorePassword = ssl.getTrustStorePassword();
final String trustStoreType = ssl.getTrustStoreType();
final TLSClientParameters tlsCP = new TLSClientParameters();
tlsCP.setDisableCNCheck(true);
if (ssl.getKeyStore() != null && !ssl.getKeyStore().isEmpty()) {
final KeyStore keyStore = KeyStore.getInstance(keystoreType);
Resource resource1 = resourceLoader.getResource(keyStoreLoc);
keyStore.load(resource1.getInputStream(), keyPassword.toCharArray());
final KeyManager[] myKeyManagers = getKeyManagers(keyStore, keyPassword);
tlsCP.setKeyManagers(myKeyManagers);
}
if (ssl.getTrustStore() != null && !ssl.getTrustStore().isEmpty()) {
final KeyStore trustStore = KeyStore.getInstance(trustStoreType);
Resource resource2 = resourceLoader.getResource(trustStoreLoc);
trustStore.load(resource2.getInputStream(), trustStorePassword.toCharArray());
final TrustManager[] myTrustStoreKeyManagers = getTrustManagers(trustStore);
tlsCP.setTrustManagers(myTrustStoreKeyManagers);
}
httpConduit.setTlsClientParameters(tlsCP);
}
private static TrustManager[] getTrustManagers(KeyStore trustStore) throws NoSuchAlgorithmException, KeyStoreException {
String alg = KeyManagerFactory.getDefaultAlgorithm();
TrustManagerFactory fac = TrustManagerFactory.getInstance(alg);
fac.init(trustStore);
return fac.getTrustManagers();
}
private static KeyManager[] getKeyManagers(KeyStore keyStore, String keyPassword) throws GeneralSecurityException, IOException {
String alg = KeyManagerFactory.getDefaultAlgorithm();
char[] keyPass = keyPassword != null ? keyPassword.toCharArray() : null;
KeyManagerFactory fac = KeyManagerFactory.getInstance(alg);
fac.init(keyStore, keyPass);
return fac.getKeyManagers();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With