
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 extends Message> 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 extends Message> 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