David Moreno García
David Moreno García

Reputation: 4523

Background execution of Spring services

I'm developing a simple Spring MVC application to download tweets from the streaming API and show them in a webpage. Users of the application can submit a Task with the keywords of the tweets that they want to download. This tasks are shared so everyone can start, stop, modify, change or cancel a task.

TwitterFetcher is the class responsible of download tweets. This class receives a Task and persists all tweets downloaded in a database.

@Service
public class TwitterFetcher {

    @Autowired
    private OAuthService oAuthService;

    @Autowired
    private TweetService tweetService;

    private Task task;
    private TwitterStream twitterStream;

    public void start(Task task) {
        /* Stop previous stream */
        stop();

        /* Get OAuth credentials */
        OAuth oAuth = oAuthService.findOneEnabled();

        if (oAuth == null) {

        } else {
            this.task = task;

            Configuration oAuthConfiguration = getOAuthConfiguration(oAuth);
            twitterStream = new TwitterStreamFactory(oAuthConfiguration).getInstance();
            twitterStream.addListener(new TwitterListener());

            String keywords = task.getBaseKeywords() + ", " + task.getExpandedKeywords();
            FilterQuery filterQuery = new FilterQuery();
            filterQuery.track(keywords.split(", "));
            twitterStream.filter(filterQuery);
        }
    }

    public void stop() {
        if (twitterStream != null) {
            twitterStream.shutdown();
        }
    }

    private Configuration getOAuthConfiguration(OAuth oAuth) {
        ConfigurationBuilder cb = new ConfigurationBuilder();
        cb.setDebugEnabled(false);
        cb.setJSONStoreEnabled(true);
        cb.setOAuthAccessToken(oAuth.getAccessToken());
        cb.setOAuthAccessTokenSecret(oAuth.getAccessTokenSecret());
        cb.setOAuthConsumerKey(oAuth.getConsumerKey());
        cb.setOAuthConsumerSecret(oAuth.getConsumerSecret());

        return cb.build();
    }

    private class TwitterListener implements StatusListener {

        @Override
        public void onStatus(Status status) {
            /* Persist new tweet */
            Tweet tweet = new Tweet();
            tweet.setJson(DataObjectFactory.getRawJSON(status));
            tweetService.save(tweet);
        }

        [Omitted code]
    }
}

The basic functionality would be the next one:

  1. A user start the fetcher from the website.
  2. The fetcher receives a new tweet and it's saved in the DB
  3. The fetcher keeps receiving tweets until a user stop it.

The application has a dashboard to control the fetchers and the tasks and the users must be able to interact with it while the fetcher is downloading.

My question is, Would the fetcher block the app or will be executed in a different thread? In the worst case, what I have to change to solve this? I'm still far from an usable app so I can't test it. Even so, I want to fix it right now if possible.

Upvotes: 1

Views: 1409

Answers (2)

shazinltc
shazinltc

Reputation: 3666

Annotate your start() method with @Async.

@Async
public void start(Task task)

This will make the start method asynchronous and will not block the application.

You can check out a simple example here.

Upvotes: 1

gerrytan
gerrytan

Reputation: 41123

You can use ExecutorService to run the fetcher in a separate thread. I'd recommend using ThreadPool so you don't blow performance if too many users running the fetcher:

ExecutorService executor = Executors.newFixedThreadPool(maxThreads)

When a task is submitted through the executor it will return a Future object from which you can check for job completion

Future f = executor.submit(myTask);
boolean isDone = f.isDone();

Please read more about Java concurrency if you're not familiar: http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html

Upvotes: 1

Related Questions