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

io.quarkiverse.kafkastreamsprocessor.impl.KStreamProcessorSupplier Maven / Gradle / Ivy

/*-
 * #%L
 * Quarkus Kafka Streams Processor
 * %%
 * Copyright (C) 2024 Amadeus s.a.s.
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package io.quarkiverse.kafkastreamsprocessor.impl;

import java.lang.annotation.Annotation;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

import org.apache.kafka.streams.processor.api.Processor;
import org.apache.kafka.streams.processor.api.ProcessorSupplier;

import lombok.extern.slf4j.Slf4j;

/**
 * {@link ProcessorSupplier} used by the {@link TopologyProducer} to produce a {@link Processor} surrounded with some
 * decorators.
 */
@Dependent
@Slf4j
public class KStreamProcessorSupplier implements ProcessorSupplier {
    /**
     * Accessors to any Kafka 3's {@link Processor} implementations
     */
    private final Instance> kafka3BeanInstances;

    /**
     * Accessors to any Kafka 2's {@link org.apache.kafka.streams.processor.Processor} implementations
     */
    private final Instance> beanInstances;

    /**
     * Builders of {@link Kafka2ProcessorAdapter} instances
     */
    private final Instance> adapterInstances;

    /**
     * Injection constructor.
     *
     * @param kafka3BeanInstances
     *        accessors to any Kafka 3's {@link Processor} implementations
     * @param beanInstances
     *        accessors to any Kafka 2's {@link org.apache.kafka.streams.processor.Processor} implementations
     * @param adapterInstances
     *        builders of {@link Kafka2ProcessorAdapter} instances
     * @param beanManager
     *        the {@link BeanManager} instance to log the ordered list of {@link Processor} decorators declared in the
     *        framework and any extensions that might have been added
     */
    @Inject
    public KStreamProcessorSupplier(Instance> kafka3BeanInstances,
            Instance> beanInstances,
            Instance> adapterInstances, BeanManager beanManager) {
        this.kafka3BeanInstances = kafka3BeanInstances;
        this.beanInstances = beanInstances;
        this.adapterInstances = adapterInstances;

        log.info("Configured Processor decorators are in order: {}",
                beanManager.resolveDecorators(Set.of(Processor.class))
                        .stream()
                        .map(Bean::getBeanClass)
                        .map(Class::getName)
                        .collect(Collectors.joining(", ")));
    }

    /**
     * Returns one instance of the {@link Processor} (or {@link org.apache.kafka.streams.processor.Processor }) annotated
     * with {@link io.quarkiverse.kafkastreamsprocessor.api.Processor} annotation.
     * 

* The instance is also decorated with the decorators logged by the constructor. *

* * @return a processor instance annotated with {@link io.quarkiverse.kafkastreamsprocessor.api.Processor} */ @Override public Processor get() { Processor processor; Optional> kafka3Processor = kafka3BeanInstances.stream() .filter(bean -> KStreamProcessorSupplier.hasAnnotation(bean, io.quarkiverse.kafkastreamsprocessor.api.Processor.class)) .findFirst(); if (kafka3Processor.isEmpty()) { // Fallback to deprecated API, for backward compatibility. Optional> kafka2Processor = beanInstances.stream() .filter(bean -> KStreamProcessorSupplier.hasAnnotation(bean, io.quarkiverse.kafkastreamsprocessor.api.Processor.class)) .findFirst(); if (kafka2Processor.isEmpty()) { throw new IllegalArgumentException( "No bean found of type " + io.quarkiverse.kafkastreamsprocessor.api.Processor.class); } Kafka2ProcessorAdapter processorAdapter = adapterInstances.get(); processorAdapter.adapt((org.apache.kafka.streams.processor.Processor) kafka2Processor.get()); processor = processorAdapter; } else { processor = kafka3Processor.get(); } if (KStreamProcessorSupplier.hasAnnotation(processor, ApplicationScoped.class) || KStreamProcessorSupplier.hasAnnotation(processor, Singleton.class) || KStreamProcessorSupplier.hasAnnotation(processor, RequestScoped.class)) { throw new IllegalArgumentException( "Processors cannot have a scope other than @Dependant, since KafkaStreams implementation classes are not thread-safe"); } return (Processor) processor; } private static boolean hasAnnotation(Object bean, Class annotation) { // OJF-4000 microprofile annotation add a subClasses level, that why we have to check at the parent level. // The current test with several microprofile annotation shows that not necessary do check upper than the // first superclass, but an iteration has been introduced to support maybe some unknown case. Class current = bean.getClass(); while (current != null) { if (current.isAnnotationPresent(annotation)) { return true; } current = current.getSuperclass(); } return false; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy