package com.ebottabi.bolt;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisPubSub;
import twitter4j.Status;
import twitter4j.json.DataObjectFactory;
import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichBolt;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
/**
* This abstract bolt class will help publishing the bolt processing into a
* redis pubsub channel
*
* @author ebot
*
*/
public abstract class RedisBolt implements IRichBolt {
protected String channel;
protected String configChannel;
protected OutputCollector collector;
protected Tuple currentTuple;
protected Logger log;
protected JedisPool pool;
protected ConfigListenerThread configListenerThread;
public interface OnDynamicConfigurationListener {
public void onConfigurationChange(String conf);
}
public RedisBolt(String channel) {
this.channel = channel;
configChannel = "config_update_" + channel;
}
@Override
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
this.collector = collector;
pool = new JedisPool(new JedisPoolConfig(), "localhost");
log = Logger.getLogger(getClass().getName());
setupNonSerializableAttributes();
}
@Override
public void execute(Tuple tuple) {
currentTuple = tuple;
List<Object> result = null;
try {
result = filter((Status) tuple.getValue(0));
} catch (ClassCastException e) {
result = filter(null);
}
if (result != null) {
for (Object obj : result) {
collector.emit(tuple, new Values(obj));
}
collector.ack(tuple);
}
}
@Override
public void cleanup() {
if (pool != null) {
pool.destroy();
}
if (configListenerThread != null) {
configListenerThread.end();
}
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields(channel));
}
public abstract List<Object> filter(Status status);
public void publish(String msg) {
Jedis jedis = pool.getResource();
jedis.publish(channel, msg);
pool.returnResource(jedis);
}
protected void setupNonSerializableAttributes() {
}
/**
* Will create a new thread (carefull, if you specify a multiplicity of 3
* for this bolt, you will create 3 new threads). That will listen to a
* redis pubsub channel, when ever a message is sent to that channel, will
* read the key with the specific name in the message and pass it to the
* onConfiguration function.
*/
protected void setupDynamicConfiguration(final OnDynamicConfigurationListener listener) {
configListenerThread = new ConfigListenerThread(listener);
configListenerThread.start();
}
private class ConfigListenerThread extends Thread {
//Use it's own pool, is in a different thread.
final Jedis jedis = new Jedis("localhost");
private OnDynamicConfigurationListener listener;
public ConfigListenerThread(OnDynamicConfigurationListener l) {
listener = l;
}
@Override
public void run() {
jedis.subscribe(new JedisPubSub() {
@Override
public void onUnsubscribe(String arg0, int arg1) {
// TODO Auto-generated method stub
}
@Override
public void onSubscribe(String arg0, int arg1) {
// TODO Auto-generated method stub
}
@Override
public void onPUnsubscribe(String arg0, int arg1) {
// TODO Auto-generated method stub
}
@Override
public void onPSubscribe(String arg0, int arg1) {
// TODO Auto-generated method stub
}
@Override
public void onPMessage(String arg0, String arg1, String arg2) {
// TODO Auto-generated method stub
}
@Override
public void onMessage(String channel, String message) {
if (message == null) {
return;
}
Jedis readConfigJedis = pool.getResource();
String config = readConfigJedis.get(message);
if (config == null) {
log.warn("Could not find any configuration with key " + message);
return;
}
pool.returnResource(readConfigJedis);
listener.onConfigurationChange(config);
}
}, configChannel);
}
public void end() {
if (jedis != null) {
jedis.quit();
}
}
}
}