All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.ridgid.oss.queue.impl.inmemory.InMemoryMultiChannelFIFOQueue Maven / Gradle / Ivy
package com.ridgid.oss.queue.impl.inmemory;
import com.ridgid.oss.queue.spi.MultiChannelFIFOQueue;
import java.io.Serializable;
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.lang.System.currentTimeMillis;
import static java.util.Comparator.comparingLong;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toConcurrentMap;
/**
* In-Memory Implementation of a MultiChannelFIFOQueue
*
* @param of messages sent and received through the queue
*/
@SuppressWarnings({"WeakerAccess", "NewMethodNamingConvention", "ClassNamePrefixedWithPackageName"})
public class InMemoryMultiChannelFIFOQueue
implements MultiChannelFIFOQueue
{
private final AtomicLong nextTimestamp = new AtomicLong(Long.MIN_VALUE);
private final
Class baseMessageType;
private final
ConcurrentMap, ConcurrentLinkedQueue>>
queues;
/**
* Construct a single-channel queue where the single channel will handle all messages
*
* @param baseMessageType of the messages to send and receive through the queue
*/
@SuppressWarnings("BoundedWildcard")
public InMemoryMultiChannelFIFOQueue(Class baseMessageType) {
this.baseMessageType = baseMessageType;
queues = makeEmptyConcurrentQueues(Stream.of(baseMessageType));
}
/**
* Construct a multi-channel queue where all of the messages extend the base message type, but, there exists
* multiple channels corresponding to the given list of message types
*
* @param baseMessageType of all messages in the queue across all channels
* @param queues of the messages for each channel
*/
@SuppressWarnings("BoundedWildcard")
public InMemoryMultiChannelFIFOQueue(Class baseMessageType,
Collection> queues)
{
this.baseMessageType = baseMessageType;
this.queues = makeEmptyConcurrentQueues(queues.stream());
}
/**
* Construct a multi-channel queue where all of the messages extend the base message type, but, there exists
* multiple channels corresponding to the given list of message types
*
* @param baseMessageType of all messages in the queue across all channels
* @param queues of the messages for each channel
*/
@SuppressWarnings({"OverloadedVarargsMethod", "BoundedWildcard"})
@SafeVarargs
public InMemoryMultiChannelFIFOQueue(Class baseMessageType,
Class extends BaseMessageType>... queues)
{
this.baseMessageType = baseMessageType;
this.queues = makeEmptyConcurrentQueues(Arrays.stream(queues));
}
/**
* Construct a multi-channel queue where all of the messages extend the base message type, but, there exists
* multiple channels corresponding to the given list of message types
*
* @param baseMessageType of all messages in the queue across all channels
* @param queues of the messages for each channel
*/
@SuppressWarnings("BoundedWildcard")
public InMemoryMultiChannelFIFOQueue(Class baseMessageType,
Stream> queues)
{
this.baseMessageType = baseMessageType;
this.queues = makeEmptyConcurrentQueues(queues);
}
private static
ConcurrentMap, ConcurrentLinkedQueue>>
makeEmptyConcurrentQueues(Stream> queues)
{
return queues.collect(toConcurrentMap(identity(),
InMemoryMultiChannelFIFOQueue::makeEmptyConcurrentQueue));
}
private static ConcurrentLinkedQueue>
makeEmptyConcurrentQueue(Class extends BaseMessageType> messageType)
{
return new ConcurrentLinkedQueue<>();
}
@Override
public Class extends BaseMessageType> getBaseMessageType() {
return baseMessageType;
}
@Override
public Stream> streamChannelMessageTypes() {
return queues.keySet().stream();
}
@Override
public
Optional extends MessageType> pollUnchecked(Class extends MessageType> messageType,
long maxWaitMillis)
throws MultiChannelFIFOQueueException
{
return Optional.ofNullable
(
waitForNextAvailable
(
messageType,
currentTimeMillis() + maxWaitMillis
)
);
}
@SuppressWarnings("OverlyBroadCatchBlock")
private
MessageType waitForNextAvailable(Class extends MessageType> messageType,
long endTimeMillis)
throws MultiChannelFIFOQueueException
{
try {
MessageType available;
do available = nextAvailableMessageForRequestedMessageType(messageType);
while
(
available == null
&&
waitingForMessagesUntil(endTimeMillis)
);
return available;
} catch ( Exception e ) {
throw new MultiChannelFIFOQueueException(e);
}
}
@SuppressWarnings("NakedNotify")
@Override
public
void sendUnchecked(MessageType message)
throws MultiChannelFIFOQueueException
{
queueMessageToMostAppropriateQueue(message);
synchronized ( queues ) {
queues.notifyAll();
}
}
private
void queueMessageToMostAppropriateQueue(MessageType message)
throws MultiChannelFIFOQueueException
{
Stream.concat(exactQueueForMessageType(message),
acceptableQueuesForMessageType(message))
.findFirst()
.map(Entry::getValue)
.filter(messageQueued(message))
.orElseThrow
(
() -> new MultiChannelFIFOQueueException
(
String.format("Message not supported on any channel: %s, %s",
message.getClass().getName(),
message)
)
);
}
private
Stream, ConcurrentLinkedQueue>>>
acceptableQueuesForMessageType(MessageType message)
{
return queues
.entrySet()
.stream()
.filter(entry -> entry.getKey().isAssignableFrom(message.getClass()));
}
private
Stream, ConcurrentLinkedQueue>>>
exactQueueForMessageType(MessageType message)
{
return queues
.entrySet()
.stream()
.filter(entry -> entry.getKey()
.isAssignableFrom(message.getClass())
&& message.getClass()
.isAssignableFrom(entry.getKey()));
}
@SuppressWarnings("LocalVariableOfConcreteClass")
private
Predicate>>
messageQueued(MessageType message)
{
Timestamped msg = new Timestamped<>(message, nextTimestamp);
return queue -> queue.offer(msg);
}
private
MessageType nextAvailableMessageForRequestedMessageType(Class extends MessageType> messageType)
{
return
nextMessageFromQueueOfExactMessageType(messageType)
.map(msg -> (MessageType) msg)
.orElseGet(() -> earliestMessageWhereSuperTypeIs(messageType));
}
@SuppressWarnings({"WaitNotInLoop", "OverlyNestedMethod", "BooleanMethodNameMustStartWithQuestion"})
private
boolean waitingForMessagesUntil(long untilMillis)
{
long maxWaitMillis = untilMillis - currentTimeMillis();
if ( maxWaitMillis > 0 )
synchronized ( queues ) {
try { queues.wait(maxWaitMillis); } catch ( InterruptedException ignore ) {}
}
return currentTimeMillis() < untilMillis;
}
@SuppressWarnings({"unchecked", "NewMethodNamingConvention"})
private Optional extends MessageType>
nextMessageFromQueueOfExactMessageType(Class extends MessageType> messageType)
{
return Optional.ofNullable(queues.get(messageType))
.map(ConcurrentLinkedQueue::poll)
.map(Timestamped::unwrap)
.map(msg -> (MessageType) msg);
}
@SuppressWarnings("LocalVariableOfConcreteClass")
private
MessageType earliestMessageWhereSuperTypeIs(Class extends MessageType> messageType) {
for
(
Entry, ConcurrentLinkedQueue>>
nextApplicableQueue : headsOfQueuesSortedByFIFOOrder(messageType)
) {
Timestamped extends BaseMessageType> val = nextApplicableQueue.getValue().poll();
if ( val != null ) return messageType.cast(val.unwrap());
}
return null;
}
private
Iterable, ConcurrentLinkedQueue>>>
headsOfQueuesSortedByFIFOOrder(Class extends MessageType> messageType)
{
return
queuesForBaseMessageType(messageType)
.map(Entry::getValue)
.map(toHeadOfQueueAndQueue())
.filter(whereNonNullHeadOfQueue())
.sorted(comparingLong(entry -> entry.getKey().getTimestamp()))
::iterator;
}
private
Stream extends Entry, ConcurrentLinkedQueue>>>
queuesForBaseMessageType(Class extends MessageType> messageType)
{
return queues.entrySet()
.stream()
.filter(whereAssignableTo(messageType));
}
private Predicate, ConcurrentLinkedQueue>>>
whereNonNullHeadOfQueue()
{
return entry -> Objects.nonNull(entry.getKey());
}
private
Predicate, ConcurrentLinkedQueue>>>
whereAssignableTo(Class extends MessageType> messageType)
{
return entry -> messageType.isAssignableFrom(entry.getKey());
}
private Function>, SimpleEntry, ConcurrentLinkedQueue>>>
toHeadOfQueueAndQueue()
{
return queue -> new SimpleEntry<>(queue.peek(), queue);
}
/**
* Note: this class has a natural ordering that is inconsistent with equals.
*/
private static final class Timestamped
implements Comparable>
{
private final T obj;
private final long timestamp;
private Timestamped(T obj, AtomicLong nextTimestamp) {
this.obj = obj;
timestamp = nextTimestamp.getAndIncrement();
}
public T unwrap() {
return obj;
}
public long getTimestamp() {
return timestamp;
}
@SuppressWarnings("MethodParameterOfConcreteClass")
@Override
public int compareTo(Timestamped o) {
return Long.compare(timestamp, o.timestamp);
}
@Override
public String toString() {
return "Timestamped{" +
"obj=" + obj +
", timestamp=" + timestamp +
'}';
}
}
@Override
public String toString() {
return "InMemoryMultiChannelFIFOQueue{" +
"nextTimestamp=" + nextTimestamp +
", baseMessageType=" + baseMessageType +
", queues=" + queues +
'}';
}
}