0
# Event System
1
2
Lifecycle hooks for intercepting and customizing mapping operations at various stages, enabling audit logging, validation, performance monitoring, and custom mapping behavior.
3
4
## Capabilities
5
6
### Event Listener Interface
7
8
Base interface for handling mapping lifecycle events.
9
10
```java { .api }
11
/**
12
* Callback handler for mapping lifecycle events
13
*/
14
public interface EventListener {
15
/**
16
* Called when mapping operation starts
17
* @param event event containing mapping context information
18
*/
19
void onMappingStarted(Event event);
20
21
/**
22
* Called before writing value to destination field
23
* @param event event containing mapping context and field information
24
*/
25
void onPreWritingDestinationValue(Event event);
26
27
/**
28
* Called after writing value to destination field
29
* @param event event containing mapping context and field information
30
*/
31
void onPostWritingDestinationValue(Event event);
32
33
/**
34
* Called when mapping operation completes
35
* @param event event containing mapping context information
36
*/
37
void onMappingFinished(Event event);
38
}
39
```
40
41
### Event Interface
42
43
Contains information about the current mapping operation and context.
44
45
```java { .api }
46
/**
47
* Represents an event triggered during mapping process
48
*/
49
public interface Event {
50
/**
51
* Gets the type of event that occurred
52
* @return event type
53
*/
54
EventTypes getType();
55
56
/**
57
* Gets the class mapping definition being used
58
* @return class mapping metadata
59
*/
60
ClassMap getClassMap();
61
62
/**
63
* Gets the field mapping definition being used (for field-level events)
64
* @return field mapping metadata or null for class-level events
65
*/
66
FieldMap getFieldMap();
67
68
/**
69
* Gets the source object being mapped from
70
* @return source object instance
71
*/
72
Object getSourceObject();
73
74
/**
75
* Gets the destination object being mapped to
76
* @return destination object instance
77
*/
78
Object getDestinationObject();
79
80
/**
81
* Gets the value being written to destination (for write events)
82
* @return destination value or null for non-write events
83
*/
84
Object getDestinationValue();
85
}
86
```
87
88
### Event Types Enumeration
89
90
Defines the types of events that can be triggered during mapping.
91
92
```java { .api }
93
/**
94
* Defines types of events that can be triggered during mapping
95
*/
96
public enum EventTypes {
97
/** Fired when mapping operation begins */
98
MAPPING_STARTED,
99
100
/** Fired before writing value to destination field */
101
MAPPING_PRE_WRITING_DEST_VALUE,
102
103
/** Fired after writing value to destination field */
104
MAPPING_POST_WRITING_DEST_VALUE,
105
106
/** Fired when mapping operation completes */
107
MAPPING_FINISHED
108
}
109
```
110
111
## Event Listener Implementation Examples
112
113
### Audit Logging Listener
114
115
```java
116
import com.github.dozermapper.core.events.Event;
117
import com.github.dozermapper.core.events.EventListener;
118
import com.github.dozermapper.core.events.EventTypes;
119
import org.slf4j.Logger;
120
import org.slf4j.LoggerFactory;
121
122
public class AuditLoggingListener implements EventListener {
123
private static final Logger logger = LoggerFactory.getLogger(AuditLoggingListener.class);
124
125
@Override
126
public void onMappingStarted(Event event) {
127
logger.info("Mapping started: {} -> {}",
128
event.getSourceObject().getClass().getSimpleName(),
129
event.getDestinationObject().getClass().getSimpleName());
130
}
131
132
@Override
133
public void onPreWritingDestinationValue(Event event) {
134
if (event.getFieldMap() != null) {
135
logger.debug("Writing field: {} = {}",
136
event.getFieldMap().getDestFieldName(),
137
event.getDestinationValue());
138
}
139
}
140
141
@Override
142
public void onPostWritingDestinationValue(Event event) {
143
// Log successful field writes
144
if (event.getFieldMap() != null) {
145
logger.debug("Field written successfully: {}",
146
event.getFieldMap().getDestFieldName());
147
}
148
}
149
150
@Override
151
public void onMappingFinished(Event event) {
152
logger.info("Mapping completed: {} -> {}",
153
event.getSourceObject().getClass().getSimpleName(),
154
event.getDestinationObject().getClass().getSimpleName());
155
}
156
}
157
```
158
159
### Performance Monitoring Listener
160
161
```java
162
public class PerformanceMonitorListener implements EventListener {
163
private final Map<Object, Long> mappingStartTimes = new ConcurrentHashMap<>();
164
private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitorListener.class);
165
166
@Override
167
public void onMappingStarted(Event event) {
168
mappingStartTimes.put(event.getDestinationObject(), System.currentTimeMillis());
169
}
170
171
@Override
172
public void onPreWritingDestinationValue(Event event) {
173
// Can be used to monitor field-level performance
174
}
175
176
@Override
177
public void onPostWritingDestinationValue(Event event) {
178
// Can be used to monitor field-level performance
179
}
180
181
@Override
182
public void onMappingFinished(Event event) {
183
Long startTime = mappingStartTimes.remove(event.getDestinationObject());
184
if (startTime != null) {
185
long duration = System.currentTimeMillis() - startTime;
186
logger.info("Mapping took {}ms: {} -> {}", duration,
187
event.getSourceObject().getClass().getSimpleName(),
188
event.getDestinationObject().getClass().getSimpleName());
189
}
190
}
191
}
192
```
193
194
### Data Validation Listener
195
196
```java
197
public class ValidationListener implements EventListener {
198
199
@Override
200
public void onMappingStarted(Event event) {
201
validateSourceObject(event.getSourceObject());
202
}
203
204
@Override
205
public void onPreWritingDestinationValue(Event event) {
206
// Validate before writing
207
if (event.getDestinationValue() != null) {
208
validateFieldValue(event.getFieldMap().getDestFieldName(),
209
event.getDestinationValue());
210
}
211
}
212
213
@Override
214
public void onPostWritingDestinationValue(Event event) {
215
// Additional validation after field write if needed
216
}
217
218
@Override
219
public void onMappingFinished(Event event) {
220
validateDestinationObject(event.getDestinationObject());
221
}
222
223
private void validateSourceObject(Object source) {
224
if (source == null) {
225
throw new MappingException("Source object cannot be null");
226
}
227
// Additional source validation logic
228
}
229
230
private void validateFieldValue(String fieldName, Object value) {
231
// Field-specific validation logic
232
if ("email".equals(fieldName) && value instanceof String) {
233
if (!isValidEmail((String) value)) {
234
throw new MappingException("Invalid email format: " + value);
235
}
236
}
237
}
238
239
private void validateDestinationObject(Object destination) {
240
// Final destination object validation
241
}
242
243
private boolean isValidEmail(String email) {
244
return email.contains("@") && email.contains(".");
245
}
246
}
247
```
248
249
### Custom Field Transformation Listener
250
251
```java
252
public class CustomTransformationListener implements EventListener {
253
254
@Override
255
public void onMappingStarted(Event event) {
256
// No action needed for mapping start
257
}
258
259
@Override
260
public void onPreWritingDestinationValue(Event event) {
261
// Transform values before they are written
262
if (event.getFieldMap() != null) {
263
String fieldName = event.getFieldMap().getDestFieldName();
264
Object value = event.getDestinationValue();
265
266
// Custom transformation logic
267
if ("fullName".equals(fieldName) && value instanceof String) {
268
// Modify the event's destination value (if mutable)
269
transformFullName(event, (String) value);
270
}
271
}
272
}
273
274
@Override
275
public void onPostWritingDestinationValue(Event event) {
276
// Post-processing after field write
277
}
278
279
@Override
280
public void onMappingFinished(Event event) {
281
// Final processing after mapping completes
282
performFinalTransformations(event.getDestinationObject());
283
}
284
285
private void transformFullName(Event event, String fullName) {
286
// Example: Convert to title case
287
String titleCase = Arrays.stream(fullName.split(" "))
288
.map(word -> word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase())
289
.collect(Collectors.joining(" "));
290
// Note: Event interface may not allow direct modification
291
// This is conceptual - actual implementation depends on Event mutability
292
}
293
294
private void performFinalTransformations(Object destination) {
295
// Apply any final transformations to the complete object
296
}
297
}
298
```
299
300
## Event Listener Registration
301
302
### Single Listener Registration
303
304
```java
305
Mapper mapper = DozerBeanMapperBuilder.create()
306
.withEventListener(new AuditLoggingListener())
307
.build();
308
```
309
310
### Multiple Listeners Registration
311
312
```java
313
Mapper mapper = DozerBeanMapperBuilder.create()
314
.withEventListener(new AuditLoggingListener())
315
.withEventListener(new PerformanceMonitorListener())
316
.withEventListener(new ValidationListener())
317
.build();
318
```
319
320
### Bulk Registration
321
322
```java
323
List<EventListener> listeners = Arrays.asList(
324
new AuditLoggingListener(),
325
new PerformanceMonitorListener(),
326
new ValidationListener()
327
);
328
329
Mapper mapper = DozerBeanMapperBuilder.create()
330
.withEventListeners(listeners)
331
.build();
332
```
333
334
## Advanced Event Handling Patterns
335
336
### Conditional Event Processing
337
338
```java
339
public class ConditionalListener implements EventListener {
340
341
@Override
342
public void onMappingStarted(Event event) {
343
// Only process certain types
344
if (shouldProcessMapping(event)) {
345
processMapping(event);
346
}
347
}
348
349
@Override
350
public void onPreWritingDestinationValue(Event event) {
351
// Only process sensitive fields
352
if (isSensitiveField(event.getFieldMap())) {
353
handleSensitiveData(event);
354
}
355
}
356
357
@Override
358
public void onPostWritingDestinationValue(Event event) {
359
// Post-processing for specific conditions
360
}
361
362
@Override
363
public void onMappingFinished(Event event) {
364
// Cleanup or finalization
365
}
366
367
private boolean shouldProcessMapping(Event event) {
368
return event.getSourceObject() instanceof UserEntity;
369
}
370
371
private boolean isSensitiveField(FieldMap fieldMap) {
372
if (fieldMap == null) return false;
373
String fieldName = fieldMap.getDestFieldName();
374
return fieldName.contains("password") || fieldName.contains("ssn");
375
}
376
377
private void handleSensitiveData(Event event) {
378
// Log or mask sensitive data
379
logger.warn("Sensitive field accessed: {}",
380
event.getFieldMap().getDestFieldName());
381
}
382
}
383
```
384
385
### Event Chain Processing
386
387
```java
388
public abstract class ChainedEventListener implements EventListener {
389
private EventListener next;
390
391
public void setNext(EventListener next) {
392
this.next = next;
393
}
394
395
protected void callNext(Event event, EventTypes eventType) {
396
if (next != null) {
397
switch (eventType) {
398
case MAPPING_STARTED:
399
next.onMappingStarted(event);
400
break;
401
case MAPPING_PRE_WRITING_DEST_VALUE:
402
next.onPreWritingDestinationValue(event);
403
break;
404
case MAPPING_POST_WRITING_DEST_VALUE:
405
next.onPostWritingDestinationValue(event);
406
break;
407
case MAPPING_FINISHED:
408
next.onMappingFinished(event);
409
break;
410
}
411
}
412
}
413
}
414
```
415
416
## Event Context Information Access
417
418
### Accessing Mapping Metadata
419
420
```java
421
public class MetadataAccessListener implements EventListener {
422
423
@Override
424
public void onMappingStarted(Event event) {
425
ClassMap classMap = event.getClassMap();
426
if (classMap != null) {
427
logger.info("Mapping class: {} -> {}",
428
classMap.getSrcClassName(),
429
classMap.getDestClassName());
430
}
431
}
432
433
@Override
434
public void onPreWritingDestinationValue(Event event) {
435
FieldMap fieldMap = event.getFieldMap();
436
if (fieldMap != null) {
437
logger.debug("Field mapping: {} -> {}",
438
fieldMap.getSrcFieldName(),
439
fieldMap.getDestFieldName());
440
}
441
}
442
443
@Override
444
public void onPostWritingDestinationValue(Event event) {
445
// Access both source and destination values
446
Object sourceValue = getSourceValue(event);
447
Object destValue = event.getDestinationValue();
448
logger.debug("Value transformation: {} -> {}", sourceValue, destValue);
449
}
450
451
@Override
452
public void onMappingFinished(Event event) {
453
// Final validation or logging
454
}
455
456
private Object getSourceValue(Event event) {
457
// Extract source value using reflection or field map
458
// Implementation depends on available Event API methods
459
return null; // Placeholder
460
}
461
}
462
```
463
464
## Best Practices
465
466
### Performance Considerations
467
- Keep event listener logic lightweight to avoid performance impact
468
- Use conditional processing to avoid unnecessary operations
469
- Consider async processing for heavyweight operations like logging
470
471
### Error Handling
472
- Catch and handle exceptions in listeners to prevent mapping failures
473
- Log listener errors without propagating them to mapping operations
474
- Provide fallback behavior for critical listener operations
475
476
### Thread Safety
477
- Make listeners thread-safe if mapper is used concurrently
478
- Use concurrent collections for shared state
479
- Avoid modifying shared mutable state without synchronization
480
481
### Modularity
482
- Create focused listeners with single responsibilities
483
- Use composition to combine multiple behaviors
484
- Make listeners configurable through constructor parameters
485
486
## Supporting Types
487
488
### ClassMap Interface
489
490
Internal interface providing access to class mapping metadata in events.
491
492
```java { .api }
493
/**
494
* Internal interface representing class mapping configuration (read-only access in events)
495
*/
496
public interface ClassMap {
497
/**
498
* Gets the source class name for this mapping
499
* @return source class name
500
*/
501
String getSrcClassName();
502
503
/**
504
* Gets the destination class name for this mapping
505
* @return destination class name
506
*/
507
String getDestClassName();
508
509
/**
510
* Gets the map ID if specified for this mapping
511
* @return map ID or null if not specified
512
*/
513
String getMapId();
514
515
/**
516
* Gets the source class type
517
* @return source class
518
*/
519
Class<?> getSrcClassToMap();
520
521
/**
522
* Gets the destination class type
523
* @return destination class
524
*/
525
Class<?> getDestClassToMap();
526
}
527
```
528
529
### FieldMap Interface
530
531
Internal interface providing access to field mapping metadata in events.
532
533
```java { .api }
534
/**
535
* Internal interface representing field mapping configuration (read-only access in events)
536
*/
537
public interface FieldMap {
538
/**
539
* Gets the source field name
540
* @return source field name
541
*/
542
String getSrcFieldName();
543
544
/**
545
* Gets the destination field name
546
* @return destination field name
547
*/
548
String getDestFieldName();
549
550
/**
551
* Gets the mapping type for this field
552
* @return field mapping type
553
*/
554
FieldMappingType getType();
555
556
/**
557
* Checks if this field should be copied by reference
558
* @return true if copy by reference, false otherwise
559
*/
560
boolean isCopyByReference();
561
562
/**
563
* Gets the custom converter class if specified
564
* @return custom converter class or null
565
*/
566
Class<? extends CustomConverter> getCustomConverter();
567
}
568
```
569
570
### BeanContainer Interface
571
572
Internal configuration container providing access to runtime configuration.
573
574
```java { .api }
575
/**
576
* Internal bean container for runtime configuration access
577
*/
578
public interface BeanContainer {
579
/**
580
* Gets the configured class loader
581
* @return DozerClassLoader instance
582
*/
583
DozerClassLoader getClassLoader();
584
585
/**
586
* Gets the proxy resolver
587
* @return proxy resolver instance
588
*/
589
DozerProxyResolver getProxyResolver();
590
591
/**
592
* Gets the expression language engine
593
* @return EL engine instance
594
*/
595
ELEngine getElEngine();
596
}
597
```
598
599
### DozerClassLoader Interface
600
601
Service provider interface for class and resource loading.
602
603
```java { .api }
604
/**
605
* Service Provider Interface for control of Dozer Class and Resource loading behavior.
606
* Could be used in order to provide specific implementations for web-container,
607
* j2ee container and osgi environments.
608
*/
609
public interface DozerClassLoader {
610
/**
611
* Loads class by provided name
612
* @param className fully qualified class name
613
* @return the class if found, null otherwise
614
*/
615
Class<?> loadClass(String className);
616
617
/**
618
* Loads the resource by URI as an URL
619
* @param uri uri of the resource
620
* @return resource URL
621
*/
622
URL loadResource(String uri);
623
}
624
```