Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/Development/2.1/DynamicMOXy/296967/BootstrapFromOXM"

(New page: <div style="margin:5px;float:right;border:1px solid #000000;padding:5px">__TOC__</div> = Dynamic MOXy - Bootstrapping from EclipseLink Externalized Metadata (OXM) = == Overview == Suppo...)
 
(Phase 7)
 
(68 intermediate revisions by the same user not shown)
Line 2: Line 2:
  
 
= Dynamic MOXy - Bootstrapping from EclipseLink Externalized Metadata (OXM) =
 
= Dynamic MOXy - Bootstrapping from EclipseLink Externalized Metadata (OXM) =
 +
 +
[https://bugs.eclipse.org/321152 ER 321152]
 +
 +
<br>
  
 
== Overview ==
 
== Overview ==
  
Support for bootstrapping a <tt>DynamicJAXBContext</tt> from an XML Schema file (XSD) will be implemented in multiple phases.  For each phase, test schemas will be created that would result in the following annotations being generated by XJC.  A <tt>DynamicJAXBContext</tt> will then be created from each schema, and an example object will be marshalledThe resulting document will be inspected to ensure that it was marshalled properly, according to the annotations being tested.
+
The purpose of this feature is to allow the user to bootstrap EclipseLink Dynamic JAXB from an EclipseLink externalized metadata file (<tt>eclipselink-oxm.xml</tt>)This is a continuation of the Dynamic JAXB features introduced in [http://wiki.eclipse.org/EclipseLink/Development/2.1/DynamicMOXy/296967/BootstrapFromXSD EclipseLink 2.1.0].
  
== Generating a DynamicJAXBContext with XJC ==
+
Support for bootstrapping a <tt>DynamicJAXBContext</tt> from an EclipseLink Externalized Metadata file (OXM) will be implemented in multiple phases.  For each phase, OXM files will be created that will exercise the associated annotations.  A <tt>DynamicJAXBContext</tt> will be created from each OXM file, and an example object will be marshalled.  The resulting document will be inspected to ensure that it was marshalled properly, according to the annotations being tested.
  
A <tt>DynamicJAXBContext</tt> is created from an XSD in the following way:
+
See also: [http://wiki.eclipse.org/EclipseLink/Development/2.1/DynamicMOXy/296967/Documentation Dynamic JAXB Documentation]
  
# The schema is parsed using <tt>com.sun.tools.xjc.api.SchemaCompiler</tt>, resulting in a <tt>com.sun.codemodel.JCodeModel</tt> being generated.
+
<br>
# <tt>JCodeModel</tt> contains XJC representations (<tt>com.sun.codemodel.JDefinedClass</tt>) of the classes to be generated. We iterate over the collection of <tt>JDefinedClasses</tt> to create EclipseLink representations of the same classes (<tt>org.eclipse.persistence.jaxb.javamodel.xjc.*</tt>)
+
 
 +
== Generating a DynamicJAXBContext with OXM ==
 +
 
 +
A <tt>DynamicJAXBContext</tt> is created from an OXM file in the following way:
 +
 
 +
# The OXM file is parsed by <tt>JAXBContextFactory</tt> to generate an <tt>org.eclipse.persistence.jaxb.xmlmodel.XmlBindings</tt> object.
 +
# The <tt>XmlBindings</tt> are inspected and an EclipseLink <tt>javamodel</tt> representing this information is created.
 
# This collection of EclipseLink <tt>javamodel</tt> classes is then passed to <tt>org.eclipse.persistence.jaxb.compiler.Generator</tt>, which generates a standard EclipseLink project.
 
# This collection of EclipseLink <tt>javamodel</tt> classes is then passed to <tt>org.eclipse.persistence.jaxb.compiler.Generator</tt>, which generates a standard EclipseLink project.
 
# A Dynamic Project is then created from the standard project via <tt>DynamicTypeBuilder.loadDynamicProject()</tt>.
 
# A Dynamic Project is then created from the standard project via <tt>DynamicTypeBuilder.loadDynamicProject()</tt>.
Line 18: Line 28:
  
 
<br>
 
<br>
 +
 +
== Public API ==
 +
 +
The user will be able to bootstrap a <tt>DynamicJAXBContext</tt> using the following method in <tt>DynamicJAXBContextFactory</tt>:
 +
 +
<source lang="java">
 +
/**
 +
* Create a <tt>DynamicJAXBContext</tt>, using an EclipseLink OXM file as the metadata source.
 +
*
 +
* @param classLoader
 +
*      The application's current class loader, which will be used to first lookup
 +
*      classes to see if they exist before new <tt>DynamicTypes</tt> are generated.  Can be
 +
*      <tt>null</tt>, in which case <tt>Thread.currentThread().getContextClassLoader()</tt> will be used.
 +
* @param properties
 +
*      Map of properties to use when creating a new <tt>DynamicJAXBContext</tt>.  This map must
 +
*      contain a key of JAXBContext.ECLIPSELINK_OXM_XML_KEY, with a value of Map<String, Source>,
 +
*      where String is the package name and Source is the metadata file for that package.
 +
*
 +
* @return
 +
*      A new instance of <tt>DynamicJAXBContext</tt>.
 +
*
 +
* @throws JAXBException
 +
*      if an error was encountered while creating the <tt>DynamicJAXBContext</tt>.
 +
*/
 +
public static DynamicJAXBContext createContextFromOXM(ClassLoader classLoader, Map<String, ?> properties) throws JAXBException
 +
</source>
 +
 +
An example of setting up the properties to point to the user's OXM file:
 +
 +
<source lang="java">
 +
InputStream iStream = classLoader.getResourceAsStream("/myapp/data/eclipselink/eclipselink-oxm.xml");
 +
HashMap<String, Source> metadataSourceMap = new HashMap<String, Source>();
 +
metadataSourceMap.put("mynamespace", new StreamSource(iStream));
 +
 +
Map<String, Map<String, Source>> properties = new HashMap<String, Map<String, Source>>();
 +
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, metadataSourceMap);
 +
 +
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromOXM(classLoader, properties);
 +
DynamicEntity person = dContext.newDynamicEntity("mynamespace.Person");
 +
...
 +
</source>
 +
 +
''Proposal'': should we provide a convenience method that takes a single package name and OXM file, to save the user having to construct two <tt>HashMaps</tt>?  eg:
 +
 +
<source lang="java">
 +
createContextFromOXM(ClassLoader classLoader, String packageName, InputStream oxmStream, Map<String, ?> properties)
 +
 +
or
 +
 +
createContextFromOXM(ClassLoader classLoader, String packageName, StreamSource oxmSource, Map<String, ?> properties)
 +
</source>
  
 
== Development Phases ==
 
== Development Phases ==
Line 52: Line 113:
 
| align="center" | &nbsp;  
 
| align="center" | &nbsp;  
 
| align="center" | &nbsp;
 
| align="center" | &nbsp;
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlTransient.html XmlTransient]
 
| xml-transient
 
| align="center" | &nbsp;
 
| align="center" | X
 
| align="center" | X
 
| align="center" | X
 
 
|}
 
|}
  
 
<br>
 
<br>
  
The following example schema demonstrates how these annotations will be derived from the schema:
+
The following example OXM file demonstrates how these annotations can be specified:
  
==== Example Schema ====
+
==== Example OXM ====
  
 
<source lang="xml">
 
<source lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
+
<?xml version="1.0" encoding="US-ASCII"?>
<xs:schema targetNamespace="myNamespace" xmlns:myns="myNamespace" xmlns:xs="http://www.w3.org/2001/XMLSchema"
+
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
     attributeFormDefault="qualified" elementFormDefault="qualified">
+
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 +
     xmlns:xs="http://www.w3.org/2001/XMLSchema">
  
     <xs:element name="person" type="myns:person"/>
+
     <xml-schema element-form-default="QUALIFIED" attribute-form-default="QUALIFIED" namespace="mynamespace">
 +
        <xml-ns prefix="ns1" namespace-uri="mynamespace"/>
 +
        <xml-ns prefix="xsd" namespace-uri="http://www.w3.org/2001/XMLSchema"/>
 +
        <xml-ns prefix="xsi" namespace-uri="http://www.w3.org/2001/XMLSchema-instance"/>       
 +
    </xml-schema>
  
     <xs:complexType name="person">
+
     <java-types>
        <xs:sequence>
+
        <java-type name="mynamespace.Person">
             <xs:element name="name" type="xs:string"/>
+
            <xml-root-element name="person"/>
         </xs:sequence>
+
             <java-attributes>
     </xs:complexType>
+
                <xml-attribute java-attribute="id" type="java.lang.Integer"/>
 +
                <xml-element java-attribute="name" type="java.lang.String"/>
 +
            </java-attributes>
 +
         </java-type>
 +
     </java-types>
  
    <xs:complexType name="customer">
+
</xml-bindings>
        <xs:complexContent>
+
            <xs:extension base="myns:person">
+
                <xs:sequence>
+
                    <xs:element name="customer-id" type="xs:int"/>
+
                </xs:sequence>
+
            </xs:extension>
+
        </xs:complexContent>
+
    </xs:complexType>
+
 
+
    <xs:complexType name="employee">
+
        <xs:complexContent>
+
            <xs:extension base="myns:person">
+
                <xs:sequence>
+
                    <xs:element name="employee-id" type="xs:string"/>
+
                </xs:sequence>
+
            </xs:extension>
+
        </xs:complexContent>
+
    </xs:complexType>
+
 
+
</xs:schema>
+
 
</source>
 
</source>
 
* The <tt>targetNamespace</tt> and <tt>elementFormDefault</tt> property will result in an <tt>@XmlSchema</tt> annotation on the generated <tt>package-info</tt> class.
 
* The inheritance relationship between <tt>person</tt>, <tt>customer</tt>, and <tt>employee</tt> will result in an <tt>@XmlSeeAlso</tt> annotation on the generated <tt>Person</tt> class.
 
  
 
==== Limitations ====
 
==== Limitations ====
  
* XJC does not interpret the <tt>attributeFormDefault</tt> property during schema parsing, and will therefore not generate this property in the <tt>@XmlSchema</tt> annotation.
+
* <tt>XmlSeeAlso</tt> is a purely Java-object annotation, it has no effect on marshalled XML.  It is therefore not applicable to Dynamic JAXB.
* There is no XML Schema construct that will result in an <tt>@XmlTransient</tt> annotation being generated.
+
  
 
<br><br>
 
<br><br>
Line 123: Line 162:
 
! Field  
 
! Field  
 
! Method
 
! Method
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlAccessorOrder.html XmlAccessorOrder]
 
| xml-accessor-order
 
| align="center" | X
 
| align="center" | X
 
| align="center" | &nbsp;
 
| align="center" | &nbsp;
 
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlAccessorType.html XmlAccessorType]
 
| xml-accessor-type
 
| align="center" | X
 
| align="center" | X
 
| align="center" | &nbsp;
 
| align="center" | &nbsp;
 
 
|-
 
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlRootElement.html XmlRootElement]  
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlRootElement.html XmlRootElement]  
Line 155: Line 180:
 
<br>
 
<br>
  
The following example schema demonstrates how these annotations will be derived from the schema:
+
The following example OXM files demonstrate how these annotations can be specified:
  
==== Example Schema ====
+
==== Example OXM ====
 +
xml-root-element
 
<source lang="xml">
 
<source lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
+
<?xml version="1.0" encoding="US-ASCII"?>
<xs:schema targetNamespace="myNamespace" xmlns:myns="myNamespace" xmlns:xs="http://www.w3.org/2001/XMLSchema"
+
<xml-bindings ...>
    attributeFormDefault="qualified" elementFormDefault="qualified">
+
  
     <xs:element name="individuo" type="myns:person"/>
+
     ...
  
     <xs:complexType name="person">
+
     <java-types>
         <xs:sequence>
+
         <java-type name="mynamespace.Person">
            <xs:element name="id" type="xs:int"/>
+
             <xml-root-element name="individuo"/>
             <xs:element name="first-name" type="xs:string"/>
+
             <java-attributes>
             <xs:element name="last-name" type="xs:string"/>
+
                <xml-element java-attribute="name" type="java.lang.String"/>
            <xs:element name="phone-number" type="xs:string"/>
+
             </java-attributes>
             <xs:element name="email" type="xs:string"/>          
+
         </java-type>
         </xs:sequence>
+
     </java-types>
     </xs:complexType>
+
  
</xs:schema>
+
</xml-bindings>
 
</source>
 
</source>
  
* The <tt>name</tt> property of the root element will appear in the <tt>@XmlRootElement</tt> annotation's <tt>name</tt> property.
+
* When the <tt>DynamicEntity</tt> is marshalled to XML, the document's root element will be <tt><individuo></tt>.
* The order of the elements of the <tt>person</tt> type will be captured in the <tt>@XmlType</tt> annotation's <tt>propOrder</tt> property.
+
  
==== Limitations ====
+
xml-type
 +
<source lang="xml">
 +
<?xml version="1.0" encoding="US-ASCII"?>
 +
<xml-bindings ...>
 +
 
 +
    ...
 +
 
 +
    <java-types>
 +
        <java-type name="mynamespace.Person">
 +
            <xml-root-element name="person"/>
 +
            <xml-type prop-order="id firstName lastName phoneNumber email"/>
 +
            <java-attributes>
 +
                <xml-element java-attribute="firstName" type="java.lang.String"/>
 +
                <xml-element java-attribute="phoneNumber" type="java.lang.String"/>
 +
                <xml-element java-attribute="id" type="java.lang.Integer"/>
 +
                <xml-element java-attribute="email" type="java.lang.String"/>
 +
                <xml-element java-attribute="lastName" type="java.lang.String"/>
 +
            </java-attributes>
 +
        </java-type>
 +
    </java-types>
 +
 
 +
</xml-bindings>
 +
</source>
  
* There is no XML Schema construct that will result in the generation of <tt>@XmlAccessorOrder</tt> or <tt>@XmlAccessorType</tt> annotations.
+
* When the <tt>DynamicEntity</tt> is marshalled to XML, the elements will appear in the order specified in the <tt>xml-type</tt> element.
  
 
<br><br>
 
<br><br>
Line 209: Line 254:
 
| align="center" | &nbsp;  
 
| align="center" | &nbsp;  
 
| align="center" | &nbsp;  
 
| align="center" | &nbsp;  
| align="center" | X
 
| align="center" | X
 
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html XmlAdapter]
 
| xml-java-type-adapter
 
| align="center" | X
 
| align="center" | X
 
 
| align="center" | X  
 
| align="center" | X  
 
| align="center" | X
 
| align="center" | X
Line 222: Line 260:
 
<br>
 
<br>
  
The following example schema demonstrates how these annotations will be derived from the schema:
+
The following example OXM files demonstrate how these annotations can be specified:
  
==== Example Schema ====
+
==== Example OXM ====
 +
xml-attribute
 
<source lang="xml">
 
<source lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
+
<?xml version="1.0" encoding="US-ASCII"?>
<xs:schema targetNamespace="myNamespace" xmlns:myns="myNamespace" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
<xml-bindings ...>
  
     <xs:element name="person" type="myns:person"/>
+
     ...
  
     <xs:complexType name="person">
+
     <java-types>
        <xs:sequence>
+
        <java-type name="mynamespace.Person">
             <xs:element name="type" type="xs:string"/>
+
             <xml-root-element name="person"/>
        </xs:sequence>
+
            <java-attributes>
        <xs:attribute name="id" type="xs:int"/>
+
                <xml-attribute java-attribute="id" type="java.lang.Integer"/>
     </xs:complexType>
+
            </java-attributes>
 +
        </java-type>
 +
     </java-types>
  
</xs:schema>
+
</xml-bindings>
 
</source>
 
</source>
  
* The <tt>type</tt> field of the generated class will be annotated with <tt>@XmlElement</tt>.
+
xml-element
* The <tt>id</tt> field of the generated class will be annotated with <tt>@XmlAttribute</tt>.
+
<source lang="xml">
 +
<?xml version="1.0" encoding="US-ASCII"?>
 +
<xml-bindings ...>
  
==== Limitations ====
+
    ...
  
* There is no XML Schema construct that will result in the generation of an <tt>@XmlAdapter</tt> annotation.
+
    <java-types>
 +
        <java-type name="mynamespace.Person">
 +
            <xml-root-element name="person"/>
 +
            <java-attributes>
 +
                <xml-element java-attribute="type" type="java.lang.String"/>
 +
            </java-attributes>
 +
        </java-type>
 +
    </java-types>
  
 +
