How to support multiple versions of Jira in one plugin

With the release of 7.6.0, there is a new Priorities API. Our plugin should support versions 7.1 - 7.6.
In my code, I want to do something like

if (jiraVersion >= 7.6.0) {
   // use new priorities API
}
else {
   // use old priorities API
}

In my pom.xml, I set the Jira version to 7.1.0, our minimum version we support. But of course, then I can’t use the 7.6 APIs in the code.

How is this usually handled? Some options I have though of:

  1. Set the Jira version to 7.6.0 in the pom.xml; the old methods are just deprecated, and so I can call both as required. But then, I might accidentally use other 7.6 APIs and break compatibility with previous versions. Also, at some point the old APIs might be removed; how can I tell when that happens?
  2. Use extra maven modules. I tried adding a module to my project, and make the new module depend on 7.6.0, and the main project depend on 7.1.0. But then the element in the top-level pom changes to “pom” not “atlassian-plugin”, and I got weird build errors. I don’t know much about maven/poms, and didn’t get much further.
  3. Create another plugin with the 7.6.0 code; and reference it from my main plugin (how to bundle these?)
  4. Something else?

I would prefer only one build step, and one jar file output at the end.

Is there a standard solution to supporting multiple versions with different APIs; or how have you tackled this issue previously?

Well, you just don’t. Managing multiple versions of API inside code will inevitably lead to a lot of troubles, bugs, frustration. You just make multiple builds (jars) that each support needed versions of vendor product and upload to marketplace, where it can be stored for different versions of vendor product. I mean why would Atlassian even make ‘versions’ page for marketplace listings if it would be smart and profitable to just make single build where you manage different versions of API inside code. That’s just my 2 cents about that.

For me, the Atlassian versions page is more about licensing and features; each version we release has a increasing feature set; and supports a fixed range of Jira versions. Customers who have purchased the addon get updates to a certain point in time; and the versions page manages that.
I agree with you in that it doesn’t make sense to support ALL versions of Jira in a single build, but I think it makes sense to support a couple of versions when new Jira features are released; thereby simplifying our own build and release processes.

I believe there are two ways of solving the problem. Both require detecting version on the host instance. To achieve that you can use BuildUtilsInfo from the JIRA Java API. After that I see two paths:

  1. use reflection to call the valid methods from the relevant API version - assuming you know the exact signatures
  2. use maven modules to create two version of ‘adapter’ classes (one depending on 7.5-and-below API, one depending on 7.6), and execute the code from the relevant module.

The second approach needs more explanation. So, for example you could make in your project following modules:
plugin-stuff
plugin-adapter-jira75-and-below
plugin-adapter-jira76-and-above

the ‘adapter’ modules would have in the dependencies something alike:

<dependency>
  <groupId>com.atlassian.jira</groupId>
  <artifactId>jira-api</artifactId>
  <version>7.5</version>  <!-- or 7.6 -->
  <scope>provided</scope>
</dependency>

in your plugin code you would probably need to declare ‘adapting’ interfaces like this (I’m making the method names up, I don’t know the new Priorities API) :

public interface PrioritiesManagerAdapter {
    List<Priority> getPrioritiesForProject(Project project);
}

You would need to pick the right instance of the above interface during runtime:

public class PrioritiesManagerAdapterFactory {

    private final PrioritiesManagerAdapter prioritiesManagerAdapter;

    public PrioritiesManagerAdapterFactory(JiraConfigService jiraConfigService) {
        if (jiraConfigService.isJiraVersionSevenSixOrNewer()) {
            prioritiesManagerAdapter = new Jira76UserProjectHistoryManager(); // <-- this class comes from 'plugin-adapter-jira76-or-above' module
        } else {
            prioritiesManagerAdapter = new Jira75UserProjectHistoryManager(); // <-- while this class comes from 'plugin-adapter-jira65-or-below' module
        }
    }

    public PrioritiesManagerAdapter get() { // <-- this would be called in other parts of plugin code 
        return prioritiesManagerAdapter;
    }
}

Later, to retrieve the list of priorities (disclaimer: I’m making this up, I lack the priorities API knowledge) one would be calling:

userProjectHistoryManagerAdapterFactory.get().getPrioritiesForProject(project)

And that would execute classes from relevant module (from ‘75-and-below’ or from ‘76-and-above’), which would contain the implementation based on the relevant API version.

Does it make sense what am I writing here?

cheers,
PS

1 Like

Absolutely makes sense; and thank you for the detailed explanations.
This is what I tried to implement too; but I got stuck at the point of getting the modules to work.
In the top-level pom; it seems the Atlassian SDK wants the packaging to be “atlassian-plugin”:
<packaging>atlassian-plugin</packaging>
But when you add modules, the packaging must be “pom”:
<packaging>pom</packaging>

I didn’t know how to resolve this problem. If anyone could show how to structure the pom files, this would be an ideal solution.

You can put the “atlassian-plugin” module as a child module, not the top-parent one. For example, given the following folder structure (that would correspond to module parent-child relation):

projectRoot/pom.xml                            <-- <packaging>pom</packaging>
projectRoot/thePlugin/pom.xml                  <-- <packaging>atlassian-plugin</packaging>
projectRoot/theAdapters/pom.xml                <-- <packaging>pom</packaging>
projectRoot/theAdapters/jira75-adapter/pom.xml <-- <packaging>jar</packaging>
projectRoot/theAdapters/jira76-adapter/pom.xml <-- <packaging>jar</packaging>

That should work. The only downward I can see is that you would need to start the plugin (atlas-debug, atlas-package etc) from the subfolder (“cd thePlugin ; atlas-debug”), and what is more painful: whenever you work on adapters, or whenever you release a new version of the artifacts and increment in the poms, you would have to do full maven install procedure (“atlas-mvn install” inside projectRoot directory) to recreate relevant jars in ~/.m2/repository… Or maybe not, with some smart configuration of poms and maven-release-plugin (if you use one).

1 Like

Got it, thank you.
For now, this adds a bit too much complexity to the build. So I will just set the Jira version to 7.6.0 in my pom, and manage the deprecated/removed features (and testing) manually.
If we get into a situation where we really need two different version dependencies, this looks like an excellent solution.