CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkus--quarkus-arc

Build time CDI dependency injection framework for Quarkus applications with conditional bean support and context management

Pending
Overview
Eval results
Files

interceptor-integration.mddocs/

Interceptor Integration

Utilities for working with interceptor bindings and context data within interceptor implementations. The InterceptorBindings utility class provides access to Arc-specific interceptor metadata.

Capabilities

InterceptorBindings Utility Class

Utility class for accessing interceptor binding metadata from invocation context.

/**
 * Utility class for accessing interceptor binding annotations and literals from invocation context.
 * @see ArcInvocationContext#getInterceptorBindings()
 */
public final class InterceptorBindings {
    /**
     * Retrieves the set of interceptor binding annotations from the invocation context.
     * @param invocationContext the interceptor invocation context
     * @return set of interceptor binding annotations
     */
    @SuppressWarnings("unchecked")
    public static Set<Annotation> getInterceptorBindings(InvocationContext invocationContext);

    /**
     * This method is just a convenience for getting a hold of AbstractAnnotationLiteral.
     * See the Javadoc of the class for an explanation of the reasons it might be used.
     * @param invocationContext the interceptor invocation context
     * @return set of interceptor binding literals
     */
    @SuppressWarnings("unchecked")
    public static Set<AbstractAnnotationLiteral> getInterceptorBindingLiterals(InvocationContext invocationContext);
}

Usage Examples:

import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;
import io.quarkus.arc.runtime.InterceptorBindings;
import java.lang.annotation.Annotation;
import java.util.Set;

// Custom interceptor binding
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@interface Audited {
    String value() default "";
    AuditLevel level() default AuditLevel.INFO;
}

enum AuditLevel {
    DEBUG, INFO, WARN, ERROR
}

// Another interceptor binding
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@interface Secured {
    String[] roles() default {};
}

// Interceptor that uses InterceptorBindings utility
@Audited
@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
public class AuditInterceptor {
    
    @AroundInvoke
    public Object audit(InvocationContext context) throws Exception {
        // Get all interceptor bindings
        Set<Annotation> bindings = InterceptorBindings.getInterceptorBindings(context);
        
        // Process each binding
        for (Annotation binding : bindings) {
            if (binding instanceof Audited) {
                Audited auditedBinding = (Audited) binding;
                performAudit(context, auditedBinding.value(), auditedBinding.level());
            } else if (binding instanceof Secured) {
                Secured securedBinding = (Secured) binding;
                checkSecurity(context, securedBinding.roles());
            }
        }
        
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = context.proceed();
            
            // Post-execution audit
            long duration = System.currentTimeMillis() - startTime;
            auditMethodExecution(context, duration, true);
            
            return result;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            auditMethodExecution(context, duration, false);
            throw e;
        }
    }
    
    private void performAudit(InvocationContext context, String auditValue, AuditLevel level) {
        String methodName = context.getMethod().getName();
        String className = context.getTarget().getClass().getSimpleName();
        
        String message = String.format("Auditing %s.%s() - %s", 
            className, methodName, auditValue);
        
        switch (level) {
            case DEBUG:
                System.out.println("DEBUG: " + message);
                break;
            case INFO:
                System.out.println("INFO: " + message);
                break;
            case WARN:
                System.out.println("WARN: " + message);
                break;
            case ERROR:
                System.out.println("ERROR: " + message);
                break;
        }
    }
    
    private void checkSecurity(InvocationContext context, String[] requiredRoles) {
        // Security check implementation
        System.out.println("Security check for roles: " + String.join(", ", requiredRoles));
        // In real implementation, would check current user's roles
    }
    
    private void auditMethodExecution(InvocationContext context, long duration, boolean success) {
        String methodName = context.getMethod().getName();
        String status = success ? "SUCCESS" : "FAILURE";
        System.out.println(String.format("Method %s completed in %dms - %s", 
            methodName, duration, status));
    }
}

Advanced Interceptor Binding Analysis

More sophisticated interceptor implementations using binding metadata.

import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;
import io.quarkus.arc.runtime.InterceptorBindings;
import io.quarkus.arc.AbstractAnnotationLiteral;
import java.lang.annotation.Annotation;
import java.util.Set;
import java.util.stream.Collectors;

// Complex interceptor binding with multiple attributes
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@interface Monitored {
    String name() default "";
    MetricType[] metrics() default {MetricType.EXECUTION_TIME};
    boolean includeParameters() default false;
    boolean includeResult() default false;
    String[] tags() default {};
}

enum MetricType {
    EXECUTION_TIME, INVOCATION_COUNT, ERROR_RATE, PARAMETER_SIZE
}

// Performance monitoring interceptor
@Monitored
@Interceptor
@Priority(Interceptor.Priority.APPLICATION + 10)
public class MonitoringInterceptor {
    
