package org.p7h.storm.sentimentanalysis.spouts; import backtype.storm.spout.SpoutOutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseRichSpout; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Values; import backtype.storm.utils.Utils; import org.p7h.storm.sentimentanalysis.utils.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import twitter4j.*; import twitter4j.conf.ConfigurationBuilder; import java.io.IOException; import java.util.Map; import java.util.Properties; import java.util.concurrent.LinkedBlockingQueue; /** * Spout which gets tweets from Twitter using OAuth Credentials. * * @author - Prashanth Babu */ public final class TwitterSpout extends BaseRichSpout { private static final Logger LOGGER = LoggerFactory.getLogger(TwitterSpout.class); private static final long serialVersionUID = -6815379407002113362L; private SpoutOutputCollector _outputCollector; private LinkedBlockingQueue<Status> _queue; private TwitterStream _twitterStream; @Override public final void open(final Map conf, final TopologyContext context, final SpoutOutputCollector collector) { this._queue = new LinkedBlockingQueue<>(1000); this._outputCollector = collector; final StatusListener statusListener = new StatusListener() { @Override public void onStatus(final Status status) { _queue.offer(status); } @Override public void onDeletionNotice(final StatusDeletionNotice sdn) { } @Override public void onTrackLimitationNotice(final int i) { } @Override public void onScrubGeo(final long l, final long l1) { } @Override public void onStallWarning(final StallWarning stallWarning) { } @Override public void onException(final Exception e) { } }; //Twitter stream authentication setup final Properties properties = new Properties(); try { properties.load(TwitterSpout.class.getClassLoader() .getResourceAsStream(Constants.CONFIG_PROPERTIES_FILE)); } catch (final IOException ioException) { //Should not occur. If it does, we cant continue. So exiting the program! LOGGER.error(ioException.getMessage(), ioException); System.exit(1); } final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); configurationBuilder.setIncludeEntitiesEnabled(true); configurationBuilder.setOAuthAccessToken(properties.getProperty(Constants.OAUTH_ACCESS_TOKEN)); configurationBuilder.setOAuthAccessTokenSecret(properties.getProperty(Constants.OAUTH_ACCESS_TOKEN_SECRET)); configurationBuilder.setOAuthConsumerKey(properties.getProperty(Constants.OAUTH_CONSUMER_KEY)); configurationBuilder.setOAuthConsumerSecret(properties.getProperty(Constants.OAUTH_CONSUMER_SECRET)); this._twitterStream = new TwitterStreamFactory(configurationBuilder.build()).getInstance(); this._twitterStream.addListener(statusListener); //Our usecase computes the sentiments of States of USA based on tweets. //So we will apply filter with US bounding box so that we get tweets specific to US only. final FilterQuery filterQuery = new FilterQuery(); //Bounding Box for United States. //For more info on how to get these coordinates, check: //https://wiki.ceh.ac.uk/display/cehigh/Bounding+box //-8.164723,49.955269,1.7425,60.6311 //-9.05, 48.77, 2.19, 58.88 final double[][] boundingBoxOfUK = {{-9.23, 49.84}, {2.69, 60.85}}; filterQuery.locations(boundingBoxOfUK); this._twitterStream.filter(filterQuery); } @Override public final void nextTuple() { final Status status = _queue.poll(); if (null == status) { //If _queue is empty sleep the spout thread so it doesn't consume resources. Utils.sleep(500); } else { //Emit the complete tweet to the Bolt. this._outputCollector.emit(new Values(status)); // LOGGER.debug("Tweet:{}", status); } } @Override public final void close() { this._twitterStream.cleanUp(); this._twitterStream.shutdown(); } @Override public final void ack(final Object id) { } @Override public final void fail(final Object id) { } @Override public final void declareOutputFields(final OutputFieldsDeclarer outputFieldsDeclarer) { //For emitting the complete tweet to the Bolt. outputFieldsDeclarer.declare(new Fields("tweet")); } }