How are SOAP headers processed in Camel-CXF? - java

I'm trying to wrap my head around Apache Camel using Jboss Fuse 6.2.1 and I don't understand how SOAP headers are being processed.
I have a WSDL from which I have generated classes for my input and output messages. The input consists of a header and a body while the output only consists of a body.
WSDL
<wsdl:types>
<xs:schema targetNamespace="http://cxftestserver.blueprint.me.com">
<xs:element name="input">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="Id" />
<xs:element type="xs:string" name="Name" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="output">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="Code" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<xs:schema targetNamespace="http://cxftestserver.blueprint.me.com/authentication">
<xs:element name="authHeader">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="username" />
<xs:element type="xs:string" name="password" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>
<wsdl:message name="inputTest">
<wsdl:part name="header" element="tns1:authHeader" />
<wsdl:part name="body" element="tns:input" />
</wsdl:message>
<wsdl:message name="outputTest">
<wsdl:part name="out" element="tns:output" />
</wsdl:message>
Blueprint
<bean id="myProcessor" class="com.me.blueprint.cxftestserver.MyProcessor" />
<cxf:cxfEndpoint id="test-ws" address="/Test" serviceClass="com.me.blueprint.cxftestserver.TestEndpoint" />
<camel:camelContext>
<camel:route>
<camel:from uri="cxf:bean:test-ws" />
<camel:process ref="myProcessor" />
</camel:route>
</camel:camelContext>
What I don't understand now is why the Message-body inside of the Exchange contains both the authHeader and the input? Every example I've read in the docs suggest that I should be able to extract the header by doing:
exchange.getIn().getHeaders(//insert your favorite parameter here);
but whenever I've tried that it would always return null. I wasted several hours looking in all the wrong places when I finally found that the Message-body contained the MessageContentsList-object holding both my SOAP-header and my SOAP-body.
Message inMessage = e.getIn();
AuthHeader header = inMessage.getBody(AuthHeader.class);
Input body = inMessage.getBody(Input.class);
Can someone please explain to me why and when this happens?

All depends on what dataFormat you prefer to use, for eg: if you use dataFormat=MESSAGE. Then you will be able to see the whole incoming message in your exchange, basically the whole message including headers will come in as a string. You can handle it straight away using xpath.
Refer this example for dataFormats -
https://github.com/gnanagurus/camel-guru-projects/blob/master/camel-cxf-geocoder-example/src/main/resources/META-INF/spring/beans-config.xml
But whatever they have suggested is also right, You can extract it using getHeaders(). Can you try configuring the CXF endpoint as mentioned in this example & make the dataFormat to be 'PAYLOAD'. (Basically in PAYLOAD you will get the soap body as a exchange body, rest in headers) I am sure, you will be able to read your headers using getHeaders().
Also, is there a reason why you have defined your CXF endpoint using your service class / why you have to generate your classes. If you have a reason ignore it, I tend to avoid classes in camel projects, rather I try the best to use the framework capabilities in XML itself. If you want to expose a SOAP based web service endpoint using camel, You don't have to generate any classes yourself - this example can lead/help you:
https://github.com/gnanagurus/camel-guru-projects/tree/master/camel-cxf-geocoder-example
Description about the above example:
http://bushorn.com/camel-web-services-expose-them/
Good luck

I believe I've found the answer to my initial question:
According to the documentation I've now come to understand that, when using POJO as data format, the consumer-endpoint looks at what parameters the requested method has in my SEI (Service Endpoint Interface (to which the serviceClass-attribute points.))
These parameters are then invoked into the Message-body (MessageContentsList-object) which would explain why both the header and the body are present.

Related

What to do with XSD and XSDL for SOAP - Java

So a guy send me a xsd and xpdl and told me to make requests to a SOAP gateway using this in Java.
What am I supposed to do with this? Load it or something? Can someone explain?
Any advice?
You should receive a WSDL as well. Using WSDL then you can generate soap client in java.
Soap client is like a library which act as local set of classes and methods. You can use those to call operations to execute on SOAP gateway. Its like you are calling a function locally but when executed, it will run on the SOAP gateway (remote server) where this function logic is implemented and hosted.
wsdl contains SOAP endpoints and xsd for data validation and description. Suppose you have a SOAP request like this
<message name = "SayHelloRequest">
<part name = "firstName" type = "xsd:string"/>
</message>
<message name = "SayHelloResponse">
<part name = "greeting" type = "xsd:string"/>
</message>
<portType name = "Hello_PortType">
<operation name = "sayHello">
<input message = "tns:SayHelloRequest"/>
<output message = "tns:SayHelloResponse"/>
</operation>
</portType>
here SayHelloRequest is request definition and SayHelloResponse is response defination. and Now suppose you have an Java Plain Object and then you need to define this on XSD like below code
<xs:element name="Person">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="address" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
you can define in XSD datatypes and its validation.
For simplicity xsd validate document and metadata anotherway WSDL is to describe the webservice location and operations. you can generate java classes from wsdl and yu can follow this link
https://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.jst.ws.cxf.doc.user%2Ftasks%2Fcreate_client.html

Can XSD Assertions be Used in Specifying a WSDL File

I have a WSDL file which also contains all types used in it (via <wsdl:types>
tag). When defining the types, I have something like this:
<wsdl:definitions name="service"
targetNamespace="http://www.xxx.yyy/reg/definitions" xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tax="http://www.xxx.yyy/reg/definitions" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
.............
<xs:complexType name="CompanyName">
<xs:sequence>
<xs:element name="Name" type="xs:string" />
<xs:element name="ShortName" type="xs:string" minOccurs="0" />
</xs:sequence>
<xs:attribute name="Language" type="tax:LanguageType" use="required"/>
<xs:assert test="ShortName or #Language != 'Language1'"/>
</xs:complexType>
.............
</wsdl:definitions>
Unfortunately, it doesn't work giving the following exception when I try to start the application on Tomcat:
javax.xml.ws.WebServiceException: org.xml.sax.SAXParseException; s4s-elt-invalid-content.1: The content of 'CompanyName' is invalid. Element 'assert' is invalid, misplaced, or occurs too often.
The WSDL version is 1.2 and I don't know which version of xsd it uses when types are described in it, so I don't know if it is a xsd version (1.0 vs. 1.1) issue or something else.
Can someone help me in finding the real issue?
EDIT: I have added the header of the wsdl.
I have added the version attribute (version="1.1") to <xs:schema>
definition but that didn't help either:
<xs:schema targetNamespace="http://www.xxx.yyy/reg/definitions" elementFormDefault="qualified" version="1.1">
Looks like assertions were introduced with XML Schema v1.1 when the XMLSchema definition moved over to w3.org.
Sample header:
<wsdl:description
targetNamespace="http://www.w3.org/2002/ws/sawsdl/spec/wsdl/order#"
xmlns="http://www.w3.org/2002/ws/sawsdl/spec/wsdl/order#"
xmlns:wsdl="http://www.w3.org/ns/wsdl"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:sawsdl="http://www.w3.org/ns/sawsdl">

Spring-ws: How to create Wsdl from an xsd with no "Request" element

Trying to implement a SOAP Webservice for a client and I need a wsdl file to test the service by soapUI. But as you can see below, this xsd has no Request and Response methods, all requests and responses are defined as a a "type" in a base ServiceProvider element. So when I try to auto generate my wsdl file by spring-ws it does not generate a proper wsdl, because Spring-ws requires all requests and responses element names should end with "Request" "Response".
What can I do?
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
attributeFormDefault="unqualified" targetNamespace="http://myurl" xmlns="http://myurl">
<xs:element name="ServiceProviderT" nillable="false">
<xs:annotation>
<xs:documentation>ServiceProviderT is the message spec for data sent between TechX and service providers or
vendors</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="Version" type="xs:string" nillable="false"/>
<xs:choice>
<xs:element name="Request" type="RequestType" nillable="false"/>
<xs:element name="Response" type="ResponseType" nillable="false"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
....
And this how I generate wsdl file
<sws:dynamic-wsdl id="myservice"
portTypeName="MyService"
locationUri="/myService"
targetNamespace="http://myurl">
<sws:xsd location="/schemas/my.xsd"/>
</sws:dynamic-wsdl>
There is no such requirement those are just the defaults. This is explained here in the Spring-WS reference guide. It also explains which properties to set to override those defaults.
The default request suffix is Request; the default response suffix is Response, though these can be changed by setting the requestSuffix and responseSuffix attributes on <dynamic-wsdl />, respectively.
<sws:dynamic-wsdl id="myservice"
portTypeName="MyService"
locationUri="/myService"
requestSuffix="YourRequestSuffixHere"
responseSuffix="YourResponseSuffixHere"
targetNamespace="http://myurl">
<sws:xsd location="/schemas/my.xsd"/>
</sws:dynamic-wsdl>

xjc: XML Validation issue

I am trying to generate java code using xjc.
My xsd is having following element
<xs:element name="DataRate">
<xs:attribute name="Limit" type="xs:decimal" use="optional" />
</xs:element>
and xjc is complaining with
[ERROR] s4s-elt-must-match.1: The content of 'DataRate' must match (annotation?, (simpleType | complexType)?, (unique | key | keyref)*)). A problem was found starting at: attribute.
After little investigation, i got to know that the attribute element is not wrapped with complexType and hence xjc is complaining. If I modify the xsd as
<xs:element name="DataRate">
<xs:complexType>
<xs:attribute name="Limit" type="xs:decimal" use="optional" />
</xs:complexType>
</xs:element>
it is working fine. But, as the xsd I am getting from 3rd party, I want to avoid the modification.
Is there any other way to do that?

How to retrieve values sent from webservice

I have a question about retrieving data values which are sent from a web service. I have a web service which receives data from a client and then does something with it. It worked all perfectly.
But now I wanted to add an extra element to the xsd which handles the message the webservice receives. I've added tests to another complexType which also exists of other elements. The element tests isn't required, but users can add one or more tests in their XML file.
<xs:element name="tests">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="999">
<xs:element name="test">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:element name="code" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="value" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
Whole XSD: http://pastebin.com/TuvYiQzE
I am using JAXB to handle the webservice messages which are send from the client. JAXB automatically generated some code for me:
public Message.Algemeen.TestCode.Tests getTests(){
return tests;
}
And
public List<Message.Algemeen.TestCode.Tests.test> getTest() {
if (test== null) {
test= new ArrayList<Message.Algemeen.TestCode.Tests.test>();
}
return this.test;
}
Now I want to return code and value per element test, the problem is Test and tests are a returned as an object and at the moment I have no idea how to read their value. The method toString() just returns cp.jaxb.classes.Message$Algemeen$Testcode$Tests$test#dcd76a
What am I doing wrong? If you need more code to understand my problem please tell me.
Thanks,
Jef
PS. English isn't my native language, I tried my best to explain my problem.
This line <xs:sequence minOccurs="1" maxOccurs="unbounded"> in the definition of Test means that you can have several code:value pair in test. is this what you want?
I'm no jaxb expert but I was told when designing schemas that it's easier to used name types in this case.
Here you have an list of "unnamed" object (the code:value pair) in a test. And I guess that makes the retrieval difficult.
What happen if you changed to maxOccurs="1"?
what Happen if you defined a new type for your code:value pair and make a list of this element?
could you try to modify it like this:
<xs:element name="tests">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="999">
<xs:element name="test">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:element name="singleTest">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="1">
<xs:element name="code" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="value" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
then you should be able to have something like
List testList = test.getSingleTest()
and iterate to get the code and value for each of them.
What language are you writing your client in? If you're using Java or .Net for example, you should simply be able to invoke your web service method and get a fully formed instance of Test back. As Udo Klimaschewski points out above, this means that you should be able to use something like
getTest().getCode()
To be clear, while it's potentially interesting to look through the XSD, you should not actually need to do this unless your development environment lacks SOAP web service support. The exact mechanism for generating the client side artifacts depends on your language and development environment; as an example, the process for referencing a web service using Netbeans is described here:
http://netbeans.org/kb/docs/websvc/client.html
This should work:
List<Message.Algemeen.TestCode.Tests.test> tests = yourObject.getTest();
for (Message.Algemeen.TestCode.Tests.test test : tests) {
test.getCode(); //Here is test object which contains strings or whatever.
}

Categories

Resources