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

com.cookingfox.lapasse.impl.message.bus.AbstractMessageBus Maven / Gradle / Ivy

The newest version!
package com.cookingfox.lapasse.impl.message.bus;

import com.cookingfox.lapasse.api.message.Message;
import com.cookingfox.lapasse.api.message.bus.MessageBus;
import com.cookingfox.lapasse.api.message.exception.NoMessageHandlersException;
import com.cookingfox.lapasse.api.message.handler.MessageHandler;
import com.cookingfox.lapasse.api.message.store.MessageStore;
import com.cookingfox.lapasse.api.message.store.OnMessageAdded;
import com.cookingfox.lapasse.impl.util.CollectionUtils;

import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Abstract message bus implementation.
 *
 * @param  The concrete message type.
 * @param  The concrete message handler type.
 */
public abstract class AbstractMessageBus>
        implements MessageBus {

    //----------------------------------------------------------------------------------------------
    // PROTECTED PROPERTIES
    //----------------------------------------------------------------------------------------------

    /**
     * A map of message types to a set of message handlers.
     */
    protected final Map, Set> messageHandlerMap = CollectionUtils.newConcurrentMap();

    /**
     * Stores messages.
     */
    protected final MessageStore messageStore;

    //----------------------------------------------------------------------------------------------
    // CONSTRUCTOR
    //----------------------------------------------------------------------------------------------

    public AbstractMessageBus(MessageStore messageStore) {
        messageStore.addMessageAddedListener(onMessageAddedToStore);

        this.messageStore = messageStore;
    }

    //----------------------------------------------------------------------------------------------
    // PUBLIC METHODS
    //----------------------------------------------------------------------------------------------

    @Override
    public void dispose() {
        messageHandlerMap.clear();
    }

    @Override
    public void handleMessage(M message) {
        Class messageClass = message.getClass();

        // no mapped handlers? throw
        if (getMessageHandlers(messageClass) == null) {
            throw new NoMessageHandlersException(messageClass);
        }

        /**
         * Store the message - will notify listeners after the message is stored.
         * @see #onMessageAddedToStore
         */
        messageStore.addMessage(message);
    }

    @Override
    public void mapMessageHandler(Class messageClass, H messageHandler) {
        Objects.requireNonNull(messageClass, "Message class can not be null");
        Objects.requireNonNull(messageHandler, "Message handler can not be null");

        Set handlers = messageHandlerMap.get(messageClass);

        // no handler collection yet? create it first
        if (handlers == null) {
            handlers = CollectionUtils.newConcurrentSet();
            messageHandlerMap.put(messageClass, handlers);
        }

        handlers.add(messageHandler);
    }

    //----------------------------------------------------------------------------------------------
    // ABSTRACT PROTECTED METHODS
    //----------------------------------------------------------------------------------------------

    /**
     * Actually execute the message handler for this message.
     *
     * @param message        The message to handle.
     * @param messageHandler The message handler that is associated with this message.
     */
    protected abstract void executeHandler(M message, H messageHandler);

    /**
     * Returns whether this message bus implementation should handle the concrete message object.
     * Typically this returns the result of an `instanceof` check for the concrete message type `M`.
     *
     * @param message The concrete message object.
     * @return Whether the message should be handled by this message bus.
     * @see M
     */
    protected abstract boolean shouldHandleMessageType(Message message);

    //----------------------------------------------------------------------------------------------
    // PROTECTED METHODS
    //----------------------------------------------------------------------------------------------

    /**
     * Get mapped handlers for this message class.
     *
     * @param messageClass The message class to get handlers for.
     * @return The handlers for this message class.
     * @throws NoMessageHandlersException when no handlers are mapped for this message.
     */
    protected Set getMessageHandlers(Class messageClass) {
        // noinspection SuspiciousMethodCalls
        Set handlers = messageHandlerMap.get(messageClass);

        // no mapped handlers for this message type: see if there's a handler for the message's
        // super type
        if (handlers == null) {
            for (Class messageSuperClass : messageHandlerMap.keySet()) {
                // does the message type extend this super type? if so, use its handlers
                if (messageSuperClass.isAssignableFrom(messageClass)) {
                    handlers = messageHandlerMap.get(messageSuperClass);
                    break;
                }
            }
        }

        return handlers;
    }

    /**
     * Listener for when a new message is added to the store.
     */
    protected final OnMessageAdded onMessageAddedToStore = new OnMessageAdded() {
        @Override
        public void onMessageAdded(Message message) {
            // check if the bus has mapped handlers
            Set handlers = getMessageHandlers(message.getClass());

            if (!shouldHandleMessageType(message) || handlers == null) {
                // this message bus should not handle messages of this type
                return;
            }

            // execute message handlers
            for (H handler : handlers) {
                // noinspection unchecked
                executeHandler((M) message, handler);
            }
        }
    };

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy