package eu.hgross.blaubot.messaging;
import java.util.Observable;
import eu.hgross.blaubot.util.Log;
/**
* Configuration for a BlaubotChannel.
* Can limit message rates and set picking strategies as well as priorities.
* The channel configuration is local - changes made are only reflected to the local device and
* not communicated to other devices.
*/
public class BlaubotChannelConfig extends Observable {
private static final String LOG_TAG = "BlaubotChannelConfig";
/**
* Use this constant for {BlaubotChannelConfig#setMessageRateLimit} to not use a message rate limit.
*/
public static final int MESSAGE_RATE_NO_LIMIT = -1;
/**
* Default maximum size of the message queue
*/
private static final int DEFAULT_QUEUE_CAPACITY = 200;
/**
* The channel id for this channel.
*/
private short channelId;
/**
* The message picker to be used
*/
private IBlaubotMessagePickerStrategy messagePicker;
/**
* The message rate limit to be used (minimum delay between to messages in ms)
* Defaults to no limit.
*/
private int minMessageRateDelay;
/**
* The priority with which messages of this channel are send.
*/
private BlaubotMessage.Priority priority;
/**
* The max size of the message queue.
*/
private int queueCapacity;
/**
* If set to true, reflexive messages (messages that are posted to a channel to which we are
* subscribed) are not send to the master device but directly posted to the registered listeners.
*/
private volatile boolean transmitReflexiveMessages = false;
/**
* If set to true, messages are sent even if there are no (yet known) subscribers to this channel.
*/
private volatile boolean transmitIfNoSubscribers = false;
/**
* Constructs a channel config for a channel id using the default
* MessagePickerStrategy (PROCESS_ALL).
* To change the MessagePickerStrategy, @see {BlaubotChannelConfig#setMessagePickerStrategy}.
*
* @param channelId the channel's id
*/
public BlaubotChannelConfig(short channelId) {
this.channelId = channelId;
_setMessageRateLimit(MESSAGE_RATE_NO_LIMIT);
_setMessagePickerStrategy(MessagePickerStrategy.PROCESS_ALL);
_setPriority(BlaubotMessage.Priority.NORMAL);
_setQueueCapacity(DEFAULT_QUEUE_CAPACITY);
_setTransmitIfNoSubscribers(false);
_setTransmitReflexiveMessages(false);
}
/**
* Sets the priority without notifying observers
* @param priority the priority for this channel
*/
private void _setPriority(BlaubotMessage.Priority priority) {
if (Log.logWarningMessages()) {
if (priority.equals(BlaubotMessage.Priority.ADMIN)) {
if (Log.logWarningMessages()) {
Log.w(LOG_TAG, "The priority of channel #" + channelId + " was set to ADMIN. I really hope you know what you are doing.");
}
}
}
this.priority = priority;
}
/**
* Sets the size of the message queue without notifying observers
* @param queueCapacity the queue size
*/
private void _setQueueCapacity(int queueCapacity) {
this.queueCapacity = queueCapacity;
}
/**
* Sets max the size of the message queue.
*
* Attention:
* If there are currently more than queueCapacity messages in the message queue, all messages
* that exceed the queue capacity will be dropped. This is likely to happen, if the queue size
* is decreased at runtime.
*
* @param queueCapacity the new capacity.
*/
public void setQueueCapacity(int queueCapacity) {
_setQueueCapacity(queueCapacity);
setChanged();
notifyObservers(Boolean.TRUE);
}
/**
* The max size of the message queue
*
* @return max number of messages in the queue
*/
public int getQueueCapacity() {
return queueCapacity;
}
/**
* Sets the message rate attribute without activating/deactivating threads and stuff.
* @param minMessageRateDelay the message rate limit
*/
private void _setMessageRateLimit(int minMessageRateDelay) {
this.minMessageRateDelay = minMessageRateDelay;
}
/**
* The channel id
* @return channel id
*/
public short getChannelId() {
return channelId;
}
/**
* Gets the mesage picker strategy specified by the user
* @return the message picker strategy
*/
protected IBlaubotMessagePickerStrategy getMessagePicker() {
return messagePicker;
}
/**
* @return the currently used picker strategy
*/
public MessagePickerStrategy getPickerStrategy() {
return messagePicker.getConstant();
}
/**
* Sets the MessagePickerStrategy that defines, how fast messages are send or discarded.
* @see eu.hgross.blaubot.messaging.BlaubotChannelConfig.MessagePickerStrategy
*
* @param strategy the strategy to use
* @return this channel config instance
*/
public BlaubotChannelConfig setMessagePickerStrategy(MessagePickerStrategy strategy) {
_setMessagePickerStrategy(strategy);
// notify listeners
setChanged();
notifyObservers(Boolean.FALSE);
return this;
}
/**
* Sets the strategy without notifying the observers.
* @see {BlaubotChannelConfig#setMessagePickerStrategy}
* @param strategy the strategy to use
*/
private void _setMessagePickerStrategy(MessagePickerStrategy strategy) {
messagePicker = strategy.getPickerStrategy();
}
/**
* Sets the message rate limit.
* @param minMessageRateDelay the minimum delay between two messages that are picked to be sent in milliseconds
* @return this channel config object
*/
public BlaubotChannelConfig setMessageRateLimit(int minMessageRateDelay) {
// maintain the attribute
_setMessageRateLimit(minMessageRateDelay);
// notify listeners
setChanged();
notifyObservers(Boolean.TRUE);
return this;
}
/**
* Sets the priority for messages sent through this channel.
* @param priority the priority
* @return this config instance
*/
public BlaubotChannelConfig setPriority(BlaubotMessage.Priority priority) {
_setPriority(priority);
setChanged();
notifyObservers(Boolean.FALSE);
return this;
}
/**
* The priority for messages sent through this channel.
* @return the priority
*/
public BlaubotMessage.Priority getPriority() {
return priority;
}
/**
* The minimun delay between two messages.
* @return minimun delay between two messages as configured by the user
*/
public int getMinMessageRateDelay() {
return minMessageRateDelay;
}
/**
* Check if reflexive messages to this channel are sent through the network or directly
* posted to your listeners instead.
*
* This means the messages published by a sending device which is also subscribed to this channel
* will not have to send and receive the message to receive it on it's handlers. Of course, all ohter
* devices have to receive this message.
*
* @return true, if reflexive messages will be sent through the network. Otherwise these message will not cause unnecessary traffic on the sending device.
*/
public boolean isTransmitReflexiveMessages() {
return transmitReflexiveMessages;
}
/**
* If set to true, reflexive messages (messages that are posted to a channel to which we are
* subscribed) are not send to the master device but directly posted to the registered listeners.
*
* This means the messages published by a sending device which is also subscribed to this channel
* will not have to send and receive the message to receive it on it's handlers. Of course, all ohter
* devices have to receive this message.
*
* @param transmitReflexiveMessages true to reduce traffic in these cases on the sending device
*/
public void setTransmitReflexiveMessages(boolean transmitReflexiveMessages) {
_setTransmitReflexiveMessages(transmitReflexiveMessages);
setChanged();
notifyObservers(Boolean.FALSE);
}
/**
* Sets the transmitReflexiveMessages option without notifying observers.
* @param transmitReflexiveMessages true to reduce traffic in reflexive message cases.
*/
private void _setTransmitReflexiveMessages(boolean transmitReflexiveMessages) {
this.transmitReflexiveMessages = transmitReflexiveMessages;
}
/**
* Check whether mesages are sent even if there are no known subscribers to this channel.
* @return true, if messages are sent even if there are no subscribers to this channel
*/
public boolean isTransmitIfNoSubscribers() {
return transmitIfNoSubscribers;
}
/**
* Sets a flag to avoid sending messages, if there are no known subscribers yet.
* @param transmitIfNoSubscribers If set to true, messages published to this channel are always send
*/
public void setTransmitIfNoSubscribers(boolean transmitIfNoSubscribers) {
_setTransmitIfNoSubscribers(transmitIfNoSubscribers);
setChanged();
notifyObservers(Boolean.FALSE);
}
/**
* Sets the transmitIfNoSubscribers option without notifying observes.
* @param transmitIfNoSubscribers see setTransmitIfNoSubscribers
*/
private void _setTransmitIfNoSubscribers(boolean transmitIfNoSubscribers) {
this.transmitIfNoSubscribers = transmitIfNoSubscribers;
}
/**
* Unique identifier for PickingStrategy-Implementations.
*/
public enum MessagePickerStrategy {
/**
* Messages are picked one by one and all are processed sequentally
*/
PROCESS_ALL,
/**
* Picks the newest message in the queue and removes all older messages without sending them.
*/
DISCARD_OLD,
/**
* Picks the oldest message in the queue and discards all newer ones.
*/
DISCARD_NEW;
/**
* Creates the picker for this strategy.
* @return the picker
* @throws UnsupportedOperationException, if the strategy requires a messageRate
*/
private IBlaubotMessagePickerStrategy getPickerStrategy() {
if (this.equals(PROCESS_ALL)) {
return new ProcessAllPickerStrategy();
} else if (this.equals(DISCARD_NEW)) {
return new DiscardNewPickerStrategy();
} else if (this.equals(DISCARD_OLD)) {
return new DiscardOldPickerStrategy();
} else {
throw new RuntimeException("Unknown strategy");
}
}
}
}