or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdjaxb-lifecycle.mdjaxb-model.mdobject-locators.mdstrategic-patterns.mdutilities.md
tile.json

jaxb-lifecycle.mddocs/

JAXB Lifecycle Integration

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.

Capabilities

Marshalling Callbacks

Interfaces for executing custom logic before and after marshalling operations.

interface BeforeMarshallCallback {
    void beforeMarshall(Marshaller marshaller);
}

interface AfterMarshallCallback {
    void afterMarshall(Marshaller marshaller);
}

Unmarshalling Callbacks

Interfaces for executing custom logic before and after unmarshalling operations.

interface BeforeUnmarshallCallback {
    void beforeUnmarshal(Unmarshaller unmarshaller, Object parent);
}

interface AfterUnmarshallCallback {
    void afterUnmarshal(Unmarshaller unmarshaller, Object parent);
}

Context Awareness

Interface for classes that need to be aware of their JAXB context path.

interface ContextPathAware {
    String getContextPath();
}

XML Adapters

Custom adapters for common data transformations during marshalling/unmarshalling.

class CommaDelimitedStringAdapter extends XmlAdapter<String, List<String>> {
    public String marshal(List<String> value) throws Exception;
    public List<String> unmarshal(String text) throws Exception;
}

Usage Examples

Basic Lifecycle Callbacks

import org.jvnet.jaxb2_commons.xml.bind.*;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

@XmlRootElement
public class Customer implements BeforeMarshallCallback, AfterMarshallCallback, 
                               BeforeUnmarshallCallback, AfterUnmarshallCallback {
    
    private String name;
    private String email;
    private Date lastModified;
    
    @Override
    public void beforeMarshall(Marshaller marshaller) {
        System.out.println("About to marshal Customer: " + name);
        // Update last modified timestamp before marshalling
        this.lastModified = new Date();
    }
    
    @Override
    public void afterMarshall(Marshaller marshaller) {
        System.out.println("Successfully marshalled Customer: " + name);
        // Perform cleanup or logging after marshalling
    }
    
    @Override
    public void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) {
        System.out.println("About to unmarshal Customer with parent: " + parent);
        // Initialize default values or prepare for unmarshalling
    }
    
    @Override
    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        System.out.println("Successfully unmarshalled Customer: " + name);
        // Perform post-processing, validation, or setup
        if (this.email != null) {
            this.email = this.email.toLowerCase().trim();
        }
    }
    
    // Standard getters and setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public Date getLastModified() { return lastModified; }
    public void setLastModified(Date lastModified) { this.lastModified = lastModified; }
}

Context Path Awareness

import org.jvnet.jaxb2_commons.xml.bind.ContextPathAware;
import javax.xml.bind.JAXBContext;

@XmlRootElement
public class ConfigurableEntity implements ContextPathAware {
    private String contextPath;
    
    @Override
    public String getContextPath() {
        if (contextPath == null) {
            // Determine context path based on package or configuration
            contextPath = this.getClass().getPackage().getName();
        }
        return contextPath;
    }
    
    public void setContextPath(String contextPath) {
        this.contextPath = contextPath;
    }
    
    // Use context path for dynamic behavior
    public JAXBContext createContext() throws JAXBException {
        return JAXBContext.newInstance(getContextPath());
    }
}

Advanced Lifecycle Integration

import org.jvnet.jaxb2_commons.xml.bind.*;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.util.concurrent.atomic.AtomicLong;

@XmlRootElement
public class AuditableEntity implements BeforeMarshallCallback, AfterUnmarshallCallback {
    
    private static final AtomicLong VERSION_COUNTER = new AtomicLong(0);
    
    private String id;
    private Long version;
    private Date createdDate;
    private Date modifiedDate;
    private transient boolean isNew = true;
    
    @Override
    public void beforeMarshall(Marshaller marshaller) {
        // Update modification timestamp and version before marshalling
        this.modifiedDate = new Date();
        if (this.version == null) {
            this.version = VERSION_COUNTER.incrementAndGet();
        }
        
        // Set marshaller properties based on entity state
        try {
            if (isNew) {
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            }
        } catch (Exception e) {
            // Handle marshaller property setting errors
            System.err.println("Could not set marshaller properties: " + e.getMessage());
        }
    }
    
    @Override
    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        // Mark entity as existing (not new) after unmarshalling
        this.isNew = false;
        
        // Set creation date if not present
        if (this.createdDate == null) {
            this.createdDate = new Date();
        }
        
        // Perform parent relationship setup
        if (parent instanceof EntityContainer) {
            EntityContainer container = (EntityContainer) parent;
            container.addEntity(this);
        }
        
