[vtk-developers] Robustifying the JNI

Steve M. Robbins steven.robbins at videotron.ca
Sun Oct 9 09:25:03 EDT 2005


Howdy,

My colleague and I spent Friday modifying the java wrapper code with
an eye to making it less likely to bring down the JVM.  We'd like to
contribute this code to VTK so please criticize this proposal.

The first thing we did was wrap each JNI function inside a 
try/catch block:

  try 
    {
      ...
    }
    catch(std::bad_alloc)
    {
    // throw OutOfMemoryError on return to java
    vtkJavaThrowOutOfMemoryError(env);
    }
    catch(...)
    {
    // throw RuntimeException on return to java
    vtkJavaThrowRuntimeException(env);
    }


We realise that VTK doesn't throw exceptions itself, but library code
might do; e.g., when out of memory.  Our new methods vtkJavaThrow*()
are at the end of this message.

This has obviously come up before, e.g.
  http://public.kitware.com/pipermail/vtkusers/2003-December/070725.html
with Jeff concluding:

    On second thought, mabey it isn't a good thing to have in vtk
    itself, but is more appropriate in your application code.  I was
    just thinking of some obscure compiler that vtk supports which
    doesn't support exception-handling.  Mabey that is the reason why
    things like this don't already exist in vtk.

We also wondered about the lack of exceptions in VTK.  Is it because
of obscure compilers in use in 2005?  Or, rather, due to the state of
exception handling in the early 90s when VTK got its start?  Is it now
acceptable to introduce exceptions?  If not in mainline code, then
at least in JNI code?  Presumably anyone wrapping it for java is using
a compiler that knows about exceptions.

The next thing we did was introduce exceptions.  ;-)


Most of the native functions call vtkJavaGetPointerFromObject() to
find the pointer to its associated C++ object; ditto for any object
arguments passed.  The code then goes on to call methods on the C++
object.  For example

  op = (vtkObject *)vtkJavaGetPointerFromObject(env,obj,(char *) "vtkObject");
  op->DebugOn();

However, it is possible that vtkJavaGetPointerFromObject() returns
NULL, resulting in a segfault and bringing down the application.
Instead of this behaviour, we'd like the code to return to java with
an exception set.  We therefore modified vtkJavaGetPointerFromObject()
so that it never returns null, but will (1) set an appropriate java
exception, then (2) throw a C++ exception to unwind the stack.  The
exception thrown is caught by the try/catch block that we've added to
the native methods.

The main reason to throw an exception rather than test the return
value is that there are a zillion callers to
vtkJavaGetPointerFromObject(), not all of which are generated by
vtkWrapJava.  It's way less error-prone to check return values in this
one method than audit all its callers.


Also we fixed the vtkJavaMakeJArrayOfXFromY() methods to
throw if the array could not be created.


Finally, here are the methods for throwing java exceptions.  Note
that the first exception thrown wins.  This allows us to set an
exception deep in the bowels of vtkJavaGetPointerFromObject(),
then throw to unwind the stack without fear of the catch(...)
clause overwriting the exception set.


// Throw a java exception, unless one has already been thrown.
// This method is static; the public API are the methods below for
// specific exception classes.
static void vtkJavaThrowException( JNIEnv *env, char* className, char* message )
{
  if (env->ExceptionOccurred() != NULL)
    return;

  jclass exceptionClass = env->FindClass( className );
  if ( exceptionClass != NULL )
    env->ThrowNew( exceptionClass, message );
}

void vtkJavaThrowIllegalArgumentException( JNIEnv *env, char* message )
{
  vtkJavaThrowException( env,
                         "java/lang/IllegalArgumentException",
                         message );
}

void vtkJavaThrowIllegalStateException( JNIEnv *env, char* message )
{
  vtkJavaThrowException( env,
                         "java/lang/IllegalStateException",
                         message );
}

void vtkJavaThrowOutOfMemoryError( JNIEnv *env )
{
  vtkJavaThrowException( env,
                         "java/lang/OutOfMemoryError",
                         "Out of memory in VTK JNI layer" );
}

// Throw from catch (...) block.
void vtkJavaThrowRuntimeException( JNIEnv *env )
{
  vtkJavaThrowException( env,
                         "java/lang/RuntimeException",
                         "Caught unknown exception in VTK JNI layer" );
}




Obviously, we've just got started on this robustification.  Any
further suggestions are welcomed.

Thanks,
-Steve




More information about the vtk-developers mailing list