TRIMM WebService

Trimm-WebServiceThe TRIMM WS module supports generating WebService contracts, in the form of WSDL documents and XML Schemas, from UML 2.x Class model.

The generator is quite versatile and allows detailed control over generated contracts (such as naming convention, SOAP headers & faults, inlining of XML schemas, etc.)

TigerTeam has created a nice Enterprise Architect MDG (UML profile) for working with TRIMM WS. It can be found on the Download page.

Example model:
UML based WebService Integration model

UML model for WebServices conventions, stereotypes and tagged-values:

  • Namespace is indicated on packages/folders using the tagged value Namespace
  • Faults have the stereotype Fault
  • WebServices are interfaces with the stereotype WebService
  • WebService operations can indicate which fault to use using the TaggedValue Fault Messages which is an IDREFS to Fault element(s) (which is basically Class ID’s separated by a comma ‘,’) – to use this feature we recommend using the TRIMM WS Enterprise Architect MDG (UML profile) to make it easy (see download link a the top of the page).
  • Per default every Class becomes a ComplexType, unless it only has a single Attribute in which case it becomes a SimpleType (which is a restriction of the type which the single attribute has).
  • Per default attributes/associations in a Class becomes XML element‘s (can also explicitly be requested using the WSElement stereotype)
  • If you want an attribute generated into an XML attribute you can mark it with the WSAttribute stereotype
  • Classic XmlSchema types are supported natively as Attribute types

Example TrimmWSMavenPlugin configuration for generating two different WSDL/XMLSchema variants based on the same input model (XMI export from Enterprise Architect)

<plugin>
    <groupId>dk.tigerteam</groupId>
    <artifactId>TrimmWSMavenPlugin</artifactId>
    <version>1.0.0</version>
    <executions>
        <!-- Generate for JAX-WS consumption, with XmlSchemas kept separate and not inlined -->
        <execution>
            <id>generate-for-backend</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <modelFilePath>model/wsmodel.xml</modelFilePath>
                <generateToFolder>target/generated-sources/wsdl</generateToFolder>
                <useAliasIfAvailable>true</useAliasIfAvailable>
                <namespaceAndFileLocationHandling>default</namespaceAndFileLocationHandling>
                <inlineReferencedSchemas>false</inlineReferencedSchemas>
                <createWrapperSchema>true</createWrapperSchema>
                <inlineWrapperSchema>true</inlineWrapperSchema>
                <soapHeaderNamespacePrefix>header</soapHeaderNamespacePrefix>
                <soapHeaderNamespace>http://bus.company.dk/iheader</soapHeaderNamespace>
                <soapHeaderLocation>header/header.xsd</soapHeaderLocation>
                <soapHeaderWsdlMessageName>OurHeaderMessage</soapHeaderWsdlMessageName>
                <soapHeaderWsdlMessagePart>iheader</soapHeaderWsdlMessagePart>
                <soapHeaderWsdlMessageElement>OurHeader</soapHeaderWsdlMessageElement>
            </configuration>
        </execution>
        <!-- Generate for Oracle Service Bus (OSB) which prefers WSDL with as many XML Schemas as possible inlined -->
        <execution>
            <id>generate-for-osb</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <modelFilePath>model/wsmodel.xml</modelFilePath>
                <generateToFolder>target/generated-sources/wsdl-osb</generateToFolder>
                <useAliasIfAvailable>true</useAliasIfAvailable>
                <namespaceAndFileLocationHandling>classic</namespaceAndFileLocationHandling>
                <inlineReferencedSchemas>true</inlineReferencedSchemas>
                <createWrapperSchema>true</createWrapperSchema>
                <inlineWrapperSchema>true</inlineWrapperSchema>
                <soapHeaderNamespacePrefix>header</soapHeaderNamespacePrefix>
                <soapHeaderNamespace>http://bus.company.dk/iheader</soapHeaderNamespace>
                <soapHeaderLocation>header/header.xsd</soapHeaderLocation>
                <soapHeaderWsdlMessageName>OurHeaderMessage</soapHeaderWsdlMessageName>
                <soapHeaderWsdlMessagePart>iheader</soapHeaderWsdlMessagePart>
                <soapHeaderWsdlMessageElement>OurHeader</soapHeaderWsdlMessageElement>
            </configuration>
        </execution>
    </executions>
</plugin>

Detailed explanation:

Parameter Example value(s) Explanation
modelFilePath model/wsmodel.xml The relative path to our XMI export file
generateToFolder/ target/generated-sources/wsdl The relative path to the folder where TRIMM WS will place the generated XML Schema and WSDL documents

NOTE: Please provide a separate directory as TRIMM WS cleans up after generation and removed all files that is not part of the generated batch of files (basically to ensure that you can refactor your model and wont have old XML/WSDL files in the folder)

useAliasIfAvailable true This allows you to swap the name of a Class/Package/Folder/Attribute/Association and swap it out with an Alias (e.g. supported by Enterprise Architect).
namespaceAndFileLocationHandling
  • default
  • classic
  • NamespaceFileNameAndLocationResolver*

default handling

Default filename resolving