        // Validate unmarshalled data
        validateEntity();
    }
    
    private void validateEntity() {
        if (id == null || id.trim().isEmpty()) {
            throw new IllegalStateException("Entity ID cannot be null or empty");
        }
        if (version == null || version < 0) {
            throw new IllegalStateException("Entity version must be non-negative");
        }
    }
    
    // Getters and setters
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public Long getVersion() { return version; }
    public void setVersion(Long version) { this.version = version; }
    public Date getCreatedDate() { return createdDate; }
    public void setCreatedDate(Date createdDate) { this.createdDate = createdDate; }
    public Date getModifiedDate() { return modifiedDate; }
    public void setModifiedDate(Date modifiedDate) { this.modifiedDate = modifiedDate; }
    public boolean isNew() { return isNew; }
}

XML Adapter Usage

import org.jvnet.jaxb2_commons.xml.bind.annotation.adapters.CommaDelimitedStringAdapter;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.List;
import java.util.Arrays;

@XmlRootElement
public class ProductInfo {
    
    @XmlJavaTypeAdapter(CommaDelimitedStringAdapter.class)
    private List<String> tags;
    
    @XmlJavaTypeAdapter(CommaDelimitedStringAdapter.class)
    private List<String> categories;
    
    // Constructor
    public ProductInfo() {
        this.tags = new ArrayList<>();
        this.categories = new ArrayList<>();
    }
    
    // Getters and setters
    public List<String> getTags() { return tags; }
    public void setTags(List<String> tags) { this.tags = tags; }
    public List<String> getCategories() { return categories; }
    public void setCategories(List<String> categories) { this.categories = categories; }
}

// Usage example:
ProductInfo product = new ProductInfo();
product.setTags(Arrays.asList("electronics", "mobile", "smartphone"));
product.setCategories(Arrays.asList("phones", "accessories"));

// When marshalled, produces XML like:
// <productInfo>
//   <tags>electronics,mobile,smartphone</tags>
//   <categories>phones,accessories</categories>
// </productInfo>

// When unmarshalled, converts comma-delimited strings back to List<String>

Custom XML Adapter

import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {
    
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    
    @Override
    public LocalDateTime unmarshal(String value) throws Exception {
        if (value == null || value.trim().isEmpty()) {
            return null;
        }
        return LocalDateTime.parse(value, FORMATTER);
    }
    
    @Override
    public String marshal(LocalDateTime value) throws Exception {
        if (value == null) {
            return null;
        }
        return value.format(FORMATTER);
    }
}

// Usage in entity class
@XmlRootElement
public class Event implements AfterUnmarshallCallback {
    
    @XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
    private LocalDateTime eventDateTime;
    
    @Override
    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        // Validate that event date is not in the past
        if (eventDateTime != null && eventDateTime.isBefore(LocalDateTime.now())) {
            System.out.println("Warning: Event date is in the past: " + eventDateTime);
        }
    }
    
    public LocalDateTime getEventDateTime() { return eventDateTime; }
    public void setEventDateTime(LocalDateTime eventDateTime) { this.eventDateTime = eventDateTime; }
}

Callback Chain with Parent-Child Relationships

@XmlRootElement
public class OrderContainer implements AfterUnmarshallCallback {
    
    @XmlElement(name = "order")
    private List<Order> orders = new ArrayList<>();
    
    @Override
    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        // Set up parent-child relationships after unmarshalling
        for (Order order : orders) {
            order.setContainer(this);
        }
        System.out.println("Unmarshalled " + orders.size() + " orders");
    }
    
    public List<Order> getOrders() { return orders; }
    public void setOrders(List<Order> orders) { this.orders = orders; }
}

@XmlType
public class Order implements BeforeMarshallCallback, AfterUnmarshallCallback {
    
    private String orderId;
    private transient OrderContainer container;
    
    @Override
    public void beforeMarshall(Marshaller marshaller) {
        System.out.println("Marshalling order: " + orderId);
    }
    
    @Override
    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        System.out.println("Unmarshalled order: " + orderId + " with parent: " + parent.getClass().getSimpleName());
        // Parent relationship will be set by OrderContainer's afterUnmarshal
    }
    
    public String getOrderId() { return orderId; }
    public void setOrderId(String orderId) { this.orderId = orderId; }
    public OrderContainer getContainer() { return container; }
    public void setContainer(OrderContainer container) { this.container = container; }
}

Integration with JAXB Runtime

The lifecycle callbacks integrate seamlessly with JAXB's standard marshalling and unmarshalling process:

  1. Marshalling Flow: beforeMarshall() → JAXB marshalling → afterMarshall()
  2. Unmarshalling Flow: beforeUnmarshal() → JAXB unmarshalling → afterUnmarshal()
  3. Adapter Flow: marshal() during marshalling, unmarshal() during unmarshalling
  4. Context Awareness: Available throughout the lifecycle for context-sensitive operations

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.