</xml-bindings>
 +
</source>
 
<br><br>
 
<br><br>
  
Line 260: Line 312:
 
! Field  
 
! Field  
 
! Method
 
! Method
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlElementWrapper.html XmlElementWrapper]
 
| xml-element-wrapper
 
| align="center" | &nbsp;
 
| align="center" | &nbsp;
 
| align="center" | X
 
| align="center" | X
 
 
|-
 
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlList.html XmlList]  
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlList.html XmlList]  
Line 306: Line 351:
 
<br>
 
<br>
  
The following example schema demonstrates how these annotations will be derived from the schema:
+
The following example OXM file demonstrates how these annotations can be specified:
 
+
==== Example Schemas ====
+
  
 +
==== Example OXM ====
 +
xml-list
 
<source lang="xml">
 
<source lang="xml">
<!-- XmlList -->
 
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
+
</source>
  
    <xs:element name="person">
+
xml-value
        <xs:complexType>
+
<source lang="xml">
            <xs:sequence>
+
<?xml version="1.0" encoding="US-ASCII"?>
                <xs:element name="name" type="xs:string"/>
+
<xml-bindings ...>
                <xs:element name="codes" type="myns:codes"/>
+
            </xs:sequence>
+
        </xs:complexType>
+
    </xs:element>
+
  
     <xs:simpleType name="codes">
+
     ...
        <xs:list itemType="xs:string"/>
+
    </xs:simpleType>
+
  
</xs:schema>
+
    <java-types>
 +
        <java-type name="mynamespace.Person">
 +
            <xml-root-element name="person"/>
 +
            <java-attributes>
 +
                <xml-element java-attribute="name" type="java.lang.String"/>
 +
                <xml-element java-attribute="salary" type="mynamespace.CdnCurrency"/>
 +
            </java-attributes>
 +
        </java-type>
 +
 
 +
        <java-type name="mynamespace.CdnCurrency">
 +
            <java-attributes>
 +
                <xml-value java-attribute="amount" type="java.math.BigDecimal"/>
 +
            </java-attributes>
 +
        </java-type>
 +
    </java-types>
 +
 
 +
</xml-bindings>
 
</source>
 
</source>
  
* An <tt>xs:simpleType</tt> containing an <tt>xs:list</tt> results in an <tt>@XmlList</tt> annotation on the <tt>codes</tt> field
+
xml-any-attribute
 
+
 
<source lang="xml">
 
<source lang="xml">
<!-- XmlValue -->
+
<?xml version="1.0" encoding="US-ASCII"?>
<?xml version="1.0" encoding="UTF-8"?>
+
<xml-bindings ...>
<xs:schema ...>
+
  
     <xs:element name="person" type="myns:person"/>
+
     ...
  
     <xs:complexType name="person">
+
     <java-types>
        <xs:sequence>
+
        <java-type name="mynamespace.Person">
             <xs:element name="name" type="xs:string"/>
+
            <xml-root-element name="person"/>
            <xs:element name="salary" type="myns:cdn_currency"/>
+
             <java-attributes>
         </xs:sequence>
+
                <xml-element java-attribute="name" type="java.lang.String"/>
     </xs:complexType>
+
                <xml-any-attribute java-attribute="otherAttributes"/>
 +
            </java-attributes>
 +
         </java-type>
 +
     </java-types>
  
    <xs:complexType name="cdn_currency">
+
</xml-bindings>
        <xs:simpleContent>
+
            <xs:extension base="xs:decimal"/>
+
        </xs:simpleContent>
+
    </xs:complexType>
+
 
+
</xs:schema>
+
 
</source>
 
</source>
  
* An <tt>xs:complexType</tt> containing <tt>xs:simpleContent</tt> results in an <tt>@XmlValue</tt> annotation on the <tt>value</tt> field of the <tt>CdnCurrency</tt> type.
+
xml-any-element
 
+
 
<source lang="xml">
 
<source lang="xml">
<!-- XmlAnyElement / XmlAnyAttribute -->
+
<?xml version="1.0" encoding="US-ASCII"?>
<?xml version="1.0" encoding="UTF-8"?>
+
<xml-bindings ...>
<xs:schema ...>
+
  
     <xs:element name="person">
+
     ...
        <xs:complexType>
+
            <xs:sequence>
+
                <xs:element name="name" type="xs:string"/>
+
                <xs:any/>
+
            </xs:sequence>
+
            <xs:anyAttribute/>
+
        </xs:complexType>
+
    </xs:element>
+
  
</xs:schema>
+
    <java-types>
 +
        <java-type name="mynamespace.Person">
 +
            <xml-root-element name="person"/>
 +
            <java-attributes>
 +
                <xml-element java-attribute="name" type="java.lang.String"/>
 +
                <xml-any-element java-attribute="any" lax="true"/>
 +
            </java-attributes>
 +
        </java-type>
 +
    </java-types>
 +
 
 +
</xml-bindings>
 
</source>
 
</source>
  
* The <tt>xs:any</tt> element results in the generation of an <tt>any</tt> field on <tt>Person</tt>, annotated with <tt>@XmlAnyElement</tt>.
+
xml-list
* The <tt>xs:anyAttribute</tt> element results in the generation of an <tt>otherAttributes</tt> field on <tt>Person</tt>, annotated with <tt>@XmlAnyAttribute</tt>.
+
 
+
 
<source lang="xml">
 
<source lang="xml">
<!-- XmlMixed -->
 
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
 
 
    <xs:element name="person">
 
        <xs:complexType mixed="true">
 
            <xs:sequence>
 
                <xs:element name="title" type="xs:string"/>
 
                <xs:element name="name" type="xs:string"/>
 
                <xs:element name="rewardPoints" type="xs:positiveInteger"/>               
 
            </xs:sequence>
 
        </xs:complexType>
 
    </xs:element>
 
 
</xs:schema>
 
 
</source>
 
</source>
 
* <tt>mixed=true</tt> results in the generation of a <tt>content</tt> field on <tt>Person</tt>, annotated with <tt>@XmlMixed</tt> (as well as <tt>@XmlElementRefs</tt> for each possible value)
 
  
 
==== Limitations ====
 
==== Limitations ====
  
* There is no XML Schema construct that will result in an <tt>@XmlElementWrapper</tt> annotation being generated.  
+
* Text.
  
 
<br><br>
 
<br><br>
Line 433: Line 464:
 
<br>
 
<br>
  
The following example schema demonstrates how these annotations will be derived from the schema:
+
The following example OXM file demonstrates how these annotations can be specified:
 
+
==== Example Schema ====
+
  
 +
==== Example OXM ====
 
<source lang="xml">
 
<source lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
+
<?xml version="1.0" encoding="US-ASCII"?>
<xs:schema ...>
+
<xml-bindings ...>
 +
 +
    ...
 +
 +
    <java-types>
 +
        <java-type name="mynamespace.Data">
 +
            <xml-root-element name="data"/>
 +
            <java-attributes>
 +
                <xml-element java-attribute="person" type="mynamespace.Person"/>
 +
                <xml-element java-attribute="company" type="mynamespace.Company"/>
 +
            </java-attributes>
 +
        </java-type>
  
    <xs:element name="data">
+
        <java-type name="mynamespace.Person">
        <xs:complexType>
+
             <java-attributes>
             <xs:sequence>
+
                 <xml-element java-attribute="name" type="java.lang.String"/>
                 <xs:element name="person" type="myns:person"/>
+
                 <xml-element java-attribute="company" type="mynamespace.Company" xml-idref="true"/>
                 <xs:element name="company" type="myns:company"/>
+
             </java-attributes>
             </xs:sequence>
+
         </java-type>      
         </xs:complexType>
+
    </xs:element>
+
  
    <xs:complexType name="person">
+
        <java-type name="mynamespace.Company">
        <xs:sequence>
+
            <java-attributes>
            <xs:element name="name" type="xs:string"/>
+
                <xml-element java-attribute="name" type="java.lang.String"/>
            <xs:element name="company" type="xs:IDREF"/>
+
                <xml-element java-attribute="address" type="java.lang.String"/>
        </xs:sequence>
+
                <xml-attribute java-attribute="id" type="java.lang.Integer" xml-id="true"/>
    </xs:complexType>
