1

Background:

We have an Oracle 11g database running a Java Virtual Machine with JRE version 1.6.0_43. Our user interface is hosted on an Apache webserver which interfaces with the database through mod-plsql.

We have some PLSQL procedures calling Java procedures stored in Java Sources in the database - one example is a Java procedure we use to generate XLS files.

We've previously been using the Apache POI suite version 3.8 to generate XLS files from XML using the DOM Parser, but we've upgraded to POI 3.9 so that we can use the Streaming XSSF Workbook classes to efficiently generate XLSX files (SXSSF was available in POI 3.8 but a core procedure, dispose(), was not made available until POI 3.9).

Also for efficiency reasons we're using StAX parsing (XMLStreamReader) when generating XLSX files as opposed to the broadly more memory-hungry DOM Parser method we use for XLS generation.

To upgrade Java POI 3.8 to 3.9 in our database we had a two-step process:

1) Run dropjava on the jars comprising the POI 3.8 suite:

dropjava -user=xxxxxx@xxxxxx poi-3.8-20120326.jar poi-examples-3.8-20120326.jar poi-excelant-3.8-20120326.jar poi-ooxml-3.8-20120326.jar poi-ooxml-schemas-3.8-20120326.jar poi-scratchpad-3.8-20120326.jar lib\commons-logging-1.1.jar lib\junit-3.8.1.jar lib\log4j-1.2.13.jar ooxml-lib\dom4j-1.6.1.jar ooxml-lib\stax-api-1.0.1.jar ooxml-lib\xmlbeans-2.3.0.jar

2) We then ran a loadjava command to install the 3.9 suite:

loadjava -user=xxxxxx@xxxxxx -genmissing -resolve -force poi-3.9-20121203.jar poi-examples-3.9-20121203.jar poi-excelant-3.9-20121203.jar poi-ooxml-3.9-20121203.jar poi-ooxml-schemas-3.9-20121203.jar poi-scratchpad-3.9-20121203.jar lib\commons-logging-1.1.jar lib\junit-3.8.1.jar lib\log4j-1.2.13.jar ooxml-lib\dom4j-1.6.1.jar ooxml-lib\stax-api-1.0.1.jar ooxml-lib\xmlbeans-2.3.0.jar

The XLSX generation did not work at all at first; we received this exception in Java at runtime:

java.lang.ClassCastException

We didn't confirm which specific class caused the problem, but we noticed that, after installing POI 3.9 (which includes the StAX stax-api-1.0.1 jar), there were some duplicates in our Java class library in the database. Specifically, these classes and their paths were duplicated (and being imported into our xlsx-generating Java Source):

import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;

Reasoning that the duplicates could be causing the exception, we removed the StAX parser jar (the StAX suite was "folded-in" to the core JDK at some point before our Java version, so there could be no need to import the StAX jar included with POI 3.9):

dropjava -user=xxxxxx@xxxxxx ooxml-lib\stax-api-1.0.1.jar

This removed the duplicate classes and resolved the ClassCastException, i.e. it no longer occurs. But there is a new issue.

Problem:

The new XLSX method now fails intermittently at runtime with this error:

javax.xml.stream.FactoryFinder$ConfigurationError: Provider com.sun.xml.stream.ZephyrParserFactory not found

The class in question, com.sun.xml.stream.ZephyrParserFactory, is seemingly installed correctly in our database. We've done some testing on a separate platform and we've found that removing that class entirely breaks our parsing completely, i.e. we are confident the class is installed on our live platform because our code works most of the time but would work none of the time if the class was not installed.

The error only occurs roughly once every 10 or so XLSX generation attempts (we've tested using both different files and the same file each time - it doesn't seem to matter what the content of the XML is). This line of code generates the error:

XMLInputFactory factory = XMLInputFactory.newInstance();    

With some investigation, we've found that whether or not the error occurs depends on which Oracle session is processing the initial HTTP request received by the webserver. I feel like the real question here is what could be causing only certain Oracle sessions to fail to access the Zephyr class as a Provider and produce the error? Any ideas would be greatly appreciated.

MrC
  • 11
  • 1
  • 5

2 Answers2

0

It's probably related to the handling of CDATA events described here http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/tutorial/doc/SJSXP5.html.

Depending on the input file this event is triggered or not. In case the event is thrown you get the Provider com.sun.xml.stream.ZephyrParserFactory not found error.

You need to investigate which code tries to instantiate this concrete service provider.

Normally a service-provider implementing a specific interface is declared in the directory META-INF/services inside a Jar file. Find here the description of the ServiceLoader which provides a simple service-provider loading facility

SubOptimal
  • 22,518
  • 3
  • 53
  • 69
  • Note that we have been testing by passing the same XML in to the Java procedure each time, the XML always contains CDATA cells/events and the error is being generated by the `XMLInputFactory factory = XMLInputFactory.newInstance();` line every time it does occur, rather than being generated when a CDATA event is encountered when reading the XML. I'm not sure whether or not those caveats preclude what you've suggested from being the exact cause; perhaps you know. Either way it's a very promising line for me to go down; thank you very much for answering. – MrC May 11 '16 at 13:01
  • @MrC Ok. Didn't knew that it fails with the same input file. Do you have the `sjsxp.jar` somewhere in your classpath (check also the classpath in the manifest.mf's)? I believe you should remove it as JSR 173 is part of Java 6. – SubOptimal May 11 '16 at 14:09
  • We don't have anything set for java.class.path in the System Properties, i.e. so technically we don't have/use a classpath. Instead, we load classes we need into the database Java Class library using the loadjava command. However, we _do_ have a setting for the java.library.path System Property; `sjsxp.jar` does not appear in that directory _but_ it does look like many other jars are still in there from over the years. I'm wondering now if any of those jars are somehow causing a conflict _sometimes_, i.e. some Oracle sessions access the Java library path rather than the db Java Class library. – MrC May 11 '16 at 16:17
  • @MrC Based on my anser you should search for Jar files which contain a file `META-INF/services/javax.xml.stream.XMLEventFactory`. Then check if that file contains the line `com.sun.xml.stream.ZephyrParserFactory`. It should be safe in your case (as JEE5 and Java 6 support JSR173) to remove this Jar file from the class path. **As always. Make a backup before. ;-)** – SubOptimal May 12 '16 at 05:59
0

Based on:

how to override a service provider in java

and

https://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/tutorial/doc/SJSXP4.html

you can set a System Property for javax.xml.stream.XMLInputFactory to specify the implementation class to use to create implementation instances of XMLInputFactory at runtime.

We had such an implementation class available in the core Java API stored in our db class library:

XMLInputFactoryImpl

We now use a line like this at the start of our Java procedure to set a System Property for the duration of that Oracle session:

System.setProperty("javax.xml.stream.XMLInputFactory", "<class path>XMLInputFactoryImpl");

This has seemingly resolved the XMLInputFactory service provider confusion that some of our Oracle sessions were encountering at runtime.

Community
  • 1
  • 1
MrC
  • 11
  • 1
  • 5