package forklift.consumer;
import forklift.connectors.ConnectorException;
import forklift.connectors.ForkliftMessage;
import forklift.controller.KafkaController;
import forklift.message.ReadableMessageStream;
/**
* Retrieves messages from a {@link forklift.controller.KafkaController}
*/
public class KafkaTopicConsumer implements ForkliftConsumerI {
private final String topic;
private final KafkaController controller;
private final ReadableMessageStream messageStream;
private volatile boolean topicAdded = false;
public KafkaTopicConsumer(String topic, KafkaController controller) {
this.topic = topic;
this.controller = controller;
this.messageStream = controller.getMessageStream();
}
/**
* {@inheritDoc}
* <p>
* Retrieves the {@link forklift.message.MessageStream#nextRecord(String, long) nextRecord} from the messageStream.
* If no record is available within the specified timeout, null is returned.
* <p>
* <strong>Note:</strong> Because we do not wish to poll for kafka topics until we are actively receiving messages, this method is
* also responsible for calling {@link forklift.controller.KafkaController#addTopic(String) addTopic} on the kafkaController.
*
* @param timeout the time in milliseconds to wait for a record to become available
* @return a message if one is available, else null
* @throws ConnectorException if the process is interrupted or the controller is no longer running
*/
@Override
public ForkliftMessage receive(long timeout) throws ConnectorException {
addTopicIfMissing();
//ensure that the controller is still running
if (!controller.isRunning()) {
throw new ConnectorException("Connection to Kafka Controller lost");
}
try {
return this.messageStream.nextRecord(this.topic, timeout);
} catch (InterruptedException e) {
throw new ConnectorException("Kafka message receive interrupted");
}
}
private void addTopicIfMissing() {
if (!topicAdded) {
synchronized (this) {
if (!topicAdded) {
controller.addTopic(topic);
topicAdded = true;
}
}
}
}
/**
* {@inheritDoc}
* <p>
* Removes this consumer's topic from the controller. Future calls to {@link #receive(long) receive} will re-add the
* topic to the controller.
* </p>
*/
@Override
public void close() {
controller.removeTopic(topic);
}
}