Jump to: navigation, search

EclipseLink/Development/2.1/DynamicMOXy/296967/BootstrapFromOXM/ObjectFactory

Supporting ObjectFactory in Dynamic JAXB

Background

In standard JAXB, when classes are generated from a schema, an ObjectFactory class is also created, with methods to create new instances of all types in the schema, etc. From the JAXB 2.2 spec:

Idea.png
A public class ObjectFactory contains:

An instance factory method signature for each Java content within the package:

public Foo createFoo();

An element instance factory method signature for each bound element declaration:

public JAXBElement<T> createFoo(T elementValue);

Dynamic instance factory allocator method signature:

public Object newInstance(Class javaContentInterface);

Property setter/getter:

public Object getProperty(String name);

public void setProperty(String name, Object value);

The ObjectFactory is home to the XmlRegistry and XmlElementDecl annotations

Why is ObjectFactory needed?

Consider the following XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
 
    <xs:element name="individuo" type="myns:person"/>
 
    <xs:complexType name="person">
        <xs:sequence>
            <xs:element name="name" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
 
</xs:schema>

In this case, the user has a complex type called "Person" but they would like the resulting XML to use "individuo" as its XML element name.

In standard JAXB we would get a ObjectFactory that looks like this:

@XmlRegistry
public class ObjectFactory {
    private final static QName _Individuo_QNAME = new QName("myNamespace", "individuo");
 
    public ObjectFactory() {}
 
    public Person createPerson() {
        return new Person();
    }
 
    @XmlElementDecl(namespace = "myNamespace", name = "individuo")
    public JAXBElement<Person> createIndividuo(Person value) {
        return new JAXBElement<Person>(_Individuo_QNAME, Person.class, null, value);
    }
}

In standard static EclipseLink JAXB, when processing annotations we will look at the ObjectFactory, see the XmlElementDecl annotation, and set the Person Descriptor's default root element to be "individuo".

Additionally, the user may have a preference to use the ObjectFactory as opposed to instantiating their types explicitly.

The Dynamic Problem

Bootstrapping from XSD

When bootstrapping from XSD, the Sun XJC compiler is used to process the schema, and in fact an ObjectFactory class is produced, and used during EclipseLink mapping generation. However the ObjectFactory is not exposed for the user to use.

Bootstrapping from OXM

When bootstrapping from an OXM, we have the XmlRegistry / XmlElementDecl annotation information available to us, but no information about any ObjectFactory class itself. Like XSD, there is obviously no public API for the user to interact with an ObjectFactory.

Proposed Solution

There are two aspects to this solution:

  • Creation of a concrete DynamicObjectFactory that the user will use to instantiate their types. Instead of methods like createPerson(), the user will call generic methods such as createInstance("myNamespace.Person").
  • During context creation, generate (using ASM) an in-memory subclass of DynamicObjectFactory, set up with the proper annotations and information from the OXM file. This DynamicObjectFactory will then be used during mapping generation. This aspect is relevant only to the OXM Bootstrapping use case, as OXM bootstrapping is already producing an annotated ObjectFactory.

1. Introduce DynamicObjectFactory

We will add a default ObjectFactory to the EclipseLink library:

@XmlRegistry
public class DynamicObjectFactory {
 
   private DynamicJAXBContext dContext;
 
   public DynamicObjectFactory() {}
 
   public DynamicEntity createInstance(String entityName) {
      return dContext.newDynamicEntity(entityName);
   }
 
   public JAXBElement<DynamicEntity> createElementInstance(String entityName, QName entityQName, DynamicEntity value) {
      DynamicEntity dEntity = dContext.newDynamicEntity(entityName);
      return new JAXBElement<DynamicEntity>(entityQName, value.getClass, null, value);
   }
 
}

2. Public API to get ObjectFactory

We could add public API to DynamicJAXBContext to allow the user to obtain an ObjectFactory:

InputStream inputStream = ClassLoader.getSystemResourceAsStream("com/foo/sales/xsd/customer.xsd");
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, null, null, null);
 
// Explicit instantiation 
DynamicEntity cust1 = dContext.newDynamicEntity("mynamespace.Customer");
cust1.set("firstName", "George");
cust1.set("lastName", "Jones");
 
// ObjectFactory instantiation
DynamicObjectFactory factory = dContext.getObjectFactory();
DynamicEntity cust2 = factory.createInstance("mynamespace.Customer");
cust2.set("firstName", "Willie");
cust2.set("lastName", "Nelson");
 
JAXBElement<DynamicEntity> cust2Elem = factory.createElementInstance("mynamespace.Customer", cust2);