JAXB RI has a plug-in mechanism to allow 3rd parties to extend its code generation behavior. In 1.0, projects like HyperJAXB and JAXB-verification took advantage of this feature and they were very suc ]]>I recently finished improving this functionality further for 2.0, so today I'm going to explain how to write a plug-in.

What can a plug-in do?

  1. XJC performs a modeling of the classes by looking at schemas. A plug-in can access this model information to figure out what kind of classes/properties are going to be generated.
  2. XJC generates the Java AST from the model. A plug-in can access this AST, and change it by adding method, fields, classes, javadoc, and etc. To make this step easier, XJC gives you the association between AST nodes and the model. This association is called "outline."
  3. A plug-in can designate namespace URIs for customization. When it does so, XJC will remember all those customizations attached to schemas and make them available as a part of the model. To users it will work exactly like the JAXB customization or the XJC customization; they can be written in external binding files or inline, and XJC will provide extensive error check on those.

How to write a plug-in

A plugin is a class that extends com.sun.tools.xjc.Pluginclass. This class defines a bunch of methods that XJC uses to interact with your plug-in, and javadoc should explain how each method should work.

Once you write your Plugin class, you need to package them into a jar file. A jar file needs to have /META-INF/services/com.sun.tools.xjc.Plugin, which contains a list of class names that extends Plugin. This allows XJC to discover your plugin at the runtime. This is a standard procedure, so it shouldn't be a surprise. If you look inside jaxb-xjc.jar, you'll see that it comes with a few plugin definitions.

What users need to do to run your plugin

Today, to run a plug-in, you define an Ant task like this:


Then you run it like this:

  ... other nested elements ...  

You can also do it from command line, but it's not very pretty. XJC should allow the plugin jars to be specified through command line options, but there's a bit of chicken-and-egg problems and this hasn't been implemented yet.

Example: Code Injector Plugin

I'll use the code-injector plug-in in the JAXB RI workspace to explain how you can write one. This plug-in allows users to write Java code fragment, and it will be injected into the generated class as-is. Here's the Plugin class:

public class PluginImpl extends Plugin { public String getOptionName() { return "Xinject-code"; } public List getCustomizationURIs() { return Collections.singletonList(Const.NS); } public boolean isCustomizationTagName(String nsUri, String localName) { return nsUri.equals(Const.NS) && localName.equals("code"); } public String getUsage() { return " -Xinject-code : inject specified Java code fragments into the generated code"; } ... } 

It defines the option name "-Xinject-code" and usage screen. The user needs to specify this option to enable your plugin.

There are a couple of methods that tell XJC that this plugin uses a customization. It tells XJC that Const.NS (which is "http://jaxb.dev.java.net/plugin/code-injector") is a customization that belongs to this plugin.

When your plugin is enabled, it allows users to write a schema like this:

 ...    void foo() { } // some java code           

The "extensionBindingPrefixes" part is necessary because of the compatibility rules in the spec. It's a downside of JAXB being a JCP standard. The good news is that XJC knows that people tend to forget this, and it usually gives the user a very accurate/friendly error messages.

The same customization can be written externally. See my previous blog and other resources for more about the external binding files.

This customization can be accessed from the Plugin.runmethod like this:

public class PluginImpl extends Plugin { ... public boolean run(Outline model, Options opt, ErrorHandler errorHandler) { for( ClassOutline co : model.getClasses() ) { CPluginCustomization c = co.target.getCustomizations().find(Const.NS,"code"); if(c==null) continue; // no customization --- nothing to inject here c.markAsAcknowledged(); // inject the specified code fragment into the implementation class. co.implClass.direct(DOMUtils.getElementText(c.element)); } return true; } }

The "markAsAcknowledged" method call allows XJC to flag an error on the customizations that weren't used by XJC.

More Resources

If you are intereted in more details, you should first get a CVS access to the JAXB RI 2.0 from here. You'll find plugin implementations in the jaxb-ri/xjc/src/com/sun/tools/xjc/addondirectory.

If you get any questions/suggestions about the internals of XJC, please join the users list

And finally, if you actually write a plugin, please let me know so that we can link to your project.