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

de.otto.synapse.annotation.EventSourceConsumerBeanPostProcessor Maven / Gradle / Ivy

Go to download

A library used at otto.de to implement Spring Boot based event-sourcing microservices.

There is a newer version: 0.33.1
Show newest version
package de.otto.synapse.annotation;

import de.otto.synapse.consumer.MethodInvokingMessageConsumer;
import de.otto.synapse.eventsource.EventSource;
import org.slf4j.Logger;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static org.slf4j.LoggerFactory.getLogger;
import static org.springframework.core.MethodIntrospector.selectMethods;

public class EventSourceConsumerBeanPostProcessor implements BeanPostProcessor, Ordered, ApplicationContextAware {

    private static final Logger LOG = getLogger(EventSourceConsumerBeanPostProcessor.class);

    private final Set> nonAnnotatedClasses =
            Collections.newSetFromMap(new ConcurrentHashMap, Boolean>(64));


    private ConfigurableApplicationContext applicationContext;

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
        if (applicationContext instanceof ConfigurableApplicationContext) {
            this.applicationContext = (ConfigurableApplicationContext) applicationContext;
        }
    }

    @Override
    public Object postProcessBeforeInitialization(final Object bean, final String beanName) {
        if (!this.nonAnnotatedClasses.contains(bean.getClass())) {
            final Class targetClass = AopUtils.getTargetClass(bean);
            final Map> annotatedMethods = findMethodsAnnotatedWithEventSourceConsumer(targetClass);
            if (annotatedMethods.isEmpty()) {
                this.nonAnnotatedClasses.add(bean.getClass());
                LOG.trace("No @EventSourceConsumer annotations found on bean type: {}", bean.getClass());
            } else {
                registerEventConsumers(bean, beanName, annotatedMethods);
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, final String beanName) {
        return bean;
    }

    private Map> findMethodsAnnotatedWithEventSourceConsumer(Class targetClass) {
        return selectMethods(targetClass,
                (MethodIntrospector.MetadataLookup>) method -> {
                    final Set consumerAnnotations = consumerAnnotationsOf(method);
                    return (!consumerAnnotations.isEmpty() ? consumerAnnotations : null);
                });
    }

    private void registerEventConsumers(final Object bean,
                                        final String beanName,
                                        final Map> annotatedMethods) {
        for (Map.Entry> entry : annotatedMethods.entrySet()) {
            final Method method = entry.getKey();
            for (EventSourceConsumer consumerAnnotation : entry.getValue()) {
                matchingEventSourceFor(consumerAnnotation)
                        .register(eventConsumerFor(consumerAnnotation, method, bean));
            }
        }
        LOG.info("{} @EventSourceConsumer methods processed on bean {} : {}'", annotatedMethods.size(), beanName, annotatedMethods);
    }

    /*
     * AnnotationUtils.getRepeatableAnnotations does not look at interfaces
     */
    private Set consumerAnnotationsOf(final Method method) {
        Set listeners = new HashSet<>();
        EventSourceConsumer ann = AnnotationUtils.findAnnotation(method, EventSourceConsumer.class);
        if (ann != null) {
            listeners.add(ann);
        }
        return listeners;
    }

    private MethodInvokingMessageConsumer eventConsumerFor(final EventSourceConsumer annotation,
                                                              final Method annotatedMethod,
                                                              final Object bean) {
        return new MethodInvokingMessageConsumer<>(annotation.keyPattern(), annotation.payloadType(), bean, annotatedMethod);
    }

    private EventSource matchingEventSourceFor(final EventSourceConsumer annotation) {
        return applicationContext.getBean(annotation.eventSource(), EventSource.class);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy