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

com.ziqni.admin.sdk.streaming.handlers.RpcResultsEventHandler Maven / Gradle / Ivy

There is a newer version: 1.0.21
Show newest version
/*
 * Copyright (c) 2022. ZIQNI LTD registered in England and Wales, company registration number-09693684
 */
package com.ziqni.admin.sdk.streaming.handlers;

import com.fasterxml.jackson.databind.JavaType;
import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;
import com.ziqni.admin.sdk.streaming.ApiCallbackResponseExpired;
import com.ziqni.admin.sdk.streaming.EventHandler;
import com.ziqni.admin.sdk.streaming.Message;
import com.ziqni.admin.sdk.streaming.OonRemovalListener;
import com.ziqni.admin.sdk.util.ClassScanner;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.simp.stomp.StompHeaders;

import java.lang.reflect.Type;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;

public class RpcResultsEventHandler extends EventHandler {

    public final static String DEFAULT_TOPIC = "/user/queue/rpc-results";
    public final static String CLASS_TO_SCAN_FOR_PAYLOAD_TYPE = "com.ziqni.admin.sdk.model";
    private static final Logger logger = LoggerFactory.getLogger(RpcResultsEventHandler.class);
    private static final AtomicLong sequenceNumber = new AtomicLong(0);

    public static final AsyncCache> awaitingResponseCache = Caffeine.newBuilder()
            .maximumSize(10000)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .evictionListener(new OonRemovalListener(logger))
            .buildAsync();
    private static final ExecutorService executorService = new ForkJoinPool(Runtime.getRuntime().availableProcessors()*4);

    private final String topic;
    private final ClassScanner classScanner;

    public RpcResultsEventHandler() {
        this(DEFAULT_TOPIC);
    }

    public RpcResultsEventHandler(String topic) {
        this.topic = topic;
        this.classScanner = new ClassScanner(CLASS_TO_SCAN_FOR_PAYLOAD_TYPE);
    }

    @Override
    public String getTopic() {
        return topic;
    }

    @Override
    public JavaType getValType(StompHeaders headers) {
        return objectMapper.constructType(getPayloadType(headers));
    }

    @Override
    public Type getPayloadType(StompHeaders headers) {
        return this.classScanner.get(headers.getFirst("objectType")).orElse(Object.class);
    }

    @Override
    public void handleFrame(StompHeaders headers, Object payload) {
        var messageId = getMessageId(headers);

        if(messageId.isPresent()){
            handleWithMessageId(messageId.get(), headers, payload);
        }
        else {
            if(!payload.getClass().isInstance(Message.class))
                logger.error("No sequence number provided. Headers: " + headers + ". Payload: " + payload);
        }
    }

    public static   RpcResultsResponse submit(String destination, TIN payload, CompletableFuture completableFuture, BiConsumer doSend){

        final var messageId = sequenceNumber.incrementAndGet();
        final var streamingResponse = new RpcResultsResponse<>(messageId, payload, completableFuture);

        try {
            var nextSeq = Long.toString(messageId);
            StompHeaders headers = new StompHeaders();
            headers.setDestination(destination);
            headers.setMessageId(nextSeq);

            logger.debug("WS sent request to destination [{}] with receipt id [{}] and payload [{}] and headers [{}] and callback []", destination, nextSeq, payload, headers.toSingleValueMap());

            doSend.accept(headers, payload);
            final var in = new CompletableFuture>();
            in.complete(streamingResponse);
            awaitingResponseCache.put(streamingResponse.getSequenceNumberAsString(), in);
            return streamingResponse;
        }
        catch (Throwable throwable){
            throw throwable;
        }
    }

    private static Optional getMessageId(StompHeaders headers){
        return Optional.ofNullable(headers.getMessageId());
    }

    private static void handleWithMessageId(String messageId, StompHeaders headers, Object payload) {
        Optional.ofNullable(awaitingResponseCache.getIfPresent(messageId)).ifPresent( callback ->
                callback.thenAccept(rpcResultsResponse ->
                                executorService.submit(rpcResultsResponse.onCallBack(headers, payload))
                        )
        );
    }

    public static RpcResultsEventHandler create(){
        return new RpcResultsEventHandler();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy