Syntax Hilighter

Friday, December 3, 2010

JAX-WS typeSafeEnum

I recently answered a question on stackoverflow.com concerning how to get WSIMPORT to generate an enum when generating java code from a wsdl that contains enum restricted types/elements.

The original question:
http://stackoverflow.com/questions/4235074/no-enums-generated-by-jax-ws-ri-2-2-1-for-amazon-ecs-wsdl-file/4348209#4348209


The question is:

I'm attempting to generate artifacts for the following amazon wsdl:
http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl

using the following ant task:


<taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport">
    <classpath>
        <pathelement location="${BUILD_LIBS}/jaxws-ri/lib/jaxws-tools.jar"/>
    </classpath>
</taskdef>

<target name="wsimport" depends="init">
    <delete dir="${generated.src}" />
    <mkdir dir="${generated.src}"/>
    <wsimport
        debug="true"
    keep="true"
    verbose="true"
    destdir="${generated.src}"
    package="com.amazon.webservices.ecs"
    wsdl="wsdl/AWSECommerceService.wsdl"/>
</target>


but no java artifacts are generated for the following element which look like this:

<xs:element name="Condition">
<xs:simpletype>
<xs:restriction base="xs:string">
<xs:enumeration value="All">
<xs:enumeration value="New">
<xs:enumeration value="Used">
<xs:enumeration value="Collectible">
<xs:enumeration value="Refurbished">
</xs:enumeration></xs:enumeration></xs:enumeration></xs:enumeration></xs:enumeration></xs:restriction>
</xs:simpletype>
</xs:element>


Basically no enums are generated even though all other elements are generated. Has anyone seen this problem before? I'm using jax-ws ri 2.2.1 http://jax-ws.java.net/2.2.1/
Thanks



and my answer:


JAXB client customization of the binding is exactly/pretty close to what you need -- you need to use a JAX-WS client customization for your example. JAXB and JAX-WS customizations essentially allows you to augment the definition of schema elements for WSDL/schema's that you do not control. there are MANY different things you can accomplish such as mapping xml element names to custom java elements, altering the generated API, and to answer your question, generating type-safe enum classes for elements that are restricted with an enum.

There are two ways/parts to doing a client customization for JAX-WS.

1) if the WSDL imports an external schema file 2) if the WSDL contains the entire schema definition without any imports

if the wsdl imports an external schema file then,

basically you need to create a new file (typically with a jxb extension, but it really doesn't matter) that you will maintain along side the wsdl you are generating the client stub/api for. typically I name these files schema-file-name_clientcustomization.jxb

every time you get an updated wsdl, you should validate that your JXB file is still valid for that wsdl. The biggest things I've found to look for, especially with enum restrictions, is restricted value changes, namespace changes, type name changes, etc..

the content of this new file will look something similar to this:

<jxb:bindings targetnamespace="http://java.sun.com/xml/ns/jaxb" version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<jxb:bindings node="/xsd:schema[@targetNamespace='SCHEMANAMESPACE']" schemalocation="NameOfYourSchemaFile.xsd">
<jxb:schemabindings>
<jxb:package name="com.amazon.webservices.ecs">
</jxb:package></jxb:schemabindings>

<jxb:bindings node="xsd:element[@name='Condition']/xsd:simpleType">
<jxb:typesafeenumclass name="ConditionEnum">
<jxb:typesafeenummember name="ALL" value="All">
<jxb:typesafeenummember name="NEW" value="New">
<jxb:typesafeenummember name="USED" value="Used">
<jxb:typesafeenummember name="COLLECTIBLE" value="Collectible">
<jxb:typesafeenummember name="REFURBISHED" value="Refurbished">
</jxb:typesafeenummember></jxb:typesafeenummember></jxb:typesafeenummember></jxb:typesafeenummember></jxb:typesafeenummember></jxb:typesafeenumclass>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>

Essentially this file defines augmentation that should be done to the referenced xsd file. all bindings elements in this file have a node attribute which is an XPATH expression that selects the schema item that you want to augment. in the example, I don't have any namespace or other information so I specified the XPATH to select just the element's simple type declaration. within that binding, we define the typesafeenumclass, this causes the jaxb/wsimport to generate an enum class to represent the referenced simple type. since it's an anonymous simple type, this effectively defines a class just for the referenced element. The generated class will be an ENUM who's memebers are defined by the "name" attribute of the typesafeEnumMember element.

to use this JXB file, you need to reference it in your ant task, like so:

<wsimport debug="true" destdir="${generated.src}" keep="true" package="com.amazon.webservices.ecs" verbose="true" wsdl="wsdl/AWSECommerceService.wsdl">

<binding dir="wsdl" includes="*.jxb">

</binding></wsimport>


if the WSDL defines the entire schema internally, then you need to use a JAX-WS customization file. This case is what matches your question.

http://jax-ws.java.net/nonav/2.1.7/docs/customizations.html

JAX-WS client customization is very similar to the JAXB customization. The idea is identical, for the most part the JAX-WS portion of the customization file will alter generated artifacts that are specifically related to WSDL, whereas the embedded JAXB customization performs the same function as an external customization file: it alters the generated objects based on the schema.

The big difference is that rather than tell the JAXB parser where the schema file is, you provide a binding section that selects the schema definition (using XPATH) that you want to apply customization to.

This example I actually tested and verified to generate an Enum class for the element you are asking questions about, so you can copy this JAX-WS customization example verbatim.

<jaxws:bindings wsdllocation="AWSECommerceService.wsdl" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:jaxws="http://java.sun.com/xml/ns/jaxws" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<jaxws:bindings node="wsdl:definitions/wsdl:types/xsd:schema[@targetNamespace='http://webservices.amazon.com/AWSECommerceService/2010-11-01']">
<jaxb:schemabindings>
<jaxb:package name="com.amazon.webservices.ecs">
</jaxb:package></jaxb:schemabindings>


<jaxb:bindings node="xsd:element[@name='Condition']/xsd:simpleType">
<jaxb:typesafeenumclass name="ConditionEnum">
<jaxb:typesafeenummember name="ALL" value="All">
<jaxb:typesafeenummember name="NEW" value="New">
<jaxb:typesafeenummember name="USED" value="Used">
<jaxb:typesafeenummember name="COLLECTIBLE" value="Collectible">
<jaxb:typesafeenummember name="REFURBISHED" value="Refurbished">
</jaxb:typesafeenummember></jaxb:typesafeenummember></jaxb:typesafeenummember></jaxb:typesafeenummember></jaxb:typesafeenummember></jaxb:typesafeenumclass>
</jaxb:bindings>

</jaxws:bindings>

</jaxws:bindings>


you would then reference this JAX-WS customization file the same way you would reference the JXB file.

I did not validate the standalone JAXB customization example, since I really only included it as an example and as a precursor explanation for the JAX-WS customization example.

The JAX-WS customization example I did actually test/validate against the WSDL you have linked, so you should be able to use it as a starting point. I noticed that there are numerous enumerated restrictions in the defined WSDL, so I would assume you'll want to generate enums for most/all of them.

I hope this helps.

No comments:

Post a Comment