A given Class becomes a part of a Schema. The schemas file name IS the name of the package within which the class lives.
If a class is placed inside a folder which is tagged using the Tagged value Namespace then that
namespace value is used as Xml schema filename (stripping the http:// part).

WSDL documents files get’s the same name as the Interface that represents the service inside the WSDL document (no replacement of spaces)

Wrapper XMl Schemas per default get the suffix “Wrapper”

Default NamespaceResolver implementation.

Namespace resolving for a given MetaClazz or MetaInterface:

  1. Look for a Package/Folder with a Tagged value called Namespace – the value of this Tagged value will become the root of the namespace that HAS to start with >http://http://ws.tigerteam.dk/)
  2. For every package/folder underneath the Tagged Package/Folder will have its Name/Alias added to the namespaces path (spaces will be replaced with an underscore ‘_’) plus a separating slash ‘/’

If the xmlType is an Interface (which is what all Services as modeled as) then we will also add the interface name/alias as the final namespace part (spaces will be replaced with an underscore ‘_’)

Example hierarchy:

  • Model (root)
    • WebServices (with tagged value Namespace with value http://ws.tigerteam.dk/"
      • Folder/Package with name "Customers"
        • Folder/Package with name "Search"
          • Interface with name "Customer Search Service"
          • Class with name "CustomerSearchCriteria"
          • Class with name "CustomerSearchResult"

This will result in the following namespaces:

Interface: "Customer Search Service" will get namespace "http://ws.tigerteam.dk/Customers/Search/Customer_Search_Service"
Class: "CustomerSearchCriteria" will get namespace "http://ws.tigerteam.dk/Customers/Search/"
Class: "CustomerSearchResult" will get namespace "http://ws.tigerteam.dk/Customers/Search/"

This resolver is also responsible for calculating where the should the files within this namespace be placed on the file system

Example: Given “http://tigerteam.dk/person/service/” you would return “person/service/(note the missing / at the start of the path)

Default File location

The default is to use the same file system nesting (using folders) as is in the original model (package becomes a folder)

classic handling

Classic handling implementation is basically the same as default handling except for these differences:

Classic File location

The default is to use the root output folder for WSDL documents and an “xsd” folder for ALL schemas

Classic filename resolving

A given Class becomes a part of a Schema. The schemas file name will be name of the package the Class is inside.
If that name has already been taken then we recursively (upwards) try to take the next super package and prefix the filename with that (with an underscore ‘_’ between)
until we reach a unique filename or run out of super packages, in which case an exception is thrown.

WSDL documents files get’s the same name as the Interface that represents the service inside the WSDL document (no replacement of spaces).
If that name has already been taken then we recursively (upwards) try to take the next super package and prefix the filename with that (with an underscore ‘_’ between)
until we reach a unique filename or run out of super packages, in which case an exception is thrown.

Wrapper XMl Schemas per default get the suffix “Wrapper”

createWrapperSchema true / false Should we create a wrapper Schemas for the Request/Response types that are used in WSDL Operations for Request parameters and Return values
inlineReferencedSchemas true / false Should referenced XmlSchemas (that we generate based on the model) be inlined into the WSDL or should they be referenced as import‘s
soapHeaderNamespacePrefix iheader If we’re adding a SoapHeader (e.g. a required header for your SOA infrastructure) what Namespace prefix should we use

<wsdl:definitions  xmlns:iheader="http://internal/header"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:CustomerService="http://ws.tigerteam.dk/customer/CustomerService"     
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
 ...
</wsdl>
soapHeaderNamespace http://internal/header What namespace does the header have

<wsdl:definitions  xmlns:iheader="http://internal/header"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:CustomerService="http://ws.tigerteam.dk/customer/CustomerService"     
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
 ...
</wsdl>
soapHeaderLocation header/header.xsd Where relative to our generated file is the header XML schema file placed (you need to write the contents if it yourself).
So if our WSDL document is written to a folder two levels down, we will automatically correct the relative path so everything works

<wsdl:types>
    <xsd:schema>
      <xsd:import namespace="http://internal/header" 
                  schemaLocation="../../header/header.xsd" />
    </xsd:schema>
soapHeaderWsdlMessageName InternalHeader When the code generator defines as WSDL Message for the Header, what should the name be

  <wsdl:message name="InternalHeader">
    <wsdl:part name="headerPart" 
               element="iheader:IHeaderElem" />
  </wsdl:message>
soapHeaderWsdlMessagePart headerPart The message part of the WSDL Operations Input/Output part and which correlates to the Header message defined according to the soapHeaderWsdlMessageName

  <wsdl:message name="InternalHeader">
    <wsdl:part name="headerPart" 
               element="iheader:IHeaderElem" />
  </wsdl:message>
...
  <wsdl:operation name="getCustomer">
      <soap:operation soapAction="getCustomer" style="document" />
      <wsdl:input>
        <soap:body parts="getCustomerParameters" use="literal" />
        <soap:header message="CustomerService:InternalHeader" 
                     part="headerPart" use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body parts="getCustomerResponseParameters" 
                   use="literal" />
        <soap:header message="CustomerService:InternalHeader"
                     part="headerPart" use="literal" />
      </wsdl:output>
    </wsdl:operation>
soapHeaderWsdlMessageElement IHeaderElem The header XML Schema element (in the header xml schema that you created yourself) which should be used for the message part element definition

  <wsdl:message name="InternalHeader">
    <wsdl:part name="headerPart" 
               element="iheader:IHeaderElem" />
  </wsdl:message>

* NamespaceFileNameAndLocationResolver specifies which dk.tigerteam.trimm.mdsd.ws.NamespaceFileNameAndLocationResolver implementation to use. The implementation name can either be a Fully Qualified Class Name (FQCN) for a class on the classpath the implements NamespaceFileNameAndLocationResolver OR it can be the relative path to a .groovy script which contains a class that implements NamespaceFileNameAndLocationResolver.