Search code examples
ejbtwitter4j

Using Twitter4J's UserStreamListener with EJB


Looking around StackOverflow, I see this answer to a similar problem - according to the Twitter4J documentation, TwitterStream#addListener takes a callback function. I have naively written my class as follows:

@Stateless
@LocalBean
public class TwitterListenerThread implements Runnable {
  private TwitterStream twitterStream;
  public TwitterListenerThread(){}
  @EJB private TwitterDispatcher dispatcher;

  @Override
  public void run() {
    ConfigurationBuilder cb = new ConfigurationBuilder();
    cb.setDebugEnabled(true)
        .setJSONStoreEnabled(true)
        .setOAuthConsumerKey(Properties.getProperty("twitter_OAuthConsumerKey"))
        .setOAuthConsumerSecret(Properties.getProperty("twitter_OAuthConsumerSecret"))
        .setOAuthAccessToken(Properties.getProperty("twitter_OAuthAccessToken"))
        .setOAuthAccessTokenSecret(Properties.getProperty("twitter_OAuthAccessTokenSecret"));

    twitterStream = new TwitterStreamFactory(cb.build()).getInstance();
    UserStreamListener listener = new UserStreamListener() {
        @Override
        public void onStatus(Status status) {
            dispatcher.dispatch(status);
        }
        // Standard code
    };
    twitterStream.addListener(listener);
    // Listen for all user activity
    String user = Properties.getProperty("twitter-userid");
    String[] users = {user};
    twitterStream.user(users);
  }
}

Now, on my colleague's PC this soon fails with an attempt to invoke when container is undeployed on the dispatcher.dispatch(status); line. I understand the reason as being due to the Twitter4J threading model not playing well with the JavaEE EJB model, but I cannot work out what to do based on the answer presented in the linked answer - how would I use a Message-Driven Bean to listen in to the Twitter stream?


Solution

  • After a little thinking, I worked out that the solution offered was to write a separate application that used just Java SE code to feed, using non-annotated code, a JMS queue with tweets, and then in my main application use a Message-Driven Bean to listen to the queue.

    However, I was not satisfied with that work-around, so I searched a little more, and found Issue TFJ-285, Allow for alternative implementations of Dispatcher classes:

    Now it is possible to introduce your own dispatcher implementation.

    It can be Quartz based, it can be MDB based, and it can be EJB-timer based. By default, Twitter4J still uses traditional and transient thread based dispatcher.

    1. Implement a class implementing twtitter4j.internal.async.Dispatcher interface
    2. put the class in the classpath
    3. set -Dtwitter4j.async.dispatcherImpl to locate your dispatcher implementation

    This is the default implementation on GitHub, so one could replace the:

    private final ExecutorService executorService;
    

    with a:

    private final ManagedExecutorService executorService;
    

    And, in theory, Bob's your uncle. If I ever get this working, I shall post the code here.