ThreadSafeBuildCompatibleExtension.java

package io.github.jonasrutishauser.thread.context.impl;

import static jakarta.interceptor.Interceptor.Priority.LIBRARY_AFTER;
import static jakarta.interceptor.Interceptor.Priority.LIBRARY_BEFORE;

import io.github.jonasrutishauser.thread.context.ThreadSafeScoped;
import jakarta.annotation.Priority;
import jakarta.enterprise.event.Shutdown;
import jakarta.enterprise.inject.build.compatible.spi.BeanInfo;
import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension;
import jakarta.enterprise.inject.build.compatible.spi.ClassConfig;
import jakarta.enterprise.inject.build.compatible.spi.Discovery;
import jakarta.enterprise.inject.build.compatible.spi.Enhancement;
import jakarta.enterprise.inject.build.compatible.spi.Messages;
import jakarta.enterprise.inject.build.compatible.spi.MetaAnnotations;
import jakarta.enterprise.inject.build.compatible.spi.Parameters;
import jakarta.enterprise.inject.build.compatible.spi.Registration;
import jakarta.enterprise.inject.build.compatible.spi.SkipIfPortableExtensionPresent;
import jakarta.enterprise.inject.build.compatible.spi.Synthesis;
import jakarta.enterprise.inject.build.compatible.spi.SyntheticComponents;
import jakarta.enterprise.inject.build.compatible.spi.SyntheticObserver;
import jakarta.enterprise.inject.build.compatible.spi.Types;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.enterprise.inject.spi.EventContext;

@SkipIfPortableExtensionPresent(ThreadSafeExtension.class)
public class ThreadSafeBuildCompatibleExtension implements BuildCompatibleExtension {

    @Priority(LIBRARY_BEFORE + 500)
    @Discovery
    public void addContext(MetaAnnotations metaAnnotations) {
        metaAnnotations.addContext(ThreadSafeScoped.class, true, ThreadSafeContext.class);
    }

    @Priority(LIBRARY_AFTER + 900)
    @Enhancement(types = Object.class, withSubtypes = true, withAnnotations = ThreadSafeScoped.class)
    public void addInterceptor(ClassConfig classConfig) {
        if (classConfig.info().hasAnnotation(ThreadSafeScoped.class)) {
            classConfig.addAnnotation(ThreadSafeScopedInterceptor.class);
        }
    }

    @Registration(types = Object.class)
    public void validateThreadSafeScopedBeans(BeanInfo bean, Messages messages) {
        if ("io.github.jonasrutishauser.thread.context.ThreadSafeScoped".equals(bean.scope().name())
                && (bean.isProducerField() || bean.isProducerMethod())) {
            messages.error("ThreadSafeScoped is not allowed on producer field or producer method in CDI Light", bean);
        }
    }

    @Priority(LIBRARY_BEFORE + 500)
    @Synthesis
    public void registerScopeShutdown(SyntheticComponents components, Types types) {
        components.addObserver(Shutdown.class) //
                .priority(LIBRARY_AFTER + 900) //
                .observeWith(ScopeShutdown.class);
    }

    public static class ScopeShutdown implements SyntheticObserver<Shutdown> {
        @Override
        public void observe(EventContext<Shutdown> event, Parameters params) throws Exception {
            ((ThreadSafeContext) CDI.current().getBeanManager().getContext(ThreadSafeScoped.class)).shutdown();
        }
    }

}