3

I have been trying to consume a SOAP service using a Spring application, but I've been absolutely stuck for a while. I expect the solution will be very simple and I'm hoping to be pointed in the right direction.

I am able to successfully make requests via SoapUI. I simply loaded the .WSDL file, selected the method to use, and added the username and password with basic authorization from the 'Auth' menu in SoapUI.

In the Spring application, I've gotten to the point of getting a 401 error, so I believe it's almost there. I referenced these two nice examples below to first add the username and password, and secondly to log the HTTP headers to verify that they are populated correctly.

https://codenotfound.com/spring-ws-log-client-server-http-headers.html

Setting a custom HTTP header dynamically with Spring-WS client

However, neither setting the 'usernamePasswordCredentials', nor setting the connection's request header seems to have any effect. I've also confirmed that the XML body is correct by testing the logged output in SoapUI. So I believe it's just an authorization issue.

Bean/Configuration Class:

@Bean
public Jaxb2Marshaller marshaller() {
    System.out.println("BEAN CREATED: MARSHALLER...");
    Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
    marshaller.setContextPath("..."); // omitted for example

    return marshaller;
}

@Bean
public UsernamePasswordCredentials usernamePasswordCredentials() {
    return new UsernamePasswordCredentials("testu", "test");
}

@Bean
@DependsOn({"usernamePasswordCredentials"})
public HttpComponentsMessageSender httpComponentsMessageSender(UsernamePasswordCredentials usernamePasswordCredentials) {
    HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
    
    httpComponentsMessageSender.setCredentials(usernamePasswordCredentials);

    return httpComponentsMessageSender;
}

@Bean
@DependsOn({"marshaller"})
public TicketDetailsClient ticketDetailsClient(Jaxb2Marshaller marshaller) {
    System.out.println("BEAN CREATED: TICKETDETAILSCLIENT...");
    TicketDetailsClient ticketDetailsClient = new TicketDetailsClient();
    
    ticketDetailsClient.setDefaultUri("..."); // omitted for example
    ticketDetailsClient.setMarshaller(marshaller);
    ticketDetailsClient.setUnmarshaller(marshaller);

    return ticketDetailsClient;
}

Bean Method:

    public GetTicketDetailsResponse getTicketDetails(long definitionId, long itemId) {
    ObjectFactory of = new ObjectFactory();

    this.template.setInterceptors(new ClientInterceptor[] {new LogHttpHeaderClientInterceptor()});
    
    GetItemDetailsRequest itemDetailsRequest = new GetItemDetailsRequest();
    itemDetailsRequest.setItemDefinitionId(definitionId);
    itemDetailsRequest.setItemId(itemId);
    
    GetTicketDetails getTicketDetails = new GetTicketDetails();
    getTicketDetails.setGetItemDetailsRequest(itemDetailsRequest);
    JAXBElement<GetTicketDetails> elGetTicketDetails = of.createGetTicketDetails(getTicketDetails);
    
    System.out.println("ABOUT TO MARSHALSENDANDRECEIVE...");
    GetTicketDetailsResponse ticketDetailsResponse = (GetTicketDetailsResponse) this.template.marshalSendAndReceive(elGetTicketDetails);
    
    return ticketDetailsResponse;
}

Interceptor:

@Override
public boolean handleRequest(MessageContext arg0) throws WebServiceClientException {
    // TODO Auto-generated method stub
    TransportContext context = TransportContextHolder.getTransportContext();
    HttpComponentsConnection connection =(HttpComponentsConnection) context.getConnection();
    try {
        connection.addRequestHeader("username", "testu");
        connection.addRequestHeader("password", "test");
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    HttpLoggingUtils.logMessage("Client Request Message", arg0.getRequest());

    return true;
}

Result Snippet (This is where I expected to see the username/password headers. Since they are missing, I'm guessing this is the issue):

ABOUT TO MARSHALSENDANDRECEIVE...
2021-08-09 13:46:18.891  INFO 23112 --- [           main] com.fp.fpcustomization.HttpLoggingUtils  : 
----------------------------
Client Request Message
----------------------------
Accept: text/xml, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
SOAPAction: ""
Content-Type: text/xml; charset=utf-8
Content-Length: 378
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><ns3:getTicketDetails xmlns:ns3="http://externalapi.business.footprints.numarasoftware.com/"><getItemDetailsRequest><_itemDefinitionId>76894</_itemDefinitionId><_itemId>30201</_itemId></getItemDetailsRequest></ns3:getTicketDetails></SOAP-ENV:Body></SOAP-ENV:Envelope>

[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.151 s <<< FAILURE! - in com.fp.fpcustomization.FpcustomizationApplicationTests
[ERROR] RequestTest  Time elapsed: 0.51 s  <<< ERROR!
org.springframework.ws.client.WebServiceTransportException:  [401]
    at com.fp.fpcustomization.FpcustomizationApplicationTests.RequestTest(FpcustomizationApplicationTests.java:29)
user2985193
  • 61
  • 1
  • 5

1 Answers1

3

Following up with the solution that worked for me. This time around, I looked at going about this via the callback function. This led me to the question asked here: Add SoapHeader to org.springframework.ws.WebServiceMessage

The second answer by Pranav Kumar is what worked for me. So I simply added the callback function to the 'marshalSendAndReceive' function call in the 'GetTicketDetailsResponse getTicketDetails(long definitionId, long itemId)' method. This way, I was able to add the Authorization header that got me past the 401 error that I was getting before.

Object theResponseObject = this.template.marshalSendAndReceive((elGetTicketDetails), new WebServiceMessageCallback(){
            @Override
            public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
                SaajSoapMessage soapMessage = (SaajSoapMessage) message;
                MimeHeaders mimeHeader = soapMessage.getSaajMessage().getMimeHeaders();
                mimeHeader.setHeader("Authorization", "Basic ...==");
            }
        });

Following this addition, the Authorization header was showing up in the request and the response was successfully returned.

user2985193
  • 61
  • 1
  • 5