Jump to: navigation, search

EclipseLink/Development/2.1/AnnoxSupportForJAXB/311570

Overview

JAXB makes use of Annotations on Java Classes to customize it's mapping of those classes to XML. However in some cases, when dealing with existing Java classes, it's preferable to be able to specify that annotation data externally. EclipseLink already supports this through it's externalized meta-data: http://wiki.eclipse.org/EclipseLink/DesignDocs/293925/MOXyExtensions

Annox (http://confluence.highsource.org/display/ANX/Home) is an open source project that allows a user to specify java annotation meta-data in an external document. In the case that a user wanted to be able to make use of Annox to specify their Annotations, EclipseLink's JAXB can be quickly extended to support this.

Annox makes use of one .xml file/class to specify the annotation data. The file needs to be in the same package as the class and uses the .ann.xml extension. For example, to add annotations to an Employee class, a file called Employee.ann.xml would be created and added into Employees package.
Sample Employee.ann.xml:

<class xmlns="http://annox.dev.java.net"
	xmlns:g="http://annox.dev.java.net/javax.xml.bind.annotation">
	<g:XmlRootElement name="employee-root" namespace="myns"/>
	<field name="id">
		<g:XmlAttribute/>
	</field>
	<field name="name">
		<g:XmlElement name="employee-name"/>
	</field>
	<field name="transientThing">
		<g:XmlTransient/>
	</field>
</class>


AnnotationHelper

By introducing the AnnotationHelper, EclipseLink can make an easy extension point available that would allow for a user to get their annotation data in any arbitrary way, including Annox, at runtime. AnnotationHelper is a class in the org.eclipse.persistence.javamodel.reflection package.

package org.eclipse.persistence.jaxb.javamodel.reflection;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
 
/**
 * <p><b>Purpose:</b>Provide a class which is responsible for returning Annotations
 * from AnnotatedElements. This class can be extended in the case that the annotation 
 * data is being provided from an external source.
 * 
 * @author mmacivor
 *
 */
public class AnnotationHelper {
 
	/**
	 * Get an annotation of type annotationClass if it's present on the AnnotatedElement
	 * elem.
	 */
	public Annotation getAnnotation(AnnotatedElement elem, Class annotationClass) {
		return elem.getAnnotation(annotationClass);
	}
 
	/**
	 * Get all annotations that exist on the AnnotatedElement elem
	 */
	public Annotation[] getAnnotations(AnnotatedElement elem) {
		return elem.getAnnotations();
	}
 
	/**
	 * Return true if the annotation annotationClass exists on the annotatedElement elem.
	 */
	public boolean isAnnotationPresent(AnnotatedElement elem, Class annotationClass) {
		return elem.isAnnotationPresent(annotationClass);
	}
}

All calls to obtain Annotations by EclipseLink during JAXBContext creation will go through the AnnotationHelper. A user can specify that they want to use a custom AnnotationHelper by setting an instance of AnnotationHelper as a property passed into the JAXBContext creation method:

MyAnnotationHelper helper = new MyAnnotationHelper();
 
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextFactory.ANNOTATION_HELPER_KEY, helper);
 
JAXBContext ctx = JAXBContextFactory.createContext(new Class[] {Employee.class}, properties, Thread.currentThread().getContextClassLoader());


Using Annox

If a user wanted to make use of Annox to obtain their annotation data, they could create a custom AnnotationHelper that makes use of the Annox APIs.

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
 
import org.eclipse.persistence.jaxb.javamodel.reflection.AnnotationHelper;
import org.jvnet.annox.reflect.AnnotatedElementFactory;
 
public class AnnoxAnnotationHelper extends AnnotationHelper {
	AnnotatedElementFactory factory;
 
	public AnnoxAnnotationHelper(AnnotatedElementFactory factory) {
		this.factory = factory;
	}
 
	@Override
	public boolean isAnnotationPresent(AnnotatedElement elem, Class annotationClass) {
		try {
			return factory.getAnnotatedElement(elem).isAnnotationPresent(annotationClass);
		} catch(Exception ex) {
			throw new RuntimeException(ex);
		}
	}
 
	@Override
	public Annotation getAnnotation(AnnotatedElement elem, Class annotationClass) {
		try {
			return factory.getAnnotatedElement(elem).getAnnotation(annotationClass);
		} catch(Exception ex) {
			throw new RuntimeException(ex);
		}
	}
 
	@Override
	public Annotation[] getAnnotations(AnnotatedElement elem) {
		try {
			return factory.getAnnotatedElement(elem).getAnnotations();
		} catch(Exception ex) {
			throw new RuntimeException(ex);
		}
	}
 
}
Then by specifying an instance of this AnnotationHelper via the property as shown above, the Annotations would now be obtained from Annox and contain the information specified in the external XML files