I am writing a plugin for Jira which involves parsing of XML documents. I am using JAXB to do so (XML to pojos and vice versa) So have a class which generates XML from pojos
The com.sun.xml.bind
package is part of the JAXB RI (http://jaxb.dev.java.net/), so you probably have that on your classpath somewhere.
Java6 has it's own version of JAXB included, in the com.sun.xml.internal.bind
package, so you don't usually need the RI in Java6 .
The RI can made made to work with Java6 , but it's uphill battle, and usually ends up with this sort of problem.
I know this is sort of a late answer, but for people landing here there are a few things not mentioned in the other posts that is of importance when developing plug-ins for JIRA (and other Atlassian products).
First JIRA, or rather Atlassian, has two types of plug-ins, see Differences between Plugins1 and Plugins2
Since it was a ClassNotFoundException (and JIRA v4.0.1), I assume the plug-in is a Plugin2, which can be used in JIRA v4 and later.
From JIRA v4, JIRA acts as an OSGi container, and hence Plugin2 is an OSGi bundle. In OSGi every bundle has its own set of class loaders. This enables different bundles to have different versions of the same jars, and to be hot deployed, among other things. The catch, however, is that not all packages from the JDK is per default available to these class loaders. This is explained under Plugins, bundles and OSGi on the Atlassian Developers web page. A more detailed description can be found in the blog Exposing the boot classpath in OSGi at Springsource. The second paragraph there even has the title NoClassDefFoundError: com.sun…
Such much for the theory.
When developing a plug-in for JIRA with the Atlassian SDK, Maven is used behind the scenes, see Atlassian Plugin SDK Documentation. So there will be a pom.xml in the plug-in project. To include JDK packages in the plug-in, one can add a <SystemProperties>
tag to the maven-jira-plugin (for other Atlassian products, there's a corresponding maven plugin), and set the bootdelegation property (and you might want to set java to 1.6 for the maven-compiler-plugin):
...
<build>
<plugins>
<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>maven-jira-plugin</artifactId>
<version>3.7.3</version>
<extensions>true</extensions>
<configuration>
<productVersion>${jira.version}</productVersion>
<productDataVersion>${jira.data.version}</productDataVersion>
<systemProperties>
<property>
<name>atlassian.org.osgi.framework.bootdelegation</name>
<value>sun.*,com.sun.*</value>
</property>
</systemProperties>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
...
</plugins>
</build>
...
The listed packages will then be available to the bundle. Be aware, though, boot delegation is not a fix it all solution, and should only be used with restriction. Read more at Boot Delegation and Avoid Classloader Hacks.
Under the dependencies, one can set the jaxb-api version needed:
...
<dependencies>
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>atlassian-jira</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugins.rest</groupId>
<artifactId>atlassian-rest-common</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.4</version>
<scope>provided</scope>
</dependency>
...
</dependencies>
...
It's not always necessary to explicitly have a dependency on jaxb-api. For example the atlassian-rest-common plug-in above has a transitive dependency on jaxb-api. Important is to understand the setting of scope. Look at Setting OSGi Manifest Instructions in your Plugin (same page as Plugins, bundles and OSGi but further down).
The intrigued can learn more at OSGi Alliance Specifications and OSGi Community Wiki.
Finally I was able find out the reason.
There are many ClassLoaders
involved while loading the plugins in JIRA (Felix). which will not delegate to 'bootstrap' ClassLoader
. And hence the problem.
To know which ClassLoader
loaded the JAXBContext
class, use JAXBContext.class.getClassLoader()
which will print some Felix ClassLoader
.
It loaded the class from jaxb-api.jar
instead of relying on rt.jar
but they implemented the classes slightly different. rt.jar
version uses
com.sun.xml.bind.internal.v2.ContextFactory
where jaxb-api
version uses com.sun.xml.bind.v2.ContextFactory
.
I am able to solve the issue using overlaoded method of JAXB which will take another parameter as ClassLoader
.
It took quite some time. But, I am surprised by the inner details & my ignorance