All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.quarkiverse.omnifaces.deployment.OmnifacesProcessor Maven / Gradle / Ivy

package io.quarkiverse.omnifaces.deployment;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.inject.spi.InjectionPoint;
import jakarta.inject.Inject;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.ClassType;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Type;
import org.jboss.logging.Logger;
import org.omnifaces.cdi.ContextParam;
import org.omnifaces.cdi.Cookie;
import org.omnifaces.cdi.Eager;
import org.omnifaces.cdi.GraphicImageBean;
import org.omnifaces.cdi.Param;
import org.omnifaces.cdi.PostScriptParam;
import org.omnifaces.cdi.Push;
import org.omnifaces.cdi.Startup;
import org.omnifaces.cdi.ViewScoped;
import org.omnifaces.cdi.converter.ConverterManager;
import org.omnifaces.cdi.eager.EagerBeansRepository;
import org.omnifaces.cdi.validator.ValidatorManager;
import org.omnifaces.cdi.viewscope.ViewScopeManager;
import org.omnifaces.resourcehandler.CombinedResourceHandler;
import org.omnifaces.resourcehandler.WebAppManifest;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
import io.quarkus.arc.deployment.BeanDefiningAnnotationBuildItem;
import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem;
import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem;
import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem.ContextConfiguratorBuildItem;
import io.quarkus.arc.deployment.CustomScopeBuildItem;
import io.quarkus.arc.deployment.KnownCompatibleBeanArchiveBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.arc.processor.AnnotationsTransformer;
import io.quarkus.arc.processor.InjectionPointInfo;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.AdditionalApplicationArchiveMarkerBuildItem;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.NativeImageFeatureBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild;
import io.quarkus.omnifaces.runtime.OmniFacesFeature;
import io.quarkus.omnifaces.runtime.OmniFacesRecorder;
import io.quarkus.omnifaces.runtime.ParamBeanCreator;
import io.quarkus.omnifaces.runtime.scopes.OmniFacesQuarkusViewScope;
import io.quarkus.undertow.deployment.ServletInitParamBuildItem;

class OmnifacesProcessor {

    private static final Logger LOGGER = Logger.getLogger("OmniFaces");
    private static final String FEATURE = "omnifaces";

    static final DotName OMNIFACES_STARTUP = DotName.createSimple(Startup.class.getName());
    static final DotName OMNIFACES_EAGER = DotName.createSimple(Eager.class.getName());
    static final DotName OMNIFACES_PARAM = DotName.createSimple(Param.class.getName());
    static final DotName INJECT_ANNOTATION = DotName.createSimple(Inject.class.getName());
    static final DotName PRODUCES_ANNOTATION = DotName.createSimple(Produces.class.getName());
    static final DotName INJECTION_POINT = DotName.createSimple(InjectionPoint.class.getName());

    private static final Class[] BEAN_CLASSES = {
            EagerBeansRepository.class,
            ValidatorManager.class,
            ViewScopeManager.class,
            ConverterManager.class
    };

    private static final String[] BEAN_DEFINING_ANNOTATION_CLASSES = {
            ContextParam.class.getName(),
            Cookie.class.getName(),
            Eager.class.getName(),
            GraphicImageBean.class.getName(),
            Param.class.getName(),
            PostScriptParam.class.getName(),
            Push.class.getName(),
            Startup.class.getName()
    };

    @BuildStep
    FeatureBuildItem feature() {
        return new FeatureBuildItem(FEATURE);
    }

