5

When I send a SOAP request to the server it returns following error, though I send similar request using SoapUI and that works. It seems I need to change my SOAP request to the one that I am sending using SoapUI. WSDL is here.

 [ truncated ] System.Web.Services.Protocols.SoapException : The value of the 
    HTTP header ' SOAPAction ' was not recognized by the server . \ r \ n at 
    System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest ( ) 
    \ r \ n at System.Web.Servic

I am sending following request using Java

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns2:SearchFlights xmlns:ns2="ElysArres.API">
         <ns2:SoapMessage>
            <ns2:Username>Test</ns2:Username>
            <ns2:Password>TestPassword</ns2:Password>
            <ns2:LanguageCode>EN</ns2:LanguageCode>
            <ns2:Request>
               <ns2:Departure>ONT</ns2:Departure>
               <ns2:Destination>EWR</ns2:Destination>
               <ns2:DepartureDate>2016-01-20</ns2:DepartureDate>
               <ns2:ReturnDate>2016-01-28</ns2:ReturnDate>
               <ns2:NumADT>1</ns2:NumADT>
               <ns2:NumINF>0</ns2:NumINF>
               <ns2:NumCHD>0</ns2:NumCHD>
               <ns2:CurrencyCode>EUR</ns2:CurrencyCode>
               <ns2:WaitForResult>true</ns2:WaitForResult>
               <ns2:NearbyDepartures>true</ns2:NearbyDepartures>
               <ns2:NearbyDestinations>true</ns2:NearbyDestinations>
               <ns2:RROnly>false</ns2:RROnly>
               <ns2:MetaSearch>false</ns2:MetaSearch>
            </ns2:Request>
         </ns2:SoapMessage>
      </ns2:SearchFlights>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

I can send following request using SoapUI and it works

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:els="ElsyArres.API">
   <soap:Header/>
   <soap:Body>
      <els:SearchFlights>
         <els:SoapMessage>
            <els:Username>Test</els:Username>
            <els:Password>TestPassword</els:Password>
            <els:LanguageCode>EN</els:LanguageCode>
            <els:Request>
               <els:Departure>ONT</els:Departure>
               <els:Destination>EWR</els:Destination>
               <els:DepartureDate>2016-01-20</els:DepartureDate>
               <els:ReturnDate>2016-01-28</els:ReturnDate>
               <els:NumADT>1</els:NumADT>
               <els:NumINF>0</els:NumINF>
               <els:NumCHD>0</els:NumCHD>
               <els:CurrencyCode>EUR</els:CurrencyCode>
               <els:WaitForResult>true</els:WaitForResult>
               <els:NearbyDepartures>true</els:NearbyDepartures>
               <els:NearbyDestinations>true</els:NearbyDestinations>
               <els:RROnly>false</els:RROnly>
               <els:MetaSearch>false</els:MetaSearch>
            </els:Request>
         </els:SoapMessage>
      </els:SearchFlights>
   </soap:Body>
</soap:Envelope>

I am not sure how to make the request that I am creating with Java same as what I am sending with SoapUI.

Code

SearchFlights

@XmlRootElement(name = "SearchFlights")
@XmlAccessorType(XmlAccessType.FIELD)
public class SearchFlights {
    @XmlElement(name = "SoapMessage")
    private SoapMessage soapMessage;

    getter and setter

SoapMessage

@XmlRootElement(name = "SoapMessage")
@XmlAccessorType(XmlAccessType.FIELD)
public class SoapMessage {
    @XmlElement(name = "Username")
    private String username;
    @XmlElement(name = "Password")
    private String password;
    @XmlElement(name = "LanguageCode")
    private String languageCode;
    @XmlElement(name = "Request")
    private Request request;

    getters and setters

Request

@XmlRootElement(name = "Request")
@XmlAccessorType(XmlAccessType.FIELD)
public class Request {
    @XmlElement(name = "Departure")
    private String departure;
    @XmlElement(name = "Destination")
    private String destination;
    @XmlElement(name = "DepartureDate")
    private String departureDate;
    @XmlElement(name = "ReturnDate")
    private String returnDate;
    @XmlElement(name = "NumADT")
    private int numADT;
    @XmlElement(name = "NumINF")
    private int numInf;
    @XmlElement(name = "NumCHD")
    private int numCHD;
    @XmlElement(name = "CurrencyCode")
    private String currencyCode;
    @XmlElement(name = "WaitForResult")
    private boolean waitForResult;
    @XmlElement(name = "NearByDepartures")
    private boolean nearByDepartures;
    @XmlElement(name = "NearByDestinations")
    private boolean nearByDestinations;
    @XmlElement(name = "RROnly")
    private boolean rronly;
    @XmlElement(name = "MetaSearch")
    private boolean metaSearch;

getters and setters

package-info.java

@XmlSchema( 
    namespace = "ElsyArres.API",
    elementFormDefault = XmlNsForm.QUALIFIED) 
package com.myproject.flights.wegolo;

import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

jaxb.index

SearchFlights
Flight
Flights
Leg
Legs
Outbound
Request
Response
SoapMessage

Code to send request

import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPConstants;

import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
......
    // populate searchFlights and other classes to create request
    try {
        SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(
                MessageFactory.newInstance());
        messageFactory.afterPropertiesSet();

        WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
                messageFactory);
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();

