JAXB Binding Compiler (XJC) that generates Java classes from XML Schema definitions with both command-line and programmatic APIs
—
JAXB XJC provides comprehensive integration capabilities for build systems including Ant tasks, Maven plugins, and Gradle integration through both dedicated tasks and programmatic APIs.
XJC provides dedicated Ant tasks for integrating schema compilation into Ant-based build processes.
/**
* Modern XJC Ant task with full feature support
*/
public class XJC2Task extends Task {
/**
* Set schema file or directory to compile
* @param schema File or directory containing schemas
*/
public void setSchema(File schema);
/**
* Set target directory for generated sources
* @param destdir Target directory
*/
public void setDestdir(File destdir);
/**
* Set target package name for generated classes
* @param packageName Package name
*/
public void setPackage(String packageName);
/**
* Set binding customization file
* @param binding Binding file
*/
public void setBinding(File binding);
/**
* Set catalog file for entity resolution
* @param catalog Catalog file
*/
public void setCatalog(File catalog);
/**
* Enable or disable package level annotations
* @param packageLevelAnnotations true to enable
*/
public void setPackageLevelAnnotations(boolean packageLevelAnnotations);
/**
* Set character encoding for generated files
* @param encoding Character encoding
*/
public void setEncoding(String encoding);
/**
* Enable read-only mode
* @param readOnly true for read-only
*/
public void setReadOnly(boolean readOnly);
/**
* Set header comment for generated files
* @param header Header comment
*/
public void setHeader(String header);
/**
* Enable extension mode
* @param extension true to enable extensions
*/
public void setExtension(boolean extension);
/**
* Execute the task
* @throws BuildException if compilation fails
*/
public void execute() throws BuildException;
}
/**
* Legacy XJC Ant task for compatibility
*/
public class XJCTask extends Task {
// Similar interface to XJC2Task but with legacy behavior
public void setSchema(String schema);
public void setDestdir(File destdir);
public void setPackage(String packageName);
public void execute() throws BuildException;
}
/**
* Base class for XJC Ant tasks
*/
public class XJCBase {
/**
* Common configuration and execution logic for XJC Ant tasks
*/
protected void configureOptions(Options options);
protected void executeXJC(Options options) throws BuildException;
}Ant Task Usage Examples:
<!-- Basic XJC task -->
<taskdef name="xjc" classname="com.sun.tools.xjc.XJC2Task">
<classpath>
<fileset dir="lib" includes="jaxb-xjc-*.jar"/>
</classpath>
</taskdef>
<!-- Simple schema compilation -->
<xjc schema="src/main/resources/schema.xsd"
destdir="target/generated-sources"
package="com.example.generated"/>
<!-- Advanced configuration -->
<xjc destdir="target/generated-sources" package="com.example.api">
<schema dir="src/main/resources/schemas" includes="**/*.xsd"/>
<binding dir="src/main/resources/bindings" includes="**/*.xjb"/>
<classpath>
<fileset dir="lib" includes="*.jar"/>
</classpath>
<arg value="-Xfluent-api"/>
<arg value="-verbose"/>
</xjc>
<!-- Multiple schemas with dependencies -->
<xjc destdir="target/generated-sources"
package="com.example.common"
extension="true">
<schema file="common.xsd"/>
<produces dir="target/generated-sources/com/example/common" includes="**/*.java"/>
<depends file="common.xsd"/>
</xjc>
<xjc destdir="target/generated-sources"
package="com.example.specific"
extension="true">
<schema file="specific.xsd"/>
<classpath>
<pathelement path="target/generated-sources"/>
</classpath>
<arg value="-episode"/>
<arg value="target/common.episode"/>
</xjc>Integration patterns for Maven-based builds using the JAXB Maven plugin.
Maven Plugin Configuration Examples:
<!-- Basic Maven configuration -->
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.14.0</version>
<configuration>
<schemaDirectory>src/main/resources/schemas</schemaDirectory>
<generatePackage>com.example.generated</generatePackage>
<generateDirectory>target/generated-sources/xjc</generateDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-xjc</artifactId>
<version>4.0.5</version>
</dependency>
</dependencies>
</plugin>
<!-- Advanced Maven configuration with plugins -->
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.14.0</version>
<configuration>
<schemaDirectory>src/main/resources/schemas</schemaDirectory>
<bindingDirectory>src/main/resources/bindings</bindingDirectory>
<generatePackage>com.example.api</generatePackage>
<generateDirectory>target/generated-sources/xjc</generateDirectory>
<extension>true</extension>
<args>
<arg>-Xfluent-api</arg>
<arg>-Xtostring</arg>
<arg>-XautoNameResolution</arg>
</args>
<plugins>
<plugin>
<groupId>com.example</groupId>
<artifactId>xjc-fluent-plugin</artifactId>
<version>1.0.0</version>
</plugin>
</plugins>
</configuration>
</plugin>Gradle integration using the JAXB plugin and custom task definitions.
Gradle Plugin Usage:
// Apply JAXB plugin
plugins {
id 'java'
id 'org.unbroken-dome.xjc' version '2.0.0'
}
// Configure XJC
xjc {
srcDirName = 'src/main/schemas'
bindingFiles = fileTree(dir: 'src/main/bindings', include: '**/*.xjb')
targetPackage = 'com.example.generated'
outputDir = file('build/generated-sources/xjc')
}
// Dependencies
dependencies {
implementation 'org.glassfish.jaxb:jaxb-runtime:4.0.5'
xjc 'org.glassfish.jaxb:jaxb-xjc:4.0.5'
}
// Custom XJC task
task generateFromCustomSchema(type: org.unbroken_dome.gradle.plugins.xjc.XjcGenerate) {
source = fileTree(dir: 'custom-schemas', include: '**/*.xsd')
bindingFiles = fileTree(dir: 'custom-bindings', include: '**/*.xjb')
targetPackage = 'com.example.custom'
outputDirectory = file('build/generated-sources/custom')
options {
extension = true
header = false
verbose = true
}
}Custom build integration using the programmatic API for specialized build requirements.
/**
* Custom build integration utility
*/
public class BuildIntegration {
/**
* Execute XJC compilation with build-specific configuration
* @param config Build configuration
* @return true if compilation successful
*/
public static boolean executeXJC(BuildConfiguration config);
/**
* Generate code with custom post-processing
* @param schemas Schema files to compile
* @param outputDir Output directory
* @param packageName Target package
* @param postProcessor Custom post-processor
* @return Generated code model
*/
public static JCodeModel generateWithPostProcessing(
List<File> schemas,
File outputDir,
String packageName,
CodePostProcessor postProcessor);
}
/**
* Configuration for build integration
*/
public class BuildConfiguration {
private List<File> schemaFiles;
private List<File> bindingFiles;
private File outputDirectory;
private String targetPackage;
private List<String> plugins;
private Map<String, String> properties;
private ErrorListener errorListener;
// Configuration methods
public void addSchemaFile(File schema);
public void addBindingFile(File binding);
public void setOutputDirectory(File dir);
public void setTargetPackage(String packageName);
public void addPlugin(String pluginName);
public void setProperty(String key, String value);
public void setErrorListener(ErrorListener listener);
}Custom Build Integration Example:
import com.sun.tools.xjc.api.*;
import com.sun.codemodel.*;
import java.io.File;
import java.util.List;
import java.util.ArrayList;
public class CustomBuildIntegration {
public static class BuildResult {
private final boolean success;
private final List<String> generatedFiles;
private final List<String> errors;
private final List<String> warnings;
public BuildResult(boolean success, List<String> generatedFiles,
List<String> errors, List<String> warnings) {
this.success = success;
this.generatedFiles = new ArrayList<>(generatedFiles);
this.errors = new ArrayList<>(errors);
this.warnings = new ArrayList<>(warnings);
}
public boolean isSuccess() { return success; }
public List<String> getGeneratedFiles() { return generatedFiles; }
public List<String> getErrors() { return errors; }
public List<String> getWarnings() { return warnings; }
}
public static BuildResult compileSchemas(
List<File> schemaFiles,
File outputDir,
String packageName,
List<String> pluginArgs) {
List<String> generatedFiles = new ArrayList<>();
List<String> errors = new ArrayList<>();
List<String> warnings = new ArrayList<>();
try {
// Create compiler
SchemaCompiler compiler = XJC.createSchemaCompiler();
// Configure error handling
compiler.setErrorListener(new ErrorListener() {
@Override
public void error(SAXParseException exception) {
errors.add(formatError("ERROR", exception));
}
@Override
public void warning(SAXParseException exception) {
warnings.add(formatError("WARNING", exception));
}
@Override
public void info(SAXParseException exception) {
// Log info messages
}
});
// Configure compiler
compiler.setDefaultPackageName(packageName);
// Parse schemas
for (File schemaFile : schemaFiles) {
InputSource source = new InputSource(new FileInputStream(schemaFile));
source.setSystemId(schemaFile.toURI().toString());
compiler.parseSchema(source);
}
// Compile
S2JJAXBModel model = compiler.bind();
if (model == null) {
errors.add("Schema compilation failed");
return new BuildResult(false, generatedFiles, errors, warnings);
}
// Generate code
JCodeModel codeModel = model.generateCode(null, null);
// Custom code writer that tracks generated files
FileCodeWriter writer = new FileCodeWriter(outputDir) {
@Override
public Writer openSource(JPackage pkg, String fileName) throws IOException {
generatedFiles.add(pkg.name().replace('.', '/') + "/" + fileName);
return super.openSource(pkg, fileName);
}
};
codeModel.build(writer);
return new BuildResult(true, generatedFiles, errors, warnings);
} catch (Exception e) {
errors.add("Build failed: " + e.getMessage());
return new BuildResult(false, generatedFiles, errors, warnings);
}
}
private static String formatError(String level, SAXParseException e) {
return String.format("%s [%s:%d:%d] %s",
level,
e.getSystemId() != null ? e.getSystemId() : "unknown",
e.getLineNumber(),
e.getColumnNumber(),
e.getMessage());
}
}Support for complex multi-module builds with shared schemas and episode files.
Episode-Based Multi-Module Build:
import com.sun.tools.xjc.Driver;
import java.io.File;
import java.util.Arrays;
public class MultiModuleBuildSupport {
public static void buildCommonModule(File schemaDir, File outputDir, File episodeFile)
throws Exception {
String[] args = {
"-d", outputDir.getAbsolutePath(),
"-p", "com.example.common",
"-episode", episodeFile.getAbsolutePath(),
new File(schemaDir, "common.xsd").getAbsolutePath()
};
int result = Driver.run(args, System.out, System.err);
if (result != 0) {
throw new Exception("Common module compilation failed");
}
}
public static void buildSpecificModule(File schemaDir, File outputDir,
File episodeFile, File commonClasspath)
throws Exception {
String[] args = {
"-d", outputDir.getAbsolutePath(),
"-p", "com.example.specific",
"-classpath", commonClasspath.getAbsolutePath(),
episodeFile.getAbsolutePath(),
new File(schemaDir, "specific.xsd").getAbsolutePath()
};
int result = Driver.run(args, System.out, System.err);
if (result != 0) {
throw new Exception("Specific module compilation failed");
}
}
}Support for incremental builds to optimize build performance.
import java.io.File;
import java.nio.file.Files;
import java.nio.file.attribute.FileTime;
import java.util.List;
import java.util.stream.Collectors;
public class IncrementalBuildSupport {
public static boolean needsRecompilation(List<File> schemaFiles,
List<File> bindingFiles,
File outputDir) {
// Check if output directory exists
if (!outputDir.exists() || !outputDir.isDirectory()) {
return true;
}
// Find newest input file
FileTime newestInput = schemaFiles.stream()
.flatMap(f -> Stream.concat(
Stream.of(f),
bindingFiles.stream()
))
.map(f -> {
try {
return Files.getLastModifiedTime(f.toPath());
} catch (IOException e) {
return FileTime.fromMillis(0);
}
})
.max(FileTime::compareTo)
.orElse(FileTime.fromMillis(0));
// Find oldest output file
try {
FileTime oldestOutput = Files.walk(outputDir.toPath())
.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.map(p -> {
try {
return Files.getLastModifiedTime(p);
} catch (IOException e) {
return FileTime.fromMillis(Long.MAX_VALUE);
}
})
.min(FileTime::compareTo)
.orElse(FileTime.fromMillis(Long.MAX_VALUE));
return newestInput.compareTo(oldestOutput) > 0;
} catch (IOException e) {
return true; // Error reading output, force recompilation
}
}
public static void cleanOutputDirectory(File outputDir, String packageName) {
File packageDir = new File(outputDir, packageName.replace('.', '/'));
if (packageDir.exists()) {
try {
Files.walk(packageDir.toPath())
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (IOException e) {
System.err.println("Warning: Could not clean output directory: " + e.getMessage());
}
}
}
}Optimization techniques for large-scale builds.
public class BuildOptimization {
/**
* Parallel schema compilation for independent schema groups
*/
public static void compileInParallel(List<SchemaGroup> schemaGroups,
int maxThreads) {
ExecutorService executor = Executors.newFixedThreadPool(maxThreads);
List<Future<BuildResult>> futures = new ArrayList<>();
for (SchemaGroup group : schemaGroups) {
Future<BuildResult> future = executor.submit(() -> {
return CustomBuildIntegration.compileSchemas(
group.getSchemaFiles(),
group.getOutputDir(),
group.getPackageName(),
group.getPluginArgs()
);
});
futures.add(future);
}
// Wait for all compilations to complete
for (Future<BuildResult> future : futures) {
try {
BuildResult result = future.get();
if (!result.isSuccess()) {
System.err.println("Compilation failed: " + result.getErrors());
}
} catch (Exception e) {
System.err.println("Compilation error: " + e.getMessage());
}
}
executor.shutdown();
}
/**
* Memory-efficient compilation for large schemas
*/
public static void compileWithMemoryOptimization(List<File> schemaFiles,
File outputDir,
String packageName) {
// Process schemas in batches to manage memory usage
int batchSize = 10;
for (int i = 0; i < schemaFiles.size(); i += batchSize) {
int endIndex = Math.min(i + batchSize, schemaFiles.size());
List<File> batch = schemaFiles.subList(i, endIndex);
try {
CustomBuildIntegration.compileSchemas(batch, outputDir,
packageName + ".batch" + (i / batchSize), Collections.emptyList());
// Force garbage collection between batches
System.gc();
} catch (Exception e) {
System.err.println("Batch compilation failed: " + e.getMessage());
}
}
}
}This build integration system provides comprehensive support for integrating JAXB XJC into various build systems and workflows, with optimizations for performance and maintainability in large-scale projects.
Install with Tessl CLI
npx tessl i tessl/maven-org-glassfish-jaxb--jaxb-xjc