CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-micronaut--micronaut-context

Context module for the Micronaut Framework that extends the micronaut-inject module with additional bean container services such as job scheduling with @Scheduled, event listeners with @EventListener, and immutable configuration properties

Pending
Overview
Eval results
Files

events.mddocs/

Event System

Reactive event publishing and listening system for decoupled communication between application components. Supports strongly-typed events, automatic event listener registration, and comprehensive application lifecycle events.

Capabilities

@EventListener Annotation

Declarative event listener registration for automatic event handling.

/**
 * Annotation for methods that should listen for application events
 * Automatically registers the method as an ApplicationEventListener
 */
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Adapter(ApplicationEventListener.class)
public @interface EventListener {
    // Marker annotation - no attributes needed
    // Event type is determined from method parameter type
}

Usage Examples:

import io.micronaut.runtime.event.annotation.EventListener;
import io.micronaut.runtime.server.event.ServerStartupEvent;
import io.micronaut.runtime.server.event.ServerShutdownEvent;
import io.micronaut.context.event.StartupEvent;
import io.micronaut.context.event.ShutdownEvent;
import jakarta.inject.Singleton;

@Singleton
public class ApplicationEventListener {
    
    @EventListener
    public void onStartup(ServerStartupEvent event) {
        EmbeddedServer server = event.getSource();
        System.out.println("Server started on: " + server.getURL());
        System.out.println("Application ready to handle requests");
    }
    
    @EventListener
    public void onShutdown(ServerShutdownEvent event) {
        System.out.println("Server shutting down");
        // Cleanup resources, close connections, etc.
    }
    
    @EventListener
    public void onApplicationStartup(StartupEvent event) {
        ApplicationContext context = event.getSource();
        System.out.println("Application context started");
    }
    
    @EventListener
    public void onApplicationShutdown(ShutdownEvent event) {
        System.out.println("Application context shutting down");
    }
    
    // Custom event listener
    @EventListener
    public void onCustomEvent(CustomBusinessEvent event) {
        System.out.println("Business event: " + event.getData());
    }
    
    // Async event handling
    @EventListener
    @Async
    public CompletableFuture<Void> onAsyncEvent(AsyncProcessingEvent event) {
        return CompletableFuture.runAsync(() -> {
            // Long-running event processing
            processEventData(event.getData());
        });
    }
}

Application Lifecycle Events

Built-in events for tracking application and server lifecycle.

/**
 * Base class for embedded application events
 * Provides common functionality for application lifecycle events
 */
public abstract class AbstractEmbeddedApplicationEvent extends ApplicationEvent {
    /**
     * Create application event with embedded application source
     * @param source The embedded application that generated the event
     */
    public AbstractEmbeddedApplicationEvent(EmbeddedApplication<?> source);
    
    /**
     * Get the embedded application that generated this event
     * @return EmbeddedApplication instance
     */
    public EmbeddedApplication<?> getSource();
}

/**
 * Event fired when EmbeddedApplication starts up
 * Indicates the application context is ready and services are available
 */
public class ApplicationStartupEvent extends AbstractEmbeddedApplicationEvent {
    /**
     * Create startup event
     * @param source The embedded application that started
     */
    public ApplicationStartupEvent(EmbeddedApplication<?> source);
}

/**
 * Event fired when EmbeddedApplication shuts down
 * Last chance to perform cleanup before application termination
 */
public class ApplicationShutdownEvent extends AbstractEmbeddedApplicationEvent {
    /**
     * Create shutdown event
     * @param source The embedded application that is shutting down
     */
    public ApplicationShutdownEvent(EmbeddedApplication<?> source);
}

Server Lifecycle Events

Events specific to embedded server lifecycle for web applications.

/**
 * Event fired when EmbeddedServer completes startup
 * Server is ready to accept HTTP requests
 */
public class ServerStartupEvent extends ApplicationStartupEvent {
    /**
     * Create server startup event
     * @param embeddedServer The server that started
     */
    public ServerStartupEvent(EmbeddedServer embeddedServer);
    
    /**
     * Get the embedded server that started
     * @return EmbeddedServer instance
     */
    public EmbeddedServer getSource();
}

/**
 * Event fired when EmbeddedServer shuts down
 * Server will no longer accept new requests
 */
public class ServerShutdownEvent extends ApplicationEvent {
    /**
     * Create server shutdown event
     * @param embeddedServer The server that is shutting down
     */
    public ServerShutdownEvent(EmbeddedServer embeddedServer);
    
