June 3, 2011

Using EclipseLink MOXy with a Metadata Repository

EclipseLink JAXB (MOXy) provides an XML mapping document as an alternative to using annotations.  This mapping document can be provided in many different forms:  resource path, URL, File, Source, etc.  If however you store your metadata in some sort of repository this flexibility might not be enough.  This is why in EclipseLink 2.3 we have introduced a new mechanism to support this use case.


MetadataSourceAdapter

The new mechanism for providing the metadata is a class called MetadataSourceAdapter. You simply need to extend this class and implement the getXmlBindings(Map, ClassLoader) method.  This method returns an instance of XmlBindings which is a JAXB model for the XML mapping document.  You can either create this model by hand, or use the JAXB APIs to populate it (as shown below).

package blog.metadatasource;

import java.net.URL;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

import org.eclipse.persistence.jaxb.metadata.MetadataSourceAdapter;
import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings;

public class ExampleMetadataSource extends MetadataSourceAdapter {

    private JAXBContext xmlBindingsContext;

    public ExampleMetadataSource() throws JAXBException {
        xmlBindingsContext = JAXBContext.newInstance("org.eclipse.persistence.jaxb.xmlmodel");
    }

    @Override
    public XmlBindings getXmlBindings(Map&String, ?> properties, ClassLoader classLoader) {
        try {
            URL xmlBindings = classLoader.getResource("blog/metadatasource/binding.xml");
            return (XmlBindings) xmlBindingsContext.createUnmarshaller().unmarshal(xmlBindings);
        } catch(JAXBException e) {
            throw new RuntimeException(e);
        }
    }

}

Mapping File (blog/metadatasource/binding.xml)

The mapping file contains the same information as the JAXB annotations.  Like with annotations you only need to specify metadata to override default behavior.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="blog.metadatasource">
    <xml-schema 
        namespace="http://www.example.com/customer" 
        element-form-default="QUALIFIED"/>
    <java-types>
        <java-type name="Customer">
            <xml-root-element/>
            <java-attributes>
                <xml-element java-attribute="firstName" name="first-name"/>
                <xml-element java-attribute="lastName" name="last-name"/>
                <xml-element java-attribute="phoneNumbers" name="phone-number"/>
            </java-attributes>
        </java-type>
        <java-type name="PhoneNumber">
            <java-attributes>
                <xml-attribute java-attribute="type"/>
                <xml-value java-attribute="number"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

Java Model

The following domain model will be used in this example.  Because the JAXB metadata is represented as XML, no annotations are used on the classes.  The get/set methods have been omitted to save space.

Customer

package blog.metadatasource;

import java.util.List;

public class Customer {

    private String firstName;
    private String lastName;
    private Address address;
    private List<PhoneNumber> phoneNumbers;

}

Address

package blog.metadatasource;

public class Address {

    private String street;

}

PhoneNumber

package blog.metadatasource;

public class PhoneNumber {

    private String type;
    private String number;

}

Demo

The XML metadata source is passed in via the properties parameter when the JAXBContext is instantiated.

package blog.metadatasource;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.metadata.MetadataSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>(1);
        MetadataSource metadataSource = new ExampleMetadataSource();
        properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, metadataSource);
        JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Customer customer = (Customer) unmarshaller.unmarshal(new File("src/blog/metadatasource/input.xml"));

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }

}

XML (blog/metadatasource/input.xml)

The following XML will be used in this example:

<?xml version="1.0" encoding="UTF-8"?>
<customer xmlns="http://www.example.com/customer">
    <first-name>Jane</first-name>
    <last-name>Doe</last-name>
    <address>
        <street>123 A Street</street>
    </address>
    <phone-number type="work">555-1111</phone-number>
    <phone-number type="cell">555-2222</phone-number>
</customer>

Further Reading

If you enjoyed this post you may also be interested in:
Other articles related to new MOXy features in EclipseLink 2.3:

No comments:

Post a Comment