Can't get Spring SOAP Client to work: content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8' - spring

Hi, I am trying to make a simple soap client work using Spring-ws. The googling I've done on this error says I'm using Soap 1.1 and need to specify Soap 1.2. I've tried to do that. Am I doing it correctly below? If this is not the problem does anybody see what the problem is?
Here's a chunk of the stack trace:
org.springframework.ws.client.WebServiceTransportException: Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'. [415]
at org.springframework.ws.client.core.WebServiceTemplate.handleError(WebServiceTemplate.java:663)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:587)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:537)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:492)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:436)
at com.jda.fileserver.FujiAuthenticationTest.testLogin(FujiAuthenticationTest.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
Here's my code, thanks for trying to help:
public class AuthTest {
#Test
public void testLogin() throws Exception {
StringBuffer loginXml = new StringBuffer();
loginXml.append("<soapenv:Envelope xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:ns=\"http://abc.com/xyz/2010/08\">");
loginXml.append(" <soapenv:Header>");
loginXml.append(" <ns:loginOperationDetails>");
loginXml.append(" </ns:loginOperationDetails>");
loginXml.append(" </soapenv:Header>");
loginXml.append(" <soapenv:Body>");
loginXml.append(" <ns:LogIn>");
loginXml.append(" <ns:logInInfo>");
loginXml.append(" <ns:CustomerAccountId>customer1</ns:CustomerAccountId>");
loginXml.append(" <ns:Username>jsmith</ns:Username>");
loginXml.append(" <ns:Password>abc123</ns:Password>");
loginXml.append(" </ns:logInInfo>");
loginXml.append(" </ns:LogIn>");
loginXml.append(" </soapenv:Body>");
loginXml.append("</soapenv:Envelope>");
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
SaajSoapMessageFactory defaultMessageFactory = (SaajSoapMessageFactory) webServiceTemplate.getMessageFactory();
defaultMessageFactory.setSoapVersion(SoapVersion.SOAP_12);
webServiceTemplate.setMessageFactory(defaultMessageFactory); // probably not needed
StreamSource source = new StreamSource(new StringReader(loginXml.toString()));
StreamResult result = new StreamResult(System.out);
String uri = "http://xyz.abcstage.com/xyz_1.0/membership.svc/ws";
SoapActionCallback requestCallback = new SoapActionCallback("http://abc.com/xyz/2010/08/MembershipService/LogIn");
try {
webServiceTemplate.sendSourceAndReceiveToResult(uri, source, requestCallback, result);
}
catch (SoapFaultException sfe) {
throw new Exception("SoapFaultException", sfe);
}
catch (WebServiceTransportException wste) {
throw new Exception("WebServiceTransportException", wste);
}
}
}

Ok, I fixed the above problem, which gets me to another problem. First here's how I fixed the above problem. Now I don't set the SOAP version on the SaajSoapMessageFactory, I set it on the wrapped MessageFactory. Now the Content-Type going out in my request is application/soap+xml.
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
MessageFactory msgFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
SaajSoapMessageFactory newSoapMessageFactory = new SaajSoapMessageFactory(msgFactory);
webServiceTemplate.setMessageFactory(newSoapMessageFactory);
Next problem, now I'm getting this:
org.springframework.ws.soap.client.SoapFaultClientException: Unexpected fault in the service.
at org.springframework.ws.soap.client.core.SoapFaultMessageResolver.resolveFault(SoapFaultMessageResolver.java:37)
at org.springframework.ws.client.core.WebServiceTemplate.handleFault(WebServiceTemplate.java:774)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:600)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:537)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:492)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:436)
and here's the info that was returned in the response:
500 Internal Server Error
The SOAP action specified on the message, '', does not match the HTTP SOAP Action, 'http://abc.com/xyz/2010/08/MembershipService/LogIn'.
I'll try to solve this, but wanted to update anybody reading this so they can stop looking into the previous error. I need to figure out how to correctly set the soap action.

Please use the below code to change the header content type to text/xml;charset=utf-8 in Spring webservice template marshallSendAndReceive method.
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.marshalSendAndReceive(url, request, new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
MimeHeaders headers = soapMessage.getSaajMessage().getMimeHeaders();
headers.addHeader(TransportConstants.HEADER_CONTENT_TYPE, "text/xml;charset=utf-8");

For adding soapheader with action and to tags, below code is working fine for me.
public void doWithMessage(WebServiceMessage message) throws IOException {
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
SoapEnvelope soapEnvelope = soapMessage.getEnvelope();
SoapHeader soapHeader = soapEnvelope.getHeader();
//Initialize QName for Action and To
QName action = new QName("{uri}", "Action", "{actionname}");
QName to = new QName("{uri}", "To", "{actionname}");
soapHeader.addNamespaceDeclaration("{actionname}", "{uri}");
SoapHeaderElement soapHeaderElementAction = soapHeader.addHeaderElement(action);
SoapHeaderElement soapHeaderElementTo = soapHeader.addHeaderElement(to);
soapHeaderElementAction.setText("{text inside the tags}");
soapHeaderElementTo.setText("{text inside the tags}");
soapMessage.setSoapAction("{add soap action uri}");
soapMessage.writeTo(out);
}

Use setHeader since you want probably want to replace the contents of an existing header or add it if it doesn't exist. Though Httpheaders are supposed to be case insensitive using addHeader with do just that. Debugging through the source code when addHeader() is called even though it ignores case you'll see that it inserts the new header after the other header.
So , at least in Java, will end up with
content-type:
Content-Type:
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
MimeHeaders headers = soapMessage.getSaajMessage().getMimeHeaders();
headers.setHeader(TransportConstants.HEADER_CONTENT_TYPE, "text/xml;charset=utf-8");

Related

Call RestApi endpoint resource from EJB

I have been looking around for sample code how to call a Restful service written in Spring boot (deployed in different server/ip) from an EJB client.
I couldn't find a simple example or reference to guide me on how to implement an EJB client that can call a restful service(deployed in different server/ip). Could you please point me to a document or example that shows or describe how the two can interface/talk to each other.
I need to call the endpoint by passing two header parameters for authentication, if authentication is success then only retrieve the details from Rest and send back the response to EJB client.
I use something like this, try
`public void calExternal() throws ProtocolException,
MalformedURLException,
IOException,
NoSuchAlgorithmException,
InvalidKeyException {
URL myurl = new URL("API END POINT URL");
ObjectMapper mapper = new ObjectMapper();
HttpURLConnection conn = (HttpURLConnection) myurl.openConnection();
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
String payLoad = mapper.writeValueAsString("your payload here");
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("AUTHORIZATION-TYPE", "HMAC");
try {
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(payLoad);
wr.flush();
InputStream in = null;
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
in = conn.getInputStream();
} else {
in = conn.getErrorStream();
}
String encoding = conn.getContentEncoding() == null ? "UTF-8" : conn.getContentEncoding();
String response = IOUtils.toString(in, encoding);
} catch (Exception e) {
e.printStackTrace();
}
}

Camel Routes - How to return the body response as xml

First of all, I am new with Spring Boot.
I am not sure if it is possible, but I would like to return the xml response from the external url.
I have this code:
#GetMapping("/myPage")
public void myPage() {
restConfiguration().host("localhost").port(8080);
from("timer://runOnce?repeatCount=1&delay=0")
.to("rest:get:/external-page")
.to("stream:out");
}
myPage() is returning a XML (that's OK). So, now I would like to return the same XML when I do:
curl http://localhost/myPage
I am not sure if I have to use .to("stream:out"), but the curl is returning an empty result.
Can someone help me?
Thanks in advance.
I found the solution, this is how to get the response.
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
public void configure() {
restConfiguration().host(sHost).port(iPort);
from("direct:start")
.setHeader(Exchange.HTTP_METHOD,simple("GET"))
.to("rest:get:/external-page");
}
});
context.start();
ProducerTemplate template = context.createProducerTemplate();
String headerValue = "application/xml";
Map<String, Object> headers = new HashMap<String,Object>();
headers.put("Content-Type", headerValue);
Object result = template.requestBodyAndHeaders("direct:start", null, headers, String.class);
Exchange exchange = new DefaultExchange(context);
String response = ExchangeHelper.convertToType(exchange, String.class, result);
context.stop();
return response;

RestTemplate execute() method cannot send JSON Payload

In my application, I need to take data from another request and chain into a new one
I must use the exchange() method of RestTemplate because I have issue with jacksons lib and I cannot add/change the libs.
this is my code:
final RequestCallback requestCallback = new RequestCallback() {
#Override
public void doWithRequest(final ClientHttpRequest request) throws IOException {
request.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
// Add basic auth header
String auth = username + ":" + password;
byte[] encodedAuth = Base64Utils.encode(auth.getBytes(StandardCharsets.US_ASCII));
String authHeader = "Basic " + new String(encodedAuth);
request.getHeaders().add("Authorization", authHeader);
// Add Headers Request
Enumeration headerNamesReq = servletRequest.getHeaderNames();
while (headerNamesReq.hasMoreElements()) {
String headerName = (String) headerNamesReq.nextElement();
if (whiteListedHeaders.contains(headerName.toLowerCase())) {
String headerValue = servletRequest.getHeader(headerName);
request.getHeaders().add(headerName, headerValue);
}
}
request.getHeaders().forEach((name, value) -> {
log.info("RestExecutorMiddleware", "HEADERS ---\t" + name + ":" + value);
});
IOUtils.copy(new ByteArrayInputStream(payload.getBytes()), request.getBody());
}
};
// Factory for restTemplate
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
ClientHttpResponse responsePost = restTemplate.execute(url, method, requestCallback, new ResponseFromHeadersExtractor());
But at the end, the endpoint cannot receive my JSON (receive data, but not JSON.)
Where I wrong?
Thanks
Very inaccuracy code. Make all steps one-to-one and it will work, you make optimization later ...
Basic Auth. Don't do unnecessary actions
var headers = new HttpHeaders();
headers.setBasicAuth(username, password);
That's all, Spring will take care of everything else - to apply Base64, add Basic: and set properly a header.
Set all required headers including headers.setContentType(MediaType.APPLICATION_JSON)
Get an entity/object which you need to send (set as a body) with a request.
Serialize your object. The most popular, proper and simple way is using fasterxml json framework, you can make serialization with mapper.writeBalueAsString(<your object>). If you really cannot use external libraries, HttpEntity should make it: var request = new HttpEntity<>(<object>, headers);
Make restTemplate request. In almost all cases more convenient methods are restTemplate.postForObject(), restTemplate.getForObject(), restTemplate.postForEntity(), etc.: restTemplate.postForObject(uri, request, ResponseObject.class)

Add custom SoapHeader using SpringBoot

Using SoapUI I am able to send a request with a custom SOAP header like this:
<soap:Header>
<To xmlns="http://www.w3.org/2005/08/addressing">ws://xxx.com/PP/QM/GPMService/Vx</To>
<Action xmlns="http://www.w3.org/2005/08/addressing">http://xmldefs.xxx.com/PP/QM/GPMService/Vx/AbcService/GetServiceInfoRequest</Action>
<MessageID xmlns="http://www.w3.org/2005/08/addressing">ITEST-2018-04-16-0001</MessageID>
<Stage xmlns="http://xmldefs.xxx.com/Technical/Addressing/V1">ProdX</Stage>
</soap:Header>
and get a reasonable response.
I can't achieve this in my SpringBoot application.
I have a service extending WebServiceGatewaySupport:
#Service
public class AbcService extends WebServiceGatewaySupport{
private AbcConfiguration abcConfiguration;
#Autowired
public void setAbcConfiguration(final AbcConfiguration abcConfiguration) {
this.abcConfiguration = abcConfiguration;
}
public GetServiceInfoResponse GetServiceInfo() {
final String actionStr = "GetServiceInfo";
final ObjectFactory factory = new ObjectFactory();
GetServiceInfo getServiceInfo = factory.createGetServiceInfo();
JAXBElement<GetServiceInfo> gsiRequest = factory.createGetServiceInfo(getServiceInfo);
WebServiceTemplate wst = this.getWebServiceTemplate();
#SuppressWarnings("unchecked")
JAXBElement<GetServiceInfoResponse> gsiResponse = (JAXBElement<GetServiceInfoResponse>)wst
.marshalSendAndReceive("https://ws-gateway-cert.xxx.com/services/", gsiRequest, new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
try {
SoapHeader soapHeader = ((SoapMessage) message).getSoapHeader();
SoapHeaderElement toElem = soapHeader.addHeaderElement(new QName("http://www.w3.org/2005/08/addressing", "To"));
toElem.setText("ws://xxx.com/PP/QM/GPMService/Vx");
...
} catch (Exception e) {
logger.error("Error during marshalling of the SOAP headers", e);
}
}
});
return gsiResponse.getValue();
}
}
What am I doing wrong? Can anybody tell me how I can do this?
Okay. I got it working so far and the SOAP XML looks as demanded and running the request (being generated form my SpringBoot app) in SoapUI I get the demanded result.
public GetServiceInfoResponse GetServiceInfo() {
final String actionStr = "GetServiceInfo";
final ObjectFactory factory = new ObjectFactory();
GetServiceInfo getServiceInfo = factory.createGetServiceInfo();
JAXBElement<GetServiceInfo> gsiRequest = factory.createGetServiceInfo(getServiceInfo);
WebServiceTemplate wst = this.getWebServiceTemplate();
#SuppressWarnings("unchecked")
JAXBElement<GetServiceInfoResponse> gsiResponse = (JAXBElement<GetServiceInfoResponse>)wst
.marshalSendAndReceive(kpmConfiguration.getEndpoint(), gsiRequest, new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
System.out.println(message.toString());
try {
// get the header from the SOAP message
final SoapHeader soapHeader = ((SoapMessage) message).getSoapHeader();
final SaajSoapMessage ssMessage = (SaajSoapMessage)message;
final SOAPEnvelope envelope = ssMessage.getSaajMessage().getSOAPPart().getEnvelope();
System.out.println("envelope.getPrefix(): " + envelope.getPrefix());
envelope.removeNamespaceDeclaration("SOAP-ENV");
envelope.setPrefix(NAMESPACE_PREFIX_SOAP);
System.out.println("envelope.getPrefix(): " + envelope.getPrefix());
envelope.getBody().setPrefix(NAMESPACE_PREFIX_SOAP);
envelope.getHeader().setPrefix(NAMESPACE_PREFIX_SOAP);
envelope.addNamespaceDeclaration(NAMESPACE_PREFIX_SOAP, NAMESPACE_PREFIX_SOAP_DEF);
envelope.addNamespaceDeclaration(NAMESPACE_PREFIX_V2, NAMESPACE_PREFIX_V2_DEF);
envelope.addNamespaceDeclaration(NAMESPACE_PREFIX_WSSE, NAMESPACE_PREFIX_WSSE_DEF);
final SoapHeaderElement toElem = soapHeader.addHeaderElement(new QName(NAMESPACE_PREFIX_ADDRESSING, "To"));
toElem.setText(TO_VALUE);
final SoapHeaderElement actionElem = soapHeader.addHeaderElement(new QName(NAMESPACE_PREFIX_ADDRESSING, "Action"));
actionElem.setText(NAMESPACE_PREFIX_V2_DEF + "/AbcService/" + actionStr + "Request");
final SoapHeaderElement messageIdElem = soapHeader.addHeaderElement(new QName(NAMESPACE_PREFIX_ADDRESSING, "MessageID"));
messageIdElem.setText(MESSAGE_ID_VALUE + UUID.randomUUID());
final SoapHeaderElement stageElem = soapHeader.addHeaderElement(new QName(NAMESPACE_PREFIX_VWA, "Stage"));
stageElem.setText("Production");
final NodeList nl = ssMessage.getSaajMessage().getSOAPPart().getEnvelope().getBody().getChildNodes();
ssMessage.getSaajMessage().getSOAPPart().getEnvelope().getBody().removeChild(nl.item(0));
final SOAPElement se = ssMessage.getSaajMessage().getSOAPPart().getEnvelope().getBody().addBodyElement(new QName(actionStr));
se.setPrefix(NAMESPACE_PREFIX_V2);
final SOAPElement userAuthElem = se.addChildElement(new QName("UserAuthentification"));
final SOAPElement userIdElem = userAuthElem.addChildElement("UserId");
userIdElem.setTextContent(kpmConfiguration.getCredentials().getUsername());
System.out.println(userIdElem.getTextContent());
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(ssMessage.getPayloadSource(), soapHeader.getResult());
} catch (Exception e) {
logger.error("Error during marshalling of the SOAP headers", e);
}
}
});
return gsiResponse.getValue();
}
However, when I submit the request from my SpringBoot app I always get an exception:
java.net.SocketException: Unexpected end of file from server
Am I missing something in the code?
See the answer to the original question above in the edited question.
Concerning the java.net.SocketException: Unexpected end of file from server it seemed to come from redirecting the request through Eclipse's TCP/IP Monitor. When sending the request directly to the server I get a meaningful response with:
INFO_001
Method compelted successfully
:-)

Spring Integration SpEL for Header evaluation

I am trying to evaluate JMSHeader for Spring Integration message using SpEL but it seems to be evaluating to false always even though message has header stamped as true
<si:router expression="headers.jms_redelivered.equals(T(java.lang.Boolean).FALSE) ? 'channel1' : 'channel2' />
JMSXDeliveryCount=10, jms_redelivered=true
Can some one please comment what I am doing wrong here?
Thanks
Works for me:
#Test
public void testRedeliveredHeaderWithSpEL() throws JMSException {
DefaultJmsHeaderMapper mapper = new DefaultJmsHeaderMapper();
javax.jms.Message jmsMessage = new StubTextMessage() {
#Override
public boolean getJMSRedelivered() throws JMSException {
return true;
}
};
Map<String, Object> headers = mapper.toHeaders(jmsMessage);
assertNotNull(headers.get(JmsHeaders.REDELIVERED));
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.addPropertyAccessor(new MapAccessor());
Expression expression =
parser.parseExpression("jms_redelivered.equals(T(java.lang.Boolean).FALSE) ? 'channel1' : 'channel2'");
assertEquals("channel2", expression.getValue(context, headers, String.class));
}
You really should be sure that you don't lose headers before your <router>.
Maybe you use some <transformer> before <router> which returns whole Message<?> istead of just payload ?

Resources