
| Key: |
AXIS-2586
|
| Type: |
Bug
|
| Status: |
Open
|
| Priority: |
Major
|
| Assignee: |
Unassigned
|
| Reporter: |
Steve McDuff
|
| Votes: |
0
|
| Watchers: |
1
|
|
If you were logged in you would be able to see more operations.
|
|
|
|
Environment:
|
Tomcat 5.5
Sun JRE 1.5.0.07
Windows XP SP2
Tomcat 5.5
Sun JRE 1.5.0.07
Windows XP SP2
|
|
|
I found a problem when passing abstract parameters with array fields to a method in Wrapped/Literal encoding:
Steps to reproduce
1 - create a base class that has an array field of itself.
2 - create a sub class that inherits from the base class
3 - create a service class to publish as a web service that uses the base class as a paramter for a public method
4 - use Java2WSDL to publish the service class using wrapped/literal encoding
5 - run the web services on a web server
6 - use WSDL2Java to create stubs for a test client
7 - use the test client to call the method on the service class with a Sub Class instance.
8 - notice that the call to the service fails with the error : org.xml.sax.SAXException: Invalid element in BaseClass - item
Here are the code samples:
class BaseClass{
private BaseClass[] childArray;
public BaseClass[] getChildArray(){
return childArray;
}
public void setChildArray(BaseClass[] childArray){
this.childArray = childArray;
}
}
class SubClass extends BaseClass{
private BaseClass[] otherChildArray;
public BaseClass[] getOtherChildArray(){
return otherChildArray;
}
public void setOtherChildArray(BaseClass[] otherChildArray){
this.otherChildArray = otherChildArray;
}
}
// Use Java2WSDL to publish this as a web service
class MyTestInterface{
// send and return the same base class parameter
public BaseClass sendBase(BaseClass base){
return base;
}
// this method just declares sub to make sure it finds its
// way in the WSDL
public void declareSub(SubClass sub){};
}
// client unit test code
class MyTest extends junit.framework.TestCase{
public void testSendBase(){
// Insert code to resolve URL and locate the web services here
MyTestInterface x = null;
SubClass sendSubClass = new SubClass();
sendSubClass.setChildArray(new BaseClass[]{new SubClass()});
// this call will fail with the message
// org.xml.sax.SAXException: Invalid element in BaseClass - item
BaseClass retVal = x.sendBase(sendSubClass);
assertEquals(sendSubClass, retVal);
}
}
Note:
- This problem is reproducible in Document/Literal
- This problem is not reproducible in RPC/Encoded
|
|
Description
|
I found a problem when passing abstract parameters with array fields to a method in Wrapped/Literal encoding:
Steps to reproduce
1 - create a base class that has an array field of itself.
2 - create a sub class that inherits from the base class
3 - create a service class to publish as a web service that uses the base class as a paramter for a public method
4 - use Java2WSDL to publish the service class using wrapped/literal encoding
5 - run the web services on a web server
6 - use WSDL2Java to create stubs for a test client
7 - use the test client to call the method on the service class with a Sub Class instance.
8 - notice that the call to the service fails with the error : org.xml.sax.SAXException: Invalid element in BaseClass - item
Here are the code samples:
class BaseClass{
private BaseClass[] childArray;
public BaseClass[] getChildArray(){
return childArray;
}
public void setChildArray(BaseClass[] childArray){
this.childArray = childArray;
}
}
class SubClass extends BaseClass{
private BaseClass[] otherChildArray;
public BaseClass[] getOtherChildArray(){
return otherChildArray;
}
public void setOtherChildArray(BaseClass[] otherChildArray){
this.otherChildArray = otherChildArray;
}
}
// Use Java2WSDL to publish this as a web service
class MyTestInterface{
// send and return the same base class parameter
public BaseClass sendBase(BaseClass base){
return base;
}
// this method just declares sub to make sure it finds its
// way in the WSDL
public void declareSub(SubClass sub){};
}
// client unit test code
class MyTest extends junit.framework.TestCase{
public void testSendBase(){
// Insert code to resolve URL and locate the web services here
MyTestInterface x = null;
SubClass sendSubClass = new SubClass();
sendSubClass.setChildArray(new BaseClass[]{new SubClass()});
// this call will fail with the message
// org.xml.sax.SAXException: Invalid element in BaseClass - item
BaseClass retVal = x.sendBase(sendSubClass);
assertEquals(sendSubClass, retVal);
}
}
Note:
- This problem is reproducible in Document/Literal
- This problem is not reproducible in RPC/Encoded
|
Show » |
|
In the class : org.apache.axis.encoding.ser.BeanDeserializer.
Problem: getDeserializer wasn't getting the right deserializer for array types when the xmlType is set.
Solution: apply the default XML Type when we are getting an array deserializer
I modified the following method (see method comments to know where changes were applied).
protected Deserializer getDeserializer(QName xmlType,
Class javaType,
String href,
DeserializationContext context) {
if (javaType.isArray()) {
context.setDestinationClass(javaType);
}
// See if we have a cached deserializer
if (cacheStringDSer != null) {
if (String.class.equals(javaType) &&
href == null &&
(cacheXMLType == null && xmlType == null ||
cacheXMLType != null && cacheXMLType.equals(xmlType))) {
cacheStringDSer.reset();
return cacheStringDSer;
}
}
Deserializer dSer = null;
// MODIFICATION DONE HERE : Added the "&& ! javatype.isArray()" part in the line below
if (xmlType != null && href == null && ! javatype.isArray() ) {
// Use the xmlType to get the deserializer.
dSer = context.getDeserializerForType(xmlType);
} else {
// If the xmlType is not set, get a default xmlType
TypeMapping tm = context.getTypeMapping();
QName defaultXMLType = tm.getTypeQName(javaType);
// If there is not href, then get the deserializer
// using the javaType and default XMLType,
// If there is an href, the create the generic
// DeserializerImpl and set its default type (the
// default type is used if the href'd element does
// not have an xsi:type.
if (href == null) {
dSer = context.getDeserializer(javaType, defaultXMLType);
} else {
dSer = new DeserializerImpl();
context.setDestinationClass(javaType);
dSer.setDefaultType(defaultXMLType);
}
}
if (javaType.equals(String.class) &&
dSer instanceof SimpleDeserializer) {
cacheStringDSer = (SimpleDeserializer) dSer;
cacheXMLType = xmlType;
}
return dSer;
}
Note: The source problem seems to be that the client sends its arrays identified with types when special conditions are met.
Here are 2 SOAP messages sent. One uses a BaseClass as a root parameter, the other uses a SubClass.
SOAP using baseclass:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<sendParent xmlns="http://text.com">
<x>
<childs>
<item xmlns:ns1="http://text.com" xsi:type="ns1:ChildInherit">
<ns1:childs>
<ns1:item xsi:type="ns1:ChildInherit">
<ns1:childs xsi:nil="true"/>
</ns1:item>
</ns1:childs>
</item>
</childs>
</x>
</sendParent>
</soapenv:Body>
</soapenv:Envelope>
SOAP using subclass:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<sendParent xmlns="http://text.com">
<x xmlns:ns1="http://text.com" xsi:type="ns1:ParentInherit">
<ns1:childs xsi:type="ns1:Child">
<ns1:item xsi:type="ns1:ChildInherit">
<ns1:childs xsi:type="ns1:Child">
<ns1:item xsi:type="ns1:ChildInherit">
<ns1:childs xsi:nil="true" xsi:type="ns1:Child"/>
</ns1:item>
</ns1:childs>
</ns1:item>
</ns1:childs>
</x>
</sendParent>
</soapenv:Body>
</soapenv:Envelope>
Notice that by using a subclass, all the arrays in the XML get tagged with "xsi:type". It is even applied to arrays that are deeper down the object hierarchy.
Conclusion:
- The fix I applied above is not the problem source, but it works as a failsafe for the server side.
- The client serializer should be fixed not to tag "xsi:type" on arrays.