Skip to content
Laurent Schoelens edited this page Aug 28, 2023 · 2 revisions

Annotate Plugin

Annotate plugins is capable of adding arbitrary annotations to the generated sources.

Annotate plugin uses JAXB Annox to read annotations from binding customizations and adds them to the schema-derived classes. Here's a small example:

<xsd:schema
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  jaxb:version="2.1" 
  xmlns:annox="http://annox.dev.java.net" 
  jaxb:extensionBindingPrefixes="annox"
  xmlns:jl="http://annox.dev.java.net/java.lang">

  <xsd:element name="foo" type="FooType"/>
  <xsd:complexType name="FooType">
    <xsd:annotation>
      <xsd:appinfo>
        <annox:annotate>
          <jl:SuppressWarnings/>
        </annox:annotate>
      </xsd:appinfo>
    </xsd:annotation>
    <xsd:sequence>
      <xsd:element name="bar" type="xsd:string"/>
      <xsd:element name="foobar" type="xsd:string">
        <xsd:annotation>
          <xsd:appinfo>
            <annox:annotate>
              <jl:SuppressWarnings/>
            </annox:annotate>
            <annox:annotate target="setter">
              <jl:Deprecated/>
            </annox:annotate>
            <annox:annotate target="setter-parameter">
              <jl:Deprecated/>
            </annox:annotate>
            <annox:annotate target="getter">
              <jl:Deprecated/>
            </annox:annotate>
            <annox:annotate target="field">
              <jl:Deprecated/>
            </annox:annotate>
            <annox:annotate target="class">
              <jl:Deprecated/>
            </annox:annotate>
          </xsd:appinfo>
        </xsd:annotation>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

Check Annox user guide for information on how to define Java annotations in XML form.

Below is the generated code:

package generated;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "FooType", propOrder = {
    "bar",
    "foobar"
})
@SuppressWarnings({

})
@Deprecated
public class FooType {

    @XmlElement(required = true)
    protected String bar;
    @XmlElement(required = true)
    @Deprecated
    protected String foobar;

    public String getBar() {
        return bar;
    }

    public void setBar(String value) {
        this.bar = value;
    }

    @Deprecated
    public String getFoobar() {
        return foobar;
    }

    @SuppressWarnings({

    })
    @Deprecated
    public void setFoobar(
        @Deprecated
        String value) {
        this.foobar = value;
    }
}

Please see this test project for example.

Activation

The plugin is activated by the -Xannotate command-line argument.

Usage

The purpose of the Annotate Plugin is to allow adding arbitrary annotations to the classes generated by the schema compiler (XJC). When schema is compiled, the Annotate Plugin reads annotation definitions from schema bindings and adds appropriate code to the generated classes.

In order to use this plugin to add your own annotations to the generated classes you have to do two things :

  • Activate the plugin.
  • Add definitions of annotations you want to add to schema bindings.

First part is trivial. Second part - defining your annotations in schema bindings can be a bit more tricky. It is explained in the next sections.

Defining annotations

Schema bindings are essentially XML documents which provide XJC with additional information which may be used to customize schema compilation. There is a number of standard JAXB customization elements (like jaxb:class or jaxb:property), but XJC also allows vendor customizations. Annotate Plugin employs this possibility and uses customization elements as a source for definitions of annotations. Annotations are defined XML elements; Annotate plugin uses Annox to read annotations from XML definitions.

Check Annox user guide for detailed information on how to define Java annotations in XML form.

There are mainly two ways to add bindings to your schema: directly in schema files or in external binding files. You can add annotation definitions in both cases, but due to certain technical reasons there are slight differences between these two variants. I'll demonstrate it on an example of org.hibernate.search.annotations.FieldBridge from Hibernate Search.

Defining annotations directly in schemas

<xsd:schema
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  jaxb:version="2.1"

  xmlns:annox="http://annox.dev.java.net" 
  xmlns:hs="http://annox.dev.java.net/org.hibernate.search.annotations"

  jaxb:extensionBindingPrefixes="annox">

  ...
  <xsd:complexType ...>
    <xsd:sequence>
      ....
      <xsd:element name="name" type="xsd:string">
        <xsd:annotation>
          <xsd:appinfo>
            <annox:annotate>
              <hs:FieldBridge impl="com.acme.foo.MyFieldBridge">
                <params>
                  <hs:Parameter name="foo" value="bar"/>
                </params>
              </hs:FieldBridge>
            </annox:annotate>
          </xsd:appinfo>
        </xsd:annotation>
      </xsd:element>
      ....
    </xsd:sequence>
      ....
  </xsd:complexType>
  ...
</xsd:schema>

Produces:

@FieldBridge(impl = com.acme.foo.MyFieldBridge.class, params = {
  @Parameter(name = "foo", value = "bar")
})

Note the annox:annotate element within schema - it is the customization element that the Annotate Plugin processes. The hs:FieldBridge sub-element is the XML: definition of the @FieldBridge annotation.

The key to the elegant definition in the example above is the http://annox.dev.java.net/org.hibernate.search.annotations associated with the hs prefix. Namespace URI points to the org.hibernate.search.annotations package - this is how the Annotate Plugin (i.e. the underlying Annox parser) knows that hs:FieldBridge is actually @org.hibernate.search.annotations.FieldBridge Java class.

Defining annotations in external binding files

Unfortunatelly the elegant syntax above does not work when defining customization elements in external binding files. XJC is for some reason too strict here. It considers the package namespace URI (like http://annox.dev.java.net/org.hibernate.search.annotations above) to be a vendor extension URI and consequently fails. I believe this to be a bug in XJC.

Accordignly, we can't use package namespaces in external binding files. All the binding definitions must be declared in a single namespace. Here's how binding.xjb binding file looks like:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings
  version="2.1"
  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"

  xmlns:annox="http://annox.dev.java.net" 

  jaxb:extensionBindingPrefixes="annox">

  <jaxb:bindings schemaLocation="schema.xsd" node="/xs:schema">
    ...
    <jaxb:bindings node="xs:complexType[@name='...']/xs:sequence/xs:element[@name='name']">
      <annox:annotate>
        <annox:annotate
          annox:class="org.hibernate.search.annotations.FieldBridge"
          impl="com.acme.foo.MyFieldBridge">
          <annox:annotate annox:field="params">
            <annox:annotate annox:class="org.hibernate.search.annotations.Parameter"
              name="foo"
              value="bar"/>
          </annox:annotate>
        </annox:annotate>
      </annox:annotate>
    </jaxb:bindings>
    ...
  </jaxb:bindings>

</jaxb:bindings>

In this case we had to use annox:annotate elements everywhere. The annox:class attribute is used to provide the class name of the annotation. In case of nested annotation definitions annox:field indicates the field of the annotation.

You can use annox:annotateClass, annox:annotateProperty, annox:annotateEnum, annox:annotateEnumConstant, annox:annotateElement, annox:annotate/@target="setter-parameter" if you want your customization to be a bit more specific.

If an annotation of the given class already exists, this annotation will be augmented. Thus, the following customizations:

<annox:annotate target="class">
  <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="someElement"/>
</annox:annotate>
<annox:annotate target="class">
  <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" namespace="someNamespace"/>
</annox:annotate>

Would result in:

@XmlRootElement(name = "someElement", namespace = "someNamespace")
Clone this wiki locally