        marshaller.setContextPath("com.myproject.flights.wegolo");
        marshaller.afterPropertiesSet();

        webServiceTemplate.setMarshaller(marshaller);
        webServiceTemplate.afterPropertiesSet();

        Response response = (Response) webServiceTemplate
                .marshalSendAndReceive(
                        "http://www5v80.elsyarres.net/service.asmx",
                        searchFlights);

        Response msg = (Response) response;
        System.err.println("Wegolo >>>"
                + msg.getFlights().getFlight().size());
    } catch (Exception s) {
        s.printStackTrace();
    }

Update

I removed package-info.java and managed to use the suggested code, but it is still sending the same header.

Response response = (Response) webServiceTemplate
                    .marshalSendAndReceive(
                            "http://www5v80.elsyarres.net/service.asmx",
                            searchFlights,
                            new WebServiceMessageCallback() {
                                public void doWithMessage(WebServiceMessage message) 
                                {
                                    ((SoapMessage)message).setSoapAction("http://www5v80.elsyarres.net/searchFlights");
                                }
                           }
                       );

enter image description here

Daniel Newtown
  • 2,693
  • 8
  • 25
  • 60
  • 1
    Looks like you need to use SOAP Version `1.1` (which uses the SOAP Action header) so that the client behaves correctly: `MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);` – fateddy Jan 06 '16 at 13:10
  • @fateddy how do I use that in my code? would you elaborate further? – Daniel Newtown Jan 07 '16 at 07:58
  • Just like that: `MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);` and then `WebServiceTemplate webServiceTemplate = new WebServiceTemplate(messageFactory);`. Let me know if this works! – fateddy Jan 07 '16 at 12:37
  • @fateddy I changed the code to MessageFactory messageFactory = MessageFactory .newInstance(SOAPConstants.SOAP_1_1_PROTOCOL); SaajSoapMessageFactory soapMsgFac = new SaajSoapMessageFactory( messageFactory.newInstance()); but I am still receiving the same error. – Daniel Newtown Jan 08 '16 at 10:34

3 Answers3

13

SOAP Version 1.1 requires a HTTP header in your SOAP request to specify the SOAP action. It's not in the actual XML, it's part of the request (in the HTTP header), so that is why you are not seeing any difference between your SoapUI request xml, and the request you're sending using the WebServiceTemplate. Soap 1.2 allows you to set it as an attribute on the media type, but that is not valid for a 1.1 server. Note that according to the specification, the value you use doesn't have to be resolvable.

SOAP places no restrictions on the format or specificity of the URI or that it is resolvable. An HTTP client MUST use this header field when issuing a SOAP HTTP Request.

Usually, it's specified in your WSDL, something like (taken from here):

<soap:operation
        soapAction="http://www5v80.elsyarres.net/searchFlights"
        style="document" />

If that is not in your WSDL, you can add it by using the action annotation in spring in your webservice endpoint class.

@Endpoint
public class MyFlightEndpoint{
    @Action("http://www5v80.elsyarres.net/searchFlights")
    public SearchFlights request() {
        ...
    }
}

If it is in your WSDL, you'll want to place that value into your HTTP header on the client side. To do this, you then need to get access to the message on the client side after it's created, but before it's sent in order to add the action header. Spring provides a message callback interface for that, that's described here. What you'll want to do is something like:

Response response = (Response) webServiceTemplate
            .marshalSendAndReceive(
                    "http://www5v80.elsyarres.net/service.asmx",
                    searchFlights,
                    new WebServiceMessageCallback() {
                        public void doWithMessage(WebServiceMessage message) 
                        {
                            ((SoapMessage)message).setSoapAction("http://www5v80.elsyarres.net/searchFlights");
                        }
                   }
               );

There's a discussion on SOAP action headers here, and the point (or lack of a point) for them if you want to know more.

Edit: So looking at the wsdl here:

<soap:operation soapAction="ElsyArres.API/SearchFlights" style="document"/>

you'll want the following action:

ElsyArres.API/searchFlights

Now just update the code to read

((SoapMessage)message).setSoapAction("ElsyArres.API/searchFlights");

and you're good to go!

Edit 2: I also notice the service you're connecting to accepts SOAP 1.2 connections, while you're using SOAP 1.1. You can force your client to use SOAP 1.2 by setting it in your factory.

messageFactory.setSoapVersion(SoapVersion.SOAP_12);
messageFactory.afterPropertiesSet();

It looks like the server uses the same endpoint, so that should be the only change.

Community
  • 1
  • 1
AndyN
  • 1,935
  • 12
  • 22
  • Your code has an error on .setSoapAction, the method setSoapAction(String is undefined. – Daniel Newtown Jan 13 '16 at 07:52
  • What version of spring-ws are you using? Javadoc suggests the method does exist (http://docs.spring.io/spring-ws/site/apidocs/org/springframework/ws/soap/SoapMessage.html). Check your imports, and make sure you're casting to org.springframework.ws.soap.SoapMessage properly. – AndyN Jan 13 '16 at 08:04
  • I am using 3.2.8.RELEASE – Daniel Newtown Jan 13 '16 at 08:22
  • That sounds like the version of Spring you're using. The current release of spring-ws is 2.1.4. Regardless of the version, org.springframework.ws.soap.SoapMessage has a setSoapAction(String soapAction) method as far back as 1.0-rc2. I strongly suspect your import statements are wrong. Make sure you can see a `import org.springframework.ws.soap.SoapMessage;` – AndyN Jan 13 '16 at 09:25
  • I included my imports, not sure what you mean by imports. – Daniel Newtown Jan 13 '16 at 09:28
  • By imports, I mean the `import` statement at the top of your class that contains the `SoapMessage` line. I can't see your SoapMessage import (which should look like `import org.springframework.ws.soap.SoapMessage;`), nor where you set the SoapAction header. Try again, make sure the import is what I've stated above, and see if you still the the error. You've either got a library problem, or an import problem. Either way, the answer I've given is what you need to get working. – AndyN Jan 13 '16 at 09:45
  • I managed to use your code, but it is still sending the same header. – Daniel Newtown Jan 13 '16 at 10:21
  • Add the HTTP header to the question, you've only got the soap header there. Also, while you're at it, post the relevant part of the wsdl (the bit that specifies the soap action that's expected). – AndyN Jan 13 '16 at 10:24
  • Great! See the `SOAPAction: "http:// ... "` bit in your HTTP header? That's what the code is doing. So it's working! But you're using the wrong action name (the url was one I made up, you need to use what's listed in your wsdl). So I've updated the answer. – AndyN Jan 13 '16 at 10:37
  • Have you double checked the header after the update? What is SoapUI sending? You realise you'll need the appropriate action for each of your requests right, so if you do a `GetFlightDetails`, you'll need the action `ElsyArres.API/GetFlightDetails` – AndyN Jan 13 '16 at 10:58
  • Yes, I cleaned and run the project again. It now shows java.lang.IllegalStateException: No unmarshaller registered. Check configuration of WebServiceTemplate. – Daniel Newtown Jan 13 '16 at 11:27
  • Yeah man, that's a whole new problem. Sorry, you may have to put up another question for that one. But at least you're no longer getting the SoapAction header issue! Good luck. – AndyN Jan 13 '16 at 11:29
  • I know just wondering if you know whats going wrong. I created a new question. Thanks for your help. – Daniel Newtown Jan 13 '16 at 11:39
  • http://stackoverflow.com/questions/34765744/java-lang-illegalstateexception-no-unmarshaller-registered-check-configuration – Daniel Newtown Jan 13 '16 at 11:39
1

I had the same problem, my fix was :

  @Configuration
  public class SoapConfiguration {
      private static final String SOAP_1_2_PROTOCOL= "SOAP 1.2 Protocol";

  @Bean
   public WebServiceTemplate webServiceTemplate() throws Exception {

   SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(javax.xml.soap.MessageFactory.newInstance(SOAP_1_2_PROTOCOL));
   messageFactory.setSoapVersion(SoapVersion.SOAP_12);
   messageFactory.afterPropertiesSet();

   WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
           messageFactory);
   Jaxb2Marshaller marshaller = new Jaxb2Marshaller();

   marshaller.setContextPath("YOUR_WSDL_GENERATED_PATH");
   marshaller.afterPropertiesSet();
   webServiceTemplate.setMarshaller(marshaller);
   webServiceTemplate.setUnmarshaller(marshaller);
   webServiceTemplate.afterPropertiesSet();
   return webServiceTemplate;
}

And my SoapService

@Service
@RequiredArgsConstructor
public class SoapDomainBoxService extends WebServiceGatewaySupport {
 private final WebServiceTemplate webServiceTemplate;

public void searchFlights(SearchFlights searchFlights) {

    String url = "YOUR.URL.asmx";
   Response response = (Response) webServiceTemplate.marshalSendAndReceive(url, searchFlights, new SoapActionCallback("ACTION.CALLBACK"));
}

Very important on creation of message factory use

   SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(javax.xml.soap.MessageFactory.newInstance(SOAP_1_2_PROTOCOL));
0

Another way to add SOAPAction header when using WebServiceGatewaySupport is to do the following:

getWebServiceTemplate().marshalSendAndReceive(request, new SoapActionCallback("http://httpheader/"));

This is using messageFactory.setSoapVersion(SoapVersion.SOAP_12);

adnans
  • 2,159
  • 2
  • 13
  • 5