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

org.kasource.spring.nats.consumer.MethodMessageHandler Maven / Gradle / Ivy

The newest version!
package org.kasource.spring.nats.consumer;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;

import org.kasource.commons.reflection.filter.MethodFilter;
import org.kasource.commons.reflection.filter.builder.MethodFilterBuilder;
import org.kasource.spring.nats.annotation.Consumer;
import org.kasource.spring.nats.consumer.argument.ArgumentMatcher;
import org.kasource.spring.nats.consumer.argument.SingleArgumentMatcher;
import org.kasource.spring.nats.consumer.argument.TwoArgumentMatcher;
import org.kasource.spring.nats.exception.MethodInvocationException;
import org.kasource.spring.nats.message.serde.NatsMessageDeserializer;
import org.kasource.spring.nats.message.serde.NatsMessageSerDeFactory;

import io.nats.client.Message;
import io.nats.client.MessageHandler;


public class MethodMessageHandler implements MessageHandler {
    private static final String ERROR_MESSAGE = "Method %s must be "
            + "public method, with return type void having one or two parameters.";

    private static final String ERROR_MESSAGE_TWO_ARGS = "One of the parameters of method %s must be of type "
            + Message.class.getName();

    private Object bean;
    private Method method;
    private NatsMessageDeserializer deserializer;
    private ArgumentMatcher argumentMatcher;


    public MethodMessageHandler(final NatsMessageSerDeFactory serDeFactory,
                                final Object bean,
                                final Method method) {
        this.method = method;
        this.bean = bean;
        validate();
        Optional> messagePayloadType = resolveMessagePayloadType();
        messagePayloadType.ifPresent(t -> this.deserializer = serDeFactory.createDeserializer(t));
    }

    private void validate() {
        if (method == null) {
            throw new IllegalArgumentException("Method may not be null");
        }
        validateMethod();

        if (method.getParameterCount() == 2) {
            validateTwoArgsMethod();
        }

        boolean isMessageArg = Message.class.isAssignableFrom(method.getParameterTypes()[0]);
        if (method.getParameterCount() == 1) {
            argumentMatcher = new SingleArgumentMatcher(isMessageArg);
        } else {
            argumentMatcher = new TwoArgumentMatcher(isMessageArg);
        }

    }

    private Optional> resolveMessagePayloadType() {
        boolean isMessageArg = Message.class.isAssignableFrom(method.getParameterTypes()[0]);
        if (method.getParameterCount() == 1 && !isMessageArg) {
            return Optional.of(method.getParameterTypes()[0]);
        } else if (method.getParameterCount() == 2) {
            if (isMessageArg) {
                return Optional.of(method.getParameterTypes()[1]);
            } else {
                return Optional.of(method.getParameterTypes()[0]);
            }
        }
        return Optional.empty();
    }


    private void validateMethod() {
        MethodFilter methodFilter = new MethodFilterBuilder()
                .isPublic()
                .returnType(Void.TYPE)
                .numberOfParameters(1).or().numberOfParameters(2)
                .build();

        if (!methodFilter.apply(method)) {
            throw new IllegalStateException(String.format(ERROR_MESSAGE, method));
        }
    }


    private void validateTwoArgsMethod() {
        MethodFilter methodFilter = new MethodFilterBuilder()
                .parametersExtendsType(Message.class, Object.class)
                .or()
                .parametersExtendsType(Object.class, Message.class)
                .not()
                .parametersExtendsType(Message.class, Message.class)
                .build();
        if (!methodFilter.apply(method)) {
            throw new IllegalStateException(String.format(ERROR_MESSAGE_TWO_ARGS, method));
        }
    }


    @SuppressWarnings("PMD.PreserveStackTrace")
    @Override
    public void onMessage(Message message) {
        try {
            method.invoke(bean, argumentMatcher.toArgs(() -> deserializer.fromMessage(message), message));
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Can't invoke @" + Consumer.class.getName() + " annotated method " + method, e);
        } catch (InvocationTargetException e) {
            throw new MethodInvocationException("Can't invoke @" + Consumer.class.getName()
                    + " annotated method " + method + " with deserializer " + deserializer, e.getCause());
        }
    }

    @Override
    public String toString() {
        return "MethodConsumer{"
                + "method=" + method
                + '}';
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy