Possible to consume badly formed Fault Messages? - soap

I've a WCF client communicating with an unknown server implementation which I have no control over. This client works fine it just doesn't like, what appears to be, incorrectly formed SOAP Fault messages. The messages I receive look like:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>...</soap:Header>
<soap:Body>
<soap:Fault>
<soap:faultcode>soap:Client</soap:faultcode>
<soap:faultstring>...</soap:faultstring>
<soap:detail>...</soap:detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>
I believe according to the soap schema the child elements shouldn't be qualified and ned to look like:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>...</soap:Header>
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>...</faultstring>
<detail>...</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>
Is there something that I can configure or override so that I can consume messages which arrive in the latter format so that I can consume the fault messages instead of xml exceptions?

I'm cannot recall how I found stumbled across Message Inspectors, but that it how I solved my problem.
This and this article provided the base for creating the inspector, and what follows is the meat of the inspector:
public void AfterReceiveReply(ref Message reply, object correlationState)
{
if (!reply.IsFault)
return;
var document = new XmlDocument();
document.Load(reply.GetReaderAtBodyContents());
var navigator = document.CreateNavigator();
var manager = new XmlNamespaceManager(navigator.NameTable);
manager.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
var it = navigator.Select("//soap:Fault", manager);
if (it.MoveNext() && it.Current.HasChildren && it.Current.MoveToChild(XPathNodeType.Element))
{
do
{
var c = it.Current;
if (string.IsNullOrEmpty(c.Prefix))
continue;
c.ReplaceSelf("<" + c.LocalName + ">" + c.InnerXml + "</" + c.LocalName + ">");
/// we may want to record the detail included inside the detail element,
/// it is not reported in the FaultException that is raised.
} while (it.Current.MoveToNext());
}
var reader = XmlDictionaryReader.CreateDictionaryReader(new XmlNodeReader(document));
reader.MoveToStartElement();
var fixedReply = Message.CreateMessage(reply.Version, null, reader);
fixedReply.Headers.CopyHeadersFrom(reply.Headers);
fixedReply.Properties.CopyProperties(reply.Properties);
reply = fixedReply;
}

It looks like the offending application is using a custom (and badly implemented) SOAP library. The following article might help (I haven't had to deal with this as of yet as I am in a pure .Net shop).
http://msdn.microsoft.com/en-us/library/ms733721.aspx

Note that the System.Web.Services.Protocols.SoapHttpClientProtocol class seems significantly more tolerant of malformed Fault responses than WCF.
This is sometimes referred to as ASMX services protocol. That may be an option to consider as well.
Howard Hoffman

} catch (SoapFaultClientException e) {
log.error(e);
SoapFaultDetail soapFaultDetail = e.getSoapFault().getFaultDetail();
SoapFaultDetailElement detailElementChild = (SoapFaultDetailElement) soapFaultDetail.getDetailEntries().next();
Source detailSource = detailElementChild.getSource();
try {
Object detail = (JAXBElement<SearchResponse>) getWebServiceTemplate().getUnmarshaller().unmarshal(detailSource);
// throw new SoapFaultWithDetailException(detail);
} catch (IOException e1) {
throw new IllegalArgumentException("cannot unmarshal SOAP fault detail object: " + soapFaultDetail.getSource());
}
}

Related

javax.xml.soap.MessageFactory.createMessage call takes too long for large content

The http response content is a soap message with a 100mb content.
messageFactory.createMessage(null, responseinputstream)
is taking 3 to 4 minutes, and the
responseSoapMessage.getSOAPBody()
takes another 4 minutes or more.
After this, since I am in WildFly application server, the transaction times out.
MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
...
HttpEntity responseHttpEntity = httpResponse.getEntity();
...
InputStream responseContentStream = responseHttpEntity.getContent();
SOAPMessage responseSoapMessage = messageFactory.createMessage(null, responseContentStream);
SOAPBody responseSoapBody = responseSoapMessage.getSOAPBody();
On creating a SOAPMessage from the InputStream through MessageFactory, I wonder why it takes that long.
Could it be due to some JAR issue?
I have two JARS in play: axis-1.4.jar, and saaj-impl-1.3.jar
However, while debugging, i found that the class used is SOAPMessageFactory1_2Impl from saaj-impl-1.3.jar.
Still, i could not reason out why it is taking more than 5 minutes to cross that two lines of code.
This is the SOAP XML in the response InputStream.
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<ReceiveDocumentResponse xmlns="https://NAMESPACEURL">
<ReceiveDocumentResult>
<base64Binary>HERE IS THE JPG IMAGE CONTENT OF SIZE 100 MB OR MORE</base64Binary>
</ReceiveDocumentResult>
</ReceiveDocumentResponse>
</soap:Body>
</soap:Envelope>
Please note that this method calls work reasonably good (quick) for contents less than 30 MBs.
Any tips to go about troubleshooting, Or any tips to go for an alternative approach to parse out the content, are really appreciated
I replaced the above logic with what is below. It does take a minute or so for 100 MB content, but it works consistently.
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
DocumentBuilder domParser = null;
domParser = builderFactory.newDocumentBuilder();
domDocument = domParser.parse(responseContentStream);
NodeList elementList = domDocument.getElementsByTagName("base64Binary");
Node element = elementList.item(0);
String base64String = element.getTextContent();
byte[] rawData= Base64.getDecoder().decode(base64String);
document.setBData(rawData);
Whatever it is...

MSF4J POST method receiving partial data

I'm new to MSF4J and I need to write a REST API that accepts a large XML data through POST. I am using
request.getMessegeBody()
method to get the data. I discovered that it's now deprecated but I couldn't find the newer version of it so I decided to use it anyway.
The problem is, when I send data to the microservice for the first time, it doesn't get the whole data. All the subsequent requests will get the full message body except the first.
When I try passing the request through ESB, ESB receives the whole body but when it reaches the endpoint it will be truncated.
I have also tried sending requests from different rest clients but for the first time it always gets the incomplete message body
#POST
#Consumes({ "application/xml", "application/json", "text/xml" })
#Path("test/")
public Response getReqNotification(#Context Request request) throws Exception {
Response.ResponseBuilder respBuilder =
Response.status(Response.Status.OK).entity(request);
ByteBuf b = request.getMessageBody();
byte[] bb = new byte[b.readableBytes()];
b.duplicate().readBytes(bb);
System.out.println(new String(bb));
return respBuilder.build();
}
I expect it to print the full message(which is about 2000 bytes long) every time when I send a request, but I'm only getting around 800 bytes when I first run the microservice.
I hope ill get assistance here. I have tried elsewhere but wso2 doesn't have much documentation (⌣_⌣”)
I still don't really understand what I was doing wrong but with the help of this link I have managed to come up with the following code and it works fine.
The major cha is that I now use request.getMessageContentStream() instead of the depricated request.getMessageBody()
#Consumes({ "application/xml", "application/json", "text/xml" })
#Path("test/")
public Response getReqNotification(#Context Request request) throws Exception {
Response.ResponseBuilder respBuilder =
Response.status(Response.Status.OK).entity(request);
String data = "";
BufferedInputStream bis = new BufferedInputStream(request.getMessageContentStream());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
int d;
while ((d = bis.read()) != -1) {
bos.write(d);
}
data = bos.toString();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
bos.close();
} catch (IOException e) {
}
}
System.out.println(data);
//////do stuff
return respBuilder.build();
}

Restlet Studio Error 422 when generating sample client and server

Hi I'm using the restlet studio to generate a client and server from your sample pet store API . Here are my steps:
Generate Java Server (JAX-RS)
Edit pom.xml to make a war file
mvn package
Deploy to jetty server as webapp
Verify it works by going to hitting the URL with a browser:
http://54.149.215.125:8080/v2/pet/findByTags
Response:
{"code":4,"type":"ok","message":"magic!"}
At this point I think it works, until I generate the client in Java
I change the endpoint from the webnik one to my webserver
Make a simple main method
public static void main(String[] args) {
try {
FindPetByTagsClientResource a = new FindPetByTagsClientResource();
Pet represent = a.represent();
} catch (Exception ex) {
Logger.getLogger(APIPetStore.class.getName()).log(Level.SEVERE, null, ex);
}
}
When I run it I get this:
run:
Starting the internal HTTP client
null
Unprocessable Entity (422) - The server understands the content type of the request entity and the syntax of the request entity is correct but was unable to process the contained instructions
at org.restlet.resource.Resource.toObject(Resource.java:893)
at org.restlet.engine.resource.ClientInvocationHandler.invoke(ClientInvocationHandler.java:326)
at com.sun.proxy.$Proxy5.represent(Unknown Source)
at net.apispark.webapi.client.FindPetByTagsClientResource.represent(FindPetByTagsClientResource.java:22)
at apipetstore.APIPetStore.main(APIPetStore.java:28)
Caused by: java.io.IOException: Unable to create the Object representation
at org.restlet.engine.converter.DefaultConverter.toObject(DefaultConverter.java:282)
at org.restlet.service.ConverterService.toObject(ConverterService.java:229)
at org.restlet.resource.Resource.toObject(Resource.java:889)
... 4 more
Caused by: java.lang.IllegalArgumentException: The serialized representation must have this media type: application/x-java-serialized-object or this one: application/x-java-serialized-object+xml
at org.restlet.representation.ObjectRepresentation.(ObjectRepresentation.java:221)
at org.restlet.representation.ObjectRepresentation.(ObjectRepresentation.java:123)
at org.restlet.representation.ObjectRepresentation.(ObjectRepresentation.java:104)
at org.restlet.engine.converter.DefaultConverter.toObject(DefaultConverter.java:279)
... 6 more
BUILD SUCCESSFUL (total time: 0 seconds)
Change the main method to this and it works:
public static void main(String[] args) {
try {
FindPetByTagsClientResource a = new FindPetByTagsClientResource();
a.getClientResource().get().write(System.out);
} catch (Exception ex) {
Logger.getLogger(APIPetStore.class.getName()).log(Level.SEVERE, null, ex);
}
}
Output:
Starting the internal HTTP client
{"code":4,"type":"ok","message":"magic!"}
Any ideas on how I can fix this?
In fact, the JAXRS server skeleton is really a server skeleton ;-) This means that it doesn't actually send back the right content according to the client. If you look at the server code, you always see this:
public Response findPetsByTags(#ApiParam(value = "Tags to filter by") #QueryParam("tags") List<String> tags)
throws NotFoundException {
// do some magic!
return Response.ok().entity(new ApiResponseMessage(ApiResponseMessage.OK, "magic!")).build();
}
It doesn't correspond to a list of pet objects...
On the client side, you got the error since you try to use annotated interfaces. They automatically try to use the internal converter of Restlet. It fails since it expects an object of type Pet and you received something with this structure: {"code":4,"type":"ok","message":"magic!"}.
In conclusion, you need to do some work to adapt the server skeleton to return the correct objects. Here is an hardcoded solution to make work your client SDK:
#GET
#Path("/findByTags")
#ApiOperation(value = "Finds Pets by tags", notes = "Finds Pets by tags", response = Pet.class, responseContainer = "List")
#ApiResponses(value = {
#ApiResponse(code = 400, message = "") })
public Response findPetsByTags(#ApiParam(value = "Tags to filter by") #QueryParam("tags") List<String> tags)
throws NotFoundException {
// do some magic!
Pet pet = new Pet();
pet.setId(10);
pet.setName("My pet");
pet.setStatus("status");
List<Tag> actualTags = new ArrayList<Tag>();
Tag tag1 = new Tag();
tag1.setId(1);
tag1.setName("tag1");
actualTags.add(tag1);
Tag tag2 = new Tag();
tag2.setId(2);
tag2.setName("tag2");
actualTags.add(tag2);
pet.setTags(actualTags);
return Response.ok().entity(pet).build();
}
I'll have a look if we can improve this for the server side. In fact, the Restlet Studio internally uses the swagger2 codegen tool chain to generate this server skeleton.
Hope it helps,
Thierry

How to aggregate the responses of two web services using spring integration

I am calling two webservices using spring-integration as shown below:
application-context.xml
<int:chain input-channel="requestChannelForHolidayService"
output-channel="outputChannelForHolidayService">
<int-ws:outbound-gateway
uri="http://localhost:8080/holidayService/holidayService" marshaller="marshaller"
unmarshaller="marshaller" />
</int:chain>
<int:chain input-channel="requestChannelForAccount"
output-channel="outputChannelForAccount">
<int-ws:outbound-gateway
uri="http://localhost:8080/spring-webservices-sample/endpoints"
marshaller="marshaller1" unmarshaller="marshaller1" />
</int:chain>
Testrunner.java
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
MessageChannel channel=(MessageChannel) context.getBean("requestChannelForHolidayService",MessageChannel.class);
HolidayRequest request=new HolidayRequest();
BigInteger b1=new BigInteger("1");
BigInteger b2=new BigInteger("50");
request.setEmpId(b1);
request.setDays(b2);
System.out.println("sending request");
System.out.println("request sent");
channel.send(MessageBuilder.withPayload(request).build());
ApplicationContext context1 = new ClassPathXmlApplicationContext("application-context.xml");
MessageChannel channel1=(MessageChannel) context1.getBean("requestChannelForAccount",MessageChannel.class);
AccountDetailsRequest request2=new AccountDetailsRequest();
request2.setAccountNumber("12345");
System.out.println("sending request2");
System.out.println("request sent2");
channel1.send(MessageBuilder.withPayload(request2).build());
Now I need to combine the output of 'outputChannelForHolidayService' and 'outputChannelForAccount'.Can anyone help out for acheiving this. Thank you in advance.
You shouldn't use two application contexts; put them in the same context. Set the correlationId header on the messages; send both results to an aggregator with release-strategy-expression="size == 2".
Consider using a Messaging Gateway instead of sending to channels. Something like:
Collection<Object> process(#Payload Object[] requests, #Header("correlationId"), String correlation);
Then in the context, have...
gateway->splitter->payload-type-router->
request1Channel->ws->toAgg
request2Channel->ws->toAgg
toAggChannel->aggregator
If you omit the output-channel from the aggregator, the result will go back to the gateway.

unable to read serialized data as message body in msmq c# 3.0

This is my method to send message to a private Q
using (MessageQueue msgQ = new MessageQueue(MessageQueueName))
{
using (System.Messaging.Message newMessage = new System.Messaging.Message(MessageBody,
new System.Messaging.ActiveXMessageFormatter()))
{
newMessage.Label = MessageLabel;
newMessage.Priority = Priority;
msgQ.Send(newMessage);
}
}
I have an order object which i serialize and send as message body. The serialized object is
<?xml version="1.0"?>
<OrderInfo>
<OrderID>11111</OrderID>
<OrderDetails>
<LineItem>
<ProductDetails>
<Name>qwqwqw</Name>
<Manufacturer>asasas</Manufacturer>
<UPC>12222222222</UPC>
<sku>2132</sku>
<Price>12.21</Price>
</ProductDetails>
<Quantity>1</Quantity>
</LineItem>
</OrderDetails>
</OrderInfo>
This is my method to receive that message in a windows service
void queue_ReceiveCompleted(object sender, ReceiveCompletedEventArgs asyncResult)
{
// Connect to the queue.
MessageQueue mq = (MessageQueue)sender;
// End the asynchronous Receive operation.
Message m = mq.EndReceive(asyncResult.AsyncResult);
m.Formatter = new System.Messaging.ActiveXMessageFormatter()
//Get Filedata from body
OrdrInfo qMessage = (OrdrInfo)XMLUtil.Deserialize(m.Body.ToString(), typeof(OrdrInfo));
}
when I try to look at m.Body in quickwatch this is what it states
m.Body.Message = Cannot find a formatter capable of reading this message.
m.Body.StackTrace = at System.Messaging.Message.get_Body()
Hopefully you're not still stuck on this, but as it came up top of my search when running into the same problem.
As no one had answered it, here is one answer that I've just found else where (thanks TechRepublic). This code assume that "MyType" is a typically basic message that can be read by XML Serialisation - this means it is marked as serializable and all data to be sent/reconstructed is in public get/set properties.
Code is:
MessageQueue msgQ = new MessageQueue(#".\private$\CreateNewEntity");
msgQ.Formatter = new XmlMessageFormatter(new []{typeof(MyType)});
var msg = msgQ.Receive();
msgQ.Close();
return msg.Body as MyType;