3. Why are we here?
• Because we all develop Atlassian
plugins:
• Commercial
• In-house
• Contracted customizations
4. Why are we here?
...in this session
• Because I want you all to
start exposing yourself
• Build, document and
support APIs for your
plugins
• Encourage one another
to build integrations
based on these APIs
22. Best Practices
• Don’t build API in a vacuum
• Dogfood your own API
• Build reference implementations
• Clearly separate API from internal
• Decouple through events, where
possible
• Document
23. Available
techniques
• Leverage the plugin system, as
much as possible
• Contexts/locations on existing
module types
• Custom module types
• Event-driven JS APIs
• Lightweight
25. Available
techniques
• Demarcate what is API through use
of a separate API artifact
• Be vigilant to ensure backwards
compatibility of API - additive
changes
• Have integration tests and alerts to
avoid regressions
26. Exposing Java API
(and playing nicely with the plugin
system)
• Mark API plugin components as
public=true
• Export-Package statements
• Limit external class references in
API artifact to JIRA + platform
• Ensure all maven dependencies are
marked <scope>provided</scope>
27. Available
techniques
• Decide: Hard or soft dependency
• Hard - your plugin will not
function in absence of depended-
upon plugin
• Soft - graceful degradation, only
integration features fail
28. Available
techniques
• Hard dependency
• <pluginDependency> in your pom
• Release your plugin as an OBR
• Soft dependency
• DynamicImport-Package, optional service pattern, OptionalFeatureService and
PluginDependencies, conditional UI elements
• ...it’s a little harder. Join me over
in IDEA
Editor's Notes
\n
Hey everyone, My name is James, I'm a dev on the GreenHopper team and in the three and a half years I've been at Atlassian, I've worked almost exclusively on JIRA plugins - plugins are my bread and butter. \n
So, we&#x2019;re all developers of Alassian plugins, some of you write commercial plugins, like we on the GreenHopper team do. Some of you write plugins which just run on your company&#x2019;s internal Atlassian product instances. And some of you do contracted customizations for all manner of other companies. But, we all write plugins and the plugins which we write will invariably run alongside other plugins - quite likely some written by other people in this room. This is the nature of developing for an ecosystem like that of Atlassian plugins.\n
Where am I going with this? Well, I want us to all start exposing ourselves, in a rather public manner.\nBy which I mean: I want to see more of us plugin developers writing, exposing, documenting and supporting APIs which other plugin developers can then use to build integrations between their plugins and your own. I also want to encourage you to start building such integrations as well.\n
Whatcha thinking? Sounds fine, right?\n
You&#x2019;re thinking: \n* APIs are hard, designing them is hard, implementing them in the OSGi-land which is Atlassian plugins sounds even harder.\n* Supporting and maintaining that? You&#x2019;re kidding me.\n* And for all that effort, what do I get? If someone else uses my API that's their feature, not mine!\n\nAnswering each of these in order: \n* Certain knowledge is required to do this well, but that&#x2019;s why we&#x2019;re here - the GH team made mistakes, so that you don&#x2019;t have to.\n* If you decouple your APIs well enough, write the right sort of tests and continue your regular CI practices, then there&#x2019;s no extra ongoing costs.\n* Trust me: a well designed API that gets integrated with by many consumers is a feature in its own right.\n
This isn&#x2019;t all talk: we&#x2019;ve really committed to this principle on the GH team - some of our real killer features have been a result of us exposing APIs, or have been achieved through us integrating with APIs exposed by other plugins. \n
This isn&#x2019;t all talk: we&#x2019;ve really committed to this principle on the GH team - some of our real killer features have been a result of us exposing APIs, or have been achieved through us integrating with APIs exposed by other plugins. \n
This isn&#x2019;t all talk: we&#x2019;ve really committed to this principle on the GH team - some of our real killer features have been a result of us exposing APIs, or have been achieved through us integrating with APIs exposed by other plugins. \n
I&#x2019;ll show you how GreenHopper users have benefited from us exposing APIs and integrating with other plugins.\nI&#x2019;ll convince you, as plugin developers, to expose yourself through APIs and to integrate with other plugins which have exposed APIs.\nTowards the end, we&#x2019;ll get into the nitty gritty technical details of how to build APIs in your Atlassian plugins, both at a design level and at an implementation level.\n
So what have we been doing?\n
We&#x2019;ve written a new plugin - called pretty urls - which exposes a <routing> plugin module type to allow for much nicer urls for your plugin, as shown in this screenshot. GreenHopper now ships as an OSGi bundle repository (or OBR) which includes our minimum required version of pretty urls. This ensures that the other plugin is available, and that the API that it exposes is available for GreenHopper to consume. We wrote this as an API-exposing plugin (rather than as a feature in GreenHopper) so that other plugins can take advantage of it. Ideally, we will eventually get it added as a bundled plugin in JIRA.\n
When GreenHopper first started working on the New GreenHopper (or Rapid Boards, as we were then calling them), we collaborated with the JIRA importers team in Poland to produce the Pivotal Tracker importer. This involved us building a number of Java API services:LabsConfiguration (so that the importer can ensure that the new GH labs feature was available), a RapidViewCreationService (for creating a new board for the newly imported issues) and later - a RankService to ensure that issues are imported in the same rank order as in Pivotal. \nThis last service was actually not developed specifically for this purpose - we developed it at the request of the Structure plugin developers, but after getting it in place, it became available for the importers plugin to use as well. When we develop an API for creating sprints in GreenHopper, then we fully expect the importers plugin to make use of it in their pivotal importer too.\n
This has been my favourite integration to work on so far, and turned out to be the easiest to develop. In this case, we exposed an API for adding tabs to the issue detail view in the new GreenHopper. We&#x2019;re all familiar with the atlassian-plugin <web-panel> tag? So this tag has a location attribute, we declared a new web-panel location &#x201C;atl.gh.issue.details.tab&#x201D;. Any plugin can now add an extra tab to the GreenHopper detail view, simply by adding a new web-panel, and adding any required JavaScript to the &#x201C;gh-rapid&#x201D; web-resource context. We currently have a small Javascript API for enabling these panels to integrate with the broader GH board.\n
GreenHopper has a relatively new feature called sample data: on project and board creation, we offer to populate the new board with some sample issues, which are structured to provide an in-product tutorial. This feature relies on the JIRA importers plugin to actually create the issue. Again, we worked with the importers team in Poland to develop an API for creating issues (with history!) from a stream of JSON data. So none of this issue creation logic actually happens in GH - we just call out to an API service published by the importers plugin, (meaning we don't have to maintain or support that code).\nA nice side effect of this is that the team in Poland can now use this new service to improve their product based importers, none of which currently import history. Building a good API helped them to build a better product.\n
This last example was one that I&#x2019;ve been working on in my 20% time. Shortly after Atlassian acquired HipChat, one of my colleagues built this really nice JIRA plugin called HipChat for JIRA, which enabled users to set JIRA up to push notifications to HipChat rooms as a post function on workflow steps. Now this is great functionality, but it&#x2019;s only really available to users who grok workflows and post functions AND have admin rights on their JIRA instance. I looked at this problem, and thought &#x201C;Hey, GreenHopper&#x2019;s column configuration is really just an abstraction layer over workflow - so if I could integrate with H4J, then users could just say &#x201C;I want notifications when issues are moved into these columns on my board&#x201D; - which enables this neat feature to a much wider range of users.\n
Why did we build these? To answer that, we have to consider what the common aspects of these features are? \n* Well, in each case the API consumer got a feature for cheaper than they would have if they built it entirely by themselves.\n* The users get extra features simply because they have plugins X and Y installed. In the case where both plugins cost money, this is awesome - They've spent money on one plugin to solve a specific problem, they decide to spend money on some other plugin to solve a seemingly unrelated problem and BAM! by the simple fact that they're using both plugins - they get something extra 'for free'. \n* By having all these plugins which sit on top of JIRA integrate with one another, we make using JIRA feel much more cohesive - rather than having a number of siloed experiences which each integrate with JIRA, we have a fully integrated network of applications which are better together.\n
Commercial devs - same as for the GH team, you lot are the easiest to convince - cheap features, better user experience.\nIn-house developers - 1 big monolithic plugin for your org means that if something goes wrong, your whole big-bank-jira-plugin comes down. If you can partition it into separate plugins which interact through well defined interfaces, then you can graceful degradation of just the integration features when things go wrong. Also, by making smaller code bases for more plugins, you can lower the barrier of entry to contribution.\nContract developers - As above, but also: reuse! You could build a customizations API plugin which contains the application logic - but not the business logic - for the kind of customizations that you often are asked to build. \n
Convinced? Cool, well - in the remaining time, I&#x2019;m going to show you how we get there.\n
* Look for painpoints - I saw the JIRA hipchat plugin and thought: "Such a cool feature, but as I described earlier - the barrier to entry is a little high.&#x201D;\n* Look for redundancy between another plugin and yours. Remove it through integrating with the other plugin.\nWhen you see a problem for your users which needs solving, consider if some other plugin already solves a similar problem.\n* Similarly, if you want to build some feature which overlaps with something another plugin already does, then try to leverage the other plugin. This is what happened with GH sample data, we thought &#x201C;Hmm... creating issues, that sounds like something the JIRA importers plugin already does&#x201D;.\n* Look for a story where a user would be doing something through plugin X and logically wants to take their current context with them when they enter plugin Y - think of the Bonfire-GH integration story: A user has just completed development on a story, they transition it into awaiting QA in greenhopper and they want to start a test session. The bonfire tab in GH allows them to do exactly this - allowing them to interact with Bonfire, from their current context inside GreenHopper.\n
* Don&#x2019;t build in a vacuum, have at least one planned use case for every bit of API you build, to help focus what you&#x2019;re building\n* Where it&#x2019;s possible, consume your own API, rather than having duplicate internal and external API. Note: build the API first, then look to remove redundancy - to avoid tailoring your API to your internal usecase. Also, be aware of performance costs.\n* In addition to having third party consumers AND internal consumers, build a reference implementation for each part of your API. We do this by having a second plugin in our source tree - the greenhopper-client-plugin. This never gets released, and serves no purpose, but as a developer you notice very quickly that it goes missing.\n* Your API should be clearly separate from internal services - both for consumers and for internal developers. Make it hard to &#x2018;accidentally&#x2019; break API.\n* Where it makes sense, use atlassian-events for backend API, and JS events for front end.\n* Document your API, so people know to use it. Open source your reference client, so that people can easily see how to make it work.\nNote: some of you who consume GreenHopper API may be finding some of this advice a little amusing - yes, I said at the top that we have made mistakes so that you don&#x2019;t have to. Some of this is a case of do as we say, not as we do.\n
The plugin system already solves many problems for us, so leverage it wherever possible. \n\nImplicit in this slide: favour non-java API - \n
Some of the eclipse folks have developed this fantastic guide to Java APIs. If you&#x2019;re embarking on this path, read this, then reread it, then keep it open in a pinned browser tab and refer back to it in perpetuity.\n