Access Keys:
Skip to content (Access Key - 0)

Knowledgebase


Abstract Schema Types


Overview

When dealing with abstract schema types, you may encounter issues with Axis. The issue is not so much that it "can't deal with abstract classes" as it is that you cannot actually create an instance document from an element which is abstract, or its type is abstract, as defined in the schema specification.

Instance documents using abstract elements or elements with abstract types must specify an xsi:type of a concrete type. Axis should honor the xsi:type information when deserializing and create an instance of a concrete type.

This all works fine in Axis on the client side. However, the service-side has problems handling these types; let's first walk through how this works, and then discuss the service-side issue and how to fix it.

Below is an example service created to illustrate this, you can also download the Introduce service and try it yourself.

Here is a modification to the service to illustrate the fixes necessary to also send abstract types in requests.

Example Schema with Abstract Types

Extracted from the schema specification:

<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://cars.example.com/schema"
xmlns:target="http://cars.example.com/schema">

<complexType name="Vehicle" abstract="true"/>

<complexType name="Car">
<complexContent>
<extension base="target:Vehicle"/>
</complexContent>
</complexType>

<complexType name="Plane">
<complexContent>
<extension base="target:Vehicle"/>
</complexContent>
</complexType>

<element name="transport" type="target:Vehicle"/>
</schema>

Example Service Method Returning an Abstract Type

public com.example.cars.Vehicle getVehicle(java.lang.String string) throws RemoteException {
	if (string.equals(Constants.CAR)) {
		return new Car();
	} else if (string.equals(Constants.PLANE)) {
		return new Plane();
	} else {
		throw new RemoteException("Unsupported transport type!");
	}
}

Example Client Invocation

Car car = (Car) client.getVehicle(Constants.CAR);
System.out.println("Got car:" + car);
Plane plane = (Plane) client.getVehicle(Constants.PLANE);
System.out.println("Got plane:" + plane);

Example Client Invocation Output

Running the Grid Service Client
Got car:com.example.cars.Car@1
Got plane:com.example.cars.Plane@1

SOAP Messages

Notice in both responses, the element is "transport" and the xsi:type specifies the concrete instance.

Captured SOAP Response for client.getVehicle(Constants.CAR)

<soapenv:Body>
<GetVehicleResponse xmlns="http://abstracttest.cagrid.nci.nih.gov/AbstractTest">
<ns1:transport xsi:type="ns1:Car" xmlns:ns1="http://cars.example.com/schema"/>
</GetVehicleResponse>
</soapenv:Body>

Captured SOAP Response for client.getVehicle(Constants.PLANE)

<soapenv:Body>
<GetVehicleResponse xmlns="http://abstracttest.cagrid.nci.nih.gov/AbstractTest">
<ns1:transport xsi:type="ns1:Plane" xmlns:ns1="http://cars.example.com/schema"/>
</GetVehicleResponse>
</soapenv:Body>

Problems on the Service-side

The client is fully able to send and receive these abstract types. However, the service does not understand how to deserialize these abstract types be default. Here is an email thread on the Globus mailing listthat discusses this problem. If you add a method to the service which sends an abstract type in the request, the service will raise an error like:

Sending plane
AxisFault
faultCode: {http://schemas.xmlsoap.org/soap/envelope/}Server.userException
faultSubcode:
faultString: org.xml.sax.SAXException: Unable to create JavaBean of type com.example.cars.Vehicle.  Missing default constructor?  Error was: java.lang.InstantiationException.
faultActor:
faultNode:
faultDetail:
{http://xml.apache.org/axis/}stackTrace:org.xml.sax.SAXException: Unable to create JavaBean of type com.example.cars.Vehicle.  Missing default constructor?  Error was: java.lang.InstantiationException.
at org.apache.axis.encoding.ser.BeanDeserializer.startElement(BeanDeserializer.java:124)
at org.apache.axis.encoding.DeserializationContext.startElement(DeserializationContext.java:1048)
at org.apache.axis.message.SAX2EventRecorder.replay(SAX2EventRecorder.java:165)
at org.apache.axis.message.MessageElement.publishToHandler(MessageElement.java:1140)
at org.apache.axis.message.RPCElement.deserialize(RPCElement.java:238)
at org.apache.axis.message.RPCElement.getParams(RPCElement.java:386)
at org.apache.axis.providers.java.RPCProvider.processMessage(RPCProvider.java:148)
at org.apache.axis.providers.java.JavaProvider.invoke(JavaProvider.java:319)
at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)
at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)
at org.apache.axis.handlers.soap.SOAPService.invoke(SOAPService.java:450)
at org.apache.axis.server.AxisServer.invoke(AxisServer.java:285)
at org.globus.wsrf.container.ServiceThread.doPost(ServiceThread.java:676)
at org.globus.wsrf.container.ServiceThread.process(ServiceThread.java:397)
at org.globus.wsrf.container.ServiceThread.run(ServiceThread.java:302)

