Extending the ObjectFactory in EclipseLink MOXy - java

I have a JAXB model generated from MOXy's version of XJC. The xjc:superclass tag was used in the binding's file so all the objects extend a common class.
package my.package
//Base.java
#XmlTransient
public class Base {
//...
}
//MyTag.java (generated from XJC)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {/*...*/})
#XmlRootElement(name = "myTag")
public class MyTag extends Base {
//...
}
The model nor the schema can be changed, but I can change the Base class.
I need to extend MyTag, as well as some other classes in the model, so I can customize its method behaviors from the Base class. So I extended MyTag and the other domain classes that needed custom behavior, as well as the ObjectFactory. These classes exist in a separate Java package.
package my.extended.package
//MyTagExtended.java
public class MyTagExtended extends MyTag {
//...
}
//CustomObjectFactory.java
public class CustomObjectFactory extends ObjectFactory {
//...
#Override
public MyTagExtended createMyTag() {
return new MyTagExtended();
}
//...
}
Application code:
package application
//Application.java
System.setProperty("org.eclipse.persistence.moxy.annotation.xml-value-extension", "true");
JAXBContext jc = (JAXBContext) JAXBContext.newInstance(XPSObjectFactory.class);
JAXBUnmarshaller u = jc.createUnmarshaller();
return u.unmarshal(xmlFile);
The problem I'm having is MOXy seems to randomly decide whether to call the CustomObjectFactory's or ObjectFactory's methods.
In the CustomObjectFactory class, if I have only one overridden method, that method is always called. However, when I put in more, MOXy seems to randomly decide whether or not to call ObjectFactory's method's or CustomObjectFactory's.
When I use the same sort of setup with the Oracle's JAXB implementation that's supplied with the JDK, it works fine. The CustomObjectFactory's methods are always called.
Is there a configuration that needs to be set? How can I configure MOXy to always use my CustomObjectFactory's methods?
EDIT:
To clarify, here's how I made the same scenario work with Oracle's JAXB implementation:
JAXBContext jc = JAXBContext.newInstance("my.package");
Unmarshaller u = jc.createUnmarshaller();
u.setProperty("com.sun.xml.internal.bind.ObjectFactory", new CustomObjectFactory());
I tried to set the ObjectFactory property on the MOXy unmarshaller, but it threw an exception.

I found a workaround for MOXy. I found some information on XmlClassExtractor, which can be used to specify which class to instantiate. Since I can't modify the domain objects, an XML file was used instead.
I kept the extended classes the same, but modified Application.java and wrote a binding.xml file and a ClassExtractor:
package application
//Application.java
System.setProperty("org.eclipse.persistence.moxy.annotation.xml-value-extension", "true");
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "classExtractor.xml");
JAXBContext jc = (JAXBContext) JAXBContextFactory.createContext(new Class[]{CustomObjectFactory.class}, properties);
JAXBUnmarshaller u = jc.createUnmarshaller();
return u.unmarshal(xmlFile);
classExtractor.xml:
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="my.package"
version="2.3">
<java-types>
<java-type name="MyTag">
<xml-class-extractor class="my.extended.package.MyTagClassExtractor"/>
</java-type>
</java-types>
</xml-bindings>
Class extractor:
package my.extended.package
//MyTagExtractor.java
public class MyTagExtractor extends ClassExtractor {
#Override
public Class<? extends Base> extractClassFromRow(Record databaseRow, Session session) {
return MyTagExtended.class;
}
}
This seems to work, but it's quirky and prone to programmer errors since multiple source files have to be edited to get the desired functionality. Oracle's ObjectFactory property on the unmarshaller was very easy and streamlined. Does anyone have a better answer?

Related

JAXB adding annotated classes with new JAR

Have following situation:
A class with a List field like:
#XMLType
#XMLAccessType(XMLAccessorType.FIELD)
#XMLRootElement(name = "container")
public class ListContainer {
#XMLElementWrapper(name="elements")
private List<Element> elements = new ArrayList<>();
....
}
The Element class is an abstract JAXB annotated class with #XMLRootElement annotation like:
#XMLType
#XMLRootElemenent
public abstract class Element {
....
}
These classes define some kind of Framework and users shall be able to add own implementations of Element class within own JAR packages. What I would like to achieve, is that after unmarshalling I would have in elements field of the instance of class ListContainer class instances which were introduced as extensions to the framework. For instance let say there is a class DummyElement in some other ext1.jar, which is in the class path, and it looks like following:
#XMLType
#XMLRootElement(name = "dummy")
public class DummyElement extends Element {
....
}
in ext2.jar I will have EasyElement like:
#XMLType
#XMLRootElement(name = "easy")
public class EasyElement extends Element {
...
}
in xml there on the place I would have something like:
<container>
<elements>
<dummy>....</dummy>
<easy>...</easy>
<easy>....</easy>
<dummy>...</dummy>
</elements>
</container>
expected result should be, that unmarshalled instance of ListContainer class will have 2 DummyElement instances and 2 EasyElement instances in elements fields.
So far if I leave ListContainer class annotated like this - I will have nothing in list. If I annotated with #XMLAnyElement(lax=true) then I will have ElementNSImpl instances.
Thank you for ideas in advance.
Update:
The solution is in control over creation of JAXBContext. I created service interface, which delivers me a list of classes required for context like:
public interface XMLContextProvider {
Set<Class> getJAXBContextClasses();
}
Then I created in framework a class which implements this interface and lists all classes I require from side of the framework. The same was done for extensions. The classes registered in META-INF/services - see ServiceLoader. Then I created a utility class, which utilises the ServiceLoader to find all providers and creates the JAXBContext with the list of all classes gathered from all providers. With this context it is possible to Marshal and Unmarshal. Additionally as I utilise JAX-RS I have created a Resolver for JAXBContext:
#Provider
public class XMLContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext ctx;
public XMLContextResolver() {
ctx = <here goes call to utility class>
}
#Override
public JAXBContext getContext(Class<?> type) {
if (this.classes.contains(type)) {
return this.ctx;
}
return null;
}
}

JAXB: Intercept during unmarshalling?

I've got a typical web service using JAX-RS and JAXB, and upon unmarshalling I would like to know which setters were explicitly called by JAXB. This effectively lets me know which elements were included in the document provided by the caller.
I know I can probably solve this with an XmlAdapter, but I have a lot of classes in a number of different packages, and I don't want to create adapters for each and every one of them. Nor do I want to put hooks into each and every setter. I would like a general solution if possible. Note that all of my classes are setup to use getters and setters; none of them use fields for the access type.
My service uses Jersey 2.4, Spring 3.2, and MOXy 2.5.1, so if there's anything that can be leveraged from any of those, that's all the better. Our original thought was we could dynamically create a factory class (akin to what #XmlType supports) that would return a proxy object that would intercept the setters. We thought we could make this happen using the MetadataSource concept in MOXy, but that does not seem to be possible.
Anyone have any ideas?
My service uses Jersey 2.4, Spring 3.2, and MOXy 2.5.1, so if there's
anything that can be leveraged from any of those, that's all the
better.
Create your own EclipseLink AttributeAccessor
MOXy (which is a component of EclipseLink) leverages a class called AttributeAccessor to do operations with fields and properties. You could wrap this class to capture all the information that you need.
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.mappings.AttributeAccessor;
public class MyAttributeAccessor extends AttributeAccessor {
private AttributeAccessor attributeAccessor;
public MyAttributeAccessor(AttributeAccessor attributeAccessor) {
this.attributeAccessor = attributeAccessor;
}
#Override
public Object getAttributeValueFromObject(Object domainObject)
throws DescriptorException {
return attributeAccessor.getAttributeValueFromObject(domainObject);
}
#Override
public void setAttributeValueInObject(Object domainObject, Object value)
throws DescriptorException {
System.out.println("Thread: " + Thread.currentThread().getId() + " - Set value: " + value + " on property: " + attributeAccessor.getAttributeName() + " for object: " + domainObject);
attributeAccessor.setAttributeValueInObject(domainObject, value);
}
}
Tell MOXy to use your AttributeAccessor
We can leverage a SessionEventListener to access the underlying metadata to specify your implementation of AttributeAccessor. This is passed in as a property when creating the JAXBContext.
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, new SessionEventAdapter() {
#Override
public void postLogin(SessionEvent event) {
Project project = event.getSession().getProject();
for(ClassDescriptor descriptor : project.getOrderedDescriptors()) {
for(DatabaseMapping mapping : descriptor.getMappings()) {
mapping.setAttributeAccessor(new MyAttributeAccessor(mapping.getAttributeAccessor()));
}
}
super.preLogin(event);
}
});
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class}, properties);
Leverage a JAX-RS ContextResolver when Creating the JAXBContext
Since you are in a JAX-RS environment you can leverage a ContextResolver to control how the JAXBContext is created.
http://blog.bdoughan.com/2011/04/moxys-xml-metadata-in-jax-rs-service.html
Standalone Example
Java Model (Foo)
Below is a sample class where we will use field access (no setters).
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
private String bar;
private String baz;
}
Demo
import java.io.StringReader;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.sessions.*;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, new SessionEventAdapter() {
#Override
public void postLogin(SessionEvent event) {
Project project = event.getSession().getProject();
for(ClassDescriptor descriptor : project.getOrderedDescriptors()) {
for(DatabaseMapping mapping : descriptor.getMappings()) {
mapping.setAttributeAccessor(new MyAttributeAccessor(mapping.getAttributeAccessor()));
}
}
super.preLogin(event);
}
});
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader xml = new StringReader("<foo><bar>Hello World</bar></foo>");
Foo foo = (Foo) unmarshaller.unmarshal(xml);
}
}
Output
Thread: 1 - Set value: Hello World on property: bar for object: forum21044956.Foo#37e47e38
UPDATE
So this works, but I have a few issues. First, the domainObject is
always logging as 0 in my system. Not sure why that's occurring.
I have not idea why that is occuring, may need to check the toString() for the object you are logging.
Second, I am not able to tell if the property in question is on the
top-level item that is being unmarshalled or on a sub-element. That's
actually quite annoying.
You will need to beef up the logic here. Based on the objects being set you should be able to do what you want.
Third, your solution is per JAXBContext, but I don't know if I really
want to create a new context for every request. Isn't that bad from an
overhead perspective?
You can cache the created JAXBContext to prevent rebuilding it.