    @AroundInvoke
    public Object monitor(InvocationContext context) throws Exception {
        // Get interceptor binding literals for detailed analysis
        Set<AbstractAnnotationLiteral> bindingLiterals = 
            InterceptorBindings.getInterceptorBindingLiterals(context);
        
        // Find the Monitored binding
        Monitored monitoredBinding = findMonitoredBinding(bindingLiterals);
        
        if (monitoredBinding != null) {
            return executeWithMonitoring(context, monitoredBinding);
        } else {
            // Fallback to default monitoring
            return executeWithDefaultMonitoring(context);
        }
    }
    
    private Monitored findMonitoredBinding(Set<AbstractAnnotationLiteral> literals) {
        return literals.stream()
                .filter(literal -> literal instanceof Monitored)
                .map(literal -> (Monitored) literal)
                .findFirst()
                .orElse(null);
    }
    
    private Object executeWithMonitoring(InvocationContext context, Monitored binding) throws Exception {
        String methodName = getMethodName(context);
        String monitorName = binding.name().isEmpty() ? methodName : binding.name();
        
        // Pre-execution monitoring
        MetricCollector collector = new MetricCollector(monitorName);
        
        if (binding.includeParameters()) {
            collector.recordParameters(context.getParameters());
        }
        
        for (MetricType metricType : binding.metrics()) {
            collector.startMetric(metricType);
        }
        
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = context.proceed();
            
            // Post-execution success monitoring
            long duration = System.currentTimeMillis() - startTime;
            collector.recordSuccess(duration);
            
            if (binding.includeResult()) {
                collector.recordResult(result);
            }
            
            // Record custom tags
            for (String tag : binding.tags()) {
                collector.addTag(tag);
            }
            
            return result;
        } catch (Exception e) {
            // Post-execution error monitoring
            long duration = System.currentTimeMillis() - startTime;
            collector.recordError(duration, e);
            throw e;
        } finally {
            collector.flush();
        }
    }
    
    private Object executeWithDefaultMonitoring(InvocationContext context) throws Exception {
        String methodName = getMethodName(context);
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = context.proceed();
            long duration = System.currentTimeMillis() - startTime;
            System.out.println(String.format("Method %s executed successfully in %dms", 
                methodName, duration));
            return result;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            System.out.println(String.format("Method %s failed after %dms: %s", 
                methodName, duration, e.getMessage()));
            throw e;
        }
    }
    
    private String getMethodName(InvocationContext context) {
        return context.getTarget().getClass().getSimpleName() + "." + 
               context.getMethod().getName();
    }
}

// Metric collection utility
class MetricCollector {
    private final String name;
    private final Map<MetricType, Long> startTimes = new HashMap<>();
    private final List<String> tags = new ArrayList<>();
    
    public MetricCollector(String name) {
        this.name = name;
    }
    
    public void startMetric(MetricType type) {
        startTimes.put(type, System.currentTimeMillis());
    }
    
    public void recordParameters(Object[] parameters) {
        System.out.println("Parameters for " + name + ": " + 
            Arrays.toString(parameters));
    }
    
    public void recordResult(Object result) {
        System.out.println("Result for " + name + ": " + result);
    }
    
    public void recordSuccess(long duration) {
        System.out.println("Success - " + name + " took " + duration + "ms");
    }
    
    public void recordError(long duration, Exception error) {
        System.out.println("Error - " + name + " failed after " + duration + "ms: " + 
            error.getMessage());
    }
    
    public void addTag(String tag) {
        tags.add(tag);
    }
    
    public void flush() {
        if (!tags.isEmpty()) {
            System.out.println("Tags for " + name + ": " + String.join(", ", tags));
        }
    }
}

Multi-Binding Interceptor

Interceptor that handles multiple different binding types using the InterceptorBindings utility.

import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;
import io.quarkus.arc.runtime.InterceptorBindings;
import java.lang.annotation.Annotation;
import java.util.Set;

// Multiple interceptor bindings
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@interface Cached {
    int ttlSeconds() default 300;
    String keyPrefix() default "";
}

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@interface RateLimited {
    int requestsPerMinute() default 60;
    String identifier() default "";
}

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@interface Validated {
    Class<?>[] groups() default {};
    boolean failFast() default true;
}

// Multi-purpose interceptor
@Cached
@RateLimited
@Validated
@Interceptor
@Priority(Interceptor.Priority.APPLICATION + 20)
public class MultiPurposeInterceptor {
    