    @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class)
    NativeImageFeatureBuildItem nativeImageFeature() {
        return new NativeImageFeatureBuildItem(OmniFacesFeature.class);
    }

    @BuildStep
    void buildCdiBeans(BuildProducer additionalBean,
            BuildProducer beanDefiningAnnotation) {
        for (Class clazz : BEAN_CLASSES) {
            additionalBean.produce(AdditionalBeanBuildItem.unremovableOf(clazz));
        }

        for (String clazz : BEAN_DEFINING_ANNOTATION_CLASSES) {
            beanDefiningAnnotation.produce(new BeanDefiningAnnotationBuildItem(DotName.createSimple(clazz)));
        }
    }

    @BuildStep
    ContextConfiguratorBuildItem registerViewScopeContext(ContextRegistrationPhaseBuildItem phase) {
        return new ContextConfiguratorBuildItem(
                phase.getContext().configure(ViewScoped.class).normal()
                        .contextClass(OmniFacesQuarkusViewScope.class));
    }

    @BuildStep
    CustomScopeBuildItem viewScoped() {
        return new CustomScopeBuildItem(DotName.createSimple(ViewScoped.class.getName()));
    }

    @BuildStep
    void produceApplicationArchiveMarker(
            BuildProducer additionalArchiveMarkers) {
        additionalArchiveMarkers.produce(new AdditionalApplicationArchiveMarkerBuildItem("org/omnifaces/component"));
    }

    @BuildStep
    void produceNativeResources(BuildProducer nativeImageResourceProducer) {
        nativeImageResourceProducer
                .produce(new NativeImageResourceBuildItem("META-INF/maven/org.omnifaces/omnifaces/pom.properties"));
    }

    @BuildStep
    void produceKnownCompatible(BuildProducer knownCompatibleProducer) {
        // GitHub #62: bean discovery mode in beans.xml
        knownCompatibleProducer.produce(new KnownCompatibleBeanArchiveBuildItem("org.omnifaces", "omnifaces"));
    }

    @BuildStep
    @Record(ExecutionTime.STATIC_INIT)
    void buildAnnotationProviderIntegration(OmniFacesRecorder recorder, CombinedIndexBuildItem combinedIndex) {
        for (String clazz : BEAN_DEFINING_ANNOTATION_CLASSES) {
            combinedIndex.getIndex()
                    .getAnnotations(DotName.createSimple(clazz))
                    .forEach(annotation -> {
                        if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) {
                            recorder.registerAnnotatedClass(annotation.name().toString(),
                                    annotation.target().asClass().name().toString());
                        }
                    });
        }
    }

    @BuildStep
    void registerForReflection(BuildProducer reflectiveClass,
            CombinedIndexBuildItem combinedIndex) {

        final List classNames = new ArrayList<>();
        // All EL functions
        classNames.addAll(collectClassesInPackage(combinedIndex, "org.omnifaces.el.functions"));
        // All utilities
        classNames.addAll(collectClassesInPackage(combinedIndex, "org.omnifaces.util"));

        reflectiveClass.produce(
                ReflectiveClassBuildItem.builder(classNames.toArray(new String[0])).methods(true).serialization(true).build());
    }

    @Record(ExecutionTime.STATIC_INIT)
    @BuildStep
    void registerWebManifests(OmniFacesRecorder recorder, BuildProducer unremovableBeans) {
        // make WebManifest beans un-removable, users still have to make them beans
        // https://github.com/quarkiverse/quarkus-omnifaces/issues/72
        unremovableBeans.produce(UnremovableBeanBuildItem.beanTypes(WebAppManifest.class));
    }

    @BuildStep
    void substrateResourceBuildItems(BuildProducer nativeImageResourceProducer,
            BuildProducer resourceBundleBuildItem) {
        nativeImageResourceProducer.produce(new NativeImageResourceBuildItem(
                "META-INF/omnifaces-functions.taglib.xml",
                "META-INF/omnifaces-ui.taglib.xml",
                "META-INF/web-fragment.xml",
                "META-INF/faces-config.xml",
                "META-INF/web.xml",
                "org/omnifaces/messages.properties",
                "META-INF/rsc/myfaces-dev-error-include.xml",
                "META-INF/services/jakarta.servlet.ServletContainerInitializer",
                "META-INF/maven/org.omnifaces/omnifaces/pom.properties"));

        resourceBundleBuildItem.produce(new NativeImageResourceBundleBuildItem("org.omnifaces.messages"));
    }

    /**
     * Builds development-specific initialization parameters.
     * This method is only executed in development mode.
     *
     * @param initParam The producer for servlet initialization parameters
     */
    @BuildStep(onlyIf = IsDevelopment.class)
    void buildDevelopmentInitParams(BuildProducer initParam) {
        // Disables combined resource handler in dev mode
        initParam.produce(new ServletInitParamBuildItem(CombinedResourceHandler.PARAM_NAME_DISABLED, "true"));
    }

    /**
     * Replaces {@link org.omnifaces.cdi.Eager} and {@link org.omnifaces.cdi.Startup} with the Quarkus equivalent
     * annotations for {@link jakarta.enterprise.context.ApplicationScoped} and {@link io.quarkus.runtime.Startup}.
     */
    @BuildStep
    AnnotationsTransformerBuildItem transformBeanScope(CombinedIndexBuildItem combinedIndex) {

        return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() {
            @Override
            public boolean appliesTo(AnnotationTarget.Kind kind) {
                return kind == org.jboss.jandex.AnnotationTarget.Kind.CLASS;
            }

            @Override
            public void transform(AnnotationsTransformer.TransformationContext ctx) {

                if (ctx.isClass()) {
                    final ClassInfo clazz = ctx.getTarget().asClass();
                    final Map> annotations = clazz.annotationsMap();
                    if (annotations.containsKey(OMNIFACES_STARTUP)) {
                        LOGGER.debugf("OmniFaces found @%s annotations on a class %s - adding @ApplicationScoped",
                                OMNIFACES_STARTUP, ctx.getTarget());
                        ctx.transform().add(ApplicationScoped.class).done();
                    }
                    if (annotations.containsKey(OMNIFACES_EAGER) || annotations.containsKey(OMNIFACES_STARTUP)) {
                        LOGGER.debugf("OmniFaces found @Eager annotations on a class %s - adding @io.quarkus.runtime.Startup",
                                ctx.getTarget());
                        ctx.transform().add(io.quarkus.runtime.Startup.class).done();
                    }
                }
            }
        });
    }

    /**
     * Inspects injection points for @Param annotations and creates synthetic beans for each required type.
     *
     * This method performs the following steps:
     * 1. Iterates through all injection points to find those annotated with @Param.
     * 2. Collects the required types for these injection points.
     * 3. For each required type, creates and registers a synthetic bean.
     *
     * @param beanDiscoveryFinishedBuildItem Contains information about discovered beans and injection points.
     * @param syntheticBeanBuildItemBuildProducer Producer for creating synthetic beans.
     */
    @BuildStep
    public void inspectInjectionPoints(BeanDiscoveryFinishedBuildItem beanDiscoveryFinishedBuildItem,
            BuildProducer syntheticBeanBuildItemBuildProducer) {
        // go through all injection points, look for those with @Param, store required types
        Set requiredParamTypes = new HashSet<>();
        for (InjectionPointInfo ipInfo : beanDiscoveryFinishedBuildItem.getInjectionPoints()) {
            if (ipInfo.getRequiredQualifier(OMNIFACES_PARAM) != null) {
                requiredParamTypes.add(ipInfo.getRequiredType());
            }
        }

        // for each required type, register a synthetic bean
        for (Type requiredType : requiredParamTypes) {
            SyntheticBeanBuildItem synthBean = SyntheticBeanBuildItem.configure(DotName.createSimple(Object.class))
                    .scope(Dependent.class)
                    .addType(requiredType)
                    .addQualifier(Param.class)
                    .unremovable()
                    .addInjectionPoint(ClassType.create(INJECTION_POINT))
                    .creator(ParamBeanCreator.class)
                    .done();
            syntheticBeanBuildItemBuildProducer.produce(synthBean);
        }
    }

    public List collectClassesInPackage(CombinedIndexBuildItem combinedIndex, String packageName) {
        final List classes = new ArrayList<>();
        final List packages = new ArrayList<>(combinedIndex.getIndex().getSubpackages(packageName));
        packages.add(DotName.createSimple(packageName));
        for (DotName aPackage : packages) {
            final List packageClasses = combinedIndex.getIndex()
                    .getClassesInPackage(aPackage)
                    .stream()
                    .map(ClassInfo::toString)
                    .toList();
            classes.addAll(packageClasses);
        }
        return classes;
    }

    private List collectSubclasses(CombinedIndexBuildItem combinedIndex, String className) {
        List classes = combinedIndex.getIndex()
                .getAllKnownSubclasses(DotName.createSimple(className))
                .stream()
                .map(ClassInfo::toString)
                .collect(Collectors.toList());
        classes.add(className);
        return classes;
    }

    public List collectImplementors(CombinedIndexBuildItem combinedIndex, String className) {
        List classes = combinedIndex.getIndex()
                .getAllKnownImplementors(DotName.createSimple(className))
                .stream()
                .map(ClassInfo::toString)
                .collect(Collectors.toList());
        classes.add(className);
        return classes;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy