The first part of the series dealth with the initial work of consuming the StackExchange REST API in order to retrieve its top questions. This second part will focus on setting up the support necessary to interact with the Twitter REST APIs using the Spring Social Twitter project. The end goal is to be able to tweet these questions, two per day, on several accounts, each focused on a single topic.
1. Using Spring Social Twitter
The required dependencies necessary to use the Spring Social Twitter project are straightforward. First, we define spring-social-twitter itself:
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-twitter</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
Then, we need to override some of it's dependencies with more up-to-date versions:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
Both spring-core and spring-web are defined as dependencies by spring-social-twitter but with older versions – 3.0.7.RELEASE and 3.1.0.RELEASE respectively. Overriding these in our own pom ensures that the project is using the up-to-date versions that we have defined instead of these older inherited versions.
2. Creating a Twitter Application
This usecase – tweeting on a personal account and not on behalf of other users on their accounts, is a simple one. The fact that it is simple allows us to dispense with most of the OAuth orchestration necessary if the application would need to tweet for multiple users, on each of their twitter accounts.
So, for our usecase, we will create the TwitterTemplate directly, as we can manually set up everything we need to do so.
First thing we need is a dev application – one can be created here, after logging in. After creating the application, we will have a Consumer Key and Consumer Secret – these are obtained from the page of the Application – on the Details tab, under OAuth settings.
Also, in order to allow the Application to tweet on the account, Read and Write Access must be set to replace the default Read only privileges.
3. Provisioning a TwitterTemplate
Next, the TwitterTemplate requires an Access Token and an Access Token Secret to be provisioned. These can also be generated from the Application page – under the Details tab – Create my access token. Both the Access Token and the Secret can then be retrieved from under the OAuth tool tab.
New ones can always be regenerated on the Details tab, via Recreate my access token action.
At this point we have everything we need – the Consumer Key and Consumer Secret, as well as the Access Token and Access Token Secret – which means we can go ahead and create our TwitterTemplate for that application:
new TwitterTemplate(consumerKey, consumerSecret, accessToken, accessTokenSecret);
4. One Template per Account
Now that we have seen how to create a single TwitterTemplate for a single account, we can look back at our usecase again – we need to tweet on several accounts – which means we need several TwitterTemplate instances.
These can be easily created on request, with a simple mechanism:
@Component
public class TwitterTemplateCreator {
@Autowired
private Environment env;
public Twitter getTwitterTemplate(String accountName) {
String consumerKey = env.getProperty(accountName + ".consumerKey");
String consumerSecret = env.getProperty(accountName + ".consumerSecret");
String accessToken = env.getProperty(accountName + ".accessToken");
String accessTokenSecret = env.getProperty(accountName + ".accessTokenSecret");
Preconditions.checkNotNull(consumerKey);
Preconditions.checkNotNull(consumerSecret);
Preconditions.checkNotNull(accessToken);
Preconditions.checkNotNull(accessTokenSecret);
TwitterTemplate twitterTemplate =
new TwitterTemplate(consumerKey, consumerSecret, accessToken, accessTokenSecret);
return twitterTemplate;
}
}
The four security artifacts are of course externalized in a properties file, by account; for example, for the SpringAtSO account:
SpringAtSO.consumerKey=nqYezCjxkHabaX6cdte12g
SpringAtSO.consumerSecret=7REmgFW4SnVWpD4EV5Zy9wB2ZEMM9WKxTaZwrgX3i4A
SpringAtSO.accessToken=1197830142-t44T7vwgmOnue8EoAxI1cDyDAEBAvple80s1SQ3
SpringAtSO.accessTokenSecret=ZIpghEJgFGNGQZzDFBT5TgsyeqDKY2zQmYsounPafE
This allows for a good mix of flexibility and safety – the security credentials are not part of the codebase (which is opensource) but live independently on the filesystem and are picked up by Spring and available in the Spring Enviroment via a simple configuration:
@Configuration
@PropertySource({ "file:///opt/stack/twitter.properties" })
public class TwitterConfig {
//
}
Properties in Spring are a subject that has been discussed before, so we won't go into further details on this subject here.
Finally, a test will verify that an account has the necessary security information readily available in the Spring Environment; if the properties are not present, the getTwitterTemplate logic should fail the test with a NullPointerException:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TwitterConfig.class })
public class TwitterTemplateCreatorIntegrationTest {
@Autowired
private TwitterTemplateCreator twitterTemplateCreator;
//
@Test
public void givenValidAccountSpringAtSO_whenRetrievingTwitterClient_thenNoException() {
twitterTemplateCreator.getTwitterTemplate(SimpleTwitterAccount.SpringAtSO.name());
}
}
5. Tweeting
With the TwitterTemplate created, let's turn to the actual action of tweeting. For this, we'll use a very simple service, accepting a TwitterTemplate and using its underlying API to create a tweet:
@Service
public class TwitterService {
private Logger logger = LoggerFactory.getLogger(getClass());
public void tweet(Twitter twitter, String tweetText) {
try {
twitter.timelineOperations().updateStatus(tweetText);
} catch (RuntimeException ex) {
logger.error("Unable to tweet" + tweetText, ex);
}
}
}
6. Testing the TwitterTemplate
And finally, we can write an integration test to perform the entire process of provisioning a TwitterTemplate for an account and tweeting on that account:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TwitterConfig.class })
public class TweetServiceLiveTest {
@Autowired
private TwitterService twitterService;
@Autowired
private TwitterTemplateCreator twitterCreator;
@Test
public void whenTweeting_thenNoExceptions() {
Twitter twitterTemplate = twitterCreator.getTwitterTemplate("SpringAtSO");
twitterService.tweet(twitterTemplate, "First Tweet");
}
}
7. Conclusion
At this point, the Twitter API we have created is completely separate from the StackExchange API and can be used independent of that particular usecase, to tweet anything.
The next logical step in the process of tweeting questions from Stack Exchange accounts is to create a component – interacting with both the Twitter and StackExchange APIs that we have presented so far – this will be the focus of the next article in this series.