    @AroundInvoke
    public Object intercept(InvocationContext context) throws Exception {
        Set<Annotation> bindings = InterceptorBindings.getInterceptorBindings(context);
        
        // Process each type of binding
        InterceptorChain chain = new InterceptorChain(context);
        
        for (Annotation binding : bindings) {
            if (binding instanceof Cached) {
                chain.addHandler(new CacheHandler((Cached) binding));
            } else if (binding instanceof RateLimited) {
                chain.addHandler(new RateLimitHandler((RateLimited) binding));
            } else if (binding instanceof Validated) {
                chain.addHandler(new ValidationHandler((Validated) binding));
            }
        }
        
        return chain.execute();
    }
}

// Interceptor chain pattern
class InterceptorChain {
    private final InvocationContext context;
    private final List<InterceptorHandler> handlers = new ArrayList<>();
    
    public InterceptorChain(InvocationContext context) {
        this.context = context;
    }
    
    public void addHandler(InterceptorHandler handler) {
        handlers.add(handler);
    }
    
    public Object execute() throws Exception {
        return executeHandlers(0);
    }
    
    private Object executeHandlers(int index) throws Exception {
        if (index >= handlers.size()) {
            return context.proceed();
        }
        
        InterceptorHandler handler = handlers.get(index);
        return handler.handle(context, () -> executeHandlers(index + 1));
    }
}

// Handler interface
interface InterceptorHandler {
    Object handle(InvocationContext context, HandlerChain next) throws Exception;
}

@FunctionalInterface
interface HandlerChain {
    Object proceed() throws Exception;
}

// Specific handlers
class CacheHandler implements InterceptorHandler {
    private final Cached cacheConfig;
    
    public CacheHandler(Cached cacheConfig) {
        this.cacheConfig = cacheConfig;
    }
    
    @Override
    public Object handle(InvocationContext context, HandlerChain next) throws Exception {
        String cacheKey = generateCacheKey(context);
        
        // Check cache
        Object cachedResult = getFromCache(cacheKey);
        if (cachedResult != null) {
            System.out.println("Cache hit for key: " + cacheKey);
            return cachedResult;
        }
        
        // Execute and cache result
        Object result = next.proceed();
        putInCache(cacheKey, result, cacheConfig.ttlSeconds());
        System.out.println("Cached result for key: " + cacheKey);
        
        return result;
    }
    
    private String generateCacheKey(InvocationContext context) {
        String methodName = context.getMethod().getName();
        String params = Arrays.toString(context.getParameters());
        String prefix = cacheConfig.keyPrefix().isEmpty() ? "" : cacheConfig.keyPrefix() + ":";
        return prefix + methodName + ":" + params.hashCode();
    }
    
    private Object getFromCache(String key) {
        // Simplified cache implementation
        return null; // Cache miss
    }
    
    private void putInCache(String key, Object value, int ttlSeconds) {
        // Simplified cache implementation
        System.out.println("Storing in cache: " + key + " for " + ttlSeconds + " seconds");
    }
}

class RateLimitHandler implements InterceptorHandler {
    private final RateLimited rateLimitConfig;
    
    public RateLimitHandler(RateLimited rateLimitConfig) {
        this.rateLimitConfig = rateLimitConfig;
    }
    
    @Override
    public Object handle(InvocationContext context, HandlerChain next) throws Exception {
        String identifier = rateLimitConfig.identifier().isEmpty() ? 
            "default" : rateLimitConfig.identifier();
        
        if (!checkRateLimit(identifier, rateLimitConfig.requestsPerMinute())) {
            throw new RateLimitExceededException(
                "Rate limit exceeded: " + rateLimitConfig.requestsPerMinute() + " requests per minute");
        }
        
        return next.proceed();
    }
    
    private boolean checkRateLimit(String identifier, int requestsPerMinute) {
        // Simplified rate limiting implementation
        System.out.println("Checking rate limit for: " + identifier + 
            " (" + requestsPerMinute + " requests/minute)");
        return true; // Allow for demo
    }
}

class ValidationHandler implements InterceptorHandler {
    private final Validated validationConfig;
    
    public ValidationHandler(Validated validationConfig) {
        this.validationConfig = validationConfig;
    }
    
    @Override
    public Object handle(InvocationContext context, HandlerChain next) throws Exception {
        Object[] parameters = context.getParameters();
        
        for (Object param : parameters) {
            if (param != null) {
                validateParameter(param, validationConfig.groups(), validationConfig.failFast());
            }
        }
        
        return next.proceed();
    }
    
    private void validateParameter(Object param, Class<?>[] groups, boolean failFast) {
        // Simplified validation implementation
        System.out.println("Validating parameter: " + param.getClass().getSimpleName() + 
            " with groups: " + Arrays.toString(groups) + ", failFast: " + failFast);
    }
}

// Custom exception
class RateLimitExceededException extends RuntimeException {
    public RateLimitExceededException(String message) {
        super(message);
    }
}

Usage Examples

