package proj.zoie.dataprovider.jms;
import java.util.Comparator;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import org.apache.log4j.Logger;
import proj.zoie.api.DataConsumer.DataEvent;
import proj.zoie.impl.indexing.StreamDataProvider;
public class JMSStreamDataProvider<T> extends StreamDataProvider<T> {
private static final Logger logger = Logger.getLogger(JMSStreamDataProvider.class);
private static final String name = "JMSStreamDataProvider";
private final String topicName;
private final String clientID;
private final TopicConnectionFactory connectionFactory;
private final TopicFactory topicFactory;
private final DataEventBuilder<T> dataEventBuilder;
private TopicSubscriber subscriber;
private TopicConnection connection;
private volatile int JMSErrorBackOffTime = 3000;
private volatile boolean stopped = true;
public JMSStreamDataProvider(String topicName, String clientID,
TopicConnectionFactory connectionFactory, TopicFactory topicFactory,
DataEventBuilder<T> dataEventBuilder, Comparator<String> versionComparator) {
super(versionComparator);
this.topicName = topicName;
this.clientID = clientID;
this.connectionFactory = connectionFactory;
this.topicFactory = topicFactory;
this.dataEventBuilder = dataEventBuilder;
}
@Override
public void start() {
logger.info("starting " + toString());
stopped = false;
super.start();
}
/**
* Tries to reconnect to the durable topic. This method blocks
* until the try is successful or this provider is stopped.
*/
private void reconnect() {
for (;;) {
if (stopped) {
return;
}
try {
//close subscriber if not previously closed
if (subscriber != null) {
subscriber.close();
}
if (connection != null) {
connection.close();
}
connection = connectionFactory.createTopicConnection();
if (clientID != null) {
connection.setClientID(clientID);
}
TopicSession session = connection.createTopicSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = topicFactory.createTopic(topicName);
subscriber = session.createDurableSubscriber(topic, name);
connection.start();
return;
} catch (JMSException e) {
logger.error("could not connect to durable topic, topic: " + topicName, e);
//step back for a while
backOffAfterJMSException(e);
}
}
}
@Override
public DataEvent<T> next() {
for (;;) {
if (subscriber == null) {
reconnect();
}
if (stopped) {
return null;
}
try {
Message m = subscriber.receive();
if (m != null) {
return dataEventBuilder.buildDataEvent(m);
}
} catch (JMSException e) {
logger.error("error receiving message", e);
//step back for a while
backOffAfterJMSException(e);
//after any JMS exception try to reconnect
reconnect();
}
}
}
/**
* Backs off certain amount of time before next interaction
* with JMS.
* @param e
*/
void backOffAfterJMSException(JMSException e) {
try {
Thread.sleep(JMSErrorBackOffTime);
} catch (InterruptedException e1) {
logger.error(e1);
}
}
public int getJMSErrorBackOffTime() {
return JMSErrorBackOffTime;
}
public void setJMSErrorBackOffTime(int jMSErrorBackOffTime) {
JMSErrorBackOffTime = jMSErrorBackOffTime;
}
@Override
public void reset() {
logger.error("reset called, not implemented by JMS data provider...");
}
@Override
public void setStartingOffset(String version){
logger.error("starting offset called, not implemented by JMS data provider...");
}
@Override
public void stop() {
logger.info("stopping " + toString());
stopped = true;
try {
connection.stop();
} catch (JMSException e) {
logger.error("could not stop connection", e);
}
try {
connection.close();
} catch (JMSException e) {
logger.error("could not close connection", e);
}
super.stop();
}
@Override
public String toString() {
return "JMSStreamDataProvider [clientID=" + clientID + ", topicName="
+ topicName + "]";
}
}