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

io.quarkus.resteasy.reactive.server.deployment.QuarkusServerEndpointIndexer Maven / Gradle / Ivy

package io.quarkus.resteasy.reactive.server.deployment;

import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import javax.ws.rs.core.MediaType;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.common.ResteasyReactiveConfig;
import org.jboss.resteasy.reactive.common.processor.DefaultProducesHandler;
import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationStore;
import org.jboss.resteasy.reactive.server.model.ServerResourceMethod;
import org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer;
import org.jboss.resteasy.reactive.server.processor.ServerIndexedParameter;
import org.jboss.resteasy.reactive.server.spi.EndpointInvokerFactory;

import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.resteasy.reactive.common.deployment.JsonDefaultProducersHandler;
import io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveRecorder;

public class QuarkusServerEndpointIndexer
        extends ServerEndpointIndexer {

    private static final org.jboss.logging.Logger LOGGER = Logger.getLogger(QuarkusServerEndpointIndexer.class);

    private final Capabilities capabilities;
    private final BuildProducer generatedClassBuildItemBuildProducer;
    private final BuildProducer bytecodeTransformerBuildProducer;
    private final BuildProducer reflectiveClassProducer;
    private final DefaultProducesHandler defaultProducesHandler;
    private final JsonDefaultProducersHandler jsonDefaultProducersHandler;
    private final ResteasyReactiveRecorder resteasyReactiveRecorder;

    private final Predicate applicationClassPredicate;

    QuarkusServerEndpointIndexer(Builder builder) {
        super(builder);
        this.capabilities = builder.capabilities;
        this.generatedClassBuildItemBuildProducer = builder.generatedClassBuildItemBuildProducer;
        this.bytecodeTransformerBuildProducer = builder.bytecodeTransformerBuildProducer;
        this.reflectiveClassProducer = builder.reflectiveClassProducer;
        this.defaultProducesHandler = builder.defaultProducesHandler;
        this.applicationClassPredicate = builder.applicationClassPredicate;
        this.resteasyReactiveRecorder = builder.resteasyReactiveRecorder;
        this.jsonDefaultProducersHandler = new JsonDefaultProducersHandler();
    }

    private DefaultProducesHandler.Context currentDefaultProducesContext;

    @Override
    protected void setupApplyDefaults(Type nonAsyncReturnType, DotName httpMethod) {
        currentDefaultProducesContext = new DefaultProducesHandler.Context() {
            @Override
            public Type nonAsyncReturnType() {
                return nonAsyncReturnType;
            }

            @Override
            public DotName httpMethod() {
                return httpMethod;
            }

            @Override
            public IndexView index() {
                return applicationIndex;
            }

            @Override
            public ResteasyReactiveConfig config() {
                return config;
            }
        };
    }

    @Override
    protected String[] applyAdditionalDefaults(Type nonAsyncReturnType) {
        List defaultMediaTypes = defaultProducesHandler.handle(currentDefaultProducesContext);
        if ((defaultMediaTypes != null) && !defaultMediaTypes.isEmpty()) {
            String[] result = new String[defaultMediaTypes.size()];
            for (int i = 0; i < defaultMediaTypes.size(); i++) {
                result[i] = defaultMediaTypes.get(i).toString();
            }
            return result;
        }
        return super.applyAdditionalDefaults(nonAsyncReturnType);
    }

    @Override
    protected boolean handleCustomParameter(Map anns, ServerIndexedParameter builder,
            Type paramType, boolean field, Map methodContext) {
        methodContext.put(GeneratedClassBuildItem.class.getName(), generatedClassBuildItemBuildProducer);
        methodContext.put(EndpointInvokerFactory.class.getName(), resteasyReactiveRecorder);
        return super.handleCustomParameter(anns, builder, paramType, field, methodContext);
    }

    public static final class Builder extends AbstractBuilder {

        private final Capabilities capabilities;

        private BuildProducer generatedClassBuildItemBuildProducer;
        private BuildProducer bytecodeTransformerBuildProducer;
        private BuildProducer reflectiveClassProducer;
        private ResteasyReactiveRecorder resteasyReactiveRecorder;
        private DefaultProducesHandler defaultProducesHandler = DefaultProducesHandler.Noop.INSTANCE;
        public Predicate applicationClassPredicate;

        public Builder(Capabilities capabilities) {
            this.capabilities = capabilities;
        }

        @Override
        public QuarkusServerEndpointIndexer build() {
            return new QuarkusServerEndpointIndexer(this);
        }

        public Builder setBytecodeTransformerBuildProducer(
                BuildProducer bytecodeTransformerBuildProducer) {
            this.bytecodeTransformerBuildProducer = bytecodeTransformerBuildProducer;
            return this;
        }

        public Builder setGeneratedClassBuildItemBuildProducer(
                BuildProducer generatedClassBuildItemBuildProducer) {
            this.generatedClassBuildItemBuildProducer = generatedClassBuildItemBuildProducer;
            return this;
        }

        public Builder setReflectiveClassProducer(
                BuildProducer reflectiveClassProducer) {
            this.reflectiveClassProducer = reflectiveClassProducer;
            return this;
        }

        public Builder setApplicationClassPredicate(Predicate applicationClassPredicate) {
            this.applicationClassPredicate = applicationClassPredicate;
            return this;
        }

        public Builder setResteasyReactiveRecorder(ResteasyReactiveRecorder resteasyReactiveRecorder) {
            this.resteasyReactiveRecorder = resteasyReactiveRecorder;
            return this;
        }

        public Builder setDefaultProducesHandler(DefaultProducesHandler defaultProducesHandler) {
            this.defaultProducesHandler = defaultProducesHandler;
            return this;
        }
    }

    @Override
    protected void handleAdditionalMethodProcessing(ServerResourceMethod method, ClassInfo currentClassInfo,
            MethodInfo info, AnnotationStore annotationStore) {
        super.handleAdditionalMethodProcessing(method, currentClassInfo, info, annotationStore);
        warnAboutMissingJsonProviderIfNeeded(method, info);
    }

    private void warnAboutMissingJsonProviderIfNeeded(ServerResourceMethod method, MethodInfo info) {
        if (!capabilities.isCapabilityWithPrefixMissing("io.quarkus.resteasy.reactive.json")) {
            return;
        }
        if (hasJson(method) || (hasNoTypesDefined(method) && isDefaultJson())) {
            LOGGER.warnf("Quarkus detected the use of JSON in JAX-RS method '" + info.declaringClass().name() + "#"
                    + info.name()
                    + "' but no JSON extension has been added. Consider adding 'quarkus-resteasy-reactive-jackson' or 'quarkus-resteasy-reactive-jsonb'.");
        }
    }

    private boolean isDefaultJson() {
        List mediaTypes = jsonDefaultProducersHandler.handle(currentDefaultProducesContext);
        for (MediaType mediaType : mediaTypes) {
            if (isJson(mediaType.toString())) {
                return true;
            }
        }
        return false;
    }

    private boolean hasJson(ServerResourceMethod method) {
        return hasJson(method.getProduces()) || hasJson(method.getConsumes()) || isJson(method.getStreamElementType());
    }

    private boolean hasNoTypesDefined(ServerResourceMethod method) {
        return (method.getProduces() == null || method.getProduces().length == 0) &&
                (method.getConsumes() == null || method.getConsumes().length == 0) &&
                (method.getStreamElementType() == null);
    }

    private boolean hasJson(String[] types) {
        if (types == null) {
            return false;
        }
        for (String type : types) {
            if (isJson(type)) {
                return true;
            }
        }
        return false;
    }

    private boolean isJson(String type) {
        if (type == null) {
            return false;
        }
        return type.startsWith(MediaType.APPLICATION_JSON);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy