I have a WSDL file from outside WS that i'm connecting to. And I'm trying to get it working with CXF (works fine with JAX-WS). But I'm getting error from other system. So I decided to take a look at data we're sending to that system and only diffrence is that CXF sets empty SOAPAction http header.
I took some reading and looks like only known solutions is pointing to WSDL directly. But I already did that.
Anyone has a clue about this?
<bean id="object" class="xxx.XxxObject" factory-bean="objectFActory"
factory-method="create"/>
<bean id="objectFActory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
<property name="serviceClass" value="xxx.XxxObject"/>
<property name="wsdlLocation" value="http://blebleble"/>
<property name="address" value="http://blebleble"/>
<property name="username" value="user"/>
<property name="password" value="password"/>
<property name="properties">
<map>
<entry key="javax.xml.ws.session.maintain" value-type="java.lang.Boolean" value="true"/>
</map>
</property>
</bean>
Headers:
POST /somepath HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Accept: */*
Authorization: Basic <randomhex>
SOAPAction: ""
User-Agent: Apache CXF 2.7.6
Cache-Control: no-cache
Pragma: no-cache
Host: somehost:8080
Connection: keep-alive
Content-Length: 2791
None of this is CXF specific. It is all standard JAX-WS.
You can use the action property of the @WebMethod annotation to set a SOAP action. For example
@WebMethod(operationName = "TestOperation", action="http://example.org/TestOperation")
If you are using wsimport to generate artifacts from the WSDL, you should already have this set in your @WebService annotated interface.
I was able to replicate the behavior you described (SOAPAction header is "") using an invocation like this:
MyPortService service = new MyPortService();
MyPort port = service.getMyPortSoap11();
MyRequest request = new MyRequest();
MyResponse response = port.subscription( request );
Here are the HTTP Headers from TCP Dump using this invocation:
POST /MyService/services HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Accept: */*
SOAPAction: ""
User-Agent: Apache CXF 2.7.6
Cache-Control: no-cache
Pragma: no-cache
Host: redacted
Connection: keep-alive
Content-Length: 377
I tried adding an out interceptor and ensuring that the SOAPAction was set as a header, but no matter what I tried that did not cause the SOAPAction to be sent as part of the HTTP request.
I then found a lead in this thread and reformatted my invocation:
ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
factory.setServiceClass( MyPort.class );
factory.setAddress( "http://www.host.com/service" );
factory.setServiceName( new QName( targetNamespace, wsdlBindingName ) );
Object myService = factory.create();
org.apache.cxf.endpoint.Client client = ClientProxy.getClient( myService );
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("SOAPAction", Arrays.asList("mySoapAction"));
client.getRequestContext().put(Message.PROTOCOL_HEADERS, headers);
client.invoke( operationName, request );
Here are the HTTP Headers from TCP Dump of an invocation in this style:
POST /MyService/services HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Accept: */*
SOAPAction: mySoapAction
User-Agent: Apache CXF 2.7.6
Cache-Control: no-cache
Pragma: no-cache
Host: redacted
Connection: keep-alive
Content-Length: 377
Hope this helps.
If it's still actual. Faced same problem and wrote interceptor. It's quite universal:
public class SoapActionInterceptor extends AbstractSoapInterceptor {
private static final String SLASH = "/";
public SoapActionInterceptor() {
super(Phase.POST_LOGICAL);
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
BindingOperationInfo bindingOperationInfo = message.getExchange().getBindingOperationInfo();
OperationInfo operationInfo = bindingOperationInfo.getOperationInfo();
InterfaceInfo interfaceInfo = operationInfo.getInterface();
QName interfaceInfoNameQName = interfaceInfo.getName();
QName operationQName = operationInfo.getName();
Map<String, List<String>> reqHeaders = CastUtils.cast((Map<?, ?>) message.get(Message.PROTOCOL_HEADERS));
if (reqHeaders == null) {
reqHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
}
if (reqHeaders.size() == 0) {
message.put(Message.PROTOCOL_HEADERS, reqHeaders);
}
reqHeaders.put(SoapBindingConstants.SOAP_ACTION, Arrays.asList(interfaceInfoNameQName.getNamespaceURI() + SLASH + interfaceInfoNameQName.getLocalPart() + SLASH + operationQName.getLocalPart()));
}
}
To use it in Spring + Apache CXF:
<jaxws:client id="client" serviceClass="some.generated.webservice.Interface"
wsdlLocation="/META-INF/wsdl/webservice.wsdl"
address="http://example.address/service">
<jaxws:outInterceptors>
<bean class="some.package.interceptor.SoapActionInterceptor"/>
</jaxws:outInterceptors>
</jaxws:client>
I found another cause of this issue, so I thought I would go ahead and post this answer in case it helps someone.
After creating a SOAP Service, WSDL-first, and generating the service interface and related classes from the XSD, I found that the soap action I was setting in my wsdl was not showing up in the CXF-generated WSDL (which you can get to by adding '?wsdl' to your service endpoint and putting that in your browser).
ex: http://localhost:8080/mywar/services/myservice?wsdl
The cause of this issue for me was that I did not properly annotate the service implementation class. Although the generated interface had the appropriate annotations on it, the implementation class was the cause of my issue.
I had to add the following to my service implementation class to resolve the issue:
@WebService(
targetNamespace="...",
portName="...",
endpointInterface="...",
serviceName="...")
Hope this helps someone...