Issue
I need to exchange data with a SOAP service. This service provides many WSDL endpoints so I took one of them to generate Java code (I followed this tutorial: https://www.baeldung.com/maven-wsdl-stubs). Many classes were generated plus an interface and a service. Consider this code:
CodesService Interface:
package my.package.soapconsumer.models.service.getCodes;
import org.springframework.web.bind.annotation.RequestHeader;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.3.2
* Generated source version: 2.2
*
*/
@WebService(name = "CodesService", targetNamespace = "https://thewsdlprovider.xyz/")
@XmlSeeAlso({
ObjectFactory.class
})
public interface CodesService {
/**
*
* @param requestMethodOne
* @return
* returns my.package.soapconsumer.models.service.getCodes.ResponseMethodOne
*/
@WebMethod
@WebResult(name = "ResponseMethodOne", targetNamespace = "")
@RequestWrapper(localName = "methodOne", targetNamespace = "https://thewsdlprovider.xyz/", className = "my.package.soapconsumer.models.service.getCodes.MethodOne")
@ResponseWrapper(localName = "methodOneResponse", targetNamespace = "https://thewsdlprovider.xyz/", className = "my.package.soapconsumer.models.service.getCodes.MethodOneResponse")
public ResponseMethodOne methodOne(
@WebParam(name = "RequestMethodOne", targetNamespace = "")
RequestMethodOne requestMethodOne);
/**
*
* @return
* returns my.package.soapconsumer.models.service.getCodes.ResponseComunicacion
*/
@WebMethod
@WebResult(name = "ResponseTwo", targetNamespace = "")
@RequestWrapper(localName = "methodTwo", targetNamespace = "https://thewsdlprovider.xyz/", className = "my.package.soapconsumer.models.service.getCodes.VerificarComunicacion")
@ResponseWrapper(localName = "methodTwoResponse", targetNamespace = "https://thewsdlprovider.xyz/", className = "my.package.soapconsumer.models.service.getCodes.VerificarComunicacionResponse")
public ResponseComunicacion methodTwo();
/**
*
* @param requestMethodThreeApp
* @return
* returns my.package.soapconsumer.models.service.getCodes.ResponseMethodThree
*/
@WebMethod
@WebResult(name = "ResponseMethodThree", targetNamespace = "")
@RequestWrapper(localName = "methodThree", targetNamespace = "https://thewsdlprovider.xyz/", className = "my.package.soapconsumer.models.service.getCodes.MethodThree")
@ResponseWrapper(localName = "methodThreeResponse", targetNamespace = "https://thewsdlprovider.xyz/", className = "my.package.soapconsumer.models.service.getCodes.MethodThreeResponse")
public ResponseMethodThree methodThree(
@WebParam(name = "RequestMethodThreeApp", targetNamespace = "")
RequestMethodThreeApp requestMethodThreeApp);
/**
*
* @param requestMethodFour
* @return
* returns my.package.soapconsumer.models.service.getCodes.ResponseMethodFour
*/
@WebMethod
@WebResult(name = "ResponseMethodFour", targetNamespace = "")
@RequestWrapper(localName = "methodFour", targetNamespace = "https://thewsdlprovider.xyz/", className = "my.package.soapconsumer.models.service.getCodes.MethodFour")
@ResponseWrapper(localName = "methodFourResponse", targetNamespace = "https://thewsdlprovider.xyz/", className = "my.package.soapconsumer.models.service.getCodes.MethodFourResponse")
public ResponseMethodFour methodFour(
@WebParam(name = "RequestMethodFour", targetNamespace = "")
RequestMethodFour requestMethodFour);
/**
*
* more methods
*
*/
}
CodesService_Service Service
package my.package.soapconsumer.models.service.getCodes;
import org.springframework.web.bind.annotation.RequestHeader;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.3.2
* Generated source version: 2.2
*
*/
@WebServiceClient(name = "CodesService", targetNamespace = "https://thewsdlprovider.xyz/Codes", wsdlLocation = "https://codes.thewsdlprovider.xyz/v2/Codes?wsdl")
public class CodesService_Service
extends Service
{
private final static URL CODES_SERVICE_WSDL_LOCATION;
private final static WebServiceException CODES_SERVICE_EXCEPTION;
private final static QName CODES_SERVICE_QNAME = new QName("https://thewsdlprovider.xyz/Codes", "CodesService");
static {
URL url = null;
WebServiceException e = null;
try {
url = new URL("https://codes.thewsdlprovider.xyz/v2/Codes?wsdl");
} catch (MalformedURLException ex) {
e = new WebServiceException(ex);
}
CODES_SERVICE_WSDL_LOCATION = url;
CODES_SERVICE_EXCEPTION = e;
}
public CodesService_Service() {
super(__getWsdlLocation(), CODES_SERVICE_QNAME);
}
public CodesService_Service(WebServiceFeature... features) {
super(__getWsdlLocation(), CODES_SERVICE_QNAME, features);
}
public CodesService_Service(URL wsdlLocation) {
super(wsdlLocation, CODES_SERVICE_QNAME);
}
public CodesService_Service(URL wsdlLocation, WebServiceFeature... features) {
super(wsdlLocation, CODES_SERVICE_QNAME, features);
}
public CodesService_Service(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public CodesService_Service(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
super(wsdlLocation, serviceName, features);
}
/**
*
* @return
* returns CodesService
*/
@WebEndpoint(name = "CodesServicePort")
public CodesService getCodesServicePort() {
return super.getPort(new QName("https://thewsdlprovider.xyz/Codes", "CodesServicePort"), CodesService.class);
}
/**
*
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values.
* @return
* returns CodesService
*/
@WebEndpoint(name = "CodesServicePort")
public CodesService getCodesServicePort(WebServiceFeature... features) {
return super.getPort(new QName("https://thewsdlprovider.xyz/Codes", "CodesServicePort"), CodesService.class, features);
}
private static URL __getWsdlLocation() {
if (CODES_SERVICE_EXCEPTION!= null) {
throw CODES_SERVICE_EXCEPTION;
}
return CODES_SERVICE_WSDL_LOCATION;
}
public String getWsdlLocation(){
return CODES_SERVICE_WSDL_LOCATION.toString();
}
}
I need to use the methods or functions provided by the interface so to testing them I'm using a REST controller:
@RestController
@RequestMapping("/codes")
public class CodigosRestController {
@GetMapping
public String obtainCode(){
CodesService_Service service = new CodesService_Service();
String code = service.getCodesServicePort().methodTwo().toString();
return code;
}
}
But when I run it to query the REST endpoint I get the following error:
{
"timestamp": "2022-02-18T13:44:03.234+00:00",
"status": 500,
"error": "Internal Server Error",
"trace": "com.sun.xml.internal.ws.fault.ServerSOAPFaultException: Client received SOAP Fault from server: THE SERVICE REQUIRES API KEY Please see the server log to find more detail regarding exact cause of the failure.\n\tat com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:178)\n\tat com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:116)\n\tat com.sun.xml.internal.ws.client.sei.StubHandler.readResponse(StubHandler.java:238)\n\tat ...
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.lang.Thread.run(Thread.java:748)\n",
"message": "Client received SOAP Fault from server: THE SERVICE REQUIRES API KEY Please see the server log to find more detail regarding exact cause of the failure.",
"path": "/codes"
}
It is necessary to send an API KEY in the headers but I don't know how.
So I used SoapUI to test the SOAP endpoint adding this header and I received a correct answer:
What I tried:
- Use the
WebServiceFeature... features
constructor parameter but I it seems that a header can't be added to this object. In fact, I don't know what those three points mean. - Use the @RequestHeader but it reports that is not applicable to web methods
So how can I add a header to my interface or service? Thanks in advance.
Solution
To work with the raw request you either need to create a request interceptor or just set the headers on the requested context on the service port. So in your case, it should be as simple as
@RestController
@RequestMapping("/codes")
public class CodigosRestController {
@GetMapping
public String obtainCode(){
CodesService_Service service = new CodesService_Service();
CodesService port = service.getCodesServicePort();
// generate the headers
Map<String, List<String>> requestHeaders = new HashMap<>();
requestHeaders.put("apiKey",Arrays.asList("TokeApi yourtokenhere"));
BindingProvider bindingProvider = (BindingProvider) port;
// set the headers on the request context
bindingProvider.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, requestHeaders);
String code = port.methodTwo().toString();
return code;
}
}
Answered By - Babl
Answer Checked By - Mary Flores (JavaFixing Volunteer)