MultiImplementationSender.java
package fr.sii.ogham.core.sender;
import static fr.sii.ogham.core.util.LogUtils.logString;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.sii.ogham.core.condition.Condition;
import fr.sii.ogham.core.exception.MessageException;
import fr.sii.ogham.core.message.Message;
import fr.sii.ogham.core.util.PriorizedList;
/**
* Decorator sender that is able to handle a particular type of message. And for
* handling this message it can rely on several possible implementations. Each
* implementation is associated to a {@link Condition}. The condition indicates
* at runtime if the message can be handled by the possible implementation.
* There can be any kind of condition (for example, based on a required class in
* the classpath or a particular property value...).
*
* The implementation selection is done in the {@link #supports(Message)}
* method.
*
* @author Aurélien Baudet
*
* @param <M>
* The type of message that the implementations can handle
* @see Condition
*/
public abstract class MultiImplementationSender<M extends Message> implements ConditionalSender {
private static final Logger LOG = LoggerFactory.getLogger(MultiImplementationSender.class);
/**
* The list of possible implementations indexed by the associated condition
*/
private final PriorizedList<Implementation> implementations;
/**
* Initialize with no registered implementation.
*/
public MultiImplementationSender() {
this(new PriorizedList<>());
}
/**
* Initialize with several implementations.
*
* @param implementations
* the list of possible implementations indexed by the condition
* that indicates if the implementation is eligible at runtime
*/
public MultiImplementationSender(PriorizedList<Implementation> implementations) {
super();
this.implementations = implementations;
}
/**
* Register a new possible implementation with the associated condition. The
* implementation is added at the end so any other possible implementation
* will be used before this one if the associated condition allow it.
*
* @param condition
* the condition that indicates if the implementation can be used
* at runtime
* @param implementation
* the implementation to register
* @param priority
* the registration priority
* @return this instance for fluent chaining
*/
public final MultiImplementationSender<M> addImplementation(Condition<Message> condition, MessageSender implementation, int priority) {
implementations.register(new Implementation(condition, implementation), priority);
return this;
}
@Override
public boolean supports(Message message) {
return getSender(message) != null;
}
@Override
public void send(Message message) throws MessageException {
MessageSender sender = getSender(message);
if (sender == null) {
LOG.warn("No implementation is able to send the message {}. Skipping", logString(message));
return;
}
LOG.debug("Sending message {} using {} implementation", logString(message), sender);
sender.send(message);
}
public List<Implementation> getImplementations() {
return implementations.getOrdered();
}
protected boolean supportsMessageType(Message message) {
Class<M> managedClass = getManagedClass();
if (managedClass == null) {
LOG.warn("No managed class is declared");
return false;
}
return managedClass.isAssignableFrom(message.getClass());
}
@SuppressWarnings("unchecked")
protected Class<M> getManagedClass() {
Type genericSuperclass = getClass().getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
return (Class<M>) ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
}
return null;
}
private MessageSender getSender(Message message) {
if (supportsMessageType(message)) {
LOG.debug("Can handle the message type {}. Is there any implementation available to send it ?", message.getClass());
for (Implementation impl : implementations.getOrdered()) {
if (impl.getCondition().accept(message)) {
LOG.debug("The implementation {} can handle the message {}", impl.getSender(), logString(message));
return impl.getSender();
}
}
}
LOG.debug("Can't handle the message type {}", message.getClass());
return null;
}
public static class Implementation {
private final Condition<Message> condition;
private final MessageSender sender;
public Implementation(Condition<Message> condition, MessageSender sender) {
super();
this.condition = condition;
this.sender = sender;
}
public Condition<Message> getCondition() {
return condition;
}
public MessageSender getSender() {
return sender;
}
}
}