Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Next challenges

Recent achievements

  • Global
  • Personal


  • Give kudos
  • Received
  • Given


  • Global

Trophy case

Kudos (beta program)

Kudos logo

You've been invited into the Kudos (beta program) private group. Chat with others in the program, or give feedback to Atlassian.

View group

It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Make your Jira Server/Data Center app work without dependant 3d party app Part 1


In this article we will talk about specific to Atlassian app problem concerning 3d party apps.

Let's say we want to create an app which have dependencies to Jira Software and Jira Service Desk. In this case you will inject services into your app provided by Jira Software and Jira Service Desk. But suppose your app is installed on a Jira instance without Jira Software but with Jira Service Desk. It is fine. You want still your app to work with Jira Service Desk. But your app will not be able to be installed because you are injecting services from Jira Software. How to make your app so that your app would be able to be installed even if Jira Software or Jira Service Desk is not available in the Jira instance where your app is going to be installed?

This problem arises every time if you use 3d party app's services in your app. For example, let's say you want to use Table Grid API or Insight API. You will inject services from those apps and your app will not work if those apps are not installed in the Jira instance. But how to make it work?

And that is exactly what we are going to talk about in this article.

We will take as an example Jira Cloud Migration Assistant (JCMA). Our app will work with JCMA but we want also our app to be able to work if JCMA is not installed.

We will start from this code:

git clone git clone --branch v.1 --single-branch

Initial code

In the initial code we have two files.

The first one is the src/main/java/ru/matveev/alexey/atlassian/tutorial/impl/ file

@Named ("myPluginComponent")
public class MyPluginComponentImpl implements MyPluginComponent

    private final AppCloudMigrationGateway appCloudMigrationGateway;

    public MyPluginComponentImpl(@ComponentImport AppCloudMigrationGateway appCloudMigrationGateway)
        this.appCloudMigrationGateway = appCloudMigrationGateway;

    public String getName()
        if(null != this.appCloudMigrationGateway)
            return "myComponent:" + appCloudMigrationGateway.getClass().getName();
        return "myComponent";

Here we import AppCloudMigrationGateway service in the constructor.

The second file is src/main/java/ru/matveev/alexey/atlassian/tutorial/

public class Caller {
    public Caller(MyPluginComponent myPluginComponent) {
        log.error(String.format("Component Class Name: %s", myPluginComponent.getName()));


Here we inject our MyPluginComponent and log a error message with the getName method of MyPluginComponent

Run app

Move into the app folder and type atlas-run. Jira should be started but your app will be disabled:

Screenshot 2020-08-31 at 12.57.31.png

If you have a look in the atlassian-jira.log file you will see the following error:

2020-08-31 12:59:43,234+0300 ThreadPoolAsyncTaskExecutor::Thread 24 ERROR      [o.e.g.b.e.i.dependencies.startup.DependencyWaiterApplicationContextExecutor] Unable to create application context for [ru.matveev.alexey.atlassian.tutorial.third-party-dependency-tutorial], unsatisfied dependencies: none
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'caller': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myPluginComponent': Resolution of declared constructors on bean Class [ru.matveev.alexey.atlassian.tutorial.impl.MyPluginComponentImpl] from ClassLoader [ru.matveev.alexey.atlassian.tutorial.third-party-dependency-tutorial [231]] failed; nested exception is java.lang.NoClassDefFoundError: com/atlassian/migration/app/AppCloudMigrationGateway

Which means that com/atlassian/migration/app/AppCloudMigrationGateway class not found.

Now install JCMA:


Now go to Manage Apps, uninstall and install your app. This time your app will be enabled:

Screenshot 2020-08-31 at 13.03.47.png

And you will also discover our message:

2020-08-31 13:03:26,388+0300 ThreadPoolAsyncTaskExecutor::Thread 27 ERROR admin 779x2400x1 1m4naq8 0:0:0:0:0:0:0:1 /rest/plugins/1.0/ [r.m.a.atlassian.tutorial.Caller] Component Class Name: myComponent:com.sun.proxy.$Proxy3421

Now Unistall JCMA and your app will become disabled.

Ok. Everything works as expected.

Change code

Now let's make modification to our code to make our app enabled even if JCMA is absent.

The problem is in the src/main/java/ru/matveev/alexey/atlassian/tutorial/impl/ file with the following code:

    public MyPluginComponentImpl(@ComponentImport AppCloudMigrationGateway appCloudMigrationGateway)
        this.appCloudMigrationGateway = appCloudMigrationGateway;

We inject AppCloudMigrationGateway as dependency in our class. We need to get rid of this injection.

How to do it?

First of all let's create an accessor class for AppCloudMigrationGateway service.

Here is the src/main/java/ru/matveev/alexey/atlassian/tutorial/ file

public class JCMAAccessor {

    public static AppCloudMigrationGateway getJCMAGateway() {
        if(ComponentAccessor.getPluginAccessor().getPlugin("com.atlassian.jira.migration.jira-migration-plugin") == null) {
            return null;
        return ComponentAccessor.getOSGiComponentInstanceOfType(AppCloudMigrationGateway.class);

We do not inject AppCloudMigrationGateway service here. We check if JCMA is installed then we return a reference to the AppCloudMigrationGateway service with the getOSGiComponentInstanceOfType method. If JCMA is not installed then we return null.

Now let's rewrite our src/main/java/ru/matveev/alexey/atlassian/tutorial/impl/ file to this one :

@Named ("myPluginComponent")
public class MyPluginComponentImpl implements MyPluginComponent

    public String getName()
        if(null != JCMAAccessor.getJCMAGateway())
            return "myComponent:" + JCMAAccessor.getJCMAGateway().getClass().getName();
        return "myComponent";

We check if JCMAAccessor.getJCMAGateway() returns not null, then we get the name of the class. If JCMAAccessor.getJCMAGateway() returns null then we return myComponent.

Run our app again

Now let's install our app again. This time our app will be enabled even if we have not installed JCMA and we will see the following message in the logs:

2020-08-31 13:37:24,274+0300 ThreadPoolAsyncTaskExecutor::Thread 27 ERROR admin 812x2453x5 28lz3m 0:0:0:0:0:0:0:1 /rest/plugins/1.0/installed-marketplace [r.m.a.atlassian.tutorial.Caller] Component Class Name: myComponent

Well. It is done. Nice and clean. You can see the final code for this part under v.2 tag

Part 2



Log in or Sign up to comment
Community showcase
Published in Marketplace Apps & Integrations

Bitbucket Smart Commits vs. Genius Commits - What's the difference?

If you already heard about Smart Commits in Bitbucket, know that you just stumbled upon something even better (and smarter!): Genius Commits by Better DevOps Automation for Jira Data Center (+ Server...

130 views 0 2
Read article

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you