Last updated Feb 19, 2024

Fisheye and Crucible Development : Stage 6: Listen for commits and post to Twitter

CommitListener skeleton

To listen for commits, you need to register yourself at EventPublisher and annotate at least one method with @EventListener. Choose the type of events you are interested in by specifying the type of the annotated method parameter.

Use InitializingBean and DisposableBean interfaces to register/unregister your listener at the right point of application startup/shut down.

CommitListener.java

1
2
import com.atlassian.event.api.EventListener;
import com.atlassian.event.api.EventPublisher;
import com.atlassian.fisheye.event.CommitEvent;
 
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class CommitListener implements InitializingBean, DisposableBean {

    public CommitListener(EventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
        eventPublisher.register(this);
    }

    @Override
    public void destroy() throws Exception {
        eventPublisher.unregister(this);
    }

    @EventListener
    public void handleEvent(CommitEvent ce) {
        
    }
}

To install the listener, configure a new component:

atlassian-plugin.xml

1
2
<component key="commit-listener" class="com.example.ampstutorial.CommitListener" public="true">
    <interface>com.example.ampstutorial.CommitListener</interface>
</component>

Use logger

When something goes wrong, it's a good idea to log, so that it's easier to investigate the root cause. Let's add a logger:

CommitListener.java

1
2
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
    private static final Logger LOGGER = LoggerFactory.getLogger(CommitListener.class); 

Work with CommitEvent

New components for working with commits

We need two components to work with CommitEvents: CommitterUserMappingManager to map from git committer to Fisheye/Crucible user and RevisionDataService to get changeset data from changeset id. Both of these are available by default and you don't need to install them in atlassian-plugin.xml. We also need a third component - TwitterLoginRecordStore that we already have.

CommitListener.java

1
2
import com.atlassian.fisheye.spi.services.RevisionDataService;
import com.cenqua.fisheye.model.manager.CommitterUserMappingManager;
 
    private final EventPublisher eventPublisher;
    private final CommitterUserMappingManager committerUserMappingManager;
    private final RevisionDataService revisionDataService;
    private final TwitterLoginRecordStore twitterLoginRecordStore;

    public CommitListener(EventPublisher eventPublisher,
                          CommitterUserMappingManager committerUserMappingManager,
                          RevisionDataService revisionDataService,
                          TwitterLoginRecordStore twitterLoginRecordStore) {
        this.eventPublisher = eventPublisher;
        this.committerUserMappingManager = committerUserMappingManager;
        this.revisionDataService = revisionDataService;
        this.twitterLoginRecordStore = twitterLoginRecordStore;
    }

Handle events

Update handleEvent method to get user login from (repository, committer) tuple. Check if user has configured Twitter access. If yes, post to Twitter:

CommitListener.java

1
2
import com.atlassian.fisheye.spi.data.ChangesetDataFE;
 
    @EventListener
    public void handleEvent(CommitEvent ce) {
        String repositoryName = ce.getRepositoryName();
        ChangesetDataFE csData = revisionDataService.getChangeset(repositoryName, ce.getChangeSetId());
        if (csData != null) {
            String commitAuthor = csData.getAuthor();
            User user = committerUserMappingManager.getUserForCommitter(repositoryName, commitAuthor);
            if (user != null) {
                TwitterLoginRecord loginRecord = twitterLoginRecordStore.getForUser(user.getUsername());
                if (loginRecord != null) {
                    postToTwitter(loginRecord, csData);
                } else {
                    LOGGER.info("Twitter account not set for user {}", user);
                }
            } else {
                LOGGER.info("Can't find a user name for commit author {} in repository {}", commitAuthor, repositoryName);
            }
        } else {
            LOGGER.warn("Failed to find changeset data for {} in repository {}", changeSetId, repositoryName);
        }
    }

Post to Twitter

Add a dependency on external library that provides Twitter API:

1
2
<dependency>
    <groupId>org.twitter4j</groupId>
    <artifactId>twitter4j-core</artifactId>
    <version>4.0.4</version>
    <scope>compile</scope>
</dependency>

See http://twitter4j.org/ for library docs.

1
2
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.conf.PropertyConfiguration;
 
    private void postToTwitter(TwitterLoginRecord loginRecord, ChangesetDataFE csData) {
        try {
            // TwitterFactory can be configured only via PropertyConfiguration
            Properties props = new Properties();
            props.put("oauth.consumerKey", loginRecord.getConsumerKey());
            props.put("oauth.consumerSecret", loginRecord.getConsumerSecret());
            props.put("oauth.accessToken", loginRecord.getToken());
            props.put("oauth.accessTokenSecret", loginRecord.getTokenSecret());
            TwitterFactory twitterFactory = new TwitterFactory(new PropertyConfiguration(props));

            Twitter twitter = twitterFactory.getInstance();
            twitter.verifyCredentials();
            twitter.updateStatus(csData.getComment());
        } catch (TwitterException exception) {
            LOGGER.error("Failed posting to Twitter", exception);
        }
    }

Test your plugin

Commit something to the test repository (you can use the short script given in the previous section) and watch your plugin post the commit message to Twitter. Congratulations, you finished the task!

Rate this page: