Jira wired plugin tests fail: java.lang.NoClassDefFoundError: MapBuilder

Immo Hüneke November 6, 2018

I have been following the excellent tutorial to create my first Jira plugin, but it has been a bit of an uphill struggle to progress beyond the noddy example. In particular, I keep hitting the same class loading error when executing the wired tests.

In order to use the backdoor methods in the Jira testkit, I have the following dependencies in my plugin's POM (note that jira.version is defined as 7.12.3):

<dependency>
<groupId>org.eclipse.gemini.blueprint</groupId>
<artifactId>gemini-blueprint-core</artifactId>
<version>${gemini.blueprint.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugins.rest</groupId>
<artifactId>atlassian-rest-module</artifactId>
<version>3.2.18</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-api</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.sal</groupId>
<artifactId>sal-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- junit.version = 4.11!!!!! -->
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-tests</artifactId>
<version>${jira.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.atlassian.jira.tests</groupId>
<artifactId>jira-testkit-client</artifactId>
<version>${testkit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>

I have reconfigured the maven-jira-plugin as follows:

<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>maven-jira-plugin</artifactId>
<version>${amps.version}</version>
<extensions>true</extensions>
<configuration>
<productVersion>${jira.version}</productVersion>
<productDataVersion>${jira.version}</productDataVersion>
<jvmArgs>-Xms1g -Xmx1g -XX:MaxPermSize=1g -XX:-UseGCOverheadLimit -server</jvmArgs>
<testBundleExcludes>
<testBundleExclude>
<groupId>atlassian</groupId>
</testBundleExclude>
<testBundleExclude>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
</testBundleExclude>
</testBundleExcludes>
<!--Uncomment to install TestKit backdoor in JIRA.-->
<pluginArtifacts>
<pluginArtifact>
<groupId>com.atlassian.jira.tests</groupId>
<artifactId>jira-testkit-plugin</artifactId>
<version>${testkit.version}</version>
</pluginArtifact>
<pluginArtifact>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-func-test-plugin</artifactId>
<version>${jira.version}</version>
</pluginArtifact>
</pluginArtifacts>
<enableQuickReload>true</enableQuickReload>
<enableFastdev>false</enableFastdev>
<!-- See here for an explanation of default instructions: -->
<!-- https://developer.atlassian.com/docs/advanced-topics/configuration-of-instructions-in-atlassian-plugins -->
<instructions>
<Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>
<!-- Add package to export here -->
<Export-Package>
com.atlassian.tutorial.myPlugin.api
</Export-Package>
<!-- Add package import here -->
<Import-Package>
org.springframework.osgi*;resolution:="optional",
org.eclipse.gemini.blueprint*;resolution:="optional",
*;version="0";resolution:="optional"
</Import-Package>
<!-- Ensure plugin is spring powered -->
<Spring-Context>*</Spring-Context>
</instructions>
</configuration>
</plugin>

This allows me to compile the following lines in the wired tests:

protected Backdoor backdoor;
...
@Before
public void setup() {
backdoor = new Backdoor(new TestKitLocalEnvironmentData());
...

The file src/test/resources/atlassian-plugin.xml (after much unsuccessful experimentation) has the following content:

<atlassian-plugin key="${project.groupId}.${project.artifactId}-tests" name="${project.name}" plugins-version="2">
<plugin-info>
<description>Tests for ${project.description}</description>
<version>${project.version}</version>
<vendor name="${project.organization.name}" url="${project.organization.url}" />
<bundle-instructions>
<Import-Package>
org.springframework.osgi*;resolution:="optional",
org.eclipse.gemini.blueprint*;resolution:="optional",
*;version="0";resolution:="optional"
</Import-Package>
</bundle-instructions>
</plugin-info>

<!-- from our base plugin -->
<component-import key="myComponent" interface="com.atlassian.tutorial.myPlugin.api.MyPluginComponent"/>

<!-- from the product container -->
<component-import key="applicationProperties" interface="com.atlassian.sal.api.ApplicationProperties" />

</atlassian-plugin>

When I execute the command "atlas-integration-test", the plugin-tests module is loaded successfully, but (regardless of whether the above bundle-instructions section is included or not) it fails to execute the wired tests:

[INFO] [talledLocalContainer] java.lang.NoClassDefFoundError: com/atlassian/jira/util/collect/MapBuilder
[INFO] [talledLocalContainer] at com.atlassian.jira.testkit.client.xmlbackup.XmlBackupCopier.createBaseUrlSubstitution(XmlBackupCopier.java:267)
[INFO] [talledLocalContainer] at com.atlassian.jira.testkit.client.xmlbackup.XmlBackupCopier.<init>(XmlBackupCopier.java:77)
[INFO] [talledLocalContainer] at com.atlassian.jira.testkit.client.DataImportControl.<init>(DataImportControl.java:115)
[INFO] [talledLocalContainer] at com.atlassian.jira.testkit.client.Backdoor.<init>(Backdoor.java:80)
[INFO] [talledLocalContainer] at it.com.atlassian.tutorial.myPlugin.MyComponentWiredTest.setup(MyComponentWiredTest.java:119)
[INFO] [talledLocalContainer] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...

What could be going wrong? I've tried everything I can think of (including asking lots of colleagues). By adding suitable debugging statements, I can see that the classloader being used is an instance of 

com.atlassian.plugins.rest.module.ChainingClassLoader

and by asking it to reveal its list of packages, I discover that it only has access to generic Java packages - no com.atlassian or org.atlassian ones.

 

1 answer

1 accepted

1 vote
Answer accepted
Immo Hüneke November 8, 2018

A colleague with deep knowledge of OSGi, Spring and Tomcat finally managed to resolve the problem for me. It turns out that you have to explicitly import the Java package that contains the "missing" class, because Jira uses Felix to wire up dependencies between OSGi bundles and the Felix classloader deliberately masks the Tomcat classloader to avoid loading multiple copies of the same packages.

So all it took to cure the problem in this case was to add the following line to the Import-Package section of the plugin-tests plugin (src/test/resources/atlassian-plugin.xml):

com.atlassian.jira.util.collect;resolution:="optional",

I had already tried something very similar, which did NOT work:

com.atlassian.jira.util*;resolution:="optional",

What this has revealed to me is the extent to which any software product these days is composed of so many other technologies that when something does not work as expected, users will generally be totally unable to make progress unless (a) they have an intimate knowledge of all the underlying technologies, or (b) they have access to expert technical support.

Immo Hüneke November 8, 2018

NB one piece of information in the above problem statement is incorrect. I had logged the class of the classloader at the time my test class was constructed. By the time it actually runs the Before method, this has changed to a 

org.apache.felix.framework.BundleWiringImpl$BundleClassLoaderJava5

(as discovered when I moved the log statement from the constructor to the Before method). This is what gave my colleague the clue as to what was actually going on.

Like 秦凡 likes this

Suggest an answer

Log in or Sign up to answer