{http://xml.apache.org/axis/}hostname:gcity

org.xml.sax.SAXException: Unable to create JavaBean of type com.example.cars.Vehicle.  Missing default constructor?  Error was: java.lang.InstantiationException.
at org.apache.axis.message.SOAPFaultBuilder.createFault(SOAPFaultBuilder.java:221)
at org.apache.axis.message.SOAPFaultBuilder.endElement(SOAPFaultBuilder.java:128)
at org.apache.axis.encoding.DeserializationContext.endElement(DeserializationContext.java:1087)
at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:375)
at org.apache.axis.encoding.DeserializationContext.parse(DeserializationContext.java:227)
at org.apache.axis.SOAPPart.getAsSOAPEnvelope(SOAPPart.java:645)
at org.apache.axis.Message.getSOAPEnvelope(Message.java:424)
at org.apache.axis.message.addressing.handler.AddressingHandler.processClientResponse(AddressingHandler.java:305)
at org.apache.axis.message.addressing.handler.AddressingHandler.invoke(AddressingHandler.java:110)
at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)
at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)
at org.apache.axis.client.AxisClient.invoke(AxisClient.java:190)
at org.apache.axis.client.Call.invokeEngine(Call.java:2727)
at org.apache.axis.client.Call.invoke(Call.java:2710)
at org.apache.axis.client.Call.invoke(Call.java:2386)
at org.apache.axis.client.Call.invoke(Call.java:2309)
at org.apache.axis.client.Call.invoke(Call.java:1766)
at gov.nih.nci.cagrid.abstracttest.stubs.bindings.AbstractTestPortTypeSOAPBindingStub.sendTransport(AbstractTestPortTypeSOAPBindingStub.java:884)
at gov.nih.nci.cagrid.abstracttest.client.AbstractTestClient.sendTransport(AbstractTestClient.java:145)
at gov.nih.nci.cagrid.abstracttest.client.AbstractTestClient.main(AbstractTestClient.java:118)

You basically need to edit the service's server-config.wsdd and add type mappings for each of the "concrete types". See the information below on the specifics. Here is the complete working example service.

Example Method Sending an Abstract Type

public void sendTransport(com.example.cars.Vehicle transport) throws RemoteException {
	System.out.println("Got the transport:" + transport);
}

Example Client Invocation

System.out.println("Sending plane");
client.sendTransport(plane);
System.out.println("Sending car");
client.sendTransport(car);

Example Client Invocation Output

Running the Grid Service Client
Got car:com.example.cars.Car@1
Got plane:com.example.cars.Plane@1
Sending plane
Sending car

Output on the Service

Got the transport:com.example.cars.Plane@1
Got the transport:com.example.cars.Car@1

Captured SOAP Request for sendTransport(plane)

<soapenv:Body>
<SendTransportRequest xmlns="http://abstracttest.cagrid.nci.nih.gov/AbstractTest">
<transport>
<ns1:transport xsi:type="ns1:Plane" xmlns:ns1="http://cars.example.com/schema"/>
</transport>
</SendTransportRequest>
</soapenv:Body>

Additions to server-config.wsdd Necessary to Get it to Work

Each concrete type needs an entry in the server-config.wsdd, specifying the serializers and deserializers which should be used.

<typeMapping xmlns:ns="http://cars.example.com/schema"
qname="ns:Car"
type="java:com.example.cars.Car"
serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
encodingStyle=""/>

<typeMapping xmlns:ns="http://cars.example.com/schema"
qname="ns:Plane"
type="java:com.example.cars.Plane"
serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
encodingStyle=""/>
Last edited by
Knowledge Center (2098 days ago) , ...
Adaptavist Theme Builder Powered by Atlassian Confluence