My application is divided into two layers, where the backend layer exposes a bunch of web services, and the presentation layers consumes those to obtain business data.
In order to make my construction as automatic as possible, I package all my resulting WSDL and associated XSD files into a ZIP dependency (using Maven assembler plugin), which gets uploaded to my nexus on the install phase.
Then on the UI POM I unpack this ZIP file (using maven dependency plugin) into a local directory (src/main/resources/wsdl
), then use CXF-CODEGEN plugin to generate clients.
The problem is -- many of these services share common model entities, for instance a lot of methods use the Catalogue
class, which is an entity I use for objects that consist of an Id and a text value.
With CXF generated code I end up with a different Catalogue
class (with differente package name) for each Web Service I have, thus losing OO polimorphism capabilities in my code since they are all different classes in practice.
I've heard you can avoid this problem by somehow compiling XSD classes first into something called "episodes" and then feeding those episode files to CXF in order to tell it to take advantage of them instead of generating classes.
I've tried to add an additional compilation step in which I run maven-jaxb2-plugin to generate the classes from XSD, but I get a
org.xml.sax.SAXParseException; (...) 'catalogueDTO' is already defined
exception since this class is repeated along the XSD files.
Can anyone point me in the right direction?
When you have some base classes in your web services that will be used across services and operations it is best to declare these classes in a single XSD and then include and use this XSD across you project.
The following example will show you how. I am basing this example on the information provided thus it might not be an exact fit to what you need but will show you the concepts.
First lets create a Catalog.xsd file and declare our catalog object in this file with the following code:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns="http://www.yourcompany.com/Services_V1/CommonType"
elementFormDefault="qualified"
targetNamespace="http://www.yourcompany.com/Services_V1/CommonType"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="CatalogueID_Type">
<xs:annotation>
<xs:documentation>The Catalogue ID is an integer value that is used to uniquely identify the catalog item on the database.</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:int" />
</xs:simpleType>
<xs:simpleType name="CatalogueValue_Type">
<xs:annotation>
<xs:documentation>The catalog is a string value that will contain the information describing the catalog key.</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string" />
</xs:simpleType>
<xs:complexType name="Catalogue_Type">
<xs:sequence>
<xs:element ref="CatalogID"
minOccurs="1"
maxOccurs="1" />
<xs:element ref="CatalogueValue"
minOccurs="0"
maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:element name="CatalogID"
type="CatalogueID_Type" />
<xs:element name="CatalogueValue"
type="CatalogueValue_Type" />
<xs:element name="Catalogue"
type="Catalogue_Type" />
Or if you like a visual representation:
Things to note is that the catalog class/object has a namespace of http://www.yourcompany.com/Services_V1/CommonType
we will use this namespace again in our WSDL. This object will now be used by two service the BathroomCatalogue Service and the KicthenCatalogue Service. For simplicity I just have one operation in the service called GetCatalogueItem. For each one of these services I will include the catalog.xsd
file and reuse this catalog object.
Here is the WSDL for the Bathroom Service:
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="BathroomCatalogueSRV"
targetNamespace="http://www.yourcompany.com/Services_V1/BathroomCatalogueService"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns="http://www.yourcompany.com/Services_V1/BathroomCatalogueService"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:catalogue="http://www.yourcompany.com/Services_V1/CommonType">
<wsdl:types>
<xs:schema elementFormDefault="qualified"
targetNamespace="http://example.com/">
<xs:import schemaLocation="Catalog.xsd"
namespace="http://www.yourcompany.com/Services_V1/CommonType" />
</xs:schema>
</wsdl:types>
<wsdl:message name="GetCatalogueItemReq">
<wsdl:part name="GetCatalogueItemReq"
element="catalogue:Catalogue" />
</wsdl:message>
<wsdl:message name="GetCatalogueItemRs">
<wsdl:part name="GetCatalogueItemRs"
element="catalogue:Catalogue" />
</wsdl:message>
<wsdl:portType name="BathroomCataloguePortType">
<wsdl:operation name="GetCatalogueItem">
<wsdl:input message="tns:GetCatalogueItemReq" />
<wsdl:output message="tns:GetCatalogueItemRs" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="BathroomCatalogueBinding"
type="tns:BathroomCataloguePortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<wsdl:operation name="GetCatalogueItem">
<wsdl:input />
<wsdl:output />
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="BathroomCatalogueService">
<wsdl:port name="BathroomCataloguePort"
binding="tns:BathroomCatalogueBinding">
<soap:address location="http://www.yourcompany.com/Services_V1/BathroomCatalogueService" />
</wsdl:port>
</wsdl:service>
Or if you want a visual representation:
The Kitchen WSDL looks like this:
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="KitchenCatalogueSRV"
targetNamespace="http://www.yourcompany.com/Services_V1/KitchenCatalogueService"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns="http://www.yourcompany.com/Services_V1/KitchenCatalogueService"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:catalogue="http://www.yourcompany.com/Services_V1/CommonType">
<wsdl:types>
<xs:schema elementFormDefault="qualified"
targetNamespace="http://example.com/">
<xs:import schemaLocation="Catalog.xsd"
namespace="http://www.yourcompany.com/Services_V1/CommonType" />
</xs:schema>
</wsdl:types>
<wsdl:message name="GetCatalogueItemReq">
<wsdl:part name="GetCatalogueItemReq"
element="catalogue:Catalogue" />
</wsdl:message>
<wsdl:message name="GetCatalogueItemRs">
<wsdl:part name="GetCatalogueItemRs"
element="catalogue:Catalogue" />
</wsdl:message>
<wsdl:portType name="KitchenCataloguePortType">
<wsdl:operation name="GetCatalogueItem">
<wsdl:input message="tns:GetCatalogueItemReq" />
<wsdl:output message="tns:GetCatalogueItemRs" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="KitchenCatalogueBinding"
type="tns:KitchenCataloguePortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<wsdl:operation name="GetCatalogueItem">
<wsdl:input />
<wsdl:output />
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="KitchenCatalogueService">
<wsdl:port name="KitchenCataloguePort"
binding="tns:KitchenCatalogueBinding">
<soap:address location="http://www.yourcompany.com/Services_V1/KitchenCatalogueService" />
</wsdl:port>
</wsdl:service>
And once again visually:
In both these WSDL files I included the catalog.xsd file. You can see this in the following lines of code:
<xs:import schemaLocation="Catalog.xsd"
namespace="http://www.yourcompany.com/Services_V1/CommonType" />
Now when I use these WSDL and XSD together with cxf there will only be one catalog object/class used by both services. I quickly did a a shell project and the following structure was generated:
Note that the CatalogueType I declare is now in one package with the namespace I declared it in.
When viewing the service classes both the Bathroom and Kitchen service will use this one class. In the kitchen service class it uses com.yourcompany.services_v1.commontype.CatalogueType
.
@WebService(targetNamespace = "http://www.yourcompany.com/Services_V1/KitchenCatalogueService", name = "KitchenCataloguePortType")
@XmlSeeAlso({com.yourcompany.services_v1.commontype.ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface KitchenCataloguePortType {
@WebResult(name = "Catalogue", targetNamespace = "http://www.yourcompany.com/Services_V1/CommonType", partName = "GetCatalogueItemRs")
@WebMethod(operationName = "GetCatalogueItem")
public com.yourcompany.services_v1.commontype.CatalogueType getCatalogueItem(
@WebParam(partName = "GetCatalogueItemReq", name = "Catalogue", targetNamespace = "http://www.yourcompany.com/Services_V1/CommonType")
com.yourcompany.services_v1.commontype.CatalogueType getCatalogueItemReq
);
}
And in the kitchen service class it uses com.yourcompany.services_v1.commontype.CatalogueType
.
@WebService(targetNamespace = "http://www.yourcompany.com/Services_V1/BathroomCatalogueService", name = "BathroomCataloguePortType")
@XmlSeeAlso({com.yourcompany.services_v1.commontype.ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface BathroomCataloguePortType {
@WebResult(name = "Catalogue", targetNamespace = "http://www.yourcompany.com/Services_V1/CommonType", partName = "GetCatalogueItemRs")
@WebMethod(operationName = "GetCatalogueItem")
public com.yourcompany.services_v1.commontype.CatalogueType getCatalogueItem(
@WebParam(partName = "GetCatalogueItemReq", name = "Catalogue", targetNamespace = "http://www.yourcompany.com/Services_V1/CommonType")
com.yourcompany.services_v1.commontype.CatalogueType getCatalogueItemReq
);
}
Just a reminder that you must never edit these classes as they are derived and generated by CXF and your changes will be lost. Thus by doing WSDL first approach you must make sure that you structure your XSD files and WSDL's correctly.
Let me know if this makes sense.