zondag 25 oktober 2009

DIY Mylyn Twitter connector

Connecting Mylyn to SQL databases is easy using the Industrial SQL Connector for Mylyn, but you can also connect to something else by implementing or extending the IPersistor interface.
In this blog I describe how to quickly create a basic connector that will allow you follow one or more Twitter feeds right in the Mylyn Task List. Twitter is used to send out notifications by an ever increasing range of hardware or processes:
  • Wim Jongman tweeted about connecting an AS/400 or iSeries to Twitter to notify on jobs>>.
  • Kim Moir added twitter as a way of build notifications for the Equinox team using Hudson twitter plugin >>
  • And there are probably many more...

We are going to create a read-only connector to start with, as these build processes and hardware will not read any responses to their tweets or please their egos by following mentions!

Initial Setup

Install the Industrial SQL Connector for Mylyn from the update site (detailed instructions) or extract the source from SVN.
2011 02 22 Updated links to EclipseLabs update site

Create fragment project

Create a fragment project selecting the org.eclipse.mylyn.industrial.core plugin as host..
Create a lib folder and a run-configs folder. (I need to create a template wizard for this soon!)

Select twitter java library

I looked at the twitter API wiki and chose for Twitter4J as various source considered it most mature. Download it and install it in the fragment's lib folder. There is no need to export any packages!

Create Persistor extension

In the extensions tab of the fragment.xml create an extension to extension point org.eclipse.mylyn.industrial.core.persistor and create a new persistor-config.

Then set the repository attributes to false except for can-create-task-from-key and can-query-repository

Then set all the task attributes to read-only.

Code the Persistor extension using the library

Now we click on the persistor hyperlink to create the Java class, we extend the class PersistorAdapter that basicaly logs all calls and returns sensible defaults.
To query and show tasks we need to implement fetchTask(...), findTasks(...), and to make querying easier also findlegalOwners(...).
package org.eclipse.mylyn.industrial.twitter.persistor;
[...]
public class TwitterPersistor extends PersistorAdapter {

public final static String ID = "org.eclipse.mylyn.industrial.twitter"; //$NON-NLS-1$

private User user;

private Twitter twitter;

public TwitterPersistor() {
}

/**
* @return the twitter, initialize if needed
*/
public Twitter getTwitter(TaskRepository repository) {
if (null == twitter) {
AuthenticationCredentials credentials = repository
.getCredentials(AuthenticationType.REPOSITORY);
String twitterPassword = credentials.getPassword();
String twitterID = credentials.getUserName();
twitter = new Twitter(twitterID, twitterPassword);
}
return twitter;
}

@Override
public IndustrialTask fetchTask(TaskRepository repository, String... taskId)
throws SQLException, CoreException {
Twitter t = getTwitter(repository);

long id = Long.parseLong(taskId[0]);

try {
twitter4j.Status result = t.showStatus(id);
IndustrialTask tweet = new IndustrialTask(repository.getUrl(),
taskId[0], result.getText());

tweet.setOwner(result.getUser().getName());
tweet.setCreationDate(result.getCreatedAt());
tweet.setNotes(result.getText());
return tweet;
} catch (TwitterException e) {
IStatus status = CoreLogger.createStatus(IStatus.ERROR, e);
throw new CoreException(status);
}
}

@Override
public List<String> getLegalOwners(TaskRepository repository)
throws SQLException, CoreException {
Twitter t = getTwitter(repository);

List<String> result = new ArrayList<String>();
List<User> friends;
try {
friends = t.getFriendsStatuses();
for (User friend : friends) {
result.add(friend.getName());
}
return result;
} catch (TwitterException e) {
IStatus status = CoreLogger.createStatus(IStatus.ERROR, e);
throw new CoreException(status);
}
}

@Override
public List<String> findTasks(TaskRepository repository,
IndustrialQueryParams criteria) throws SQLException, CoreException {
Twitter t = getTwitter(repository);

List<String> result = new ArrayList<String>();
try {
for (String user : criteria.getOwner()) {
List<twitter4j.Status> timeline;
timeline = t.getUserTimeline(user);
for (twitter4j.Status s : timeline) {
result.add(Long.toString(s.getId()));
}
}
return result;
} catch (TwitterException e) {
IStatus status = CoreLogger.createStatus(IStatus.ERROR, e);
throw new CoreException(status);
}
}

@Override
public boolean validate(TaskRepository repository) throws SQLException,
CoreException {
return null != getTwitter(repository);
}

@Override
public boolean isAuthenticated(TaskRepository repository)
throws SQLException, CoreException {

try {
user = getTwitter(repository).verifyCredentials();
return true;
} catch (TwitterException e) {
Status status = new Status(IStatus.ERROR, TwitterPersistor.ID,
"Cannot validate Twitter"); //$NON-NLS-1$
throw new CoreException(status);
}
}
}

We add the methods isAuthenticated(...) and validate(...) but both can just default to returning true when just statrting your development.

Now Run and we're Done!


OK the we run the whole project and create a new twitter repository, using your own name and password:

It will even check whether we entered our password correctly when we press the Validate button and call validate(...)

We can now do a simple query on the list of tweeters we are following using a form based query.

And presto, we have the tweeted AS/400 messages in our Mylyn Tasklist!

Conclusion


By parameterizing the creation of Mylyn connectors, the Industrial SQL Connector for Mylyn makes creating a quick and dirty connector to almost anything very, very easy. Creating this connector took me a little more than 2 hours, almost less than creating this blog entry.
Source code can be downloaded here.