View Full Version : Error: SOAP response is missing outer xml element
benethridge
Jul 27th, 2006, 04:57 PM
Hi, everyone.
Hopefully I'm just missing something simple on this:
Using the SOAPMessage.writeto method (thanks for the tip elsewhere on that one), I see that my response is missing the outermost element in the payload (i.e. my outermost application-functionality element, not any of the SOAP elements):
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><TxInfo xmlns="http://www.myapp.com"><StatCd>N</StatCd>...
This SHOULD have contained the outer wrapper element, like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><GetCitationLstRs xmlns="http://www.myapp.com"><TxInfo><StatCd>N</StatCd>...
I've used the airline sample as a pattern for a web service, and it (the airline sample) correctly returns the outer wrappering xml element.
I use JAXB and extend my endpoint class from AbstractMarshallingPayloadEndpoint, same as the airline sample. When I break inside AbstractMarshallingPayloadEndpoint, the requestObject and the responseObject both look reasonable, i.e. contain the outer wrapping element, so I'm thinking the problem is in the marshaller itself...or maybe there is some subtlety to the WSLD or xsd syntax.
I say "subtlety" coz my WSDL and .xsd validate in Eclipse and I've looked them over carefully. They look reasonable to me.
Tomcat log shows no errors.
Has anyone else encountered this problem? Thoughts on where I might look next?
Arjen Poutsma
Jul 28th, 2006, 07:43 AM
I take it that the PayloadLoggingInterceptor also only shows <TxInfo xmlns="http://www.myapp.com"><StatCd>N</StatCd>... without the wrapping <GetCitationLstRs xmlns="http://www.myapp.com"> element as well?
It would be interesting to see what kind of XML the unwrapped marshaller generates itself. Could you write a little test code for that, somethis like:
import javax.xml.bind.*;
public class Sample {
public static void main(String[] args) throws Exception {
Object responseObject = createResponseObject();
JAXBContext jc = JAXBContext.newInstance( "com.acme.foo" );
Marshaller m = jc.createMarshaller();
m.marshal( fooObj, System.out );
}
}
benethridge
Jul 28th, 2006, 09:37 AM
Hi, Arjen.
I'm not finding a call to PayloadLoggingInterceptor anywhere in the airline sample. Also, when I debug through the Spring code, I'm not seeing it called anywhere in the call stack. Is this a class I'm supposed to add to my app somewhere? or ?
Ben
mskendrick
Jul 28th, 2006, 09:52 AM
This may be similar to a problem I was having. Take a look here:
http://forum.springframework.org/showthread.php?t=26218
Shannon Kendrick
benethridge
Jul 28th, 2006, 10:36 AM
It would be interesting to see what kind of XML the unwrapped marshaller generates itself.
Arjen, I think maybe there are some obvious errors in your sample above, but no biggie as I think I understood where you were going with it. Implementing your sample in the Endpoint class (i.e. the server side) shows this in the tomcat log:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TxInfo xmlns="http://www.uptodate.com"><StatCd>N</StatCd><InfoMsgs/><WarningMsg
s/><ErrorMsgs/></TxInfo>
...which makes sense, i.e. it looks like a marshalling problem.
Note that I'm using the JAXB Marshaller for this sample test, not any of the Spring Marshallers.
Shannon, thanks for the tip. I'll check in out.
Ben
benethridge
Jul 28th, 2006, 11:02 AM
This may be similar to a problem I was having. Take a look here:
http://forum.springframework.org/showthread.php?t=26218
Shannon Kendrick
I looked this over and I'm not using a binding file (i.e. letting JAXB take all defaults). Also, I'm using JAXB 2.0.1 (I think) since I'm using the same Ivy "repository" and config as the Spring airline sample.
Ben
Arjen Poutsma
Jul 28th, 2006, 04:19 PM
Arjen, I think maybe there are some obvious errors in your sample above, but no biggie as I think I understood where you were going with it.
I'm sorry for that, though I can find the error myself ;). Most of my snippets are sample code, and don't necessarily compile, but I guess it helped you enough.
...which makes sense, i.e. it looks like a marshalling problem.
If it's a marshalling problem, you probably have to tweak the JAXB2 annotations somewhat. You can write some unit tests which validate the XML. XMLUnit really helps you with this (I use it a lot in the SWS test code, especially assertXmlEquals() ).
benethridge
Aug 2nd, 2006, 01:53 PM
Hi, Arjen.
I wrote a simple test app in Eclipse that exhibits the problem. I imagine I'm missing something simple, but I just can't see it yet.
Like the Airline project, but unlike the Echo project, this project uses JAXB. Also the project does NOT use Ivy or multiple build files, so it is quite simple. I can send it to you, but it's 42meg zipped, so I don't think I can post it, can I?
Arjen, in hindsight, i.e. having gotten the airline sample running ok, the Ivy and the multiple build files might make things easy on you Spring core developers, but they make it hard on us new to Spring, coz we have to go through multiple learning curves at once, making it very hard to figure out if the problem is in our code, the Spring code, the Ivy code, the JAXB code, the Ant code, the Sun code, the Eclipse, and on and on. Each of these comes with its own learning curve and "issues" (quirks and gotcha's).
Since I really like the Spring Web Service model, and I want it to succeed, and be well-accepted quickly by the java community, it might make that acceptance go faster if the Spring team could remove some of the "variables" in the sample apps. On this I speak with some authority, having been a very successful flight instructor for many years. (Lots of fun but VERY little money in that profession. :)). As a teacher, one often has to break down the complex into simple learnable units, not for the benefit of the teacher, but for the benefit of the student...which ultimately "benefits" the teacher as well, now that I think of it.
If you think this is a good, simple example of JAXB usage in Spring WS, you have my permission to use it and modify it in any way you choose.
Ben
Arjen Poutsma
Aug 3rd, 2006, 06:41 AM
I wrote a simple test app in Eclipse that exhibits the problem. I imagine I'm missing something simple, but I just can't see it yet.
Like the Airline project, but unlike the Echo project, this project uses JAXB. Also the project does NOT use Ivy or multiple build files, so it is quite simple. I can send it to you, but it's 42meg zipped, so I don't think I can post it, can I?
Great! I am certainly interested. You could use http://www.yousendit.com/ to send me the files. My email address is arjenp at interface21 dot com.
Arjen, in hindsight, i.e. having gotten the airline sample running ok, the Ivy and the multiple build files might make things easy on you Spring core developers, but they make it hard on us new to Spring, coz we have to go through multiple learning curves at once, making it very hard to figure out if the problem is in our code, the Spring code, the Ivy code, the JAXB code, the Ant code, the Sun code, the Eclipse, and on and on. Each of these comes with its own learning curve and "issues" (quirks and gotcha's).
Yes, I've found Ivy to be quite difficult to grasp at some times as well. That's one of the reasons I've switched to Maven 2. It's becoming more and more of a standard now, and offers nice things like creating an Eclipse workspace from the project descriptor, to get people started.
Since I really like the Spring Web Service model, and I want it to succeed, and be well-accepted quickly by the java community, it might make that acceptance go faster if the Spring team could remove some of the "variables" in the sample apps. On this I speak with some authority, having been a very successful flight instructor for many years. (Lots of fun but VERY little money in that profession. :)). As a teacher, one often has to break down the complex into simple learnable units, not for the benefit of the teacher, but for the benefit of the student...which ultimately "benefits" the teacher as well, now that I think of it.
I see your point. You must understand that you are in fact the "early adaptors" of this project. After the Spring 2.0 release, Rick Evans, who also wrote most of the Spring 2.0 reference documentation, will devote some of his time to write more docs for Spring-WS. I do love the fact that the project already has such a loyal backing.
In the near future, I will devote more time to make common things easier. Now that there actually people using Spring-WS, this can be done. I'm open for suggestions on this, though. You, after all, are the users, and you can decide the future of this project.
Cheers,
benethridge
Aug 3rd, 2006, 10:35 AM
Hi, Arjen.
I sent this to you via yousendit.com.
The maven2 sounds promising, but heads-up that the user feedback on it indicates that there may still be significant bugs:
http://www.javaworld.com/javaworld/jw-12-2005/jw-1205-maven-p3.html
Also I worry that maven2 may be yet another third-party tool that will add to the learning complexity (though granted that in general these tools are great once one is through the learning curve of Spring itself).
I accept the fact that I'm an "early adapter". To me, from a design point of view Spring WS looks like the best thing on the market today. If you can keep the pattern examples simple to deploy into IDEs, I think it will self-propel into a market leader.
Ben
benethridge
Aug 4th, 2006, 03:37 PM
Hi, everyone.
I imagine Arjen will try to resolve the problem for this in JAXB 1.0, but I have solved it by moving to JAXB 2.0.
Basically, you have to:
(1) Change your request element in invokeInternal(Object requestObject) to cast from JAXBElement, and you have to create a new JAXBElement as the return value. Example (may not be perfect, but should get you close):
protected Object invokeInternal(Object requestObject) throws Exception {
JAXBElement<TestRqTyp> rqElement = (JAXBElement<TestRqTyp>) requestObject;
TestRqTyp request = rqElement.getValue();
TestService ts = new TestService();
TestRsTyp response = ts.getRs(request);
QName rsQName = new QName("http://www.mycompany.com","TestRs");
JAXBElement<TestRsTyp> rsElement = new JAXBElement<TestRsTyp>(rsQName,TestRsTyp.class,response);
return rsElement;
Note the difference in the above, when compared to the airline sample, which uses JAXB 1.0.
(2) Change your ant taskdef for the JAXB code generation from source="1.0" to source="2.0". Example:
<target name="generate-jaxb">
<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask" classpathref="compile.classpath"/>
<xjc target="${target.gen.java.dir}" package="${jaxb.package}" source="2.0">
<schema dir="${src.web.dir}">
<include name="**/*.xsd"/>
</schema>
<produces dir="${target.gen.java.dir}" includes="**/schema/**/*.java"/>
</xjc>
</target>
(3) Change your jaxbMarshaller in your application context file to use the Jaxb2Marshaller's syntax. Example:
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<description>
</description>
<property name="contextPath" value="com.mycompany.test.schema"/>
<property name="schema" value="test.xsd"/>
</bean>
This should probably give someone a good hint as to what the problem is in JAXB 1.0, but I don't see it. Moot point for me, coz I'm shifting to JAXB2. It generates fewer classes and does a few other things better than 1.0.
Ben
Arjen Poutsma
Aug 4th, 2006, 05:03 PM
I looked at the sample you provided, and I I've solved your issue (though a bit too late, so it seems ;) ). It has to do with the difference between elements and types in XSD. A type (complex of simple) in an XSD schema is "abstract", meaning that it cannot be used directly, but only referred to in elements or attributes. Though not entirely the same, you can compare a XSD type to an interface in Java, and it's even more like a typedef in C.
When you generate Java code from the XSD using JAXB, it also generates a class for the type. In the sample you sent me, this was part of the XSD:
<element name="TestRq" type="tns:TestRqTyp" />
<complexType name="TestRqTyp" mixed="false">
<sequence>
<element name="TestInput" type="string" minOccurs="0" />
</sequence>
</complexType>
<element name="TestRs" type="tns:TestRsTyp" />
<complexType name="TestRsTyp" mixed="false">
<sequence>
<element name="TestOutput" type="string" />
</sequence>
</complexType>
This resulted in four generated interfaces: TestRq and TestRs (for the elements), TestRqTyp and TestRsTyp (for the complexTypes). The element interfaces extend the type interfaces, but they are not the same! One is the element, the other is the type.
Now, the endpoint code contained the following (I've removed some debugging code):
public Object invokeInternal(Object requestObject) throws Exception {
TestRqTyp request = (TestRqTyp) requestObject;
TestService ts = new TestService();
TestRsTyp response = ts.getRs(request);
return response;
}
Note that you're using the type interfaces here, and not the elements. This is ok for the requestObject, because the element extends the type. But this is not OK for the response object, because that results in the type being written, without a wrapping TestRs element:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<TestOutput xmlns="http://www.mycompany.com">BenTestOutputValue</TestOutput>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
To fix this, I changed the above code to:
public Object invokeInternal(Object requestObject) throws Exception {
TestRq request = (TestRq) requestObject;
TestService ts = new TestService();
TestRs response = ts.getRs(request);
return response;
}
which results in the following response message:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<TestRs xmlns="http://www.mycompany.com">
<TestOutput>BenTestOutputValue</TestOutput>
</TestRs>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
So, in conclusion, you have to cast the requestObject to a generated element when using JAXB 1.0. When using JAXB 2.0, this is a different matter, because it does not generate classes for elements. So then you have to cast it to a JAXBElement, like you've shown in your post. This is rather unfortunate, because it ties your endpoint code to the marshalling technology you use, while the whole point of the Spring OXM abstractions is to forego this tie-in. I don't know if there is any way to work around this unnecessary tie-in.
benethridge
Aug 7th, 2006, 09:17 AM
Thanks, Arjen. That all makes sense now. Again, feel free to modify/use this as a simple JAXB1 sample (i.e. no Ivy and no database - just simple JAXB), if you wish.
Ben
Powered by vBulletin® Version 4.2.1 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.