Absolutely no idea

A blog by Lukas

Generating polymorphic classes from XSD using JAXB 1.2

November 12th, 2009 by lukas

JAXB 1.x DOES support the xsd extension syntax for polymorphism in schema. JAXB 2 supports this build-in but for JAXB 1 some customisation is required through the use of the type substitution vendor extension.

Either jump straight to relevant Sun documentation:
http://java.sun.com/webservices/docs/1.4/jaxb/vendorCustomizations.html#typesub
and
http://java.sun.com/webservices/docs/1.4/jaxb/vendorSchemaLangs.html#typesub

Or follow this to get up and running in no time:

We’re basically looking for a way to correctly generate the corresponding Java inheritance hierarchy for a schema where we have subtypes extending a parent:

      <xsd:complexType name="AddressType" abstract="true">
      </xsd:complexType>
 
      <xsd:complexType name="BusinessAddressType">
               <xsd:complexContent>
                       <xsd:extension base="AddressType">
                               <xsd:sequence>
                                   ...
                               </xsd:sequence>
                       </xsd:extension>
               </xsd:complexContent>
       </xsd:complexType>
 
        <xsd:complexType name="HomeAddressType">
               <xsd:complexContent>
                       <xsd:extension base="AddressType">
                               <xsd:sequence>
                                     ...
                               </xsd:sequence>
                       </xsd:extension>
               </xsd:complexContent>
       </xsd:complexType>

From the above schema we want AddressType to be generated as an interface and superclass Impl and BusinessAddressType and HomeAddressType should extend that parent interface and the relevant impl classes.

To get this up and running in 5 minutes, assuming your XSD schema is
in place already:
- Hide quoted text -

1. set the “extension” option in your Ant xjc target: (Ant task excerpt)

<xjc target="${src.gen.dir}" removeOldoutput="yes" extension="true">
    <schema dir="${xsd.schemas.dir}">
        <include name="schema/**/*.xsd"/>
    </schema>
</xjc>

2. put the following typeSubstitution.xsd in your schemas directory (or where the above xjc target can access it)

<!--
 Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
-->
 
<xs:schema
  xmlns:xs  ="http://www.w3.org/2001/XMLSchema"
  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  xmlns:xjc ="http://java.sun.com/xml/ns/jaxb/xjc"
  jaxb:extensionBindingPrefixes="xjc"
  jaxb:version="1.0">
 
 <!--
   This schema file is used to enable the type substitution support, so we can use type
   inheritance in schema. It doesn't define any schema definition at all.
   See http://java.sun.com/webservices/docs/1.5/jaxb/vendorSchemaLangs.html#typesub for details.
 -->
 
 <xs:annotation>
         <xs:appinfo>
           <jaxb:globalBindings>
             <xjc:typeSubstitution type="complex"/>
           </jaxb:globalBindings>
         </xs:appinfo>
 </xs:annotation>
 
</xs:schema>

3. Kick-off the xjc Ant task to regenerate your bindings! - that’s it.
There will be a new file called Table.java generated which maps the QNames of the XSD types to the all classes in the hierarchy - make sure that is included in your source and it should all work fine.

Posted in xml, programming | No Comments »

WSDL to web service code in Weblogic 10

July 5th, 2008 by lukas

xml logoWeblogic 10 web services runtime uses JAX-WS and JAXB for web service generation. Following the preferred contract-first (or wsdl-first) design approach one can use the wsdlc Ant task to generate all web services artifacts (Impl file, proxy classes and java bean types and their xml serializers & deserializers) from a WSDL file and associated xsd schemas.

(Note: Before starting, make sure Weblogic 10 is correctly installed. Also, the web service generation relies on annotations so Java 5 must also be installed. Note that you need the JDK - not just the JRE - as the WSDL generation required tools.jar which comes with the JDK only)

WSDLC takes a WSDL file as parameter and uses custom JAXB binding files to configure the generation of Java files. Although this process is much more complicated than the simple package to namespace mapping file (NStoPkg.properties) used by Apache Axis it is also much more configurable. For example the JAXB binding files allow you to also specify things such as the name of the generated Impl file, names of operations, parameters as well as configure SOAP handlers. It’s basically a one-stop-shop for the configuration of the web service.

I use the JAXB binding files mainly to generate the the correct package structure of the various Java artifacts.

My WSDL and accompanying schema structure is something like this:

/JobMaintenanceService.wsdl                > defines services
/jobmaintenance/JobMaintenanceService.xsd  > defines main types used in the service
/enterprise_schemas/assignment/*.xsd   > defines types from the assignment functional area 
/enterprise_schemas/common/*.xsd       > defines various common types reused by all other schemas
etc..

It is important to note that JobMaintenanceService imports JobMaintenanceService.xsd which in turn imports other schemas which in turn may import or include other schemas as required. See my previous post on the difference between import vs. include.

The package structure I wanted from the above schemas was as follows:

com.mycompany.myapp.ws.client    > to hold service client classes - proxy, etc.
com.mycompany.myapp.ws.service   > to hold JobMaintenanceService impl class
com.mycompany.myapp.ws.dto       > to hold JobMaintenanceService schema files
com.mycompany.myapp.ws.types.assignment  > to hold types from the assignment schemas
com.mycompany.myapp.ws.types.common      > to hold types from common enterprise schemas
etc..

The following is the Ant target to generate the webservice code. The wsdlc ant task comes with Weblogic 10 (weblogic.wsee.tools.anttasks.WsdlcTask) and is actually a wrapper around Sun’s wsimport task so don’t be surprised if you get warnings or errors referring to wsimport.

build.xml:

<property name="bea.home" value="C:/apps/bea10" />
<property name="jdk.home" value="${bea.home}/jdk150_06" />
<property name="weblogic.home" value="${bea.home}/wlserver_10.0" />
<property name="src.dir" value="src"/>
<property name="wsdl.file.name" value="JobMaintenanceService"/>
 
<target name="run-wsdlc">
	<path id="compile.classpath">
		<fileset dir="${jdk.home}">
			<include name="lib/tools.jar" />
		</fileset>
		<fileset dir="${weblogic.home}">
			<include name="server/lib/weblogic.jar"/>
		</fileset>
	</path>
 
	<taskdef name="wsdlc" classname="weblogic.wsee.tools.anttasks.WsdlcTask" classpathref="compile.classpath" />
	<mkdir dir="${src.dir}"/>
 
	<property name="binding.declaration.file" value="schema-jobmaintenance.xjb,wsdl-jobmaintenance.xjb"/>
 
	<wsdlc
		type="JAXWS"
		srcWsdl="etc/job_maint/${wsdl.file.name}.wsdl"
		destJwsDir="${src.dir}/jws" 
		destImplDir="${src.dir}/impl" 
		explode="true" 
		failonerror="true"
	>
                <binding dir="etc" includes="${binding.declaration.file}"/>
		<classpath>
			<path refid="compile.classpath"/>
   		</classpath>
	</wsdlc>
</target>

The above will generate all required java files into the correct package structure. Here is an excerpt from the schemas binding file showing the mappings of the common and assignment namespaces.

schema-jobmaintenance.xjb:

<jxb:bindings
  version="1.0"
  xmlns:jxb="http://java.sun.com/xml/ns/jaxb" 
>
    <jxb:bindings
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      schemaLocation="job_maint/enterprise_schemas/common/Common.xsd" 
      node="/xs:schema"
    >
	<jxb:schemaBindings>
		<jxb:package name="com.mycompany.myapp.ws.types.common"/>
	</jxb:schemaBindings>
   </jxb:bindings>
 
   <jxb:bindings
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      schemaLocation="job_maint/enterprise_schemas/assignment/Assignment.xsd" 
      node="/xs:schema"
    >
	<jxb:schemaBindings>
		<jxb:package name="com.mycompany.myapp.ws.types.assignment"/>
	</jxb:schemaBindings>
   </jxb:bindings>  
....   
 
</jxb:bindings>

Some notes:

  • The wsdlc Ant task configures namespace to package mappings using custom JAXB binding files - schema-jobmaintenance.xjb and wsdl-jobmaintenance.xjb - one to map schema types and the other to map definitions from the wsdl.
  • Do NOT use the wsdlc option “packageName” in the Ant wsdlc task - this will override any custom bindings made in the jaxb config files.
  • Make sure you do NOT have Apache Axis and related jars in your classpath - they interfere with the weblogic ones from above and results in the binding files not being read!
  • I don’t think you can mix bindings for jaxws (for wsdl) and jaxb (for schema) in the same binding file - I spent quite a bit of time on it but could not get all my bindings to be read from a single binding file - if you know whether this is possible and how this is done please let me know!
  • I could not get nested bindings to work correctly for schemas (e.g. you’re supposed to be able to nest bindings for your imported schemas within bindings for the encompassing schema as in one of the example articles further below but that didn’t work for me - it seemed like the xpath expression with the namespace does not get evaluated correctly). I therefore created separate binding entries for each XSD of a particular namespace.
  • The only item I could NOT generate in it’s correct location was the JWS file (service Impl skeleton file). I tried many things but it just would not pick up the package from the binding file. I also stumbled on a WL10 support case that had this logged as an issue so maybe it is a true bug in this release of WL. Fortunately this is not too big of a deal as it is only a single file - and the one you have to manually edit anyway - to complete the web service implementation.
  • I recommend using an “copy” Ant task to only copy the *.java files into your src project structure as “wsdlc” automatically compiles the java src in the target directory:
    <copy todir="dest/dir">
        <fileset dir="${src.dir}">
            <exclude name="**/*.class"/>
        </fileset>
    </copy>

Finally here is a list of links I recommend to get more information on some of the above:

Wsdlc ant task reference on BEA’s Edocs website
Iterative Development of Weblogic 10 webservices from BEA

The following 2 awesome articles from dev2dev:
Using JAX-WS & JAXB with Weblogic 10
JAX-WS customization binding

Best of luck! If you have any questions or comments please feel free to post below.

Posted in xml, programming | No Comments »

How to import multiple schemas from the same namespace? (import vs. include)

June 5th, 2008 by lukas

xml logoWhen developing XML schemas we may encounter a situation where we have several XSDs that belong to the same namespace - e.g. a common namespace that contains common types:

  • AddressType.xsd - in namespace=”http://company.com/common”
  • UserType.xsd - also in namespace=”http://company.com/common”

To use these schemas in a WSDL or another schema programmers often try to incorrectly import them like this:

<xsd:import namespace="http://company.com/common" schemaLocation="common/AddressType.xsd"/>
<xsd:import namespace="http://company.com/common" schemaLocation="common/UserType.xsd"/>

This will NOT work. We are confusing the schema processor by telling it that components from namespace “http://company.com/common” are to be found at two different locations! In most instances the second import location won’t even be processed.

The above syntax is illegal and in Eclipse this will result in failed schema validation with the following error:

src-resolve: Cannot resolve the name 'common:UserType' to a(n) 'type definition' component.

Now, if our target namespace is the SAME as that of the above schemas we can just use “include” to combine them into the current schema with the same namespace. But if the target namespace is different another approach is required.

One way around this (if you want to keep the objects in individual XSDs) is to collect together - using “include” - the various subschemas from the above “http://company.com/common” using “include” and then import the result:

First, include into intermediate.xsd:

<schema targetNamespace="http://company.com/common" ...>
   <include schemaLocation="AddressType.xsd"/>
   <include schemaLocation="UserType.xsd"/>
     ...
</schema>

Then, import the above into someservice.xsd:

<schema targetNamespace="http://company.com/someservice"
    ...>
    <import namespace="http://company.com/common" schemaLocation="intermediate.xsd"/>
     ...
</schema>

Another approach (which I prefer) is to define related objects from the same namespace in a single schema and then import this entire schema when required. This way we would have AddressType and UserType defined in common.xsd directly under the “http://company.com/common” namespace and we would only need to import a single namespace.

Posted in xml, programming | No Comments »