Examples of using the interceptor bindings in service classes.

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class BusinessService {
    
    // Method with multiple interceptor bindings
    @Audited(value = "user-creation", level = AuditLevel.INFO)
    @Secured(roles = {"admin", "user-manager"})
    @Monitored(name = "create-user", 
               metrics = {MetricType.EXECUTION_TIME, MetricType.INVOCATION_COUNT},
               includeParameters = true,
               tags = {"business", "user-management"})
    public User createUser(String username, String email) {
        // Business logic
        return new User(username, email);
    }
    
    // Cached method
    @Cached(ttlSeconds = 600, keyPrefix = "user-lookup")
    @Monitored(metrics = {MetricType.EXECUTION_TIME})
    public User findUser(String username) {
        // Expensive lookup operation
        return performUserLookup(username);
    }
    
    // Rate limited method
    @RateLimited(requestsPerMinute = 10, identifier = "email-sender")
    @Audited(value = "email-sent", level = AuditLevel.INFO)
    public void sendEmail(String to, String subject, String body) {
        // Email sending logic
        performEmailSend(to, subject, body);
    }
    
    // Validated method
    @Validated(groups = {ValidationGroup.Create.class}, failFast = true)
    @Audited(value = "order-processing", level = AuditLevel.WARN)
    public Order processOrder(OrderRequest request) {
        // Order processing logic
        return new Order(request);
    }
    
    // Multiple caching and monitoring
    @Cached(ttlSeconds = 300, keyPrefix = "reports")
    @Monitored(name = "generate-report", 
               includeParameters = true, 
               includeResult = false,
               tags = {"reports", "business-intelligence"})
    public ReportData generateReport(String reportType, Date fromDate, Date toDate) {
        // Report generation logic
        return new ReportData(reportType, fromDate, toDate);
    }
    
    // Helper methods (not intercepted)
    private User performUserLookup(String username) {
        // Database lookup
        return new User(username, username + "@example.com");
    }
    
    private void performEmailSend(String to, String subject, String body) {
        System.out.println("Sending email to: " + to + ", subject: " + subject);
    }
}

// Supporting classes
class User {
    private final String username;
    private final String email;
    
    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }
    
    // getters...
}

class Order {
    private final OrderRequest request;
    
    public Order(OrderRequest request) {
        this.request = request;
    }
}

class OrderRequest {
    // Order request fields
}

class ReportData {
    private final String type;
    private final Date fromDate;
    private final Date toDate;
    
    public ReportData(String type, Date fromDate, Date toDate) {
        this.type = type;
        this.fromDate = fromDate;
        this.toDate = toDate;
    }
}

interface ValidationGroup {
    interface Create {}
    interface Update {}
}

Best Practices

Defensive Programming

@Interceptor
public class SafeInterceptor {
    
    @AroundInvoke
    public Object safeIntercept(InvocationContext context) throws Exception {
        try {
            Set<Annotation> bindings = InterceptorBindings.getInterceptorBindings(context);
            
            if (bindings == null || bindings.isEmpty()) {
                // No bindings found, proceed without special handling
                return context.proceed();
            }
            
            // Process bindings safely
            return processBindings(context, bindings);
            
        } catch (Exception e) {
            // Log error but don't prevent method execution
            System.err.println("Interceptor error: " + e.getMessage());
            return context.proceed();
        }
    }
    
    private Object processBindings(InvocationContext context, Set<Annotation> bindings) throws Exception {
        // Safe binding processing
        for (Annotation binding : bindings) {
            if (binding != null) {
                processBinding(context, binding);
            }
        }
        return context.proceed();
    }
    
    private void processBinding(InvocationContext context, Annotation binding) {
        // Individual binding processing with error handling
        try {
            // Process specific binding
        } catch (Exception e) {
            System.err.println("Error processing binding " + binding.getClass().getSimpleName() + 
                ": " + e.getMessage());
        }
    }
}

Performance Considerations

@Interceptor
public class OptimizedInterceptor {
    
    // Cache binding analysis results
    private final Map<Method, Set<Annotation>> bindingCache = new ConcurrentHashMap<>();
    
    @AroundInvoke
    public Object optimizedIntercept(InvocationContext context) throws Exception {
        Method method = context.getMethod();
        
        // Use cached bindings if available
        Set<Annotation> bindings = bindingCache.computeIfAbsent(method, 
            m -> InterceptorBindings.getInterceptorBindings(context));
        
        if (bindings.isEmpty()) {
            return context.proceed();
        }
        
        // Process with cached bindings
        return processOptimized(context, bindings);
    }
    
    private Object processOptimized(InvocationContext context, Set<Annotation> bindings) throws Exception {
        // Optimized processing logic
        return context.proceed();
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-io-quarkus--quarkus-arc

docs

bean-container.md

bean-invocation.md

build-profiles.md

build-properties.md

index.md

interceptor-integration.md

logger-injection.md

runtime-lookup.md

tile.json