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.
package org.ak.trafficController.messaging.mem;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Each object of this class act as in memory queue. One can attach listener to it.
* The listener can be of direct retrieval or list retrieval.
* One can consider them as event handler as well.
* Whenever any data added to it will be processed by available listener.
*
* This works on observer pattern where all listeners are waiting for notification which will be sent when any item is added.
*
* @author Amit Khosla
* @param This defines the type of object the queue will work on.
*/
public class GenericInMemoryQueue {
static Logger logger = Logger.getLogger(GenericInMemoryQueue.class.getName());
/**
* Shutdown flag, when all consumers will stop working.
*/
private boolean hasShutdown;
/**
* The value contains the channel name.
*/
private String queueName;
/**
* This is the time for which consumer wait for notification before retrying. This is for removing any missed signal.
*/
int waitTimeout = 1000;
/**
* The value contains the batch size for batch consumers.
*/
private int batchSize = 10;
/**
* This is the data queue where data will be kept before any listener can read.
*/
private Queue dataQueue = new ConcurrentLinkedQueue();
/**
* The instance of {@link ConcurrentLinkedQueue} containing the direct consumer list.
*/
private Collection directConsumersList = new ConcurrentLinkedQueue<>();
/**
* The instance of {@link ConcurrentLinkedQueue} containing the batch consumer list.
*/
private Collection batchConsumersList = new ConcurrentLinkedQueue<>();
/**
* Current items in queue.
*/
private AtomicLong numberOfItemsInQueue = new AtomicLong(0L);
/**
* Constructor of Generic in memory queue.
* @param name Name of queue
*/
public GenericInMemoryQueue(String name) {
this.queueName = name;
}
/**
* Constructor which also configures batch size which is used by batch consumers.
* Batch size set here is the maximum items in list a consumer can expect.
* @param name Name of queue
* @param batchSize Batch size
*/
public GenericInMemoryQueue(String name, int batchSize) {
this.queueName = name;
this.batchSize = batchSize;
}
/**
* This method register the direct consumer.
*
* @param consumer
* The instance of {@link Consumer} to set
* @param consumerName
* The consumer name.
*/
public void register(Consumer consumer, String consumerName) {
if (!directConsumersList.contains(consumerName)) {
startListening(
() -> singleItemProcess(consumer, consumerName),
consumerName,
directConsumersList
);
}
}
/**
* This method polls from the queue and process the consumer with data found.
* The method keeps control till there is any data in queue.
*
* @param consumer
* The {@link Consumer} instance to set
* @param consumerName
* The {@link String} consumer name to set
*/
protected void singleItemProcess(Consumer consumer, String consumerName) {
while (!dataQueue.isEmpty()) {
T item = dataQueue.poll();
if (item == null) {
continue;
}
numberOfItemsInQueue.decrementAndGet();
try {
consumer.accept(item);
} catch (Exception e) {
logError(getExceptionMessage(consumerName, item) + e, e);
}
}
}
/**
* Log error if some exception occurs.
* @param message Error message
* @param e Exception to be logged
*/
private void logError(String message, Exception e) {
logger.log(Level.WARNING, message, e);
}
/**
* This method prepares the exception message containing name of consumer and queue.
*
* @param consumerName
* The {@link String} consumer name
* @param item
* The item of T type to set
* @return The {@link String} exception message
*/
protected String getExceptionMessage(String consumerName, T item) {
StringBuilder sb = buildErrorMessage(consumerName)
.append(" while consuming for item :").append(item);
return sb.toString();
}
/**
* This method builds the exception message for batch consumer.
*
* @param consumerName
* the consumerName
* @param items
* the list of items
* @return String append message
*/
protected String getExceptionMessage(String consumerName, List items) {
StringBuilder sb = buildErrorMessage(consumerName)
.append(" while consuming for items :");
items.forEach(item->sb.append(item).append(","));
sb.deleteCharAt(sb.length()-1);
return sb.toString();
}
/**
* Build error message using consumer name and queue name.
* @param consumerName Consumer name
* @return Message built in string builder
*/
protected StringBuilder buildErrorMessage(String consumerName) {
StringBuilder sb = new StringBuilder();
return sb.append("Exception occured in queue ").append(this.queueName)
.append(" for consumer : ").append(consumerName);
}
/**
* Register batch consumer
* @param consumer
* the consumer
* @param consumerName
* the consumer name
*/
public void registerBatchConsumer(Consumer> consumer, String consumerName) {
if (!batchConsumersList.contains(consumerName)) {
startListening(() -> batchProcess(consumer, consumerName), consumerName, batchConsumersList);
}
}
/**
* This method start listener. It creates a thread which will perform the operation required on the message in queue.
*
* @param consumerLogic
* The instance of {@link Runnable} to set
* @param consumerName
* The {@link String} consumer name to set
* @param consumerList consumersList direct or batch
*/
protected void startListening(Runnable consumerLogic, String consumerName, Collection consumerList) {
consumerList.add(consumerName);
Runnable executionLogic = () -> {
while (!hasShutdown && consumerList.contains(consumerName)) {
consumerLogic.run();
if (consumerList.contains(consumerName)) {
try {
synchronized (dataQueue) {
dataQueue.wait(waitTimeout);
}
} catch (InterruptedException e) {
logError("Interrupted exception occured in channel", e);
}
}
}
doNotify();
};
String name = this.queueName + "-" + consumerName;
startConsumer(executionLogic, name);
}
/**
* This method starts the consumer. This method will decide whether to use any executor or create new thread.
* Currently it looks like creating thread is better than managing executors as these threads will keep on running.
* @param executionLogic Execution logic
* @param name Name of consumer which will be set as name.
*/
protected void startConsumer(Runnable executionLogic, String name) {
Thread t= new Thread(executionLogic, name);
t.start();
}
/**
* This method gets from the queue and process.
*
* @param consumer
* The {@link Consumer} instance to set
* @param consumerName
* The {@link String} consumer name to set
*/
protected void batchProcess(Consumer> consumer, String consumerName) {
while (!dataQueue.isEmpty()) {
List output = getBatchData();
if (output.isEmpty()) {
continue;
}
try {
consumer.accept(output);
} catch (Exception e) {
logError(this.getExceptionMessage(consumerName, output), e);
}
}
}
/**
* Get batch data which will be used to pass to batch consumer.
* This method tries to get maximum "batchsize" messages.
* It will not wait if the number of messages is lesser.
* @return List containing data
*/
protected List getBatchData() {
int itemsPresentInOutputList = 0;
List output = new ArrayList<>();
while (!dataQueue.isEmpty() && itemsPresentInOutputList++ < batchSize) {
T item = dataQueue.poll();
if (item != null) {
output.add(item);
numberOfItemsInQueue.decrementAndGet();
} else {
break;
}
}
return output;
}
/**
* This method unregister the consumer.
*
* @param consumerName
* The {@link String} consumer name to remove
*/
public void unregister(String consumerName) {
directConsumersList.remove(consumerName);
batchConsumersList.remove(consumerName);
doNotify();
}
/**
* This method Notify all consumers.
* This is done whenever a new message arrives or a consumer is asked to stop.
*/
protected void doNotify() {
synchronized (dataQueue) {
dataQueue.notifyAll();
}
}
/**
* To cleanup, shutdown method should be called.
* This method shutdown all consumers batch or direct.
*/
public void shutdown() {
this.hasShutdown = true;
directConsumersList.clear();
batchConsumersList.clear();
doNotify();
}
/**
* This method add an item to the queue.
*
* @param item
* The item to add
*/
public void add(T item) {
dataQueue.add(item);
numberOfItemsInQueue.incrementAndGet();
notifyConsumers();
}
/**
* This method notify on addition of an item in the queue.
*/
protected void notifyConsumers() {
if (!directConsumersList.isEmpty() || !batchConsumersList.isEmpty()) {
doNotify();
}
}
/**
* This method adds list of items to be processed in queue.
*
* @param items
* The {@link List} of items to add
*/
public void add(Collection items) {
dataQueue.addAll(items);
numberOfItemsInQueue.addAndGet(items.size());
notifyConsumers();
}
/**
* Get number of direct consumers.
* @return Direct consumers count
*/
public int getDirectConsumerCount() {
return directConsumersList.size();
}
/**
* Get batch consumer count.
* @return Batch consumer count
*/
public int getBatchConsumerCount() {
return batchConsumersList.size();
}
/**
* Have this queue shutdown?
* @return Have this queue shutdown?
*/
public boolean isHasShutdown() {
return hasShutdown;
}
/**
* Get queue name.
* @return the queueName
*/
public String getQueueName() {
return queueName;
}
/**
* Set name of the queue
* @param queueName the queueName to set
* @return This object for further use
*/
public GenericInMemoryQueue setQueueName(String queueName) {
this.queueName = queueName;
return this;
}
/**
* Batch size which will be used by batch consumers.
* Any batch consumer will receive maximum of this many data items in list.
* The batch consumer might get fewer amount as well as consumers are called with
* only current data in queue and not waited for this list to be filled
* @return the batchSize
*/
public int getBatchSize() {
return batchSize;
}
/**
* Setter Batch size which will be used by batch consumers.
* Any batch consumer will receive maximum of this many data items in list.
* The batch consumer might get fewer amount as well as consumers are called with
* only current data in queue and not waited for this list to be filled
* @param batchSize the batchSize to set
* @return This object for further use
*/
public GenericInMemoryQueue setBatchSize(int batchSize) {
this.batchSize = batchSize;
return this;
}
/**
* Get number of items in queue.
* This method is used for diagnostics or by some tuner which want to resize number of consumers.
* @return the numberOfItemsInQueue
*/
public Long getNumberOfItemsInQueue() {
return numberOfItemsInQueue.get();
}
/**
* Set number of items in queue.
* This method is used for diagnostics or by some tuner which want to resize number of consumers.
* @param numberOfItemsInQueue the numberOfItemsInQueue to set
* @return This object for further use
*/
public GenericInMemoryQueue setNumberOfItemsInQueue(AtomicLong numberOfItemsInQueue) {
this.numberOfItemsInQueue = numberOfItemsInQueue;
return this;
}
/**
* This method clears the current queue and pass the data to the consumer.
* Post this data is consumed by cleaner, queue is cleaned.
* This method is used by scenarios where the messages are allowed to be wiped off to save the health of the system.
* This method is called by some tuner.
* @param preCleanHandlers Cleaners
*/
protected void clear(Consumer>... preCleanHandlers) {
if (preCleanHandlers != null) {
for (Consumer> c : preCleanHandlers) {
c.accept(dataQueue);
}
}
this.numberOfItemsInQueue.set(0);
this.dataQueue.clear();
}
/**
* Removes all direct consumers.
* This method is called either at shutdown or we want to move to different logic of processing the data.
*/
public void removeAllDirectConsumers() {
this.directConsumersList.clear();
doNotify();
}
/**
* Remove all batch consumers.
* This method is called either at shutdown or we want to move to different logic of processing the data.
*/
public void removeAllBatchConsumers() {
this.batchConsumersList.clear();
doNotify();
}
/**
* This is the wait time for which different consumers wait for some message.
* Post wait, the consumers retry finding data and if not found go to wait again.
* In rare case if some notification is lost, this mechanism guarantees that messages will be picked if present post this time.
* @return Wait time
*/
public int getWaitTimeout() {
return waitTimeout;
}
/**
* This is the wait time for which different consumers wait for some message.
* Post wait, the consumers retry finding data and if not found go to wait again.
* In rare case if some notification is lost, this mechanism guarantees that messages will be picked if present post this time.
* @param waitTimeout wait time out to be set
* @return This object to further use
*/
public GenericInMemoryQueue setWaitTimeout(int waitTimeout) {
this.waitTimeout = waitTimeout;
return this;
}
}