How to work around the @SupportedMethod problem?

On Jira 9.0 EAP 04 is here! - #51 by rw-dennis , Dennis highlights that we can’t build a webwork1 action for Jira 9.0 which would also be compatible with Jira 8.

I’ll provide the answer below.

1 Like

The principle is:

  • Compile against Jira 8.13,
  • Add the @SupportedMethods in a library that you build yourself, which you set as ‘provided’ (the solution of @adam.labus on Jira 9.0 EAP 04 is here! - #45 by adam.labus )
  • Import that library and specify resolution:=optional in your pom.xml,
  • Ensure that classes using this annotation are never loaded in your Jira 8 plugin, otherwise you get a ClassNotFoundError. To do this, define a Webwork action specifically for Jira 9, which uses @SupportedMethods and inherits all your normal actions from the parent class (The implementation below is by @LaurentNonnenmacher ),
  • Only activate this Webwork module when Jira 9 is running.

Here’s our specific Jira 9.0 action, which inherits from the “normal” action:

package com.playsql.requirementyogijira.web;

import com.atlassian.jira.security.request.RequestMethod;
import com.atlassian.jira.security.request.SupportedMethods; // Don't import it from Jira 9, see below.

@SupportedMethods(RequestMethod.GET)
public class RYConfigureRelationshipsAction9 extends RYConfigureRelationshipsAction {

    @SupportedMethods({RequestMethod.GET, RequestMethod.POST})
    @Override
    public String doDefault() {
        return super.doDefault(); // So, just reuse the actions of the parent class.
    }
}

So you need a @SupportedMethods annotation, define it in a module:


and add the module to your pom.xml:

    <dependencies>
        <dependency>
            <!-- For Jira 8/9 compatibility-->
            <groupId>com.requirementyogi.plugins</groupId>
            <artifactId>workarounds-for-atlassian-problems</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-api</artifactId>
            <version>8.13.0</version> <!-- No need to depend on Jira 9! -->
            <scope>provided</scope>
        </dependency>

Don’t forget the Import-Package section in your atlassian-plugin.xml:


    <build>
        <plugins>
            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>amps-maven-plugin</artifactId>
                <version>${amps.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <product>jira</product>
                    <productVersion>${jira.version}</productVersion>
                    <productDataVersion>${jira.version}</productDataVersion>
                    <applications>
                        <application>
                            <applicationKey>jira-software</applicationKey>
                            <version>${jira.version}</version>
                        </application>
                    </applications>
                    <ajpPort>5013</ajpPort>
                    <jvmDebugPort>5008</jvmDebugPort>
                    <jvmArgs>-Xmx1500m -Xms700m</jvmArgs>
                    <installPlugin>false</installPlugin>
                    <skipTests>${skipTests}</skipTests>
                    <instructions>
                        <Import-Package>
                            com.atlassian.jira;version="0.0.0",
                            com.atlassian.jira*;resolution:=optional;version="0.0.0", <!-- THIS is important, it's an OPTIONAL import -->
                            ...

Then, declare TWO webworks in your atlassian-plugin.xml:

<webwork1 key="ry-webwork-8" name="Requirement Yogi web pages for Jira 7 and 8" class="java.lang.Object" state="disabled">
        <action name="com.playsql.requirementyogijira.web.RYConfigureRelationshipsAction" alias="ry-configure-relationships" roles-required="admin">
            <view name="error">/templates/admin/ry-configure-relationships.vm</view>
            <view name="input">/templates/admin/ry-configure-relationships.vm</view>
        </action>
    </actions>
</webwork1>

<webwork1 key="ry-webwork-9" name="Requirement Yogi web pages for Jira 9 and above" class="java.lang.Object" state="disabled">
    <actions>
        <action name="com.playsql.requirementyogijira.web.RYConfigureRelationshipsAction9" alias="ry-configure-relationships" roles-required="admin">
            <view name="error">/templates/admin/ry-configure-relationships.vm</view>
            <view name="input">/templates/admin/ry-configure-relationships.vm</view>
        </action>
    </actions>
</webwork1>

And use this class to activates/deactivates the correct webwork action depending on the version of Jira:

package com.playsql.requirementyogijira.managers;

import com.atlassian.plugin.PluginController;
import com.atlassian.sal.api.ApplicationProperties;
import com.playsql.utils.SemVer;
import org.springframework.beans.factory.InitializingBean;

/**
 * This component handle which module to enable.
 *
 * Modules are located in atlassian-plugin.xml
 */
public class JiraVersionManager implements InitializingBean {

    private final PluginController pluginController;
    private final ApplicationProperties applicationProperties;

    public JiraVersionManager(PluginController pluginController,
                              ApplicationProperties applicationProperties) {

        this.pluginController = pluginController;
        this.applicationProperties = applicationProperties;
    }

    @Override
    public void afterPropertiesSet() {
        String jiraVersionString = applicationProperties.getVersion();
        SemVer jiraVersion = SemVer.parse(jiraVersionString);
        if (SemVer.compare(jiraVersion, SemVer.parse("9.0")) > 0) {
            pluginController.enablePluginModule("com.playsql.requirementyogijira:ry-webwork-9");
            pluginController.disablePluginModule("com.playsql.requirementyogijira:ry-webwork-8");
        } else {
            pluginController.enablePluginModule("com.playsql.requirementyogijira:ry-webwork-8");
            pluginController.disablePluginModule("com.playsql.requirementyogijira:ry-webwork-9");
        }
    }
}
3 Likes

For the separate maven module;

It’s a local library but my app is looking for dependencies in the atlassian repository:

    <repositories>
        <repository>
            <id>atlassian-public</id>
            <url>https://packages.atlassian.com/maven/repository/public</url>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
                <checksumPolicy>warn</checksumPolicy>
            </snapshots>
            <releases>
                <enabled>true</enabled>
                <checksumPolicy>warn</checksumPolicy>
            </releases>
        </repository>
    </repositories>

This of course results in errors that the dependency isn’t found. Are you deploying this separate maven component to a local repo inside your app codebase or how are you managing that?

Hi Dennis
We currently have it in the same project, so it is built when the whole project is built.
In a near future, it will be in a separate bitbucket repository and uploaded in our local private “nexus” repository with a fixed version 1.0.0.

In our case, I have added an additional module, which is imported as provided and has a dummy implementation of @SupportedMethods and RequestMethod POST/GET (no other thing is there)… and this module has a dependency on jira-api ( 8.x version).

And after trying different things the easiest way that was working was not to do anything else, still is working, I have tested on jira8 and on jira9, I can deploy using jira9 and run on jira8 and on jira9…
In other words I did not have to make any difference between jira8/jira9 (other than setting the jira version)

I recommend during development to compile using jira8 and jira9, just to be sure that our code works on both cases (there are some methods that were removed on jira9 that used to exist on jira8).
To deploy to prod I would only deploy using jira9 so that we have one jar for both versions. (but for other steps as development/testing I would still use jira8 and jira9 just to double check everything is working correctly)

Hi @aragot , I tried your solution but it is not working for me. Even I tried to use @SupportedMethods({RequestMethod.GET, RequestMethod.POST}) directly with Jira 9 but still POST requests not working. However the methods are working fine when I execute the post request trough the browser console. Is there any working sample project available to go trough? Also Appreciate if you could mention the versions(amps.version and jira 9 version) that you have used here in your solution.
Thanks.

Hi, in our sample, ${jira.version} = 8.13.0, and ${amps.version} = 8.4.1.

Concerning your POST requests not working, do you see any error message? Did you use the exact correct package name for com.atlassian.jira.security.request.SupportedMethods, and is this class defined, as we said, in a project that you wrote?

We haven’t published a working demo in public, because most of the code is here in our first answer.

Hi @aragot , I dont see any error messages and I have used the correct package name which is com.atlassian.jira.security.request.SupportedMethods. With using the @SupportedMethods I was able to resolve 405 error which I got initially and now the pages are loading fine. But when I try to click any buttons which binds the POST request with the action, it wont work as expected. Following is the sample AJAX call and I always get the success in return and the actual method which binds the POST request never executed. I have used the @SupportedMethods annotation at the method also.

AJS.$('#TestConnection').click(function() {
			AJS.$.ajax({
				type: "POST",
				dataType: "html",
				url: contextPath + "/secure/admin/jira/testConnection.jspa",
				data: {
					'Id': AJS.$('#Id').val(),
					'Key': AJS.$('#Key').val()
				},
				cache: false,
				error: function(response, textStatus, errorThrown) {
					alert("Error!");
				} ,
				success: function(response, textStatus) {
					alert("Success.");
				}
			});

		});

I am using Jira Server atlassian-jira-software-9.0.0-EAP04 for testing.

What I did what was working is compile against Jira 9 and set com.atlassian.jira.security.request;resolution:=optional. Then you do not need any extra dependencies or version managers :slight_smile: