com.addc.commons.queue.PersistingQueueReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of addc-queues Show documentation
Show all versions of addc-queues Show documentation
The addc-queues library supplies support for internal persistent queues using an optional DERBY database for storage.
The newest version!
package com.addc.commons.queue;
import java.util.LinkedList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.addc.commons.Mutex;
/**
* The PersistingQueueReader reads items from a PersistingQueue and passes them
* on to a PayloadDispatcher. This class will ensure that, on shutdown, any
* outstanding items are sent on through the dispatcher or saved to the
* persistence if the dispatcher fails.
*
*/
public class PersistingQueueReader implements Runnable {
private static final Logger LOGGER= LoggerFactory.getLogger(PersistingQueueReader.class);
private final PersistingQueueReaderState dispatcherState= new PersistingQueueReaderState<>();
private final PersistingQueue queue;
private final PayloadDispatcher dispatcher;
private final Mutex delayMutex= new Mutex();
private final ReaderDelayGenerator delayGenerator;
private final List> listeners;
private final String threadName;
private Thread runnerThread;
/**
* Create a PersistingQueueReader
* @param queue The {@link PersistingQueue} to read
* @param threadName The name of the thread
* @param dispatcher The {@link PayloadDispatcher} to send messages to
*/
public PersistingQueueReader(PersistingQueue queue, String threadName, PayloadDispatcher dispatcher) {
this.queue= queue;
this.threadName= threadName;
this.dispatcher= dispatcher;
listeners= new LinkedList<>();
delayGenerator= new ReaderDelayGenerator<>(this);
}
/**
* Add a {@link PersistingQueueReaderListener}
* @param listener The listener to add
*/
public void addListener(PersistingQueueReaderListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
@Override
public void run() {
LOGGER.info("Thread starts...");
do {
// *******************************************************
// If there was a Protocol Exception, wait for a while
// *******************************************************
if (dispatcherState.connectionLost && !isShutdown()) {
delay();
}
// *******************************************************
// If there isn't a batch to send, get one
// *******************************************************
if (dispatcherState.currentPayload == null) {
dispatcherState.currentPayload= queue.take();
}
// *******************************************************
// Take may return null at shutdown when the wait is
// interrupted
// *******************************************************
if (dispatcherState.currentPayload != null) {
processCurrentPayload();
}
} while (!isShutdown());
LOGGER.info("Thread ends...");
}
/**
* Shut down the dispatcher. Processes any outstanding payloads
*/
public void shutdown() {
LOGGER.info("Stop the dispatcher thread...");
setShutdown(true);
breakDelay();
queue.interruptTake();
try {
runnerThread.join();
} catch (InterruptedException e) {
LOGGER.debug("Interrupted", e);
}
LOGGER.info("Recover any outstanding batches and send them...");
if (dispatcherState.currentPayload == null) {
dispatcherState.currentPayload= queue.poll();
}
if (dispatcherState.currentPayload != null) {
sendPendingPayloads();
}
LOGGER.info("Dispatcher terminated, returning {}", dispatcherState.currentPayload);
queue.shutdown(dispatcherState.currentPayload);
}
/**
* Start the reader
*/
public void start() {
runnerThread= new Thread(this, threadName);
runnerThread.start();
}
/**
* Query whether the reader is shut down
* @return true
if the reader is dtopped
*/
public boolean isShutdown() {
synchronized (dispatcherState.shutdownLock) {
return dispatcherState.shutdown;
}
}
private void delay() {
synchronized (delayMutex) {
try {
delayMutex.wait(delayGenerator.getDelay());
} catch (InterruptedException e) {
LOGGER.debug("Interrupted", e);
}
}
}
private void processCurrentPayload() {
try {
dispatcher.dispatch(dispatcherState.currentPayload);
notifyForward(null);
dispatcherState.currentPayload= null;
} catch (DispatcherException e) {
if (e.isRecoverable()) {
dispatcherState.connectionLost= true;
notifyForward(e);
if (e.getRetryDelay() != null) {
delay(e.getRetryDelay());
}
} else {
dispatcherState.connectionLost= true;
notifyDispatcherError(e);
setShutdown(true);
}
} catch (Exception e) {
LOGGER.warn("Unexpected exception, aborting queue reader", e);
setShutdown(true);
}
}
private void delay(long timeout) {
synchronized (delayMutex) {
try {
delayMutex.wait(timeout);
} catch (InterruptedException e) {
LOGGER.trace("Interrupted", e);
}
}
}
private void sendPendingPayloads() {
boolean success= true;
do {
try {
dispatcher.dispatch(dispatcherState.currentPayload);
dispatcherState.currentPayload= null;
} catch (Exception e) {
dispatcherState.connectionLost= true;
notifyForward(e);
success= false;
}
// If the forward failed, currentBatch will NOT be null
if (dispatcherState.currentPayload == null) {
dispatcherState.currentPayload= queue.poll();
}
} while ((dispatcherState.currentPayload != null) && success);
}
private void notifyForward(Exception e) {
synchronized (listeners) {
for (PersistingQueueReaderListener listener : listeners) {
listener.onProcess(dispatcherState.connectionLost, e);
}
}
}
private void notifyDispatcherError(DispatcherException e) {
synchronized (listeners) {
for (PersistingQueueReaderListener listener : listeners) {
listener.onDispatcherError(e);
}
}
}
private void setShutdown(boolean b) {
synchronized (dispatcherState.shutdownLock) {
dispatcherState.shutdown= b;
}
}
private void breakDelay() {
synchronized (delayMutex) {
delayMutex.notifyAll();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy