or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bean-factory.mdbuilder-configuration.mdcore-mapping.mdcustom-conversion.mdevent-system.mdindex.mdmetadata-access.mdprogrammatic-mapping.md

event-system.mddocs/

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

```