Project

Profile

Help

Managing Saxon/C's Java VM

Added by Aleksandar Jelenak about 5 years ago

Hello!

I am developing Python bindings for the Saxon/C HE library. The code is on GitHub (https://github.com/ajelenak/pysaxon).

When running unit tests I get the error: "JNI_CreateJavaVM() failed with result: -5" when a SaxonProcessor object is created in more than one test. This error (JNI_EEXIST) indicates that a Java VM already exists by the time the second unit test is executed. Since I have never before used a C/C++ library built from its Java version I would like to know how to manage Saxon/C's Java VM. When is it started? When a first SaxonProcessor object is created? When is it terminated? If a SaxonProcessor object is destroyed, how does that affect the Java VM? Or is the Java VM terminated only when the executing process ends. Should I create and use a single SaxonProcessor object in all unit tests?

Thanks!

      Aleksandar

Replies (4)

Please register to reply

RE: Managing Saxon/C's Java VM - Added by O'Neil Delpratt about 5 years ago

Hi Aleksandar,

Interesting project you are undertaking and thanks for contacting us in regards to your issue. It is true the Java VM can only be created once per process. The VM is a static component that should be available for multiple instance of the SaxonProcessor object. As a safety measure I added a boolean variable to prevent it being created more than once (i.e. jvmCreatedCPP). I am a bit puzzled as to how you are getting that error. It is possible that you have discovered a bug here.

Please can you send your code for the unit tests so that I can better examine where things could be going wrong. Usually with a test harness I create the SaxonProcessor once and create multiple instances for any of the following engines XsltProcessor/XQueryProcessor/XPathProcessor/SchemaValidator via the SaxonProcessor object.

RE: Managing Saxon/C's Java VM - Added by Aleksandar Jelenak about 5 years ago

Hello!

Thanks for the prompt reply. These are the Python unit tests:

def test_create_bool(self):
    """Create SaxonProcessor object with a boolean argument"""
    from random import choice
    sp = SaxonProcessor(choice([True, False]))
    self.assertIsInstance(sp, SaxonProcessor)

def test_create_procs(self):
    """Create XPathProcessor, XsltProcessor from SaxonProcessor object"""
    sp = SaxonProcessor(False)
    xp = sp.newXPathProcessor()
    xsl = sp.newXsltProcessor()
    self.assertIsInstance(xp, XPathProcessor)
    self.assertIsInstance(xsl, XsltProcessor)

These are very basic tests. If only one of them is run, there is no error. Here's the test log:

test_create_bool
In SaxonProcessor.__cinit__()
In SaxonProcessor.__dealloc__()
PASSED
test_create_procs
In SaxonProcessor.__cinit__()
JNI_CreateJavaVM() failed with result: -5

The @cinit()@ and @dealloc()@ are special methods called when Python creates and destroys SaxonProcessor extension type objects. The @cinit()@ method just calls the @SaxonProcessor(bool l)@ class constructor. The @dealloc()@ method calls the @void release()@ method and a @delete@ on the SaxonProcessor's C++ pointer. I can provide more information if required.

RE: Managing Saxon/C's Java VM - Added by O'Neil Delpratt about 5 years ago

Hi,

Thanks for sending your python code snippet. The release method called in dealloc() should only be called once at the end of your program. This is because the VM can only be created and destroyed once in the same process.

I will try now to answer your initial questions:

I would like to know how to manage Saxon/C's Java VM. When is it started? When a first SaxonProcessor object is created?

Yes. It is started when you call the constructor of the SaxonProcessor. It is a static variable so it is shared between instances of SaxonProcessor.

When is it terminated? The VM is destroyed when you call the release() method on SaxonProcessor. If there are multiple instances of the SaxonProcessor class alive then a reference count is used to prevent the release method destroying the VM until the last SAxonProcessor object has been destroyed.

If a SaxonProcessor object is destroyed, how does that affect the Java VM? Calling the destructor of SaxonProcessor does not affect the VM.

Or is the Java VM terminated only when the executing process ends. Should I create and use a single SaxonProcessor object in all unit tests?

You can have more than one SaxonProcessor, it all depends on the design of the program. In the PHP world you would have created a SaxonProcessor for each request. The reference counter is important in this case to keep track of how many SaxonProcessor object where created so we only call release when the last SaxonProcessor has been destroyed.

In the sample C++ code we see that a single SaxonProcessor is necessary when sharing commonalities between parsed XML documents.

See details of the Processor object for Java but relevant to C++:

It is possible to run more than one Saxon Processor concurrently, but only when running completely independent workloads. 
Nothing can be shared between Processor instances. Within a query or transformation, all source documents and schemas 
must be built using the same Processor, which must also be used to compile the query or stylesheet.

From http://saxonica.com/documentation/index.html#!javadoc/net.sf.saxon.s9api/Processor

RE: Managing Saxon/C's Java VM - Added by Aleksandar Jelenak almost 5 years ago

Thanks for clarification. I changed my code accordingly and now those simple tests work.

    (1-4/4)

    Please register to reply