How can I use XPath when writing a plugin?

aware74 November 28, 2011

The plugin I'm writing needs to pull data from an XML configuration file to determine the right automated action(s) to take depending upon the type of issue, its state, and a value of its custom field. I've laid out these behavioral rules in an XML file, which I am reading in from the classpath.

I want to use XPath to quickly obtain the node(s) that are relevant to my query at runtime depending upon the variables stated above.

But I get the following exception when I attempt to get a new instance of an XPathFactory:

[INFO] [talledLocalContainer] Caused by: java.lang.RuntimeException: XPathFactory#newInstance() failed to create an XPathFactory for the default object model: http://java.sun.com/jaxp/xpath/dom with the XPathFactoryConfigurationException: javax.xml.xpath.XPathFactoryConfigurationException: No XPathFactory implementation found for the object model: http://java.sun.com/jaxp/xpath/dom
[INFO] [talledLocalContainer]   at javax.xml.xpath.XPathFactory.newInstance(XPathFactory.java:101)

This error is typically encountered when a component like Xalan cannot be found in the runtime classpath. But is that still the case in Java 1.6? I thought that there wasn't a dependency on Xalan anymore. Or is the problem that the runtime container contains both a Xalan OSGi runtime bundle and the native Java XPath implementation?

Is there a string that I need to give to XPathFactory.newInstance() that would avoid any ambiguity?

I tried at first to instead use JDOM's XPath APIs, but the JIRA OSGi runtime container doesn't contain an OSGi bundle for Jaxen, which is used by JDOM for its XPath implementation.

Thanks in advance for your help.

4 answers

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

0 votes
Answer accepted
aware74 December 19, 2011

So, for now, you can either use the boot delegation work-around documented above or, if you're feeling adventurous, you can download and install the Xalan 2.7.1 and its dependencies into the Felix Web Management console, and update your plugin's configuration per the following documentation to depend upon that exact version of Xalan:

https://developer.atlassian.com/display/DOCS/Bundling+extra+dependencies+in+an+OBR

An enhancement request has also been opened with Atlassian to include Xalan 2.7.1 and its dependencies in a future version of JIRA:

https://jira.atlassian.com/browse/JRA-26495

2 votes
David_Talalayevsky January 24, 2018

Had this issue when using a third party library that did XML parsing, specifically

com.monitorjbl.xlsx.StreamingReader

 None of the above options worked, tried this change to the code that ran the XPathFactory.newInstance() call:

ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(XPathFactory.class.getClassLoader());
try {
workbook = StreamingReader.builder()
.rowCacheSize(100)
.bufferSize(4096)
.open(file);
} finally {
Thread.currentThread().setContextClassLoader(originalContextClassLoader);
}

and this was added to the POM.xml

<systemProperties>
<property>
<name>atlassian.org.osgi.framework.bootdelegation</name>
<value>sun.*,com.sun.*</value>
</property>
<property>
<name>org.osgi.framework.bootdelegation</name>
<value>sun.*,com.sun.*</value>
</property>
<property>
<name>javax.xml.xpath.XPathFactory:http://java.sun.com/jaxp/xpath/dom</name>
<value>com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl</value>
</property>
</systemProperties>

That worked.  

 

Be careful not to do this unless you are sure that the call that eventually runs

XPathFactory.newInstace()

is in the same thread.

0 votes
aware74 March 28, 2013

FYI, even if you use the boot delegation work-around, it will not work if you have code that executes XPathFactory.newInstance() within the afterPropertiesSet() method of a any Event Listener that you implement. You need to move that code to sometime after that method is called, like within onIssueEvent() or some delegated method, etc.

0 votes
JamesJ December 1, 2011

Hi,

I noticed your comments in the IRC channel, but every time I go to respond you're offline (I'm in Europe). XML APIs and OSGi is unfortunately a bit of a nightmare, the way the Java XML APIs load their implementations is by scanning the context classloader, which in an OSGi environment, is not likely to ever be able to find the implementation. The solutions we've been using at Atlassian involve a mixture of context classloader hacks, and hiding most needs for XML behind bundles that we provide, such as JAXB marshalling.

The other thing that we find works very well is to use JDOM or Dom4J for XML processing. Information on how to use XPath using Dom4J can be found here:

http://dom4j.sourceforge.net/dom4j-1.6.1/guide.html

We also find these libraries much easier to work with in general than the built in Java libraries, unless you have a particular need to use JAXP, I would strongly recommend you use Dom4J, as it is much more friendly to an OSGi environment, and its APIs tend to be much easier to work with.

Cheers,

James

aware74 December 1, 2011

James,

Sorry we kept missing each other, and thank you for the background and for a viable work-around.

I actually tried using JDOM first, but JIRA doesn't include the requisite Jaxen and Jaxen-JDOM OSGi bundles to allow a user to use JDOM's XPath class. Maybe this could be considered as an enhancement request?

I can look into dom4j, but I kept on the path of trying to get JAXP to work, as it should work in a Java 1.6 env, OSGi container or not.

Through some hints from the Apache Felix user group and a review of this JIRA issue (https://jira.atlassian.com/browse/CWD-2414), I found that you can access Sun's internal implementations of JAXP classes using the following System property:

            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>maven-jira-plugin</artifactId>
                <version>3.4</version>
                <extensions>true</extensions>
                <configuration> 
                    ...
<systemProperties> <property> <name>atlassian.org.osgi.framework.bootdelegation</name> <value>sun.*,com.sun.*</value> </property> </systemProperties> </configuration> </plugin>

I tested in code that the above system property was not set until I set it in my plugin's pom.xml file.

The alternative I tried was to download and install the Xalan 2.7.1 OSGi bundle and its dependencies into the Felix Web Management console, and to update my plugin's pom.xml to set Xalan 2.7.1 as a dependency, but it didn't seem to matter. JAXP always went for the Xalan 2.7.0 JAR that's within the JIRA Web app's lib folder, even if I excluded "xalan" from "atlassian-jira", and Eclipse's Dependency Hierarchy view showed it as excluded.

Have you encountered the above, where the classloader seems to ignore that you said to use version Y of a dependency instead of version X?

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

TAGS
AUG Leaders

Atlassian Community Events