In our project, we follow a strict governance process for governing our services. We identify the services and their operations from our Business Process Model, then proceed to producing a WSDL and associated XSDs to represent that service. Only once this is done will we proceed to implementation. This is called top down design and is generally a good thing. By doing this, we end up with a clean design that represents the ideal, pure business requirements, rather than being technology driven as is often the case with bottom up designs. Nothing new here. Sooner or later, the rubber hits the road, and we end up implementing the service using some sort of technology. In a recent case, Oracle’s ESB product was selected as the implementation technology for some entity services that we are developing. It allowed us to provide a SOAP interface to our entities, whilst still preserving transactions and speedy performance by tying into WSIF and Oracle’s optimised message delivery capabilities. So far, everything was looking rosy. Then we got to implementing the fault handling. Now, I think most people would agree that having a variety of different faults for an operation is a good idea. That way, the caller can distinguish between the different types of fault that can happen. In particular, this service had these faults:
- PersistenceFault, if there was a general technical fault with the service (e.g. the database was down)
- IllegalUpdateFault, if the caller has attempted to modify a field that they shouldn't be.
- Create some sort of XSL mapping the ESB to try and fudge multiple faults: This is impossible, as normally faults would have different messages, and the ESB will only route to one destination message.
- Use a common fault type, and distinguish between the different faults using a fault code, either numeric or enumeration based. This will work, but there is no way of including structured data in the fault, as it will need to be generic
- Use a common fault type which is a <choice> of the different faults, and get the caller to work out which one it was.
- Use a common fault type, and then extend it via XSD methods, and use polymorphism to tell the difference.
Option 3.
<xsd:complexType name="SimpleFaultType">
<xsd:sequence>
<xsd:element name="faultstring" type="xsd:string"/>
<xsd:element name="detail" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="CombinedFault">
<xsd:complexType>
<xsd:choice>
<xsd:element name="PersistenceFailureFault" type="SimpleFaultType"/>
<xsd:element name="IllegalUpdateFault" type="PersistenceFailureFaultType"/>
</xsd:choice>
</xsd:complexType>
</xsd:element>
Option 4.
<xsd:complexType name="BaseFaultType">
<xsd:sequence>
<xsd:element name="faultstring" type="xsd:string"/>
<xsd:element name="detail" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PersistenceFailureFaultType">
<xsd:complexContent>
<xsd:extension base="BaseFaultType">
<!-- Place additional fields in here -->
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="IllegalUpdateFault">
<xsd:complexContent>
<xsd:extension base="BaseFaultType">
<!-- Place additional fields in here -->
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
Using this approach, the fault message/element in the WSDL will be of type BaseFaultType. The caller can then interrogate the xsi:type attribute to work out exactly which fault has been thrown.