Force JAXB to ignore getters/setters on 3rd-party class file

Background
I have a class in a JAR file called Attachment. The important parts of the definition are below.
public class Attachment
{
public List<Media> media;
public List<Media> getMedia()
{
return this.media;
}
public void setMedia(List<Media> media)
{
this.media = media;
}
}
I am attempting to use JAXB-impl 2.1.3 to deserialize this with the following code:
JAXBContext jaxbContext = JAXBContext.newInstance(Attachment.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(text);
Attachment attachment = (Attachment) unmarshaller.unmarshal(reader);
However, this gives me the following error (snipped for brevity)
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException:
1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "media"
this problem is related to the following location:
at public java.util.List com.thirdparty.Attachment.getMedia()
...
this problem is related to the following location:
at public java.util.List com.thirdparty.Attachment.media
...
I understand that the issue is that, by default, JAXB will use an access type of PUBLIC_MEMBER, which is defined as:
Every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.
Question
So, my question is, how can I tell JAXB to ignore the field and just use the getter/setter. Note that I would prefer it this way round, as there are a number of private fields that I would need to ignore (I believe that Attachment has been set public in error in fact).
Take a look at Annox.It looks to have what you need.
JAXB reference implementation can be configured with a special annotation reader which may implement a different strategy for reading annotations. Annox takes advantage of this feature and implements an annotation reader which can load JAXB annotations from XML.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
MOXy offers an external mapping document extension to support 3rd party classes.
Sample Mapping Document
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="blog.bindingfile">
<xml-schema
namespace="http://www.example.com/customer"
element-form-default="QUALIFIED"/>
<java-types>
<java-type name="Customer">
<xml-root-element/>
<xml-type prop-order="firstName lastName address phoneNumbers"/>
<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>
Bootstrapping from External Mapping Document
Below is an example of how you create the JAXBContext:
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "blog/bindingfile/binding.xml");
JAXBContext jc = JAXBContext.newInstance("blog.bindingfile", Customer.class.getClassLoader() , properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Customer customer = (Customer) unmarshaller.unmarshal(new File("src/blog/bindingfile/input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

Is it possible to programmatically configure JAXB?

Say I have two JavaBeans Person and Address.
If I create a list of Person objects, I'd like to marshal to something like this:
<persons>
<person>...</person>
</persons>
It's possible to use the technique described here:
Using JAXB to unmarshal/marshal a List<String>
By annotating JaxbList with #XmlRootElement(name = "persons") and #XmlElement(name = "person"), then it's possible to marshal to the XML above.
But, it'd be nice to be able to reuse the same JaxbList<T> class to also marshal a list of Address objects. And in reality, I will have many other types of beans. I can go with something like:
<list>
<item xsi:type="person" xmlns:xsi="http://www.w2.org/2001/XmlSchema-instance"></item>
</list>
But, ideally, it'd be nice to have it replace "list" with the plural version of class name and "item" with the class name.
So, is it possible to programmatically configure the JaxbContext or something during runtime and essentially set the value of the name inside #XmlRootElement and #XmlElement?
Or any other way to get this working without having to write a separate implementation of JaxbList for every bean type? Maybe XmlJavaTypeAdapter can achieve this sort of thing?
Update
#Blaise Doughan's solution accepted below works great. For my use case, I needed to go straight from Java object to XML, here's what worked (note this is not my full implementation, it's sort of just pseudo code for demonstration):
//JAXBContext is thread safe and so create it in constructor or
//setter or wherever:
...
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz);
...
public String marshal(List<T> things, Class clazz) {
//configure JAXB and marshaller
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//Create wrapper based on generic list of objects
Wrapper<T> wrapper = new Wrapper<T>(things);
JAXBElement<Wrapper> wrapperJAXBElement = new JAXBElement<Wrapper>(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper);
StringWriter result = new StringWriter();
//marshal!
m.marshal(wrapperJAXBElement, result);
return result.toString();
}
You could create a generic Wrapper object like the following:
Wrapper
You could create a generic wrapper class with a List property annotated with #XmlAnyElement(lax=true). The type of the object used to populate this list will be based on its root element (see: http://blog.bdoughan.com/2010/08/using-xmlanyelement-to-build-generic.html).
package forum13272288;
import java.util.*;
import javax.xml.bind.annotation.XmlAnyElement;
public class Wrapper<T> {
private List<T> items = new ArrayList<T>();
#XmlAnyElement(lax=true)
public List<T> getItems() {
return items;
}
}
Address
You will need to annotate the possible contents of the list with #XmlRootElement.
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Address {
}
Person
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Person {
}
Demo
The demo code below demonstrates how to use the Wrapper class. Since the root element can be different you will need to specify that you want to unmarshal to the wrapper class. Alternatively you could leverage the #XmlElementDecl annotation to associate multiple root elements with the wrapper class (see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html).
package forum13272288;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, Person.class, Address.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StreamSource personsXML = new StreamSource("src/forum13272288/persons.xml");
JAXBElement<Wrapper> wrapper1 = unmarshaller.unmarshal(personsXML, Wrapper.class);
marshaller.marshal(wrapper1, System.out);
StreamSource addressesXML = new StreamSource("src/forum13272288/addresses.xml");
JAXBElement<Wrapper> wrapper2 = unmarshaller.unmarshal(addressesXML, Wrapper.class);
marshaller.marshal(wrapper2, System.out);
}
}
Output
Below is the output from running the demo code. The files persons.xml and addresses.xml look just like there corresponding output.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persons>
<person/>
<person/>
</persons>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addresses>
<address/>
<address/>
</addresses>
For More Information
http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html

What is the ObjectFactory role during JAXB-Unmarshalling?

I'm using JAXB 2.2.2 to parse a simple XML-REST stream. This is the piece of code:
JAXBContext jc = JAXBContext.newInstance( "com.example.entities" );
Unmarshaller u = jc.createUnmarshaller();
r = (Response )u.unmarshal( inputStream );
ObjectFactory class:
#XmlRegistry
public class ObjectFactory {
public Response createRsp() {
return new Response();
}
}
Response class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="rsp")
#XmlType
public class Response { ... }
The "com.example.entities" must contain the ObjectFactory class or jaxb.index. I would like to use the ObjectFactory class in order to decide some pojo initialization, but these class is never used: the Response class is always instantiated by class.newInstance() directly.
Is there something wrong in this?
You can leverage the #XmlType annotation to control how the objects are created:
#XmlType(factoryClass=ObjectFactory.class, factoryMethod="createRsp")
public class Response {
}
For More Information
http://bdoughan.blogspot.com/2011/06/jaxb-and-factory-methods.html
The ObjectFactory class generated by the XJC compiler is not useful to the factoryClass and factoryMethod #XmlType annotation because the factoryMethod must be a static no-arg method and the XJC generate instance methods.

Categories