SoapAction header missing when using CXF
Asked Answered
I

4

8

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
Inedible answered 1/8, 2013 at 7:14 Comment(4)
only diffrence is that CXF sets empty SOAPAction http header. Did the request that worked (JAX-WS I assume) not set the SOAPAction HTTP header at all or did it set it to a (non-empty) value?Much
JaxWS: SOAPAction: "Get" CXF: SOAPAction: ""Inedible
The only ways help you require more details of the problem, code you use with cxf, the wsdl file, headers you are seeing...Arsenate
JAX-WS is the spec. The reference implementation is called JAX-WS RI (or Metro)Kimon
A
8

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.

Alleviation answered 5/8, 2013 at 22:31 Comment(4)
It is set, but it isnt respected. :/Inedible
Can you post the WSDL you are using, and note the operation you are trying to call? Also, how are you creating the client in CXF, via Spring config? Programmatically?Alleviation
Hey, Sorry for wait. I can't post this WSDL. I'm creating this as Spring bean, but not with <jaxws> namespace.Inedible
The SOAP action value in your WSDL will override what you specify in the @WebMethod annotation. Did you try removing the wsdlLocation from your 'objectFActory' definition, and then using the action property on @WebMethod? Without seeing the WSDL, its hard to know why CXF might not read its soapAction correctly.Alleviation
H
6

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.

Horseback answered 11/8, 2013 at 23:39 Comment(3)
I was hoping for something, that would make CXF respect WSDL action settings. I have already written whole application, just want to swap JAXWS for CXF to test something.Inedible
Sorry I don't have a better answer - I debugged CXF through the interceptors and the eventual stream writer, but the code is a mess with tons of no parameter + void return method calls. I can tell the SOAPAction header stays on the Message instance until just before it's written to the stream, but couldn't pinpoint why SOAPAction gets dropped.Horseback
Setting Message.PROTOCOL_HEADERS using a map is the only programmatic solution that worked for me.Carthy
A
1

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>
Arctic answered 10/7, 2014 at 12:29 Comment(0)
S
0

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...

Specimen answered 1/12, 2015 at 22:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.