0
# Plugin System
1
2
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.
3
4
## Capabilities
5
6
### Base Plugin Class
7
8
Abstract base class that all XJC plugins must extend to participate in the code generation process.
9
10
```java { .api }
11
/**
12
* Abstract base class for XJC plugins that customize code generation
13
*/
14
public abstract class Plugin {
15
/**
16
* Get the command-line option name for this plugin
17
* @return Option name without leading dash (e.g., "fluent-api")
18
*/
19
public abstract String getOptionName();
20
21
/**
22
* Get usage description for help output
23
* @return Multi-line usage description
24
*/
25
public abstract String getUsage();
26
27
/**
28
* Main plugin execution method called during code generation
29
* @param outline Generated code outline containing all classes and fields
30
* @param opt Compilation options and configuration
31
* @param errorHandler Error handler for reporting issues
32
* @return true if plugin execution was successful
33
* @throws SAXException if plugin execution fails
34
*/
35
public abstract boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException;
36
37
/**
38
* Parse plugin-specific command line arguments
39
* @param opt Options object for storing parsed values
40
* @param args Full command line arguments array
41
* @param i Current position in args array
42
* @return Number of arguments consumed (0 if argument not recognized)
43
* @throws BadCommandLineException if argument is invalid
44
* @throws IOException if I/O error occurs during parsing
45
*/
46
public int parseArgument(Options opt, String[] args, int i) throws BadCommandLineException, IOException;
47
48
/**
49
* Get list of XML namespace URIs that this plugin handles in binding customizations
50
* @return List of namespace URIs, empty list if none
51
*/
52
public List<String> getCustomizationURIs();
53
54
/**
55
* Called when plugin is activated via command line
56
* @param opts Options object for configuration
57
* @throws BadCommandLineException if plugin activation fails
58
*/
59
public void onActivated(Options opts) throws BadCommandLineException;
60
61
/**
62
* Post-process the schema model before code generation
63
* @param model Schema model containing parsed information
64
* @param errorHandler Error handler for reporting issues
65
*/
66
public void postProcessModel(Model model, ErrorHandler errorHandler);
67
}
68
```
69
70
**Plugin Development Example:**
71
72
```java
73
import com.sun.tools.xjc.Plugin;
74
import com.sun.tools.xjc.outline.Outline;
75
import com.sun.tools.xjc.outline.ClassOutline;
76
import com.sun.tools.xjc.Options;
77
import com.sun.codemodel.*;
78
import org.xml.sax.ErrorHandler;
79
import org.xml.sax.SAXException;
80
81
public class ToStringPlugin extends Plugin {
82
83
@Override
84
public String getOptionName() {
85
return "Xtostring";
86
}
87
88
@Override
89
public String getUsage() {
90
return " -Xtostring : Generate toString() methods for all classes";
91
}
92
93
@Override
94
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException {
95
// Iterate through all generated classes
96
for (ClassOutline classOutline : outline.getClasses()) {
97
generateToString(classOutline);
98
}
99
return true;
100
}
101
102
private void generateToString(ClassOutline classOutline) {
103
JDefinedClass implClass = classOutline.ref;
104
105
// Generate toString method
106
JMethod toString = implClass.method(JMod.PUBLIC, String.class, "toString");
107
toString.annotate(Override.class);
108
109
JBlock body = toString.body();
110
JVar sb = body.decl(outline.getCodeModel().ref(StringBuilder.class), "sb",
111
JExpr._new(outline.getCodeModel().ref(StringBuilder.class)));
112
113
// Add class name
114
sb.invoke("append").arg(implClass.name() + "{");
115
116
// Add field values
117
boolean first = true;
118
for (FieldOutline field : classOutline.getDeclaredFields()) {
119
if (!first) {
120
sb.invoke("append").arg(", ");
121
}
122
sb.invoke("append").arg(field.getPropertyInfo().getName(false) + "=");
123
sb.invoke("append").arg(JExpr.refthis(field.getPropertyInfo().getName(false)));
124
first = false;
125
}
126
127
sb.invoke("append").arg("}");
128
body._return(sb.invoke("toString"));
129
}
130
}
131
```
132
133
### Built-in Plugins
134
135
XJC includes several built-in plugins that demonstrate common customization patterns.
136
137
```java { .api }
138
/**
139
* Built-in plugins provided by XJC
140
*/
141
142
// Accessor plugin for generating additional getter/setter methods
143
public class AccessorsPlugin extends Plugin {
144
public String getOptionName() { return "Xaccessors"; }
145
// Generates additional accessor methods
146
}
147
148
// @Generated annotation plugin
149
public class GeneratedPlugin extends Plugin {
150
public String getOptionName() { return "Xgenerated"; }
151
// Adds @Generated annotations to all generated classes
152
}
153
154
// Code injection plugin for custom code insertion
155
public class CodeInjectorPlugin extends Plugin {
156
public String getOptionName() { return "Xinject-code"; }
157
// Injects custom code from external sources
158
}
159
160
// Episode file generation plugin
161
public class EpisodePlugin extends Plugin {
162
public String getOptionName() { return "Xepisode"; }
163
// Generates episode files for modular compilation
164
}
165
166
// Source location tracking plugin
167
public class SourceLocationPlugin extends Plugin {
168
public String getOptionName() { return "Xlocator"; }
169
// Adds source location tracking to generated classes
170
}
171
172
// Synchronized method generation plugin
173
public class SynchronizedPlugin extends Plugin {
174
public String getOptionName() { return "Xsync-methods"; }
175
// Makes generated methods synchronized
176
}
177
```
178
179
### Plugin Registration and Discovery
180
181
Plugins are discovered and loaded using Java's ServiceLoader mechanism.
182
183
**Plugin Registration (META-INF/services/com.sun.tools.xjc.Plugin):**
184
```text
185
com.example.plugins.ToStringPlugin
186
com.example.plugins.BuilderPlugin
187
com.example.plugins.ValidationPlugin
188
```
189
190
**Module Declaration:**
191
```java
192
module my.xjc.plugins {
193
requires transitive org.glassfish.jaxb.xjc;
194
195
provides com.sun.tools.xjc.Plugin with
196
com.example.plugins.ToStringPlugin,
197
com.example.plugins.BuilderPlugin,
198
com.example.plugins.ValidationPlugin;
199
}
200
```
201
202
### Plugin Configuration and Arguments
203
204
Plugins can accept command-line arguments for configuration.
205
206
```java { .api }
207
/**
208
* Example plugin with configuration options
209
*/
210
public class ConfigurablePlugin extends Plugin {
211
private String prefix = "generated";
212
private boolean includeFields = true;
213
private Set<String> excludedClasses = new HashSet<>();
214
215
@Override
216
public int parseArgument(Options opt, String[] args, int i) throws BadCommandLineException {
217
String arg = args[i];
218
219
if (arg.equals("-Xplugin-prefix")) {
220
if (i + 1 < args.length) {
221
prefix = args[i + 1];
222
return 2; // Consumed current argument and next
223
} else {
224
throw new BadCommandLineException("Missing value for -Xplugin-prefix");
225
}
226
}
227
228
if (arg.equals("-Xplugin-no-fields")) {
229
includeFields = false;
230
return 1; // Consumed current argument
231
}
232
233
if (arg.equals("-Xplugin-exclude")) {
234
if (i + 1 < args.length) {
235
excludedClasses.add(args[i + 1]);
236
return 2;
237
} else {
238
throw new BadCommandLineException("Missing class name for -Xplugin-exclude");
239
}
240
}
241
242
return 0; // Argument not recognized
243
}
244
245
@Override
246
public void onActivated(Options opts) throws BadCommandLineException {
247
// Validate configuration after all arguments are parsed
248
if (prefix.isEmpty()) {
249
throw new BadCommandLineException("Plugin prefix cannot be empty");
250
}
251
}
252
}
253
```
254
255
### Advanced Plugin Patterns
256
257
**Model Post-Processing:**
258
```java
259
@Override
260
public void postProcessModel(Model model, ErrorHandler errorHandler) {
261
// Modify model before code generation
262
for (CClassInfo classInfo : model.beans().values()) {
263
// Add custom properties or modify existing ones
264
if (classInfo.getTypeName().getLocalPart().endsWith("Type")) {
265
// Rename classes ending with "Type"
266
String newName = classInfo.getTypeName().getLocalPart().replace("Type", "");
267
// Implementation would modify the class info
268
}
269
}
270
}
271
```
272
273
**Customization Processing:**
274
```java
275
@Override
276
public List<String> getCustomizationURIs() {
277
return Arrays.asList(
278
"http://example.com/xjc/builder",
279
"http://example.com/xjc/validation"
280
);
281
}
282
283
@Override
284
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException {
285
for (ClassOutline classOutline : outline.getClasses()) {
286
// Look for binding customizations
287
CPluginCustomization customization = classOutline.target.getCustomizations()
288
.find("http://example.com/xjc/builder", "builder");
289
290
if (customization != null) {
291
// Process the customization
292
generateBuilderPattern(classOutline, customization);
293
customization.markAsAcknowledged();
294
}
295
}
296
return true;
297
}
298
```
299
300
**Field-Level Customization:**
301
```java
302
@Override
303
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException {
304
for (ClassOutline classOutline : outline.getClasses()) {
305
for (FieldOutline fieldOutline : classOutline.getDeclaredFields()) {
306
CPropertyInfo property = fieldOutline.getPropertyInfo();
307
308
// Check for field-level customizations
309
if (property.getName(false).startsWith("id")) {
310
// Generate special handling for ID fields
311
generateIdFieldMethods(classOutline, fieldOutline);
312
}
313
314
// Modify field annotations
315
JFieldVar field = getFieldVar(fieldOutline);
316
if (field != null) {
317
field.annotate(MyCustomAnnotation.class);
318
}
319
}
320
}
321
return true;
322
}
323
```
324
325
### Plugin Usage Examples
326
327
**Command Line Usage:**
328
```bash
329
# Use built-in plugin
330
xjc -Xgenerated schema.xsd
331
332
# Use custom plugin with options
333
xjc -Xtostring schema.xsd
334
335
# Multiple plugins
336
xjc -Xgenerated -Xtostring -Xlocator schema.xsd
337
338
# Plugin with configuration
339
xjc -Xbuilder -Xbuilder-prefix=with schema.xsd
340
```
341
342
**Programmatic Usage:**
343
```java
344
import com.sun.tools.xjc.api.*;
345
import com.sun.tools.xjc.Plugin;
346
347
// Load and configure plugins
348
SchemaCompiler compiler = XJC.createSchemaCompiler();
349
compiler.parseSchema(schemaSource);
350
351
S2JJAXBModel model = compiler.bind();
352
353
// Apply plugins during code generation
354
Plugin[] plugins = {
355
new ToStringPlugin(),
356
new BuilderPlugin()
357
};
358
359
JCodeModel codeModel = model.generateCode(plugins, errorListener);
360
```
361
362
### Plugin Development Best Practices
363
364
1. **Error Handling**: Always use the provided ErrorHandler for reporting issues
365
2. **Resource Management**: Clean up any resources in plugin lifecycle methods
366
3. **Thread Safety**: Ensure plugins are thread-safe for concurrent usage
367
4. **Validation**: Validate configuration in onActivated() method
368
5. **Documentation**: Provide clear usage description and examples
369
6. **Customization**: Support binding customizations for fine-grained control
370
7. **Testing**: Test with various schema patterns and edge cases
371
372
### Common Plugin Use Cases
373
374
- **Code Quality**: Add toString(), equals(), hashCode() methods
375
- **Design Patterns**: Generate Builder, Factory, or Visitor patterns
376
- **Validation**: Add Bean Validation annotations
377
- **Documentation**: Generate JavaDoc comments from schema annotations
378
- **Serialization**: Add custom serialization support
379
- **Framework Integration**: Add Spring, JPA, or other framework annotations
380
- **Code Style**: Enforce naming conventions or coding standards