    /**
     * Get the embedded server that is shutting down
     * @return EmbeddedServer instance
     */
    public EmbeddedServer getSource();
}

Usage Examples:

import io.micronaut.runtime.server.event.ServerStartupEvent;
import io.micronaut.runtime.server.event.ServerShutdownEvent;
import io.micronaut.runtime.event.annotation.EventListener;

@Singleton
public class ServerLifecycleListener {
    
    @EventListener
    public void onServerStartup(ServerStartupEvent event) {
        EmbeddedServer server = event.getSource();
        
        // Log server startup details
        System.out.println("🚀 Server started successfully!");
        System.out.println("   URL: " + server.getURL());
        System.out.println("   Port: " + server.getPort());
        System.out.println("   Host: " + server.getHost());
        
        // Initialize external services
        initializeExternalConnections();
        
        // Register with service discovery
        registerWithServiceRegistry(server);
    }
    
    @EventListener
    public void onServerShutdown(ServerShutdownEvent event) {
        System.out.println("🛑 Server shutting down...");
        
        // Deregister from service discovery
        deregisterFromServiceRegistry();
        
        // Close external connections
        closeExternalConnections();
        
        System.out.println("✅ Graceful shutdown completed");
    }
}

Event Publishing

Programmatic event publishing for custom business events.

/**
 * ApplicationEventPublisher interface for publishing events
 * Automatically available as an injectable bean
 */
public interface ApplicationEventPublisher<T> {
    /**
     * Publish an event to all registered listeners
     * Executes synchronously, blocking until all listeners complete
     * @param event The event to publish
     */
    void publishEvent(@NonNull T event);
    
    /**
     * Publish an event asynchronously
     * @param event The event to publish
     * @return Future representing the async operation
     */
    @NonNull Future<Void> publishEventAsync(@NonNull T event);
    
    /**
     * Check whether this publisher is empty (has no listeners)
     * @return true if there are no subscribers
     */
    boolean isEmpty();
    
    /**
     * Returns a no-op instance of ApplicationEventPublisher
     * @param <K> The event type
     * @return no-op ApplicationEventPublisher instance
     */
    static <K> ApplicationEventPublisher<K> noOp();
}

Usage Examples:

import io.micronaut.context.event.ApplicationEventPublisher;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

// Custom business event
public class OrderProcessedEvent {
    private final String orderId;
    private final BigDecimal amount;
    private final Instant processedAt;
    
    public OrderProcessedEvent(String orderId, BigDecimal amount) {
        this.orderId = orderId;
        this.amount = amount;
        this.processedAt = Instant.now();
    }
    
    // Getters...
    public String getOrderId() { return orderId; }
    public BigDecimal getAmount() { return amount; }
    public Instant getProcessedAt() { return processedAt; }
}

@Singleton
public class OrderService {
    
    @Inject
    private ApplicationEventPublisher<OrderProcessedEvent> eventPublisher;
    
    public void processOrder(Order order) {
        // Process the order
        doOrderProcessing(order);
        
        // Publish event synchronously
        OrderProcessedEvent event = new OrderProcessedEvent(
            order.getId(), 
            order.getTotal()
        );
        eventPublisher.publishEvent(event);
    }
    
    public CompletableFuture<Void> processOrderAsync(Order order) {
        return CompletableFuture.runAsync(() -> {
            doOrderProcessing(order);
        }).thenCompose(v -> {
            // Publish event asynchronously
            OrderProcessedEvent event = new OrderProcessedEvent(
                order.getId(), 
                order.getTotal()
            );
            return eventPublisher.publishEventAsync(event);
        });
    }
}

// Event listeners for the custom event
@Singleton
public class OrderEventHandlers {
    
    @EventListener
    public void onOrderProcessed(OrderProcessedEvent event) {
        System.out.println("Order processed: " + event.getOrderId());
        
        // Send confirmation email
        sendConfirmationEmail(event.getOrderId());
    }
    
    @EventListener
    @Async
    public CompletableFuture<Void> updateAnalytics(OrderProcessedEvent event) {
        return CompletableFuture.runAsync(() -> {
            // Update business analytics asynchronously
            analyticsService.recordOrderProcessed(event);
        });
    }
    
    @EventListener
    public void auditOrderProcessing(OrderProcessedEvent event) {
        auditService.log("ORDER_PROCESSED", event.getOrderId(), event.getAmount());
    }
}

Conditional Event Listeners

Event listeners with conditional execution based on application configuration.

/**
 * Conditional event listener execution
 * Use @Requires annotation to control when listeners are active
 */
import io.micronaut.context.annotation.Requires;

@Singleton
@Requires(property = "app.monitoring.enabled", value = "true")
public class MonitoringEventListener {
    
    @EventListener
    public void onServerStartup(ServerStartupEvent event) {
        // Only active when monitoring is enabled
        startupMonitor.recordStartup(event);
    }
}

@Singleton
@Requires(env = "production")
public class ProductionEventListener {
    
    @EventListener
    public void onApplicationStartup(ApplicationStartupEvent event) {
        // Only active in production environment
        productionMetrics.recordStartup();
    }
}

Error Handling in Event Listeners

Best practices for handling exceptions in event listeners.

@Singleton
public class RobustEventListener {
    
    private static final Logger logger = LoggerFactory.getLogger(RobustEventListener.class);
    
    @EventListener
    public void onServerStartup(ServerStartupEvent event) {
        try {
            // Event processing logic
            performStartupOperations(event);
        } catch (Exception e) {
            // Log error but don't propagate to prevent disrupting other listeners
            logger.error("Error processing server startup event", e);
            
            // Optional: publish error event for monitoring
            errorEventPublisher.publishEvent(new EventProcessingError(e));
        }
    }
    
    @EventListener
    @Retryable(attempts = "3", delay = "1s")
    public void onRetryableEvent(CustomEvent event) {
        // This listener will be retried up to 3 times with 1-second delay
        riskyOperation(event);
    }
}

Custom Event Types

Creating Custom Events

Guidelines for creating custom event types.

/**
 * Base pattern for custom business events
 * Extend ApplicationEvent or create POJO with relevant data
 */
public class CustomBusinessEvent extends ApplicationEvent {
    private final String businessData;
    private final Instant timestamp;
    
    public CustomBusinessEvent(Object source, String businessData) {
        super(source);
        this.businessData = businessData;
        this.timestamp = Instant.now();
    }
    
    public String getBusinessData() { return businessData; }
    public Instant getTimestamp() { return timestamp; }
}

// Alternative: Simple POJO event (recommended for most cases)
public class UserRegisteredEvent {
    private final String userId;
    private final String email;
    private final Instant registeredAt;
    
    public UserRegisteredEvent(String userId, String email) {
        this.userId = userId;
        this.email = email;
        this.registeredAt = Instant.now();
    }
    
    // Getters...
}

Complete Custom Event Example:

// Event definition
public class PaymentProcessedEvent {
    private final String paymentId;
    private final String customerId;
    private final BigDecimal amount;
    private final String currency;
    private final PaymentStatus status;
    private final Instant processedAt;
    
    // Constructor and getters...
}

// Event publisher service
@Singleton
public class PaymentService {
    
    @Inject
    private ApplicationEventPublisher<PaymentProcessedEvent> eventPublisher;
    
    public void processPayment(PaymentRequest request) {
        // Process payment logic
        PaymentResult result = processPaymentInternal(request);
        
        // Publish event
        PaymentProcessedEvent event = new PaymentProcessedEvent(
            result.getPaymentId(),
            request.getCustomerId(),
            request.getAmount(),
            request.getCurrency(),
            result.getStatus()
        );
        
        eventPublisher.publishEvent(event);
    }
}

// Multiple event listeners
@Singleton
public class PaymentEventHandlers {
    
    @EventListener
    public void onPaymentProcessed(PaymentProcessedEvent event) {
        if (event.getStatus() == PaymentStatus.SUCCESS) {
            // Send success notification
            notificationService.sendPaymentConfirmation(event);
        } else {
            // Handle payment failure
            alertService.notifyPaymentFailure(event);
        }
    }
    
    @EventListener
    @Async
    public CompletableFuture<Void> updateCustomerBalance(PaymentProcessedEvent event) {
        return CompletableFuture.runAsync(() -> {
            customerService.updateBalance(event.getCustomerId(), event.getAmount());
        });
    }
    
    @EventListener
    public void auditPayment(PaymentProcessedEvent event) {
        auditService.recordPayment(event);
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-io-micronaut--micronaut-context

docs

events.md

index.md

runtime.md

scheduling.md

scopes.md

server.md

shutdown.md

tile.json