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

io.quarkus.smallrye.reactivemessaging.deployment.WiringHelper Maven / Gradle / Ivy

There is a newer version: 3.15.2
Show newest version
package io.quarkus.smallrye.reactivemessaging.deployment;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.stream.Collectors;

import org.eclipse.microprofile.config.ConfigProvider;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.MethodInfo;
import org.objectweb.asm.Opcodes;

import io.quarkus.arc.deployment.TransformedAnnotationsBuildItem;
import io.quarkus.arc.processor.BeanInfo;
import io.quarkus.arc.processor.InjectionPointInfo;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.smallrye.reactivemessaging.deployment.items.ChannelBuildItem;
import io.quarkus.smallrye.reactivemessaging.deployment.items.ChannelDirection;
import io.quarkus.smallrye.reactivemessaging.deployment.items.ConnectorBuildItem;
import io.smallrye.reactive.messaging.annotations.ConnectorAttribute;

public class WiringHelper {

    private WiringHelper() {
        // Avoid direct instantiation
    }

    /**
     * Retrieves the connector name from the given bean.
     * Throws a {@link NoSuchElementException} if the given bean does not have the {@code @Connector} qualifier
     * 
     * @param bi the bean
     * @return the connector name
     * @throws NoSuchElementException if the bean does not have the {@code @Connector} qualifier
     */
    static String getConnectorName(BeanInfo bi) {
        return bi.getQualifier(ReactiveMessagingDotNames.CONNECTOR)
                .orElseThrow(() -> new NoSuchElementException("Expecting a @Connector"))
                .value().asString();
    }

    static void produceIncomingChannel(BuildProducer producer, String name) {
        Optional managingConnector = getManagingConnector(ChannelDirection.INCOMING, name);
        if (managingConnector.isPresent()) {
            if (isChannelEnabled(ChannelDirection.INCOMING, name)) {
                producer.produce(ChannelBuildItem.incoming(name, managingConnector.get()));
            }
        } else {
            producer.produce(ChannelBuildItem.incoming(name, null));
        }
    }

    static void produceOutgoingChannel(BuildProducer producer, String name) {
        Optional managingConnector = getManagingConnector(ChannelDirection.OUTGOING, name);
        if (managingConnector.isPresent()) {
            if (isChannelEnabled(ChannelDirection.OUTGOING, name)) {
                producer.produce(ChannelBuildItem.outgoing(name, managingConnector.get()));
            }
        } else {
            producer.produce(ChannelBuildItem.outgoing(name, null));
        }
    }

    /**
     * Gets the name of the connector managing the channel if any.
     * This method looks inside the application configuration.
     * 
     * @param direction the direction (incoming or outgoing)
     * @param channel the channel name
     * @return an optional with the connector name if the channel is managed, empty otherwise
     */
    static Optional getManagingConnector(ChannelDirection direction, String channel) {
        return ConfigProvider.getConfig().getOptionalValue(
                "mp.messaging." + direction.name().toLowerCase() + "." + channel + ".connector",
                String.class);
    }

    /**
     * Checks if the given channel is enabled / disabled in the configuration
     *
     * @param direction the direction (incoming or outgoing)
     * @param channel the channel name
     * @return {@code true} if the channel is enabled, {@code false} otherwise
     */
    static boolean isChannelEnabled(ChannelDirection direction, String channel) {
        return ConfigProvider.getConfig()
                .getOptionalValue("mp.messaging." + direction.name().toLowerCase() + "." + channel + ".enabled",
                        Boolean.class)
                .orElse(true);
    }

    /**
     * Checks if the given class is an inbound (incoming) connector.
     * 
     * @param ci the class
     * @return {@code true} if the class implements the inbound connector interface
     */
    static boolean isInboundConnector(ClassInfo ci) {
        // TODO Add the internal interface support
        return ci.interfaceNames().contains(ReactiveMessagingDotNames.INCOMING_CONNECTOR_FACTORY);
    }

    /**
     * Checks if the given class is an outbound (outgoing) connector.
     * 
     * @param ci the class
     * @return {@code true} if the class implements the outbound connector interface
     */
    static boolean isOutboundConnector(ClassInfo ci) {
        // TODO Add the internal interface support
        return ci.interfaceNames().contains(ReactiveMessagingDotNames.OUTGOING_CONNECTOR_FACTORY);
    }

    /**
     * Collects connector attributes from the given connector implementation.
     * 
     * @param bi the bean implementing the connector interfaces
     * @param index the index
     * @param directions the attribute direction to includes in the result
     * @return the list of connector attributes, empty if none
     */
    static List getConnectorAttributes(BeanInfo bi, CombinedIndexBuildItem index,
            ConnectorAttribute.Direction... directions) {
        List attributes = bi.getImplClazz()
                .classAnnotationsWithRepeatable(ReactiveMessagingDotNames.CONNECTOR_ATTRIBUTES, index.getIndex())
                .stream().flatMap(ai -> Arrays.stream(ai.value().asNestedArray())).collect(Collectors.toList());
        if (attributes.isEmpty()) {
            AnnotationInstance attribute = bi.getImplClazz().classAnnotation(ReactiveMessagingDotNames.CONNECTOR_ATTRIBUTE);
            if (attribute != null) {
                attributes = Collections.singletonList(attribute);
            }
        }

        List att = new ArrayList<>();
        for (AnnotationInstance instance : attributes) {
            ConnectorAttribute.Direction direction = ConnectorAttribute.Direction
                    .valueOf(instance.value("direction").asString().toUpperCase());

            if (Arrays.asList(directions).contains(direction)) {
                ConnectorAttribute literal = createConnectorAttribute(instance, direction);
                att.add(literal);
            }
        }
        return att;
    }

    /**
     * Creates a {@code ConnectorAttribute} literal for the given instance.
     * 
     * @param instance the instance
     * @param direction the direction
     * @return the connector attribute.
     */
    private static ConnectorAttribute createConnectorAttribute(AnnotationInstance instance,
            ConnectorAttribute.Direction direction) {
        String name = instance.value("name").asString();
        String type = instance.value("type").asString();
        String description = instance.value("description").asString();

        boolean hidden = getBooleanValueOrDefault(instance, "hidden");
        boolean mandatory = getBooleanValueOrDefault(instance, "hidden");
        boolean deprecated = getBooleanValueOrDefault(instance, "deprecated");
        String defaultValue = getStringValueOrDefault(instance, "defaultValue");
        String alias = getStringValueOrDefault(instance, "alias");

        return ConnectorAttributeLiteral.create(name, description, hidden, mandatory,
                direction, defaultValue, deprecated, alias, type);
    }

    private static String getStringValueOrDefault(AnnotationInstance instance, String attribute) {
        AnnotationValue value = instance.value(attribute);
        if (value != null) {
            return value.asString();
        }
        return ConnectorAttribute.NO_VALUE;
    }

    private static boolean getBooleanValueOrDefault(AnnotationInstance instance, String attribute) {
        AnnotationValue value = instance.value(attribute);
        if (value != null) {
            return value.asBoolean();
        }
        return false;
    }

    /**
     * Finds a connector by name and direction in the given list.
     * 
     * @param connectors the list of connectors
     * @param name the name
     * @param direction the direction
     * @return the found connector, {@code null} otherwise
     */
    static ConnectorBuildItem find(List connectors, String name, ChannelDirection direction) {
        for (ConnectorBuildItem connector : connectors) {
            if (connector.getDirection() == direction && connector.getName().equalsIgnoreCase(name)) {
                return connector;
            }
        }
        return null;
    }

    static boolean hasConnector(List connectors, ChannelDirection direction, String name) {
        return connectors.stream().anyMatch(c -> c.getName().equalsIgnoreCase(name) && c.getDirection() == direction);
    }

    static Class toType(String type) throws ClassNotFoundException {
        if (type.equalsIgnoreCase("string")) {
            return String.class;
        }
        if (type.equalsIgnoreCase("int")) {
            return Integer.class;
        }
        if (type.equalsIgnoreCase("long")) {
            return Long.class;
        }
        if (type.equalsIgnoreCase("boolean")) {
            return Boolean.class;
        }
        return WiringHelper.class.getClassLoader().loadClass(type);
    }

    static boolean isSynthetic(MethodInfo method) {
        short flag = method.flags();
        return (flag & Opcodes.ACC_SYNTHETIC) != 0;
    }

    static Optional getAnnotation(TransformedAnnotationsBuildItem transformedAnnotations,
            InjectionPointInfo injectionPoint,
            DotName annotationName) {
        Collection annotations = transformedAnnotations.getAnnotations(injectionPoint.getTarget());
        for (AnnotationInstance annotation : annotations) {
            if (annotationName.equals(annotation.name())) {
                // For method parameter we must check the position
                if (annotation.target().kind() == AnnotationTarget.Kind.METHOD_PARAMETER
                        && injectionPoint.isParam()
                        && annotation.target().asMethodParameter().position() == injectionPoint.getPosition()) {
                    return Optional.of(annotation);
                } else if (annotation.target().kind() != AnnotationTarget.Kind.METHOD_PARAMETER) {
                    // For other kind, no need to check anything else
                    return Optional.of(annotation);
                }
            }
        }
        return Optional.empty();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy