CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-glassfish-jaxb--jaxb-xjc

JAXB Binding Compiler (XJC) that generates Java classes from XML Schema definitions with both command-line and programmatic APIs

Pending
Overview
Eval results
Files

plugin-system.mddocs/

Plugin System

The JAXB XJC plugin system provides an extensible architecture for customizing code generation, adding annotations, and implementing domain-specific features through a well-defined plugin interface.

Capabilities

Base Plugin Class

Abstract base class that all XJC plugins must extend to participate in the code generation process.

/**
 * Abstract base class for XJC plugins that customize code generation
 */
public abstract class Plugin {
    /**
     * Get the command-line option name for this plugin
     * @return Option name without leading dash (e.g., "fluent-api")
     */
    public abstract String getOptionName();
    
    /**
     * Get usage description for help output
     * @return Multi-line usage description
     */
    public abstract String getUsage();
    
    /**
     * Main plugin execution method called during code generation
     * @param outline Generated code outline containing all classes and fields
     * @param opt Compilation options and configuration
     * @param errorHandler Error handler for reporting issues
     * @return true if plugin execution was successful
     * @throws SAXException if plugin execution fails
     */
    public abstract boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException;
    
    /**
     * Parse plugin-specific command line arguments
     * @param opt Options object for storing parsed values
     * @param args Full command line arguments array
     * @param i Current position in args array
     * @return Number of arguments consumed (0 if argument not recognized)
     * @throws BadCommandLineException if argument is invalid
     * @throws IOException if I/O error occurs during parsing
     */
    public int parseArgument(Options opt, String[] args, int i) throws BadCommandLineException, IOException;
    
    /**
     * Get list of XML namespace URIs that this plugin handles in binding customizations
     * @return List of namespace URIs, empty list if none
     */
    public List<String> getCustomizationURIs();
    
    /**
     * Called when plugin is activated via command line
     * @param opts Options object for configuration
     * @throws BadCommandLineException if plugin activation fails
     */
    public void onActivated(Options opts) throws BadCommandLineException;
    
    /**
     * Post-process the schema model before code generation
     * @param model Schema model containing parsed information
     * @param errorHandler Error handler for reporting issues
     */
    public void postProcessModel(Model model, ErrorHandler errorHandler);
}

Plugin Development Example:

import com.sun.tools.xjc.Plugin;
import com.sun.tools.xjc.outline.Outline;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.Options;
import com.sun.codemodel.*;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;

public class ToStringPlugin extends Plugin {
    
    @Override
    public String getOptionName() {
        return "Xtostring";
    }
    
    @Override
    public String getUsage() {
        return "  -Xtostring          : Generate toString() methods for all classes";
    }
    
    @Override
    public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException {
        // Iterate through all generated classes
        for (ClassOutline classOutline : outline.getClasses()) {
            generateToString(classOutline);
        }
        return true;
    }
    
    private void generateToString(ClassOutline classOutline) {
        JDefinedClass implClass = classOutline.ref;
        
        // Generate toString method
        JMethod toString = implClass.method(JMod.PUBLIC, String.class, "toString");
        toString.annotate(Override.class);
        
        JBlock body = toString.body();
        JVar sb = body.decl(outline.getCodeModel().ref(StringBuilder.class), "sb", 
                           JExpr._new(outline.getCodeModel().ref(StringBuilder.class)));
        
        // Add class name
        sb.invoke("append").arg(implClass.name() + "{");
        
        // Add field values
        boolean first = true;
        for (FieldOutline field : classOutline.getDeclaredFields()) {
            if (!first) {
                sb.invoke("append").arg(", ");
            }
            sb.invoke("append").arg(field.getPropertyInfo().getName(false) + "=");
            sb.invoke("append").arg(JExpr.refthis(field.getPropertyInfo().getName(false)));
            first = false;
        }
        
        sb.invoke("append").arg("}");
        body._return(sb.invoke("toString"));
    }
}

Built-in Plugins

XJC includes several built-in plugins that demonstrate common customization patterns.

/**
 * Built-in plugins provided by XJC
 */

// Accessor plugin for generating additional getter/setter methods
public class AccessorsPlugin extends Plugin {
    public String getOptionName() { return "Xaccessors"; }
    // Generates additional accessor methods
}

// @Generated annotation plugin
public class GeneratedPlugin extends Plugin {
    public String getOptionName() { return "Xgenerated"; }
    // Adds @Generated annotations to all generated classes
}

// Code injection plugin for custom code insertion
public class CodeInjectorPlugin extends Plugin {
    public String getOptionName() { return "Xinject-code"; }
    // Injects custom code from external sources
}

// Episode file generation plugin
public class EpisodePlugin extends Plugin {
    public String getOptionName() { return "Xepisode"; }
    // Generates episode files for modular compilation
}

// Source location tracking plugin
public class SourceLocationPlugin extends Plugin {
    public String getOptionName() { return "Xlocator"; }
    // Adds source location tracking to generated classes
}

// Synchronized method generation plugin
public class SynchronizedPlugin extends Plugin {
    public String getOptionName() { return "Xsync-methods"; }
    // Makes generated methods synchronized
}

Plugin Registration and Discovery

Plugins are discovered and loaded using Java's ServiceLoader mechanism.

Plugin Registration (META-INF/services/com.sun.tools.xjc.Plugin):

com.example.plugins.ToStringPlugin
com.example.plugins.BuilderPlugin
com.example.plugins.ValidationPlugin

Module Declaration:

module my.xjc.plugins {
    requires transitive org.glassfish.jaxb.xjc;
    
    provides com.sun.tools.xjc.Plugin with
        com.example.plugins.ToStringPlugin,
        com.example.plugins.BuilderPlugin,
        com.example.plugins.ValidationPlugin;
}

Plugin Configuration and Arguments

Plugins can accept command-line arguments for configuration.

/**
 * Example plugin with configuration options
 */
public class ConfigurablePlugin extends Plugin {
    private String prefix = "generated";
    private boolean includeFields = true;
    private Set<String> excludedClasses = new HashSet<>();
    
    @Override
    public int parseArgument(Options opt, String[] args, int i) throws BadCommandLineException {
        String arg = args[i];
        
        if (arg.equals("-Xplugin-prefix")) {
            if (i + 1 < args.length) {
                prefix = args[i + 1];
                return 2; // Consumed current argument and next
            } else {
                throw new BadCommandLineException("Missing value for -Xplugin-prefix");
            }
        }
        
        if (arg.equals("-Xplugin-no-fields")) {
            includeFields = false;
            return 1; // Consumed current argument
        }
        
        if (arg.equals("-Xplugin-exclude")) {
            if (i + 1 < args.length) {
                excludedClasses.add(args[i + 1]);
                return 2;
            } else {
                throw new BadCommandLineException("Missing class name for -Xplugin-exclude");
            }
        }
        
        return 0; // Argument not recognized
    }
    
    @Override
    public void onActivated(Options opts) throws BadCommandLineException {
        // Validate configuration after all arguments are parsed
        if (prefix.isEmpty()) {
            throw new BadCommandLineException("Plugin prefix cannot be empty");
        }
    }
}

Advanced Plugin Patterns

Model Post-Processing:

@Override
public void postProcessModel(Model model, ErrorHandler errorHandler) {
    // Modify model before code generation
    for (CClassInfo classInfo : model.beans().values()) {
        // Add custom properties or modify existing ones
        if (classInfo.getTypeName().getLocalPart().endsWith("Type")) {
            // Rename classes ending with "Type"
            String newName = classInfo.getTypeName().getLocalPart().replace("Type", "");
            // Implementation would modify the class info
        }
    }
}

Customization Processing:

@Override
public List<String> getCustomizationURIs() {
    return Arrays.asList(
        "http://example.com/xjc/builder",
        "http://example.com/xjc/validation"
    );
}

@Override
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException {
    for (ClassOutline classOutline : outline.getClasses()) {
        // Look for binding customizations
        CPluginCustomization customization = classOutline.target.getCustomizations()
            .find("http://example.com/xjc/builder", "builder");
        
        if (customization != null) {
            // Process the customization
            generateBuilderPattern(classOutline, customization);
            customization.markAsAcknowledged();
        }
    }
    return true;
}

Field-Level Customization:

@Override
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException {
    for (ClassOutline classOutline : outline.getClasses()) {
        for (FieldOutline fieldOutline : classOutline.getDeclaredFields()) {
            CPropertyInfo property = fieldOutline.getPropertyInfo();
            
            // Check for field-level customizations
            if (property.getName(false).startsWith("id")) {
                // Generate special handling for ID fields
                generateIdFieldMethods(classOutline, fieldOutline);
            }
            
            // Modify field annotations
            JFieldVar field = getFieldVar(fieldOutline);
            if (field != null) {
                field.annotate(MyCustomAnnotation.class);
            }
        }
    }
    return true;
}

Plugin Usage Examples

Command Line Usage:

# Use built-in plugin
xjc -Xgenerated schema.xsd

# Use custom plugin with options
xjc -Xtostring schema.xsd

# Multiple plugins
xjc -Xgenerated -Xtostring -Xlocator schema.xsd

# Plugin with configuration
xjc -Xbuilder -Xbuilder-prefix=with schema.xsd

Programmatic Usage:

import com.sun.tools.xjc.api.*;
import com.sun.tools.xjc.Plugin;

// Load and configure plugins
SchemaCompiler compiler = XJC.createSchemaCompiler();
compiler.parseSchema(schemaSource);

S2JJAXBModel model = compiler.bind();

// Apply plugins during code generation
Plugin[] plugins = {
    new ToStringPlugin(),
    new BuilderPlugin()
};

JCodeModel codeModel = model.generateCode(plugins, errorListener);

Plugin Development Best Practices

  1. Error Handling: Always use the provided ErrorHandler for reporting issues
  2. Resource Management: Clean up any resources in plugin lifecycle methods
  3. Thread Safety: Ensure plugins are thread-safe for concurrent usage
  4. Validation: Validate configuration in onActivated() method
  5. Documentation: Provide clear usage description and examples
  6. Customization: Support binding customizations for fine-grained control
  7. Testing: Test with various schema patterns and edge cases

Common Plugin Use Cases

  • Code Quality: Add toString(), equals(), hashCode() methods
  • Design Patterns: Generate Builder, Factory, or Visitor patterns
  • Validation: Add Bean Validation annotations
  • Documentation: Generate JavaDoc comments from schema annotations
  • Serialization: Add custom serialization support
  • Framework Integration: Add Spring, JPA, or other framework annotations
  • Code Style: Enforce naming conventions or coding standards

Install with Tessl CLI

npx tessl i tessl/maven-org-glassfish-jaxb--jaxb-xjc

docs

build-integration.md

cli.md

code-generation.md

error-handling.md

index.md

plugin-system.md

programmatic-api.md

tile.json