0
# JAXB Lifecycle Integration
1
2
Callback interfaces for integrating with JAXB marshalling and unmarshalling lifecycle events, enabling custom processing during XML binding operations. These interfaces allow JAXB-generated classes to execute custom logic at specific points in the marshalling/unmarshalling process.
3
4
## Capabilities
5
6
### Marshalling Callbacks
7
8
Interfaces for executing custom logic before and after marshalling operations.
9
10
```java { .api }
11
interface BeforeMarshallCallback {
12
void beforeMarshall(Marshaller marshaller);
13
}
14
15
interface AfterMarshallCallback {
16
void afterMarshall(Marshaller marshaller);
17
}
18
```
19
20
### Unmarshalling Callbacks
21
22
Interfaces for executing custom logic before and after unmarshalling operations.
23
24
```java { .api }
25
interface BeforeUnmarshallCallback {
26
void beforeUnmarshal(Unmarshaller unmarshaller, Object parent);
27
}
28
29
interface AfterUnmarshallCallback {
30
void afterUnmarshal(Unmarshaller unmarshaller, Object parent);
31
}
32
```
33
34
### Context Awareness
35
36
Interface for classes that need to be aware of their JAXB context path.
37
38
```java { .api }
39
interface ContextPathAware {
40
String getContextPath();
41
}
42
```
43
44
### XML Adapters
45
46
Custom adapters for common data transformations during marshalling/unmarshalling.
47
48
```java { .api }
49
class CommaDelimitedStringAdapter extends XmlAdapter<String, List<String>> {
50
public String marshal(List<String> value) throws Exception;
51
public List<String> unmarshal(String text) throws Exception;
52
}
53
```
54
55
## Usage Examples
56
57
### Basic Lifecycle Callbacks
58
59
```java
60
import org.jvnet.jaxb2_commons.xml.bind.*;
61
import javax.xml.bind.Marshaller;
62
import javax.xml.bind.Unmarshaller;
63
64
@XmlRootElement
65
public class Customer implements BeforeMarshallCallback, AfterMarshallCallback,
66
BeforeUnmarshallCallback, AfterUnmarshallCallback {
67
68
private String name;
69
private String email;
70
private Date lastModified;
71
72
@Override
73
public void beforeMarshall(Marshaller marshaller) {
74
System.out.println("About to marshal Customer: " + name);
75
// Update last modified timestamp before marshalling
76
this.lastModified = new Date();
77
}
78
79
@Override
80
public void afterMarshall(Marshaller marshaller) {
81
System.out.println("Successfully marshalled Customer: " + name);
82
// Perform cleanup or logging after marshalling
83
}
84
85
@Override
86
public void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) {
87
System.out.println("About to unmarshal Customer with parent: " + parent);
88
// Initialize default values or prepare for unmarshalling
89
}
90
91
@Override
92
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
93
System.out.println("Successfully unmarshalled Customer: " + name);
94
// Perform post-processing, validation, or setup
95
if (this.email != null) {
96
this.email = this.email.toLowerCase().trim();
97
}
98
}
99
100
// Standard getters and setters
101
public String getName() { return name; }
102
public void setName(String name) { this.name = name; }
103
public String getEmail() { return email; }
104
public void setEmail(String email) { this.email = email; }
105
public Date getLastModified() { return lastModified; }
106
public void setLastModified(Date lastModified) { this.lastModified = lastModified; }
107
}
108
```
109
110
### Context Path Awareness
111
112
```java
113
import org.jvnet.jaxb2_commons.xml.bind.ContextPathAware;
114
import javax.xml.bind.JAXBContext;
115
116
@XmlRootElement
117
public class ConfigurableEntity implements ContextPathAware {
118
private String contextPath;
119
120
@Override
121
public String getContextPath() {
122
if (contextPath == null) {
123
// Determine context path based on package or configuration
124
contextPath = this.getClass().getPackage().getName();
125
}
126
return contextPath;
127
}
128
129
public void setContextPath(String contextPath) {
130
this.contextPath = contextPath;
131
}
132
133
// Use context path for dynamic behavior
134
public JAXBContext createContext() throws JAXBException {
135
return JAXBContext.newInstance(getContextPath());
136
}
137
}
138
```
139
140
### Advanced Lifecycle Integration
141
142
```java
143
import org.jvnet.jaxb2_commons.xml.bind.*;
144
import javax.xml.bind.Marshaller;
145
import javax.xml.bind.Unmarshaller;
146
import java.util.concurrent.atomic.AtomicLong;
147
148
@XmlRootElement
149
public class AuditableEntity implements BeforeMarshallCallback, AfterUnmarshallCallback {
150
151
private static final AtomicLong VERSION_COUNTER = new AtomicLong(0);
152
153
private String id;
154
private Long version;
155
private Date createdDate;
156
private Date modifiedDate;
157
private transient boolean isNew = true;
158
159
@Override
160
public void beforeMarshall(Marshaller marshaller) {
161
// Update modification timestamp and version before marshalling
162
this.modifiedDate = new Date();
163
if (this.version == null) {
164
this.version = VERSION_COUNTER.incrementAndGet();
165
}
166
167
// Set marshaller properties based on entity state
168
try {
169
if (isNew) {
170
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
171
}
172
} catch (Exception e) {
173
// Handle marshaller property setting errors
174
System.err.println("Could not set marshaller properties: " + e.getMessage());
175
}
176
}
177
178
@Override
179
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
180
// Mark entity as existing (not new) after unmarshalling
181
this.isNew = false;
182
183
// Set creation date if not present
184
if (this.createdDate == null) {
185
this.createdDate = new Date();
186
}
187
188
// Perform parent relationship setup
189
if (parent instanceof EntityContainer) {
190
EntityContainer container = (EntityContainer) parent;
191
container.addEntity(this);
192
}
193
194
// Validate unmarshalled data
195
validateEntity();
196
}
197
198
private void validateEntity() {
199
if (id == null || id.trim().isEmpty()) {
200
throw new IllegalStateException("Entity ID cannot be null or empty");
201
}
202
if (version == null || version < 0) {
203
throw new IllegalStateException("Entity version must be non-negative");
204
}
205
}
206
207
// Getters and setters
208
public String getId() { return id; }
209
public void setId(String id) { this.id = id; }
210
public Long getVersion() { return version; }
211
public void setVersion(Long version) { this.version = version; }
212
public Date getCreatedDate() { return createdDate; }
213
public void setCreatedDate(Date createdDate) { this.createdDate = createdDate; }
214
public Date getModifiedDate() { return modifiedDate; }
215
public void setModifiedDate(Date modifiedDate) { this.modifiedDate = modifiedDate; }
216
public boolean isNew() { return isNew; }
217
}
218
```
219
220
### XML Adapter Usage
221
222
```java
223
import org.jvnet.jaxb2_commons.xml.bind.annotation.adapters.CommaDelimitedStringAdapter;
224
import javax.xml.bind.annotation.XmlRootElement;
225
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
226
import java.util.List;
227
import java.util.Arrays;
228
229
@XmlRootElement
230
public class ProductInfo {
231
232
@XmlJavaTypeAdapter(CommaDelimitedStringAdapter.class)
233
private List<String> tags;
234
235
@XmlJavaTypeAdapter(CommaDelimitedStringAdapter.class)
236
private List<String> categories;
237
238
// Constructor
239
public ProductInfo() {
240
this.tags = new ArrayList<>();
241
this.categories = new ArrayList<>();
242
}
243
244
// Getters and setters
245
public List<String> getTags() { return tags; }
246
public void setTags(List<String> tags) { this.tags = tags; }
247
public List<String> getCategories() { return categories; }
248
public void setCategories(List<String> categories) { this.categories = categories; }
249
}
250
251
// Usage example:
252
ProductInfo product = new ProductInfo();
253
product.setTags(Arrays.asList("electronics", "mobile", "smartphone"));
254
product.setCategories(Arrays.asList("phones", "accessories"));
255
256
// When marshalled, produces XML like:
257
// <productInfo>
258
// <tags>electronics,mobile,smartphone</tags>
259
// <categories>phones,accessories</categories>
260
// </productInfo>
261
262
// When unmarshalled, converts comma-delimited strings back to List<String>
263
```
264
265
### Custom XML Adapter
266
267
```java
268
import javax.xml.bind.annotation.adapters.XmlAdapter;
269
import java.time.LocalDateTime;
270
import java.time.format.DateTimeFormatter;
271
272
public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {
273
274
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
275
276
@Override
277
public LocalDateTime unmarshal(String value) throws Exception {
278
if (value == null || value.trim().isEmpty()) {
279
return null;
280
}
281
return LocalDateTime.parse(value, FORMATTER);
282
}
283
284
@Override
285
public String marshal(LocalDateTime value) throws Exception {
286
if (value == null) {
287
return null;
288
}
289
return value.format(FORMATTER);
290
}
291
}
292
293
// Usage in entity class
294
@XmlRootElement
295
public class Event implements AfterUnmarshallCallback {
296
297
@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
298
private LocalDateTime eventDateTime;
299
300
@Override
301
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
302
// Validate that event date is not in the past
303
if (eventDateTime != null && eventDateTime.isBefore(LocalDateTime.now())) {
304
System.out.println("Warning: Event date is in the past: " + eventDateTime);
305
}
306
}
307
308
public LocalDateTime getEventDateTime() { return eventDateTime; }
309
public void setEventDateTime(LocalDateTime eventDateTime) { this.eventDateTime = eventDateTime; }
310
}
311
```
312
313
### Callback Chain with Parent-Child Relationships
314
315
```java
316
@XmlRootElement
317
public class OrderContainer implements AfterUnmarshallCallback {
318
319
@XmlElement(name = "order")
320
private List<Order> orders = new ArrayList<>();
321
322
@Override
323
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
324
// Set up parent-child relationships after unmarshalling
325
for (Order order : orders) {
326
order.setContainer(this);
327
}
328
System.out.println("Unmarshalled " + orders.size() + " orders");
329
}
330
331
public List<Order> getOrders() { return orders; }
332
public void setOrders(List<Order> orders) { this.orders = orders; }
333
}
334
335
@XmlType
336
public class Order implements BeforeMarshallCallback, AfterUnmarshallCallback {
337
338
private String orderId;
339
private transient OrderContainer container;
340
341
@Override
342
public void beforeMarshall(Marshaller marshaller) {
343
System.out.println("Marshalling order: " + orderId);
344
}
345
346
@Override
347
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
348
System.out.println("Unmarshalled order: " + orderId + " with parent: " + parent.getClass().getSimpleName());
349
// Parent relationship will be set by OrderContainer's afterUnmarshal
350
}
351
352
public String getOrderId() { return orderId; }
353
public void setOrderId(String orderId) { this.orderId = orderId; }
354
public OrderContainer getContainer() { return container; }
355
public void setContainer(OrderContainer container) { this.container = container; }
356
}
357
```
358
359
## Integration with JAXB Runtime
360
361
The lifecycle callbacks integrate seamlessly with JAXB's standard marshalling and unmarshalling process:
362
363
1. **Marshalling Flow**: `beforeMarshall()` → JAXB marshalling → `afterMarshall()`
364
2. **Unmarshalling Flow**: `beforeUnmarshal()` → JAXB unmarshalling → `afterUnmarshal()`
365
3. **Adapter Flow**: `marshal()` during marshalling, `unmarshal()` during unmarshalling
366
4. **Context Awareness**: Available throughout the lifecycle for context-sensitive operations
367
368
These callbacks provide hooks for custom validation, data transformation, relationship setup, auditing, and other cross-cutting concerns that need to execute during XML binding operations.