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

io.fluxcapacitor.javaclient.configuration.spring.FluxCapacitorSpringConfig Maven / Gradle / Ivy

There is a newer version: 0.1015.0
Show newest version
package io.fluxcapacitor.javaclient.configuration.spring;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.fluxcapacitor.common.Registration;
import io.fluxcapacitor.javaclient.FluxCapacitor;
import io.fluxcapacitor.javaclient.common.serialization.Serializer;
import io.fluxcapacitor.javaclient.common.serialization.jackson.JacksonSerializer;
import io.fluxcapacitor.javaclient.common.serialization.upcasting.UpcastInspector;
import io.fluxcapacitor.javaclient.configuration.DefaultFluxCapacitor;
import io.fluxcapacitor.javaclient.configuration.FluxCapacitorBuilder;
import io.fluxcapacitor.javaclient.configuration.client.Client;
import io.fluxcapacitor.javaclient.configuration.client.InMemoryClient;
import io.fluxcapacitor.javaclient.configuration.client.WebSocketClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;

@Configuration
@Slf4j
public class FluxCapacitorSpringConfig implements BeanPostProcessor {

    private final ApplicationContext context;
    private final List springBeans = new CopyOnWriteArrayList<>();
    private final AtomicReference handlerRegistration = new AtomicReference<>();

    @Autowired
    protected FluxCapacitorSpringConfig(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        springBeans.add(bean);
        return bean;
    }

    @EventListener
    public void handle(ContextRefreshedEvent event) {
        FluxCapacitor fluxCapacitor = context.getBean(FluxCapacitor.class);
        Object[] localHandlers =
                springBeans.stream().filter(bean -> bean.getClass().isAnnotationPresent(LocalHandler.class)).toArray();
        Object[] trackingCandidates =
                springBeans.stream().filter(bean -> !bean.getClass().isAnnotationPresent(LocalHandler.class)).toArray();
        handlerRegistration.updateAndGet(
                r -> r == null ? fluxCapacitor.startTracking(trackingCandidates)
                                .merge(fluxCapacitor.registerLocalHandlers(localHandlers)) : r);
    }

    @EventListener
    public void handle(ContextClosedEvent event) {
        handlerRegistration.getAndUpdate(r -> null).cancel();
    }

    @Bean
    @ConditionalOnMissingBean
    public Serializer serializer() {
        List upcasters = new ArrayList<>();
        for (String beanName : context.getBeanDefinitionNames()) {
            Optional.ofNullable(context.getType(beanName)).filter(UpcastInspector::hasAnnotatedMethods)
                    .ifPresent(t -> upcasters.add(context.getAutowireCapableBeanFactory().getBean(beanName)));
        }
        return getBean(ObjectMapper.class).map(objectMapper -> new JacksonSerializer(objectMapper, upcasters))
                .orElse(new JacksonSerializer(upcasters));
    }

    @Bean
    @ConditionalOnMissingBean
    public FluxCapacitorBuilder fluxCapacitorBuilder(Serializer serializer) {
        return DefaultFluxCapacitor.builder().serializer(serializer).snapshotSerializer(serializer);
    }

    @Bean
    @ConditionalOnMissingBean
    public FluxCapacitor fluxCapacitor(FluxCapacitorBuilder builder) {
        Client client = getBean(Client.class).orElseGet(() -> getBean(WebSocketClient.Properties.class).map(
                WebSocketClient::newInstance).orElseGet(() -> {
            log.info("Using in-memory Flux Capacitor client");
            return InMemoryClient.newInstance();
        }));
        return builder.build(client);
    }

    protected  Optional getBean(Class type) {
        return context.getBeansOfType(type).values().stream().findFirst();
    }

}