Available: | Activity Streams 5.0 and later. |
Activity Streams 5.0 includes a new, lightweight API that allows plugins to add activities to the Activity Stream. This API doesn't give you as much power as building a full-fledged Activity Provider, but it's extremely easy to use.
We'll demonstrate the API by building a sample plugin that uses a Jira issue event listener to add an activity whenever an issue is deleted.
Source code for the complete plugin is available on Bitbucket
The sample code shown here assumes that Activity Streams 5.0 is already bundled with Jira. The source code in the Bitbucket repository bundles Activity Streams 5 into an alpha version of Jira 5.0. I'll clean up the discrepancy when Streams 5.0 final is bundled with Jira
First we need to set up our development environment and create our plugin project and source directories. Assuming you have the Atlassian Plugin SDK installed, execute the following:
1 2$ atlas-create-jira-plugin
Follow the prompts to complete the initial plugin setup. We've used com.atlassian.labs as our groupId and streams-jira-delete-issue-plugin as our artifactId.
First, we need to add a dependency on the streams-thirdparty-api and streams-api artifacts to the dependencies section in our pom.xml file:
1 2<dependency> <groupId>com.atlassian.streams</groupId> <artifactId>streams-thirdparty-api</artifactId> <version>5.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.atlassian.streams</groupId> <artifactId>streams-api</artifactId> <version>5.0</version> <scope>provided</scope> </dependency>
The scope should be provided, as the library is part of Activity Streams 5, which is already bundled in Jira. To create an event listener, we also need to add a few other dependencies:
1 2<dependency> <groupId>com.atlassian.event</groupId> <artifactId>atlassian-event</artifactId> <version>${atlassian.event.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.atlassian.sal</groupId> <artifactId>sal-api</artifactId> <scope>provided</scope> <version>${sal.api.version}</version> </dependency>
ActivityServiceWe need to import the ActivityService component in our atlassian-plugin.xml:
1 2<component-import key="activityService" interface="com.atlassian.streams.thirdparty.api.ActivityService"/>
We can then use constructor injection whenever we need the activityService.
To get our event listener example working, we'll also need to inject a few other components from Jira. We'll add the following lines to our atlassian-plugin.xml:
1 2<component-import key="eventPublisher" interface="com.atlassian.event.api.EventPublisher"/> <component-import key="applicationProperties" interface="com.atlassian.sal.api.ApplicationProperties"/>
We're going to use applicationProperties to fetch the base URL of our Jira application. We'll need the eventPublisher to subscribe to Jira's issue events.
DeleteIssueListenerWe're going to create a new component that registers itself as an event listener, and responds to deletion events by posting an activity via the ActivityService. Let's first declare the component in our atlassian-plugin.xml:
1 2<component key="userListener" class="com.atlassian.labs.streams.jira.DeleteIssueListener"> <description>Class that processes JIRA delete issue events.</description> </component>
As the xml snippet indicates, we'll also want to create a class called DeleteIssueListener in the com.atlassian.labs.streams package. Create that class, and add a constructor that injects the components that we need:
1 2public class DeleteIssueListener { private final EventPublisher eventPublisher; private final ActivityService activityService; private final ApplicationProperties applicationProperties; public DeleteIssueListener(EventPublisher eventPublisher, ActivityService activityService, ApplicationProperties applicationProperties) { this.eventPublisher = eventPublisher; this.activityService = activityService; this.applicationProperties = applicationProperties; } }
As described in our event listener plugin tutorial, this component needs to tie its registration with the eventPublisher to our plugin's lifecycle. We can do that by implementing DisposableBean and LifecycleAware
1 2public class DeleteIssueListener implements DisposableBean, LifecycleAware { ... @Override public void destroy() throws Exception { eventPublisher.unregister(this); } @Override public void afterPropertiesSet() throws Exception { eventPublisher.register(this); } }
We'll create an activity whenever a Jira issue is deleted, and post it using our ActivityService. The code is as follows:
1 2@EventListener public void onIssueDelete(IssueEvent issueEvent) { if (issueEvent.getEventTypeId().equals(EventType.ISSUE_DELETED_ID)) { User user = issueEvent.getUser(); Either<ValidationErrors, Activity> result = new Activity.Builder(application("JIRA Deleted Issues", URI.create(applicationProperties.getBaseUrl())), new DateTime(), new UserProfile.Builder(user.getName()).fullName(user.getDisplayName()) .build()) .content(some(html(issueEvent.getIssue().getDescription()))) .title(some(html(user.getDisplayName() + " deleted " + issueEvent.getIssue().getKey() + " - " + issueEvent.getIssue().getSummary()))) .url(some(URI.create(applicationProperties.getBaseUrl()))) .build(); for (Activity activity : result.right()) { activityService.postActivity(activity); } for (ValidationErrors errors : result.left()) log.error("Errors encountered attempting to post activity: " + errors.toString()); } } }
We create a new Activity by using the Activity.Builder helper class. Activity.Builder.build() returns Either<ValidationErrors, Activity>, which is a disjoint union : either we created a valid Activity, or we received a set of errors indicating the reasons why the Activity we tried to construct wasn't well formed. The two for loops are a clean way of handling the two cases : only one of them actually executes, so we're either going to log the errors, or post the valid activity. Hopefully, the latter is the case, and our nice new activity well appear in the feed!

Rate this page: