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

io.fluxcapacitor.javaclient.common.serialization.DeserializingMessage Maven / Gradle / Ivy

There is a newer version: 0.1015.0
Show newest version
package io.fluxcapacitor.javaclient.common.serialization;

import io.fluxcapacitor.common.MessageType;
import io.fluxcapacitor.common.Registration;
import io.fluxcapacitor.common.api.Metadata;
import io.fluxcapacitor.common.api.SerializedMessage;
import io.fluxcapacitor.common.handling.HandlerInspector.MethodHandlerInvoker;
import io.fluxcapacitor.common.handling.MethodInvokerFactory;
import io.fluxcapacitor.common.handling.ParameterResolver;
import io.fluxcapacitor.javaclient.common.Message;
import io.fluxcapacitor.javaclient.modeling.AggregateIdResolver;
import io.fluxcapacitor.javaclient.modeling.AggregateTypeResolver;
import io.fluxcapacitor.javaclient.tracking.handling.DeserializingMessageParameterResolver;
import io.fluxcapacitor.javaclient.tracking.handling.MessageParameterResolver;
import io.fluxcapacitor.javaclient.tracking.handling.MetadataParameterResolver;
import io.fluxcapacitor.javaclient.tracking.handling.PayloadParameterResolver;
import io.fluxcapacitor.javaclient.tracking.handling.authentication.UserParameterResolver;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.experimental.Delegate;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static java.time.Instant.ofEpochMilli;
import static java.util.stream.Collectors.toList;

@Value
@AllArgsConstructor
@Slf4j
public class DeserializingMessage {
    public static MessageFormatter messageFormatter = MessageFormatter.DEFAULT;
    public static List> defaultParameterResolvers =
            Arrays.asList(new DeserializingMessageParameterResolver(),
                          new PayloadParameterResolver(), new MetadataParameterResolver(),
                          new MessageParameterResolver(), new AggregateIdResolver(),
                          new AggregateTypeResolver(), new UserParameterResolver());
    public static MethodInvokerFactory defaultInvokerFactory = MethodHandlerInvoker::new;

    private static final ThreadLocal> messageCompletionHandlers = new ThreadLocal<>();
    private static final ThreadLocal> batchCompletionHandlers = new ThreadLocal<>();
    private static final ThreadLocal> batchResources = new ThreadLocal<>();
    private static final ThreadLocal current = new ThreadLocal<>();

    @Delegate
    DeserializingObject delegate;
    MessageType messageType;

    public DeserializingMessage(SerializedMessage message, Supplier payload, MessageType messageType) {
        this(new DeserializingObject<>(message, payload), messageType);
    }

    public void run(Consumer task) {
        apply(m -> {
            task.accept(m);
            return null;
        });
    }

    public  T apply(Function action) {
        return handleBatch(Stream.of(this)).map(action).collect(toList()).get(0);
    }

    public Metadata getMetadata() {
        return delegate.getSerializedObject().getMetadata();
    }

    public Message toMessage() {
        return new Message(delegate.getPayload(), getMetadata(),
                           delegate.getSerializedObject().getMessageId(),
                           ofEpochMilli(delegate.getSerializedObject().getTimestamp()));
    }

    public static DeserializingMessage getCurrent() {
        return current.get();
    }

    public static Registration whenBatchCompletes(Runnable handler) {
        if (batchCompletionHandlers.get() == null) {
            batchCompletionHandlers.set(new ArrayList<>());
        }
        Collection handlers = batchCompletionHandlers.get();
        handlers.add(handler);
        return () -> handlers.remove(handler);
    }

    public static void whenMessageCompletes(Runnable handler) {
        if (messageCompletionHandlers.get() == null) {
            messageCompletionHandlers.set(new ArrayList<>());
        }
        Collection handlers = messageCompletionHandlers.get();
        handlers.add(handler);
    }


    public static Stream handleBatch(Stream batch) {
        return StreamSupport.stream(new MessageSpliterator(batch.spliterator()), false);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static  V computeForBatch(K key, BiFunction function) {
        return (V) getResources().compute(key, (BiFunction) function);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static  V computeForBatchIfAbsent(K key, Function function) {
        return (V) getResources().computeIfAbsent(key, (Function) function);
    }

    @SuppressWarnings("unchecked")
    public static  V getBatchResource(Object key) {
        return (V) getResources().get(key);
    }

    private static Map getResources() {
        if (batchResources.get() == null) {
            batchResources.set(new HashMap<>());
        }
        return batchResources.get();
    }

    @Override
    public String toString() {
        return messageFormatter.apply(this);
    }

    private static void setCurrent(DeserializingMessage message) {
        current.set(message);
        if (message == null) {
            Optional.ofNullable(messageCompletionHandlers.get()).ifPresent(handlers -> {
                messageCompletionHandlers.remove();
                handlers.forEach(Runnable::run);
            });
        }
    }

    private static class MessageSpliterator extends Spliterators.AbstractSpliterator {
        private final Spliterator upStream;

        public MessageSpliterator(Spliterator upStream) {
            super(upStream.estimateSize(), upStream.characteristics());
            this.upStream = upStream;
        }

        @Override
        public boolean tryAdvance(Consumer action) {
            boolean hadNext = upStream.tryAdvance(d -> {
                DeserializingMessage previous = getCurrent();
                try {
                    setCurrent(d);
                    action.accept(d);
                } finally {
                    setCurrent(previous);
                }
            });
            if (!hadNext && DeserializingMessage.getCurrent() == null) {
                try {
                    Optional.ofNullable(batchCompletionHandlers.get()).ifPresent(handlers -> {
                        batchCompletionHandlers.remove();
                        handlers.forEach(Runnable::run);
                    });
                } finally {
                    batchResources.remove();
                    batchCompletionHandlers.remove();
                }
            }
            return hadNext;
        }
    }
}