+
             </java-attributes>
 
+
         </java-type>      
    <xs:complexType name="company">
+
     </java-types>
        <xs:sequence>
+
            <xs:element name="name" type="xs:string"/>
+
</xml-bindings>
             <xs:element name="address" type="xs:string"/>          
+
         </xs:sequence>
+
        <xs:attribute name="id" type="xs:ID"/>
+
     </xs:complexType>
+
 
+
</xs:schema>
+
 
</source>
 
</source>
 
* The <tt>id</tt> attribute, of type <tt>xs:ID</tt>, in the <tt>company</tt> type results in an <tt>@XmlID</tt> annotation on the <tt>id</tt> field.
 
* The <tt>company</tt> element, of type <tt>xs:IDREF</tt>, in the <tt>person</tt> type results in an <tt>@XmlIDREF</tt> annotation on the <tt>company</tt> field.
 
 
==== Limitations ====
 
 
* Currently disabled due to bug [https://bugs.eclipse.org/bugs/show_bug.cgi?id=307939 Bug 307939]
 
  
 
<br><br>
 
<br><br>
Line 501: Line 527:
 
| align="center" | X  
 
| align="center" | X  
 
| align="center" | X
 
| align="center" | X
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlElementRefs.html XmlElementRefs]
 
| xml-element-refs
 
| align="center" |
 
| align="center" |
 
| align="center" | X<br>
 
| align="center" | X<br>
 
 
|}
 
|}
  
 
<br>
 
<br>
  
The following example schema demonstrates how these annotations will be derived from the schema:
+
The following example OXM file demonstrates how these annotations can be specified:
 
+
==== Example Schema ====
+
  
 +
==== Example OXM ====
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
+
</source>
  
    <xs:element name="person">
+
* Text.
        <xs:complexType>
+
* Text.
            <xs:sequence>
+
                <xs:choice minOccurs="1" maxOccurs="unbounded">
+
                    <xs:element name="name" type="xs:string"/>
+
                    <xs:element name="reference-number" type="xs:int"/>
+
                </xs:choice>
+
            </xs:sequence>
+
        </xs:complexType>
+
    </xs:element>
+
  
</xs:schema>
+
==== Limitations ====
</source>
+
  
* The <tt>xs:choice</tt> will result in the generation of a <tt>nameOrReferenceNumber</tt> field, which is annotated with <tt>@XmlElements</tt> (with one <tt>@XmlElement</tt> annotation for both <tt>name</tt> and <tt>reference-number</tt>.
+
* Text.
* <tt>@XmlElementRefs</tt> is covered along with the <tt>@XmlMixed</tt> annotation from Phase 4.
+
  
 
<br><br>
 
<br><br>
Line 556: Line 564:
 
| align="center" | X  
 
| align="center" | X  
 
| align="center" | X
 
| align="center" | X
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlSchemaTypes.html XmlSchemaTypes]
 
| xml-schema-types
 
| align="center" | X
 
| align="center" | &nbsp;
 
| align="center" | &nbsp;<br>
 
| align="center" | &nbsp;<br>
 
 
|}
 
|}
  
 
<br>
 
<br>
  
The following example schema demonstrates how these annotations will be derived from the schema:
+
The following example OXM file demonstrates how these annotations can be specified:
 
+
==== Example Schema ====
+
  
 +
==== Example OXM ====
 
<source lang="xml">
 
<source lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
+
<?xml version="1.0" encoding="US-ASCII"?>
<xs:schema ...>
+
<xml-bindings ...>
 
+
     <xs:element name="person">
+
    ...
        <xs:complexType>
+
             <xs:sequence>
+
     <java-types>
                 <xs:element name="date-of-birth" type="xs:date"/>
+
        <java-type name="mynamespace.Person">
             </xs:sequence>
+
            <xml-root-element name="person"/>
         </xs:complexType>
+
             <java-attributes>
     </xs:element>
+
                 <xml-element java-attribute="dateOfBirth">
 
+
                    <xml-schema-type name="date"/>
</xs:schema>
+
                </xml-element>
 +
             </java-attributes>
 +
         </java-type>      
 +
     </java-types>
 +
 +
</xml-bindings>
 
</source>
 
</source>
  
* The <tt>dateOfBirth</tt> attribute will be annotated with <tt>@XmlSchemaType</tt>, because its XML type, <tt>xs:date</tt>, is an XML datatype (not a primitive type).
+
* the <tt>dateOfBirth</tt> will be written out as an <tt>"xs:date"</tt> element (corresponding to a <tt>javax.xml.datatype.XMLGregorianCalendar</tt> in Java).
 
+
==== Limitations ====
+
 
+
* There is no XML Schema construct that will result in an <tt>@XmlSchemaTypes</tt> annotation being generated.  
+
  
 
<br><br>
 
<br><br>
Line 622: Line 623:
 
<br>
 
<br>
  
The following example schema demonstrates how these annotations will be derived from the schema:
+
The following example OXM file demonstrates how these annotations can be specified:
 
+
==== Example Schema ====
+
  
 +
==== Example OXM ====
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
+
</source>
  
    <xs:element name="person">
+
* Text.
        <xs:complexType>
+
* Text.
            <xs:sequence>
+
                <xs:element name="quadrant" type="myns:compass-direction"/>
+
            </xs:sequence>
+
        </xs:complexType>
+
    </xs:element>
+
 
+
    <xs:simpleType name="compass-direction">
+
        <xs:restriction base="xs:string">
+
<xs:enumeration value="NORTH"/>
+
<xs:enumeration value="SOUTH"/>
+
<xs:enumeration value="EAST"/>
+
<xs:enumeration value="WEST"/>
+
        </xs:restriction>
+
    </xs:simpleType>
+
 
+
</xs:schema>
+
</source>
+
  
 
==== Limitations ====
 
==== Limitations ====
  
* Waiting on [https://bugs.eclipse.org/bugs/show_bug.cgi?id=308367 Bug 308367].
+
* Text.
  
 
<br><br>
 
<br><br>
  
 
=== Phase 9 ===
 
=== Phase 9 ===
 
{|{{BMTableStyle}}
 
|-{{BMTHStyle}}
 
! Annotation
 
! XML Metadata Tag
 
! Package
 
! Type
 
! Field
 
! Method
 
|-
 
| [http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlInlineBinaryData.html XmlInlineBinaryData]
 
| xml-inline-binary-data
 
| align="center" | &nbsp;
 
| align="center" | X
 
| align="center" | X
 
| align="center" | X
 
|}
 
 
<br>
 
 
* There is no XML Schema construct that will result in an <tt>@XmlInlineBinaryData</tt> annotation being generated.
 
 
<br><br>
 
 
=== Phase 10 ===
 
  
 
{|{{BMTableStyle}}
 
{|{{BMTableStyle}}
Line 709: Line 667:
 
<br>
 
<br>
  
The following example schema demonstrates how these annotations will be derived from the schema:
+
The following example OXM file demonstrates how these annotations can be specified:
 
+
==== Example Schema ====
+
  
 +
==== Example OXM ====
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
+
</source>
  
    <xs:element name="individuo" type="myns:person"/>
+
* Text.
 
+
* Text.
    <xs:complexType name="person">
+
        <xs:sequence>
+
            <xs:element name="name" type="xs:string"/>
+
        </xs:sequence>
+
    </xs:complexType>
+
 
+
</xs:schema>
+
</source>
+
  
 +
==== Limitations ====
  
* The element declaration for <tt>individuo</tt> will result in an <tt>@XmlElementDecl</tt> annotation on the generated <tt>ObjectFactory</tt> class.
+
* Text.
* As a result of the <tt>@XmlElementDecl</tt> annotation, <tt>ObjectFactory</tt> will also be annotated with an <tt>@XmlRegistry</tt>.
+
  
 
<br><br>
 
<br><br>
Line 736: Line 685:
 
== Design ==
 
== Design ==
  
When constructing a <code>DynamicJAXBContext</code> from XML Schema, we can use APIs from the Java XJC compiler to parse a schema and create Java class definitions (XJC's <code>JCodeModel</code>) in memory, then pass these class definitions to an EclipseLink <code>Generator</code> (<code>org.eclipse.persistence.jaxb.compiler.Generator</code>) to generate an EclipseLink project.  After we have created this project, we can use the <code>DynamicTypeBuilder</code> to create a dynamic project, generating Java classes in memory along the way.
+
When constructing a <code>DynamicJAXBContext</code> from an OXM file, we will first parse the metadata in order to create our own EclipseLink <code>JavaModel</code> classes, then pass this code model into an EclipseLink <code>Generator</code> (<code>org.eclipse.persistence.jaxb.compiler.Generator</code>) to generate an EclipseLink project.  After we have created this project, we can use the <code>DynamicTypeBuilder</code> to create a dynamic project, generating Java classes in memory along the way.
  
First, we use XJC API to parse an XSD and generate a <code>JCodeModel</code>.  Note that this code stops short of actually generating <code>.java</code> files, it only generates an in-memory representation of the Java files that would normally be created.
+
First, we use an EclipseLink utility method to parse the OXM file and return an <tt>XmlBindings</tt> object, and use it to create a collection of <tt>OXMJavaClassImpl</tt> objects:
  
 
<source lang="java5">
 
<source lang="java5">
// Use XJC API to parse the schema and generate its JCodeModel
+
Map<String, XmlBindings> bindings = JAXBContextFactory.getXmlBindingsFromProperties(properties, classLoader);
SchemaCompiler sc = XJC.createSchemaCompiler();
+
 
InputSource inputSource = new InputSource(schemaInputStream);
+
JavaClass[] elinkClasses = createClassModelFromOXM(bindings);
sc.parseSchema(inputSource);
+
 
S2JJAXBModel model = sc.bind();
+
...
JCodeModel jCodeModel = model.generateCode(new Plugin[0], null);
+
private JavaClass[] createClassModelFromOXM(Map<String, XmlBindings> bindings) throws JAXBException {
 +
    List<OXMJavaClassImpl> oxmJavaClasses = new ArrayList<OXMJavaClassImpl>();
 +
   
 +
    Iterator<String> keys = bindings.keySet().iterator();
 +
   
 +
    while (keys.hasNext()) {
 +
        XmlBindings b = bindings.get(keys.next());
 +
       
 +
        List<JavaType> javaTypes = b.getJavaTypes().getJavaType();
 +
        for (Iterator<JavaType> iterator = javaTypes.iterator(); iterator.hasNext();) {
 +
            JavaType type = iterator.next();
 +
            oxmJavaClasses.add(new OXMJavaClassImpl(type));
 +
        }
 +
    }
 +
    ...
 
</source>
 
</source>
  
We can then wrap these XJC classes in our own implentations of EclipseLink's JAXB <code>JavaModel</code> classes.  This will allow us to use the <code>Generator</code> to create an EclipseLink project and mappings.  The <code>JavaModel</code> interfaces define "wrappers" for the various Java language constructs that make up the domain model (e.g. classes, methods, constructors, annotations, packages, etc).  For example:
+
At this point, we can instantiate a <code>Generator</code> and obtain a "dry" EclipseLink project, which we can then turn into a dynamic project with generated in-memory classes.  Finally, we create and store an <code>XMLContext</code> and a <code>DynamicHelper</code>:
  
 
<source lang="java5">
 
<source lang="java5">
public class XJCJavaFieldImpl implements JavaField {
+
// Use the JavaModel to setup a Generator to generate an EclipseLink project
 +
OXMJavaModelImpl javaModel = new OXMJavaModelImpl(classLoader, elinkClasses);
 +
OXMJavaModelInputImpl javaModelInput = new OXMJavaModelInputImpl(elinkClasses, javaModel);
 +
Generator g = new Generator(javaModelInput, bindings, dynamicClassLoader, null);
  
     // XJC's definition of a Field
+
Project p = null;
     protected JFieldVar xjcField;
+
Project dp = null;
 +
try {
 +
    p = g.generateProject();
 +
     // Clear out InstantiationPolicy because it refers to ObjectFactory, which we won't be using
 +
     Vector<ClassDescriptor> descriptors = (Vector<ClassDescriptor>) p.getOrderedDescriptors();
 +
    for (ClassDescriptor classDescriptor : descriptors) {
 +
        classDescriptor.setInstantiationPolicy(new InstantiationPolicy());
 +
    }
 +
    dp = DynamicTypeBuilder.loadDynamicProject(p, null, dynamicClassLoader);
 +
} catch (Exception e) {
 +
    throw new JAXBException(org.eclipse.persistence.exceptions.JAXBException.errorCreatingDynamicJAXBContext(e));
 +
}
  
    ...
+
this.xmlContext = new XMLContext(dp);
  
    public int getModifiers() {
+
List<Session> sessions = (List<Session>) this.xmlContext.getSessions();
        return xjcField.mods().getValue();
+
for (Object session : sessions) {
    }
+
    this.helpers.add(new DynamicHelper((DatabaseSession) session));
 +
}
 +
</source>
  
    public String getName() {
+
<br>
        return xjcField.name();
+
    }
+
  
     ...
+
== Testing ==
 +
 
 +
=== Annotation Tests ===
 +
 
 +
A series of tests will be added to test each of the annotations listed in the phases above.  These tests will use <tt>eclipselink-oxm.xml</tt> files tailored to exercise a specific annotation.  In each test, a <tt>DynamicJAXBContext</tt> will be created for the OXM file, and new <tt>DynamicEntities</tt> will be created and marshalled out to a <tt>org.w3c.dom.Document</tt> object.  The nodes of the Document can then be examined to make sure they adhere to the configuration made in the OXM file.
 +
 
 +
For example, to test the <tt>XmlSchema's</tt> <tt>element-form-default</tt> and <tt>attribute-form-default</tt> options:
 +
 
 +
'''Example OXM File:'''
 +
<source lang="xml">
 +
<?xml version="1.0" encoding="US-ASCII"?>
 +
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
 +
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 +
 
 +
    <xml-schema element-form-default="QUALIFIED" attribute-form-default="QUALIFIED" namespace="mynamespace">
 +
        <xml-ns prefix="ns1" namespace-uri="mynamespace"/>
 +
        <xml-ns prefix="xsd" namespace-uri="http://www.w3.org/2001/XMLSchema"/>
 +
        <xml-ns prefix="xsi" namespace-uri="http://www.w3.org/2001/XMLSchema-instance"/>       
 +
    </xml-schema>
 +
 
 +
    <java-types>
 +
        <java-type name="mynamespace.Person">
 +
            <xml-root-element name="person"/>
 +
            <java-attributes>
 +
                <xml-attribute java-attribute="id" type="java.lang.Integer"/>
 +
                <xml-element java-attribute="name" type="java.lang.String"/>
 +
            </java-attributes>
 +
        </java-type>
 +
    </java-types>
 +
 
 +
</xml-bindings>
 +
</source>
 +
 
 +
'''Test Case:'''
 +
<source lang="java">
 +
public void testXmlSchemaQualified() throws Exception {
 +
    // ... BUILD properties MAP CONTAINING LINK TO THIS TEST'S OXM FILE ...
 +
       
 +
    jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(classLoader, properties);
 +
       
 +
    DynamicEntity person = jaxbContext.newDynamicEntity(PACKAGE + "." + PERSON);
 +
    assertNotNull("Could not create Dynamic Entity.", person);
 +
    person.set("id", 456);
 +
    person.set("name", "Bob Dobbs");
 +
       
 +
    Document marshalDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
 +
    jaxbContext.createMarshaller().marshal(person, marshalDoc);
 +
       
 +
    // Make sure "targetNamespace" was interpreted properly.
 +
    Node node = marshalDoc.getChildNodes().item(0);
 +
    assertEquals("Target Namespace was not set as expected.", "mynamespace", node.getNamespaceURI());
 +
 
 +
    // Make sure "elementFormDefault" was interpreted properly.
 +
    // elementFormDefault=qualified, so the root node, the
 +
    // root node's attribute, and the child node should all have a prefix.
 +
    assertNotNull("Root node did not have namespace prefix as expected.", node.getPrefix());
 +
 
 +
    Node attr = node.getAttributes().item(0);
 +
    assertNotNull("Attribute did not have namespace prefix as expected.", attr.getPrefix());
 +
 
 +
    Node childNode = node.getChildNodes().item(0);
 +
    assertNotNull("Child node did not have namespace prefix as expected.", childNode.getPrefix());
 
}
 
}
 
</source>
 
</source>
  
Creating the <code>JavaModel</code> classes:
+
=== Model Tests ===
  
<source lang="java5">
+
A second set of tests will take a more model-centric approach, using OXM files that contain larger domain models to exercise MOXy's mappings functionality.  For example, to test Collection mappings, an example OXM will define a series of <tt>JavaTypes</tt> that use collections:
// Create EclipseLink JavaModel objects for each of XJC's JDefinedClasses
+
 
ArrayList<JDefinedClass> classesToProcess = new ArrayList<JDefinedClass>();
+
'''Example OXM File:'''
Iterator<JPackage> packages = jCodeModel.packages();
+
<source lang="xml">
while (packages.hasNext()) {
+
<?xml version="1.0" encoding="US-ASCII"?>
  JPackage pkg = packages.next();
+
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
  Iterator<JDefinedClass> classes = pkg.classes();
+
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  while (classes.hasNext()) {
+
 
      JDefinedClass cls = classes.next();
+
    <xml-schema element-form-default="QUALIFIED" attribute-form-default="QUALIFIED" namespace="mynamespace">
      classesToProcess.add(cls);
+
        <xml-ns prefix="ns1" namespace-uri="mynamespace"/>
  }
+
        <xml-ns prefix="xsd" namespace-uri="http://www.w3.org/2001/XMLSchema"/>
+
        <xml-ns prefix="xsi" namespace-uri="http://www.w3.org/2001/XMLSchema-instance"/>
JavaClass[] jotClasses = createClassModelFromXJC(classesToProcess, jCodeModel);
+
    </xml-schema>
 +
 
 +
    <java-types>
 +
        <java-type name="mynamespace.Person">
 +
            <xml-root-element name="person"/>
 +
            <java-attributes>
 +
                <xml-element java-attribute="name" type="java.lang.String"/>
 +
                <xml-element java-attribute="address" type="mynamespace.Address"/>
 +
                <xml-element java-attribute="addresses" type="mynamespace.Address" xml-list="true"/>
 +
                <xml-element java-attribute="phoneNumber" type="mynamespace.PhoneNumber"/>
 +
                <xml-element java-attribute="phoneNumbers" type="mynamespace.PhoneNumber" xml-list="true"/>
 +
            </java-attributes>
 +
        </java-type>
 +
        <java-type name="mynamespace.Address">
 +
            ...
 +
        </java-type>
 +
        <java-type name="mynamespace.PhoneNumber">
 +
            ...
 +
        </java-type>
 +
    </java-types>
 +
 
 +
</xml-bindings>
 
</source>
 
</source>
  
At this point, we can instantiate a <code>Generator</code> and obtain a "dry" EclipseLink project, which we can then turn into a dynamic project with generated in-memory classes.  Finally, we create and store an <code>XMLContext</code> and a <code>DynamicHelper</code>:
+
'''Test Case:'''
 +
<source lang="java">
 +
public void testUnmarshal() throws JAXBException {
 +
    // ... BUILD properties MAP CONTAINING LINK TO THIS TEST'S OXM FILE ...
  
<source lang="java5">
+
    // ... BUILD xmlStream POINTING TO TEST INSTANCE DOCUMENT ...
// Use the JavaModel to setup a Generator to generate an EclipseLink project
+
 
XJCJavaModelImpl javaModel = new XJCJavaModelImpl(Thread.currentThread().getContextClassLoader(), jCodeModel);
+
    jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(classLoader, properties);
XJCJavaModelInputImpl javaModelInput = new XJCJavaModelInputImpl(jotClasses, javaModel);
+
 
Generator g = new Generator(javaModelInput);
+
    JAXBElement<DynamicEntity> jaxbElement = (JAXBElement<DynamicEntity>) jaxbContext.createUnmarshaller().unmarshal(xmlStream);
Project p = g.generateProject();
+
 
       
+
    DynamicEntity customer = jaxbElement.getValue();
// Make a Dynamic Project from this project, because these classes do not exist on the classpath
+
    assertNotNull("Could not create Dynamic Entity.", customer);
DynamicClassLoader dynamicClassLoader;
+
 
if (classLoader instanceof DynamicClassLoader) {
+
    List<DynamicEntity> addresses = customer.<List<DynamicEntity>>get("addresses");
  dynamicClassLoader = classLoader;
+
    assertEquals("addresses list did not contain the correct number of Addresses.", 2, addresses.size());
} else {
+
    DynamicEntity firstAddress = addresses.get(0);
  dynamicClassLoader = new DynamicClassLoader(classLoader);          
+
    assertEquals("First Address city was not correct", "Any Town", firstAddress.get("city"));
 +
 
 +
    List<DynamicEntity> phoneNumbers = customer.<List<DynamicEntity>>get("phoneNumbers");
 +
    assertEquals("phoneNumbers list did not contain the correct number of PhoneNumbers.", 3, phoneNumbers.size());
 +
    DynamicEntity firstPhoneNumber = phoneNumbers.get(0);
 +
    assertEquals("First PhoneNumber type was not correct", "work", firstPhoneNumber.get("type"));
 +
 
 +
    // ... ETC ...
 
}
 
}
Project dp = DynamicTypeBuilder.loadDynamicProject(p, null, dynamicClassLoader);
 
       
 
this.xmlContext = new XMLContext(dp);
 
this.dynamicHelper = new DynamicHelper(xmlContext.getSession(0));     
 
 
</source>
 
</source>
 +
 +
<br>
 +
 +
== Document History ==
 +
{|{{BMTableStyle}}
 +
|-{{BMTHStyle}}
 +
! Date
 +
! Author
 +
! Version Description & Notes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 +
|-
 +
| 2010/07/26
 +
| Rick Barkhouse
 +
| Initial contribution
 +
|-
 +
| 2010/07/28
 +
| Rick Barkhouse
 +
| Formatting, expanded overview, added bugzilla and doc links
 +
|-
 +
| 2010/07/30
 +
| Rick Barkhouse
 +
| Added Testing section
 +
|-
 +
| 2010/08/03
 +
| Rick Barkhouse
 +
| Added Public API section
 +
|-
 +
| 2010/08/11
 +
| Rick Barkhouse
 +
| Added information on Phases 1-3
 +
|}

Latest revision as of 15:13, 26 August 2010

Dynamic MOXy - Bootstrapping from EclipseLink Externalized Metadata (OXM)

ER 321152


Overview

The purpose of this feature is to allow the user to bootstrap EclipseLink Dynamic JAXB from an EclipseLink externalized metadata file (eclipselink-oxm.xml). This is a continuation of the Dynamic JAXB features introduced in EclipseLink 2.1.0.

Support for bootstrapping a DynamicJAXBContext from an EclipseLink Externalized Metadata file (OXM) will be implemented in multiple phases. For each phase, OXM files will be created that will exercise the associated annotations. A DynamicJAXBContext will be created from each OXM file, and an example object will be marshalled. The resulting document will be inspected to ensure that it was marshalled properly, according to the annotations being tested.

See also: Dynamic JAXB Documentation


Generating a DynamicJAXBContext with OXM

A DynamicJAXBContext is created from an OXM file in the following way:

  1. The OXM file is parsed by JAXBContextFactory to generate an org.eclipse.persistence.jaxb.xmlmodel.XmlBindings object.
  2. The XmlBindings are inspected and an EclipseLink javamodel representing this information is created.
  3. This collection of EclipseLink javamodel classes is then passed to org.eclipse.persistence.jaxb.compiler.Generator, which generates a standard EclipseLink project.
  4. A Dynamic Project is then created from the standard project via DynamicTypeBuilder.loadDynamicProject().
  5. An org.eclipse.persistence.oxm.XMLContext is then created from the Dynamic Project, and a DynamicJAXBContext is returned backed by this XMLContext.


Public API

The user will be able to bootstrap a DynamicJAXBContext using the following method in DynamicJAXBContextFactory:

/**
 * Create a <tt>DynamicJAXBContext</tt>, using an EclipseLink OXM file as the metadata source.
 *
 * @param classLoader
 *      The application's current class loader, which will be used to first lookup
 *      classes to see if they exist before new <tt>DynamicTypes</tt> are generated.  Can be
 *      <tt>null</tt>, in which case <tt>Thread.currentThread().getContextClassLoader()</tt> will be used.
 * @param properties
 *      Map of properties to use when creating a new <tt>DynamicJAXBContext</tt>.  This map must
 *      contain a key of JAXBContext.ECLIPSELINK_OXM_XML_KEY, with a value of Map<String, Source>,
 *      where String is the package name and Source is the metadata file for that package.
 *
 * @return
 *      A new instance of <tt>DynamicJAXBContext</tt>.
 *
 * @throws JAXBException
 *      if an error was encountered while creating the <tt>DynamicJAXBContext</tt>.
 */
public static DynamicJAXBContext createContextFromOXM(ClassLoader classLoader, Map<String, ?> properties) throws JAXBException

An example of setting up the properties to point to the user's OXM file:

InputStream iStream = classLoader.getResourceAsStream("/myapp/data/eclipselink/eclipselink-oxm.xml");
HashMap<String, Source> metadataSourceMap = new HashMap<String, Source>();
metadataSourceMap.put("mynamespace", new StreamSource(iStream));
 
Map<String, Map<String, Source>> properties = new HashMap<String, Map<String, Source>>();
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, metadataSourceMap);
 
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromOXM(classLoader, properties);
DynamicEntity person = dContext.newDynamicEntity("mynamespace.Person");
...

Proposal: should we provide a convenience method that takes a single package name and OXM file, to save the user having to construct two HashMaps? eg:

createContextFromOXM(ClassLoader classLoader, String packageName, InputStream oxmStream, Map<String, ?> properties)
 
or
 
createContextFromOXM(ClassLoader classLoader, String packageName, StreamSource oxmSource, Map<String, ?> properties)

Development Phases

Phase 1

Annotation XML Metadata Tag Package Type Field Method
XmlNs xml-ns        
XmlSchema xml-schema X      
XmlSeeAlso xml-see-also   X    


The following example OXM file demonstrates how these annotations can be specified:

Example OXM

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
 
    <xml-schema element-form-default="QUALIFIED" attribute-form-default="QUALIFIED" namespace="mynamespace">
        <xml-ns prefix="ns1" namespace-uri="mynamespace"/>
        <xml-ns prefix="xsd" namespace-uri="http://www.w3.org/2001/XMLSchema"/>
        <xml-ns prefix="xsi" namespace-uri="http://www.w3.org/2001/XMLSchema-instance"/>        
    </xml-schema>
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-attribute java-attribute="id" type="java.lang.Integer"/>
                <xml-element java-attribute="name" type="java.lang.String"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>

Limitations

  • XmlSeeAlso is a purely Java-object annotation, it has no effect on marshalled XML. It is therefore not applicable to Dynamic JAXB.



Phase 2

Annotation XML Metadata Tag Package Type Field Method
XmlRootElement xml-root-element   X    
XmlType xml-type   X    


The following example OXM files demonstrate how these annotations can be specified:

Example OXM

xml-root-element

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings ...>
 
    ...
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="individuo"/>
            <java-attributes>
                <xml-element java-attribute="name" type="java.lang.String"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>
  • When the DynamicEntity is marshalled to XML, the document's root element will be <individuo>.

xml-type

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings ...>
 
    ...
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <xml-type prop-order="id firstName lastName phoneNumber email"/>
            <java-attributes>
                <xml-element java-attribute="firstName" type="java.lang.String"/>
                <xml-element java-attribute="phoneNumber" type="java.lang.String"/>
                <xml-element java-attribute="id" type="java.lang.Integer"/>
                <xml-element java-attribute="email" type="java.lang.String"/>
                <xml-element java-attribute="lastName" type="java.lang.String"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>
  • When the DynamicEntity is marshalled to XML, the elements will appear in the order specified in the xml-type element.



Phase 3

Annotation XML Metadata Tag Package Type Field Method
XmlAttribute xml-attribute     X X
XmlElement xml-element     X X


The following example OXM files demonstrate how these annotations can be specified:

Example OXM

xml-attribute

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings ...>
 
    ...
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-attribute java-attribute="id" type="java.lang.Integer"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>

xml-element

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings ...>
 
    ...
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-element java-attribute="type" type="java.lang.String"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>



Phase 4

Annotation XML Metadata Tag Package Type Field Method
XmlList xml-list     X X
XmlValue xml-value X X
XmlAnyElement xml-any-element     X X
XmlAnyAttribute xml-any-attribute     X X
XmlMixed xml-mixed     X X


The following example OXM file demonstrates how these annotations can be specified:

Example OXM

xml-list

<?xml version="1.0" encoding="UTF-8"?>

xml-value

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings ...>
 
    ...
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-element java-attribute="name" type="java.lang.String"/>
                <xml-element java-attribute="salary" type="mynamespace.CdnCurrency"/>
            </java-attributes>
        </java-type>
 
        <java-type name="mynamespace.CdnCurrency">
            <java-attributes>
                <xml-value java-attribute="amount" type="java.math.BigDecimal"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>

xml-any-attribute

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings ...>
 
    ...
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-element java-attribute="name" type="java.lang.String"/>
                <xml-any-attribute java-attribute="otherAttributes"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>

xml-any-element

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings ...>
 
    ...
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-element java-attribute="name" type="java.lang.String"/>
                <xml-any-element java-attribute="any" lax="true"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>

xml-list

<?xml version="1.0" encoding="UTF-8"?>

Limitations

  • Text.



Phase 5

Annotation XML Metadata Tag Package Type Field Method
XmlID
xml-id     X X
XmlIDREF
xml-idref   X
X


The following example OXM file demonstrates how these annotations can be specified:

Example OXM

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings ...>
 
    ...
 
    <java-types>
        <java-type name="mynamespace.Data">
            <xml-root-element name="data"/>
            <java-attributes>
                <xml-element java-attribute="person" type="mynamespace.Person"/>
                <xml-element java-attribute="company" type="mynamespace.Company"/>
            </java-attributes>
        </java-type>
 
        <java-type name="mynamespace.Person">
            <java-attributes>
                <xml-element java-attribute="name" type="java.lang.String"/>
                <xml-element java-attribute="company" type="mynamespace.Company" xml-idref="true"/>
            </java-attributes>
        </java-type>        
 
        <java-type name="mynamespace.Company">
            <java-attributes>
                <xml-element java-attribute="name" type="java.lang.String"/>
                <xml-element java-attribute="address" type="java.lang.String"/>
                <xml-attribute java-attribute="id" type="java.lang.Integer" xml-id="true"/>
            </java-attributes>
        </java-type>        
    </java-types>
 
</xml-bindings>



Phase 6

Annotation XML Metadata Tag Package Type Field Method
XmlElements xml-elements     X X
XmlElementRef xml-element-ref     X X


The following example OXM file demonstrates how these annotations can be specified:

Example OXM

<?xml version="1.0" encoding="UTF-8"?>
  • Text.
  • Text.

Limitations

  • Text.



Phase 7

Annotation XML Metadata Tag Package Type Field Method
XmlSchemaType xml-schema-type X   X X


The following example OXM file demonstrates how these annotations can be specified:

Example OXM

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings ...>
 
    ...
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-element java-attribute="dateOfBirth">
                    <xml-schema-type name="date"/>
                </xml-element>
            </java-attributes>
        </java-type>        
    </java-types>
 
</xml-bindings>
  • the dateOfBirth will be written out as an "xs:date" element (corresponding to a javax.xml.datatype.XMLGregorianCalendar in Java).



Phase 8

Annotation XML Metadata Tag Package Type Field Method
XmlEnum xml-enum   X    
XmlEnumValue xml-enum-value     X
X


The following example OXM file demonstrates how these annotations can be specified:

Example OXM

<?xml version="1.0" encoding="UTF-8"?>
  • Text.
  • Text.

Limitations

  • Text.



Phase 9

Annotation XML Metadata Tag Package Type Field Method
XmlElementDecl xml-element-decl     X X
XmlRegistry xml-registry   X    


The following example OXM file demonstrates how these annotations can be specified:

Example OXM

<?xml version="1.0" encoding="UTF-8"?>
  • Text.
  • Text.

Limitations

  • Text.



Design

When constructing a DynamicJAXBContext from an OXM file, we will first parse the metadata in order to create our own EclipseLink JavaModel classes, then pass this code model into an EclipseLink Generator (org.eclipse.persistence.jaxb.compiler.Generator) to generate an EclipseLink project. After we have created this project, we can use the DynamicTypeBuilder to create a dynamic project, generating Java classes in memory along the way.

First, we use an EclipseLink utility method to parse the OXM file and return an XmlBindings object, and use it to create a collection of OXMJavaClassImpl objects:

Map<String, XmlBindings> bindings = JAXBContextFactory.getXmlBindingsFromProperties(properties, classLoader);
 
JavaClass[] elinkClasses = createClassModelFromOXM(bindings);
 
...
private JavaClass[] createClassModelFromOXM(Map<String, XmlBindings> bindings) throws JAXBException {
    List<OXMJavaClassImpl> oxmJavaClasses = new ArrayList<OXMJavaClassImpl>();
 
    Iterator<String> keys = bindings.keySet().iterator();
 
    while (keys.hasNext()) {
        XmlBindings b = bindings.get(keys.next());
 
        List<JavaType> javaTypes = b.getJavaTypes().getJavaType();
        for (Iterator<JavaType> iterator = javaTypes.iterator(); iterator.hasNext();) {
            JavaType type = iterator.next();
            oxmJavaClasses.add(new OXMJavaClassImpl(type));
        }
    }
    ...

At this point, we can instantiate a Generator and obtain a "dry" EclipseLink project, which we can then turn into a dynamic project with generated in-memory classes. Finally, we create and store an XMLContext and a DynamicHelper:

// Use the JavaModel to setup a Generator to generate an EclipseLink project
OXMJavaModelImpl javaModel = new OXMJavaModelImpl(classLoader, elinkClasses);
OXMJavaModelInputImpl javaModelInput = new OXMJavaModelInputImpl(elinkClasses, javaModel);
Generator g = new Generator(javaModelInput, bindings, dynamicClassLoader, null);
 
Project p = null;
Project dp = null;
try {
    p = g.generateProject();
    // Clear out InstantiationPolicy because it refers to ObjectFactory, which we won't be using
    Vector<ClassDescriptor> descriptors = (Vector<ClassDescriptor>) p.getOrderedDescriptors();
    for (ClassDescriptor classDescriptor : descriptors) {
        classDescriptor.setInstantiationPolicy(new InstantiationPolicy());
    }
    dp = DynamicTypeBuilder.loadDynamicProject(p, null, dynamicClassLoader);
} catch (Exception e) {
    throw new JAXBException(org.eclipse.persistence.exceptions.JAXBException.errorCreatingDynamicJAXBContext(e));
}
 
this.xmlContext = new XMLContext(dp);
 
List<Session> sessions = (List<Session>) this.xmlContext.getSessions();
for (Object session : sessions) {
    this.helpers.add(new DynamicHelper((DatabaseSession) session));
}


Testing

Annotation Tests

A series of tests will be added to test each of the annotations listed in the phases above. These tests will use eclipselink-oxm.xml files tailored to exercise a specific annotation. In each test, a DynamicJAXBContext will be created for the OXM file, and new DynamicEntities will be created and marshalled out to a org.w3c.dom.Document object. The nodes of the Document can then be examined to make sure they adhere to the configuration made in the OXM file.

For example, to test the XmlSchema's element-form-default and attribute-form-default options:

Example OXM File:

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 
    <xml-schema element-form-default="QUALIFIED" attribute-form-default="QUALIFIED" namespace="mynamespace">
        <xml-ns prefix="ns1" namespace-uri="mynamespace"/>
        <xml-ns prefix="xsd" namespace-uri="http://www.w3.org/2001/XMLSchema"/>
        <xml-ns prefix="xsi" namespace-uri="http://www.w3.org/2001/XMLSchema-instance"/>        
    </xml-schema>
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-attribute java-attribute="id" type="java.lang.Integer"/>
                <xml-element java-attribute="name" type="java.lang.String"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>

Test Case:

public void testXmlSchemaQualified() throws Exception {
    // ... BUILD properties MAP CONTAINING LINK TO THIS TEST'S OXM FILE ...
 
    jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(classLoader, properties);
 
    DynamicEntity person = jaxbContext.newDynamicEntity(PACKAGE + "." + PERSON);
    assertNotNull("Could not create Dynamic Entity.", person);
    person.set("id", 456);
    person.set("name", "Bob Dobbs");
 
    Document marshalDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
    jaxbContext.createMarshaller().marshal(person, marshalDoc);
 
    // Make sure "targetNamespace" was interpreted properly.
    Node node = marshalDoc.getChildNodes().item(0);
    assertEquals("Target Namespace was not set as expected.", "mynamespace", node.getNamespaceURI());
 
    // Make sure "elementFormDefault" was interpreted properly.
    // elementFormDefault=qualified, so the root node, the
    // root node's attribute, and the child node should all have a prefix.
    assertNotNull("Root node did not have namespace prefix as expected.", node.getPrefix());
 
    Node attr = node.getAttributes().item(0);
    assertNotNull("Attribute did not have namespace prefix as expected.", attr.getPrefix());
 
    Node childNode = node.getChildNodes().item(0);
    assertNotNull("Child node did not have namespace prefix as expected.", childNode.getPrefix());
}

Model Tests

A second set of tests will take a more model-centric approach, using OXM files that contain larger domain models to exercise MOXy's mappings functionality. For example, to test Collection mappings, an example OXM will define a series of JavaTypes that use collections:

Example OXM File:

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 
    <xml-schema element-form-default="QUALIFIED" attribute-form-default="QUALIFIED" namespace="mynamespace">
        <xml-ns prefix="ns1" namespace-uri="mynamespace"/>
        <xml-ns prefix="xsd" namespace-uri="http://www.w3.org/2001/XMLSchema"/>
        <xml-ns prefix="xsi" namespace-uri="http://www.w3.org/2001/XMLSchema-instance"/>
    </xml-schema>
 
    <java-types>
        <java-type name="mynamespace.Person">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-element java-attribute="name" type="java.lang.String"/>
                <xml-element java-attribute="address" type="mynamespace.Address"/>
                <xml-element java-attribute="addresses" type="mynamespace.Address" xml-list="true"/>
                <xml-element java-attribute="phoneNumber" type="mynamespace.PhoneNumber"/>
                <xml-element java-attribute="phoneNumbers" type="mynamespace.PhoneNumber" xml-list="true"/>
            </java-attributes>
        </java-type>
        <java-type name="mynamespace.Address">
            ...
        </java-type>
        <java-type name="mynamespace.PhoneNumber">
            ...
        </java-type>
    </java-types>
 
</xml-bindings>

Test Case:

public void testUnmarshal() throws JAXBException {
    // ... BUILD properties MAP CONTAINING LINK TO THIS TEST'S OXM FILE ...
 
    // ... BUILD xmlStream POINTING TO TEST INSTANCE DOCUMENT ...
 
    jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(classLoader, properties);
 
    JAXBElement<DynamicEntity> jaxbElement = (JAXBElement<DynamicEntity>) jaxbContext.createUnmarshaller().unmarshal(xmlStream);
 
    DynamicEntity customer = jaxbElement.getValue();
    assertNotNull("Could not create Dynamic Entity.", customer);
 
    List<DynamicEntity> addresses = customer.<List<DynamicEntity>>get("addresses");
    assertEquals("addresses list did not contain the correct number of Addresses.", 2, addresses.size());
    DynamicEntity firstAddress = addresses.get(0);
    assertEquals("First Address city was not correct", "Any Town", firstAddress.get("city"));
 
    List<DynamicEntity> phoneNumbers = customer.<List<DynamicEntity>>get("phoneNumbers");
    assertEquals("phoneNumbers list did not contain the correct number of PhoneNumbers.", 3, phoneNumbers.size());
    DynamicEntity firstPhoneNumber = phoneNumbers.get(0);
    assertEquals("First PhoneNumber type was not correct", "work", firstPhoneNumber.get("type"));
 
    // ... ETC ...
}


Document History

Date Author Version Description & Notes                                                                                
2010/07/26 Rick Barkhouse Initial contribution
2010/07/28 Rick Barkhouse Formatting, expanded overview, added bugzilla and doc links
2010/07/30 Rick Barkhouse Added Testing section
2010/08/03 Rick Barkhouse Added Public API section
2010/08/11 Rick Barkhouse Added information on Phases 1-3

Back to the top