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

Re: Using classes from other plugin

Tin Wa Yeung September 7, 2011

Hi all,

I have 2 plugins, all version 2. Lets say plugin A and plugin B.

In plugin A, I want to call a function in plugin B (inside one of the classes).

I wonder how can this be done?

Thank you.

2 answers

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

4 votes
Answer accepted
Wojciech Seliga
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 8, 2011

In more complicated scenarios, if you don't want to create and expose a component (which is usually serves as a singleton service in a plugin), but just have the access to classes from the other plugin (e.g. to call some static method, construct their objects on your own, etc.) you have to make sure that the original plugin exports appropriate packages (where such classes reside) in terms of OSGi export and your second plugin imports these packages (in terms of OSGi import).

You do it (unless you want to manually create MANIFEST.MF files) by specyfing instrumentation instructions in your pom.xml file. This page talks about it in details. So for instance to be able to use GreenHopper classes you need to make sure that GH exports the classes you need. A quick look at its pom.xml indicates that all subpackages of com.pyxis and com.atlassian.greenhopper are exported:

...
<plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-jira-plugin</artifactId> <version>3.4</version> <extensions>true</extensions> <configuration> <instructions> <Export-Package> com.pyxis.*, com.atlassian.greenhopper.* </Export-Package>
...
and that your plugin imports them using <Import-Package> in similar way.

using <component> and public=true and <component-import> does OSGi magic for you - but for components only. Remember if you start playing manually with OSGi export/imports, plugin SDK will let you alone there (it won't interfere with what you are doing anymore). Which often means in practice: hours spent in OSGi Classloader trying to figure out what is going on and why your classes are not visible in the other plugin.

With debugging OSGi related problems I found UPM helpful. It has a separate tab called "OSGi" (visible by default in dev mode). If are troubleshooting apps running in non-dev mode, you can still open such OSGi tab adding #osgi to the url (e.g. <your JIRA URL>/plugins/servlet/upm#osgi

Igor Sereda [ALM Works]
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 8, 2011

That's a good one - I forgot to mention OSGi export/import. Do you know if OSGi magic covers the classes that the exported component depends on? If they are from a different package?

It's also interesting how we can make plugin A work if plugin B is missing. We can use "resolution:=optional" OSGi import classifier to avoid bundle loading problems, but it's not clear how to deal with possible NoClassDefFoundError, and general disabling of the modules that depend on the imported interface.

Wojciech Seliga
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 8, 2011

Yep, good call Igor - I forgot to mention resolution optional in OSGi. :)

AFAIK SDK (via its bnd plugin) handles transitive dependencies of importet components so most mortals who just base on components and component-import should not to bother with such low level OSGi stuff.

WRT how to handle missing classes during runtime (with expected optional presence of another plugin) - I have used in the past catching NoClassDefFoundError in some initializator and then switched off the flag which meant "skip this stuff as other plugin is not present"

We also used 0...1 cardinality in our Spring injection mechanism. That is our context.xml file in META-INF/spring included something like:

<osgi:reference  id="someId"
interface="interface.of.injected.optional.dependency"
cardinality="0..1"/>

Which meant null was injected instead of throwing an exception (about missing dependency).
I have no experience though with disabling whole modules if something they depend on is missing. That would be interesting. Maybe Don Brown or Jon Kodumal will comment here.

Igor Sereda [ALM Works]
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 8, 2011

Ah, nice trick with cardinality. By "disabling modules" I mean that if the constructor of a module class (like a class for component module) requires a bunch of parameters, these parameters are injected, and if at least one of the parameters cannot be auto-wired, you get an exception in the log and the module gets disabled.

Wojciech Seliga
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 8, 2011

Yes, I understand it. However I'd expect some kind of special marker in my atlassian-plugin.xml where I could say that this module may be disabled when 3rd party dependency is not present and then the plugin framework could silently diable such module if it cannot inject all required arguments (maybe with INFO in the log).

MattS
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 8, 2011

OSGI magic, ack. I've spent too many hours trying different options, very frustrating. I didn't find the explanations in Atlassian's documentation gave me a solid understanding of what the expected process is and how to debug it when things go wrong. A rough spot for plugin devs in general.

Igor Sereda [ALM Works]
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 11, 2011

Incidentally, working on this right now. Looks like "OSGi Magic" does not work if the interface made public is from a different JAR (something like B-api.jar), which is probably always the case. So we need to use <instructions> anyway.

3 votes
Igor Sereda [ALM Works]
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 7, 2011

Generally, you need to expose a component in plugin B and import it in plugin A.

In plugin B atlassian-plugin.xml you'd have something like this:

<component name="XYZ" key="xyz" class="my.plugin.B.xyzImpl" public="true">

<interface>my.plugin.B.xyz</interface>

</component>

And in plugin A descriptor you'd have something like this:

<component-import key="xyz-import" interface="my.plugin.B.xyz"/>

You will also need to build plugin B API jar (with interfaces and their dependencies) and declare dependency on that library in plugin A POM:
<dependency>
<groupId>my.plugin</groupId>
<artifactId>plugin-B</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

The related documentation:

http://confluence.atlassian.com/display/JIRADEV/Component+Import+Plugin+Module

http://confluence.atlassian.com/display/JIRADEV/Component+Plugin+Module

MattS
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 7, 2011

After you build plugin B, the jar file has to be installed in the local maven repo for plugin A to find in it's pom.xml.

Does anyone know a neat way around this - perhaps building with a single pom.xml will get this right?

Igor Sereda [ALM Works]
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 7, 2011

I usually install the file into the local repository - just run atlas-package and wait until it fails. As a part of error message Maven gives a neat line that you can copy to install the existing file into repo.

Can't see a good way to do this somehow else, unless both plugins are really a single product.

Oh, and you can publish your API JARs through Sonatype, of course!

MattS
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 7, 2011

I should check whether if using a parent pom.xml with modules will resolve the dependency properly. It's a pain installing locally every time.

Tin Wa Yeung September 8, 2011

Thank you for all the good answers.

Yes, the installation on the local repository is really a pain!

But currently, there aren't any good solution to it right?

TAGS
AUG Leaders

Atlassian Community Events