I'm trying to implement a soap service consumer in Java, using spring WebServiceGatewaySupport.
When I'm using curl to consume the service as below, it is giving proper response.
curl -d #request.xml -H 'SOAPAction:abc:mnEvent#DoAction' https://myhost.org/cd/doAction.jsp
I'm trying to implement the same using JAVA, by adding following HttpHeaders in a template class inheriting from WebServiceGatewaySupport
public O callWebService(String url, I request) {
return (O) getWebServiceTemplate().marshalSendAndReceive(url, request, new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
TransportContext transportContext = TransportContextHolder.getTransportContext();
HttpComponentsConnection connection = (HttpComponentsConnection) transportContext.getConnection();
connection.getHttpPost().addHeader("SOAPAction", "abc:mnEvent#DoAction");
}
});
}
With this code, I'm getting an error message like below.
SOP-330006 The method 'DoAction, ""' is not defined in SOAP service 'abc:mnEvent'.
What do I miss here when moving curl command to JAVA?
The error message SOP-330006 The method 'DoAction, ""' is not defined in SOAP service 'abc:mnEvent'. indicates, there are two soap actions in the request.
Explicit SoapAction added in HttpHeader
Implicit SoapAction in SoapMessage
To avoid this issue, we need to remove the soapAction from header and set it in SoapMessage.
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
soapMessage.setSoapAction("abc:mnEvent#DoAction");
Related
I need to communicate via SOAP with an application that was bought from another company. The wsdl we received is for SOAP 1.2 and the same is stated in the minimalistic technical documentation. However every request produces the following error:
Cannot process the message because the content type 'application/soap+xml; charset=utf-8' was not the expected type 'text/xml; charset=utf-8'. [415]
According to the supplier their software was reprogrammed to use SOAP 1.2 with the content-type from SOAP 1.1 (text/xml; charset=utf-8). I'm using Spring Boot with the WebServiceGatewaySupport. The SoapMessageFactory is configured to use SOAP 1.2 which (as expected) sets the content-type to 'application/soap+xml; charset=utf-8':
#Bean(name = "mySoapMessageFactory")
public SaajSoapMessageFactory saajSoapMessageFactory() throws SOAPException {
SaajSoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL));
saajSoapMessageFactory.setSoapVersion(SoapVersion.SOAP_12);
return saajSoapMessageFactory;
}
In the long run the company which supplied the SOAP endpoint should fix their software to honor the standards. But until then I am looking for a quick fix since the data needs to be tested...
What I tried so far:
1) Change the content-type header with a WebServiceMessageCallback. Results in the same error as the content-type seems to be switched back to the SOAP 1.2 default before the request is sent.
public GetOrganisationUnitDataResponse getOrganisationUnitDataResponse(Integer skip) {
//SNIP
GetOrganisationUnitDataResponse response = (GetOrganisationUnitDataResponse) getWebServiceTemplate().marshalSendAndReceive(getOrganisationUnitData, getWebServiceMessageCallback());
//SNIP
return response;
}
private WebServiceMessageCallback getWebServiceMessageCallback() {
return webServiceMessage -> {
SaajSoapMessage soapMessage = (SaajSoapMessage)webServiceMessage;
MimeHeaders headers = soapMessage.getSaajMessage().getMimeHeaders();
headers.setHeader(TransportConstants.HEADER_CONTENT_TYPE, "text/xml; charset=utf-8");
headers.setHeader("Accept", "text/xml; charset=utf-8");
};
}
2) ClientInterceptor modifying the SaajSoapMessage. Same modification as in point 1) with the same result.
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
WebServiceMessage request = messageContext.getRequest();
SaajSoapMessage soapMessage = (SaajSoapMessage)request;
MimeHeaders headers = soapMessage.getSaajMessage().getMimeHeaders();
headers.removeHeader("Content-Type");
headers.setHeader("Content-Type", "text/xml; charset=utf-8");
return true;
}
3) ClientInterceptor modifying the TransportContext. This results in a http 400 Bad Request.
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
TransportContext context = TransportContextHolder.getTransportContext();
HttpComponentsConnection connection =(HttpComponentsConnection) context.getConnection();
connection.addRequestHeader("Content-Type", "text/xml; charset=utf-8");
return true;
}
Is there a 'proper' Spring Boot way to modify the content-type?
I followed this guide "Consuming a SOAP web service", at
https://spring.io/guides/gs/consuming-web-service/
and changed it to call my own internal SOAP service, it makes the call
as expected, however now I need to pass an http header via the WsTemplate,
what is the easiest way to do this?
public class WsHttpHeaderCallback implements WebServiceMessageCallback
{
public WsHttpHeaderCallback()
{
super();
}
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
{
String headerKey="headerkey";
String headerValue="headervalue";
addRequestHeader(headerKey, headerValue);
}
private void addRequestHeader(String headerKey, String headerValue) throws IOException
{
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection connection = context.getConnection();
if (connection instanceof HttpUrlConnection) {
HttpUrlConnection conn = (HttpUrlConnection) connection;
conn.addRequestHeader(headerKey, headerValue);
}
}
}
I'm not sure if this helps but found some documentation
For setting WS-Addressing headers on the client, you can use the org.springframework.ws.soap.addressing.client.ActionCallback. ...
webServiceTemplate.marshalSendAndReceive(o, new ActionCallback("http://samples/RequestOrder"));
I've faced the same problem. If it can help someone, I've found a solution here: Spring WS Add Soap Header in Client
The idea is to create a class implementing org.springframework.ws.client.core.WebServiceMessageCallback and override the doWithMessage() method.
The doItMessage() method takes a WebServiceMessage as argument and is invoqued by the springWs process before sending the request, allowing to modify it before it is send.
What is done in the exemple above is marschalling the object and adding it to the header of the request.
In my case I have to be carefull with XML annotions of the object to be set as header, especially the #XmlRootElement with the namespace attribute.
Once this is done, the WSClient has to be adjusted to use the marshalSendAndReceive() method that takes a request and an uri, a payload object, and a WebServiceMessageCallback.
I know the question is weird. Unfortunately I have a service that requires everything to have the header ContentType=application/x-www-form-urlencoded, eventhough the body is JSON
I am trying to use JAX-RS 2.0 ClientBuilder to call it:
String baseUrl = "http://api.example.com/";
JSONObject body = new JSONObject();
body.put("key", "value");
Client client = ClientBuilder.newClient();
client.register(new LoggingFilter());
Builder builder = client.target(baseUrl).path("something").request();
Invocation inv = builder
.header("Content-type", MediaType.APPLICATION_FORM_URLENCODED)
.buildPost(Entity.json(body));
Response response = inv.invoke();
int status = response.getStatus();
// I get 415, unsupported media type (in this case is unexpected)
I have checked my logs and I eventhough I am setting application/x-www-form-urlencoded (via the MediaType) the request appearantly has the Content-type of application/json
How can I force the request to have the Content-type I want?
BTW: This is my custom logger:
public class LoggingFilter implements ClientRequestFilter {
private static final Logger LOG = Logger.getLogger(LoggingFilter.class.getName());
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
LOG.log(Level.INFO, "body");
LOG.log(Level.INFO, requestContext.getEntity().toString());
LOG.log(Level.INFO, "headers");
LOG.log(Level.INFO, requestContext.getHeaders().toString());
}
}
And these are the logs I get:
com.acme.LoggingFilter I body
com.acme.LoggingFilter I {"key":"value"}
com.acme.LoggingFilter I headers
com.acme.LoggingFilter I {Content-type=[application/json]}
The problem with trying to use one of the static Entity helper methods is that it overrides any previous Content-Type header you may have set. In your current case, Entity.json automatically sets the header to application/json.
Instead of using the .json method, you can just use the general purpose Entity.entity(Object, MediaType) method. With your current case though, you can just do Entity.entity(body, MediaType.APPLICATION_FORM_URLENCODED_TYPE) though. The reason is that the client will look for a provider that knows how to serialize a JSONObject to application/x-www-form-urlencoded data, which there is none. So you will need to first serialize it to a String. That way the provider that handles application/x-www-form-urlencoded doesn't need to serialize anything. So just do
Entity.entity(body.toString(), MediaType.APPLICATION_FORM_URLENCODED_TYPE);
Some background: This is a Weblogic Web Services created Service client creates via Eclipse. I believe this uses clientgen behind the scenes.
I'm trying to make a SOAP call that requires preemptive Basic Authentication. The request is being sent but the Mimeheaders I'm setting are not going with it. The recipient of the call has informed me that the request itself is coming through but any mimeheaders I set are not.
The service call is rather simple.
DescriptionService service = new DescriptionService(wsdlLocation, new QName("urn:descriptionService.service.company.com", "DescriptionService"));
service.setHandlerResolver(new HandlerResolver() {
#SuppressWarnings("rawtypes")
#Override
public List<Handler> getHandlerChain(final PortInfo portInfo) {
final List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new SOAPDescriptionServiceHeaderHandler());
return handlerList;
}
});
DescriptionServicePortType portType = service.getDescriptionServicePort();
DescriptionRequest request = new DescriptionRequest();
request.setId(id);
DescriptionResponse description = portType.describe(request);
The handler is where I set the Mimeheaders:
#Override
public boolean handleMessage(final SOAPMessageContext context) {
final Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
final SOAPMessage message = context.getMessage();
if (outboundProperty.booleanValue()) {
try {
MimeHeaders mimeheaders = message.getMimeHeaders();
String encodedAuth = Base64.encode(new String("un:pw").getBytes());
mimeheaders.addHeader("Authorization", "Basic " + encodedAuth);
this.logMessage(message, outboundProperty.booleanValue(), false);
} catch (final Exception e) {
// Log Error
}
} else {
this.logMessage(message, outboundProperty.booleanValue(), false);
}
return true;
}
It does hit this handler and set the mimeheaders. If I set a break point and look at the mime headers before it leaves the handleMessage method, I can see that they are set.
I'm able to call the request and get a response in SoapUI. I set up preemptive basic auth and it works fine. When I send the request through the Java Client, I get no response and actually get an error that says it's the incorrect content type. I believe this error is referring to the fault response as I don't actually get the response (doesn't hit the handleMessage() method in the handler either) and I know the request is going through with text/xml which is what the error is asking for.
I'm unsure if it has something to do with the "preemptive" requirement? Is there a way to set basic auth set up this way as preemptive?
Thoughts?
Any assistance would be appreciated.
Basic Auth is done at the HTTP layer, not the SOAP layer, so you need to configure the underlying HTTP library. (MIME headers have nothing to do with it)
For example for CXF, have a look at this question HTTP basic authentication through CXF interceptor not working
I'm trying to a write simple REST Java web-service. I use Provider interface on the server side and Dispatch on the clients. I'm trying to transmit a value in header of POST request but there is some problem.
My client code:
private void invoke(Dispatch<Source> dispatch
Object data) {
Map<String, Object> request_context = dispatch.getRequestContext();
request_context.put(MessageContext.HTTP_REQUEST_METHOD, "POST");
request_context.put("org.kpi.asd", "SOME TEXT");
StreamSource source = make_stream_source(data.toString());
dispatch.invoke(source);
}
My server code:
public Source invoke(Source request) {
// Filter on the HTTP request verb
if (ws_ctx == null) throw new RuntimeException("DI failed on ws_ctx.");
// Grab the message context and extract the request verb.
MessageContext msg_ctx = ws_ctx.getMessageContext();
String aaa = (String) msg_ctx.get("org.kpi.asd");
}
On server: aaa is null. I can't understand why. Help me, please (:
the message context is not where you set arbitrary http headers. there is another property in the message context which contains the http headers, see HTTP_REQUEST_HEADERS.