コード例 #1
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
void onProcessCreation() throw ( ::jace::JNIException ) {

#ifdef SUPPORTS_PTHREADS
    int rc = pthread_key_create( &CLASSLOADER_KEY, classLoaderDestructor );
    switch ( rc )
    {
    case 0:
        break;
    case ENOMEM:
    case EAGAIN: {
        std::string msg = "Out of memory";
        throw JNIException( msg );
    }
    default: {
        std::string msg = "JNIHelper::setClassLoader()\n"
                "phread_key_create() returned " + std::to_string( rc ) + ".";
        throw JNIException( msg );
    }
    }
#elif _WIN32
    CLASSLOADER_KEY = TlsAlloc();
    if ( CLASSLOADER_KEY == TLS_OUT_OF_INDEXES ) {
        std::string msg = "JNIHelper::setClassLoader()\n"
                "TlsAlloc() returned " + std::to_string( GetLastError() ) + ".";
        throw JNIException( msg );
    }
#endif
}
コード例 #2
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
JavaVM* getJavaVM() throw ( JNIException ) {

    if ( ! javaVM ) {

        jsize numVMs;
        if (globalLoader.get() == 0) {
            string msg = string( "JNIHelper::getJavaVM\n" ) +
                    "Unable to find the JVM loader";
            throw JNIException( msg );
        }
        jint result = globalLoader->getCreatedJavaVMs( &javaVM, 1, &numVMs );

        if ( result != 0 ) {
            string msg = string( "JNIHelper::getJavaVM\n" ) +
                    "Unable to find the JVM. The specific JNI error code is " +
                    std::to_string( result );
            throw JNIException( msg );
        }

        if ( numVMs != 1 ) {
            string msg = string( "JNIHelper::getJavaVM\n" ) +
                    "Looking for exactly 1 JVM, but " +
                    std::to_string( numVMs ) +
                    " were found.";
            throw JNIException( msg );
        }
    }

    return javaVM;
}
コード例 #3
0
ファイル: JClassImpl.cpp プロジェクト: alaineman/PowerGrid
jclass JClassImpl::getClass() const throw ( JNIException ) {
    if ( mClass ) {
        return mClass;
    }

    JNIEnv* env = helper::attach();

	jobject classLoader = jace::helper::getClassLoader();
	jclass localClass;

    if ( classLoader != 0 ) {
        std::string binaryName( getName() );
		
		// Replace '/' by '.' in the name
        std::replace(binaryName.begin(), binaryName.end(), '/', '.');

		jclass classLoaderClass = env->GetObjectClass( classLoader );
		jmethodID loadClass = env->GetMethodID( classLoaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;" );
		if ( loadClass == 0 ) {
			string msg = "JClass::getClass - Unable to find the method JNIHelper::getClassLoader().loadClass()";
			try
			{
				helper::catchAndThrow();
			}
			catch (JNIException& e)
			{
				msg.append("\ncaused by:\n");
				msg.append(e.what());
			}
			throw JNIException( msg );
		}
		jstring javaString = env->NewStringUTF( binaryName.c_str() );
		localClass = static_cast<jclass>( env->CallObjectMethod( classLoader, loadClass, javaString ) );
		env->DeleteLocalRef( javaString );
    } else {
        localClass = env->FindClass( getName().c_str() );
    }

    if ( ! localClass ) {
        string msg = "JClass::getClass - Unable to find the class <" + getName() + ">";
		try
		{
			helper::catchAndThrow();
		}
		catch (JNIException& e)
		{
			msg.append("\ncaused by:\n");
			msg.append(e.what());
		}
        throw JNIException( msg );
    }

    mClass = static_cast<jclass>( helper::newGlobalRef( env, localClass ) );
    helper::deleteLocalRef( env, localClass );
    return mClass;
}
コード例 #4
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
std::string toString( jobject obj ) {

    JNIEnv* env = attach();

    jclass objectClass = env->FindClass( "java/lang/Object" );

    if ( ! objectClass ) {
        string msg = "Assert failed: Unable to find the class, java.lang.Object.";
        try
        {
            helper::catchAndThrow();
        }
        catch (JNIException& e)
        {
            msg.append("\ncaused by:\n");
            msg.append(e.what());
        }
        throw JNIException( msg );
    }

    jmethodID toString = env->GetMethodID( objectClass, "toString", "()Ljava/lang/String;" );

    if ( ! toString ) {
        string msg = "Assert failed: Unable to find the method, Object.toString().";
        try
        {
            helper::catchAndThrow();
        }
        catch (JNIException& e)
        {
            msg.append("\ncaused by:\n");
            msg.append(e.what());
        }
        throw JNIException( msg );
    }

    jstring javaStr = static_cast<jstring>( env->CallObjectMethod( obj, toString ) );

    const char* strBuf = env->GetStringUTFChars( javaStr, 0 );
    string value = string( strBuf );

    env->ReleaseStringUTFChars( javaStr, strBuf );

    deleteLocalRef( env, javaStr );
    deleteLocalRef( env, objectClass );

    return value;
}
コード例 #5
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
void onProcessDestruction() throw ( ::jace::JNIException ) {

#ifdef SUPPORTS_PTHREADS
    int rc = pthread_key_delete( CLASSLOADER_KEY );
    if ( rc != 0 ) {
        std::string msg = "JNIHelper::setClassLoader()\n"
                "thread_key_delete() returned " + std::to_string( rc ) + ".";
        throw JNIException( msg );
    }
#elif _WIN32
    BOOL rc = TlsFree( CLASSLOADER_KEY );
    if ( !rc ) {
        std::string msg = "JNIHelper::setClassLoader()\n"
                "TlsFree() returned " + std::to_string( GetLastError() ) + ".";
        throw JNIException( msg );
    }
#endif
}
コード例 #6
0
ファイル: JConstructor.cpp プロジェクト: alaineman/PowerGrid
/**
 * Gets the method id matching the given arguments.
 *
 */
jmethodID JConstructor::getMethodID( const JClass* jClass, const JArguments& arguments ) {

  /* We cache the jmethodID locally, so if we've already found it,
   * we don't need to go looking for it again.
   */
  if ( mMethodID ) {
    return mMethodID;
  }

  /* If we don't already have the jmethodID, we need to determine
   * the signature of this method.
   */

  /* We construct this signature with a void return type,
   * because the return type for constructors is void.
   */
  JSignature signature( *JVoid::staticGetJavaJniClass() ); 
  typedef list<JValue*> ValueList;
  ValueList args = arguments.asList();
  
  ValueList::iterator i = args.begin();
  ValueList::iterator end = args.end();

  for ( ; i != end; ++i ) {
    JValue* value = *i;
    signature << *value->getJavaJniClass();
  }

  string methodSignature = signature.toString();

  /* Now that we have the signature for the method, we could look
   * in a global cache for the jmethodID corresponding to this method,
   * but for now, we'll always find it.
   */
  JNIEnv* env = helper::attach();

  mMethodID = env->GetMethodID( jClass->getClass(), "<init>", methodSignature.c_str() );

  if ( mMethodID == NULL ) {
    string msg = string( "JConstructor::getMethodID\n" ) +
                 "Unable to find a constructor for " + jClass->getName() + "\n" +
                 "The signature is <" + methodSignature + ">";
		try
		{
			helper::catchAndThrow();
		}
		catch (JNIException e)
		{
			msg.append("\ncaused by:\n");
			msg.append(e.what());
		}
    throw JNIException( msg );
  }

  return mMethodID;
}
コード例 #7
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
std::string asString( JNIEnv* env, jstring str ) {
    const char* utfString = env->GetStringUTFChars( str, 0 );
    if ( ! utfString ) {
        std::string msg = "Unable to retrieve the character string for an exception message.";
        throw JNIException( msg );
    }
    std::string stdString = utfString;
    env->ReleaseStringUTFChars( str, utfString );
    return stdString;
}
コード例 #8
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
JNIEnv* attach() throw ( JNIException ) {

    if ( globalHasShutdown ) {
        throw JNIException( "The VM has already been shutdown." );
    }

    JNIEnv* env;

    jint result = getJavaVM()->AttachCurrentThread( reinterpret_cast<void**>( &env ), 0 );

    if ( result != 0 ) {
        string msg = "JNIHelper::attach\n" \
                "Unable to attach the current thread. The specific JNI error code is " +
                std::to_string( result );
        throw JNIException( msg );
    }

    return env;
}
コード例 #9
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
jobject newGlobalRef( JNIEnv* env, jobject ref ) {

    jobject globalRef = env->NewGlobalRef( ref );

    if ( ! globalRef ) {
        string msg = string( "JNIHelper::newGlobalRef\n" ) +
                "Unable to create a new global reference.\n" +
                "It is likely that you have exceeded the max heap size of your virtual machine.";
        throw JNIException( msg );
    }

    return globalRef;
}
コード例 #10
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
jobject newLocalRef( JNIEnv* env, jobject ref ) {

    jobject localRef = env->NewLocalRef( ref );

    if ( ! localRef ) {
        string msg = string( "JNIHelper::newLocalRef\n" ) +
                "Unable to create a new local reference.\n" +
                "It is likely that you have exceeded the maximum local reference count.\n" +
                "You can increase the maximum count with a call to EnsureLocalCapacity().";
        throw JNIException( msg );
    }

    return localRef;
}
コード例 #11
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
void setClassLoader(jobject classLoader) {

    JNIEnv* env = attach();
    jobject oldRef = getClassLoader();

#ifdef SUPPORTS_PTHREADS
    int rc;
#elif _WIN32
    BOOL rc;
#endif

    // Set the new value
    if ( classLoader != 0 ) {
        classLoader = static_cast<jobject> ( newGlobalRef( env, classLoader ) );
    }
#ifdef SUPPORTS_PTHREADS
    rc = pthread_setspecific( CLASSLOADER_KEY, classLoader );
    if ( rc != 0 ) {
        std::string msg = "JNIHelper::setClassLoader()\n"
                "pthread_setspecific() returned " + std::to_string( rc ) + ".";
        throw JNIException( msg );
    }
#elif _WIN32
    rc = TlsSetValue( CLASSLOADER_KEY, classLoader );
    if ( !rc ) {
        std::string msg = "JNIHelper::setClassLoader()\n"
                "TlsSetValue() returned " + std::to_string( GetLastError() ) + ".";
        throw JNIException( msg );
    }
#endif

    // Delete the old value if necessary
    if ( oldRef != 0 ) {
        classLoaderDestructor( oldRef );
    }
}
コード例 #12
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
QString getJavaProperty(QString name) {
    JNIEnv* env = attach();
    jclass systemClass = env->FindClass("java/lang/System");
    if (systemClass) {
        jmethodID getProperty = env->GetStaticMethodID(systemClass, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
        if (getProperty) {
            const char* propUtf = name.toStdString().c_str();
            jstring property = env->NewStringUTF(propUtf);
            jstring result = (jstring) env->CallStaticObjectMethod(systemClass, getProperty, property);
            const char* resUtf = env->GetStringUTFChars(result, NULL);
            QString returnValue (resUtf);

            env->DeleteLocalRef(result);
            env->DeleteLocalRef(property);
            env->DeleteLocalRef(systemClass);

            return returnValue;
        } else {
            throw JNIException("cannot find System.getProperty method");
        }
    } else {
        throw JNIException("cannot find System class");
    }
}
コード例 #13
0
void JNIHandleException()
{
	jthrowable exception = JNIObject::getJNIEnv()->ExceptionOccurred();
	if ( exception )
	{
		JNIObject::getJNIEnv()->ExceptionDescribe();
		JNIObject::getJNIEnv()->ExceptionClear();

		const char* exceptionClassName = JNIGetClassName( exception );
		if ( !exceptionClassName ) // TODO: fix, doesn't seem to ever be working, will remove need for describe and allow us to mirror with explicit C++ exception classes
		{
	    	// TODO: handle gracefully but don't let go unnoticed!
			JNIObject::getJNIEnv()->ExceptionClear();
			exceptionClassName = "Unknown exception";
		}
		JNIObject::getJNIEnv()->DeleteLocalRef( exception );
		throw JNIException( exceptionClassName );
	}
}
コード例 #14
0
Object ExpressionParser::evaluate(QString expression) {
    QStringList parts = expression.split(".");
    QString str(parts.at(0));
    str.replace('/', '.');
    str.replace(" ", "");
    str.replace("()", "");
    Class cls = Class::forName(str);
    if (parts.size() == 1) {
        return cls;
    }
    Object obj = cls.getStaticFieldContent(parts.at(1));
    for (int nextField = 2; nextField < parts.size(); nextField++) {
        if (obj.isNull()) {
            throw JNIException("Dereferenced NULL at position: " % QString::number(nextField));
        }
        Class objClass = obj.getClass();
        QString fieldName = convertFieldName(obj, parts.at(nextField));
        obj = objClass.getFieldContent(fieldName, obj);
    }
    return obj;
}
コード例 #15
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
void checkAndThrow(QString context, const char* file, int line) throw(JNIException) {
    JNIEnv* env = attach();
    if (!env->ExceptionCheck()) {
        return;
    }
    jthrowable thrown = env->ExceptionOccurred();
    env->ExceptionClear();

    jclass throwable  = env->FindClass("java/lang/Throwable");
    if ( !throwable ) throw JNIException("Assert failed: Could not find Throwable class");

    jmethodID getMessage = env->GetMethodID(throwable, "getMessage", "()Ljava/lang/String;");
    if ( !getMessage ) throw JNIException("Assert failed: Could not find Throwable.getMessage() method");

    jmethodID getClass = env->GetMethodID(throwable, "getClass", "()Ljava/lang/Class;");
    if ( !getClass ) throw JNIException("Assert failed: Could not find Throwable.getClass() method");

    jclass classClass = env->FindClass("java/lang/Class");
    if ( !classClass ) throw JNIException("Assert failed: Could not find Class class");

    jmethodID classGetName = env->GetMethodID(classClass, "getName", "()Ljava/lang/String;");
    if ( !classGetName ) throw JNIException("Assert failed: Could not find Class.getName() method");

    jobject type     = env->CallObjectMethod(thrown, getClass);
    jstring jmessage = static_cast<jstring>(env->CallObjectMethod(thrown, getMessage));
    jstring jname    = static_cast<jstring>(env->CallObjectMethod(type, classGetName));

    string  message  = jmessage == NULL ? "No Message"   : asString(env, jmessage);
    string  name     = jname    == NULL ? "Unknown Type" : asString(env, jname);

    env->DeleteLocalRef(throwable);
    env->DeleteLocalRef(thrown);
    env->DeleteLocalRef(classClass);
    env->DeleteLocalRef(jmessage);
    env->DeleteLocalRef(jname);

    throw JNIException(context % " (in " % file % ":" % QString::number(line) %
                       ")\nCaused by " % name.c_str() % ": " % message.c_str());
}
コード例 #16
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
void catchAndThrow() {

    JNIEnv* env = attach();

    if ( ! env->ExceptionCheck() ) {
        return;
    }

    jthrowable jexception = env->ExceptionOccurred();
    env->ExceptionClear();

    /* Find the fully qualified name for the exception type, so
     * we can find a matching C++ proxy exception.
     *
     * In java, this looks like:
     *   String typeName = exception.getClass().getName();
     */
    jclass throwableClass = env->FindClass( "java/lang/Throwable" );

    if ( ! throwableClass ) {
        string msg = "Assert failed: Unable to find the class, java.lang.Throwable.";
        throw JNIException( msg );
    }

    jclass classClass = env->FindClass( "java/lang/Class" );
    if ( ! classClass ) {
        string msg = "Assert failed: Unable to find the class, java.lang.Class.";
        throw JNIException( msg );
    }

    jmethodID throwableGetClass = env->GetMethodID( throwableClass, "getClass", "()Ljava/lang/Class;" );
    if ( ! throwableGetClass ) {
        string msg = "Assert failed: Unable to find the method, Throwable.getClass().";
        throw JNIException( msg );
    }
    deleteLocalRef( env, throwableClass );

    jmethodID classGetName = env->GetMethodID( classClass, "getName", "()Ljava/lang/String;" );
    if ( ! classGetName ) {
        string msg = "Assert failed: Unable to find the method, Class.getName().";
        throw JNIException( msg );
    }

    jmethodID classGetSuperclass = env->GetMethodID( classClass, "getSuperclass", "()Ljava/lang/Class;" );
    if ( ! classGetSuperclass ) {
        string msg = "Assert failed: Unable to find the method, Class.getSuperclass().";
        throw JNIException( msg );
    }
    deleteLocalRef( env, classClass );

    jobject exceptionClass = env->CallObjectMethod( jexception, throwableGetClass );
    if ( env->ExceptionOccurred() ) {
        env->ExceptionDescribe();
        string msg = "helper::catchAndThrow()\n" \
                "An error occurred while trying to call getClass() on the thrown exception.";
        throw JNIException( msg );
    }

    jstring exceptionType = static_cast<jstring>( env->CallObjectMethod( exceptionClass, classGetName ) );
    if ( env->ExceptionOccurred() ) {
        env->ExceptionDescribe();
        string msg = "helper::catchAndThrow()\n" \
                "An error occurred while trying to call getName() on the class of the thrown exception.";
        throw JNIException( msg );
    }

    string exceptionTypeString = asString( env, exceptionType );

    /* Now, find the matching factory for this exception type.
     */
    while ( true ) {

        FactoryMap::iterator it = getFactoryMap()->find( exceptionTypeString );

        /* If we couldn't find a match, try to find the parent exception type.
         */
        if ( it == getFactoryMap()->end() ) {

            jobject superClass = env->CallObjectMethod( exceptionClass, classGetSuperclass );

            if ( env->ExceptionOccurred() ) {
                env->ExceptionDescribe();
                string msg = "helper::catchAndThrow()\n" \
                        "An error occurred while trying to call getSuperclass() on the thrown exception.";
                throw JNIException( msg );
            }

            /* We get NULL if we've already reached java.lang.Object, in which case,
             * we couldn't find any match at all.
             */
            if ( ! superClass ) {
                break;
            }

            deleteLocalRef( env, exceptionClass );
            deleteLocalRef( env, exceptionType );
            exceptionClass = superClass;

            exceptionType = static_cast<jstring>( env->CallObjectMethod( exceptionClass, classGetName ) );

            if ( env->ExceptionOccurred() ) {
                env->ExceptionDescribe();
                string msg = "helper::catchAndThrow()\n" \
                        "An error occurred while trying to call getName() on the superclass " \
                        "of the thrown exception.";
                throw JNIException( msg );
            }

            exceptionTypeString = asString( env, exceptionType );
            if ( exceptionTypeString == "java.lang.Object" ) {
                /*
                 * Couldn't find a matching exception. Abort!
                 */
                break;
            }
            continue;
        }

        /* Ask the factory to throw the exception.
         */
        jvalue value;
        value.l = jexception;
        it->second->throwInstance( value );
    }

    exceptionClass = env->CallObjectMethod( jexception, throwableGetClass );

    if ( env->ExceptionOccurred() ) {
        env->ExceptionDescribe();
        string msg = "helper::catchAndThrow()\n" \
                "An error occurred while trying to call getClass() on the thrown exception.";
        throw JNIException( msg );
    }

    exceptionType = static_cast<jstring>( env->CallObjectMethod( exceptionClass, classGetName ) );

    if ( env->ExceptionOccurred() ) {
        env->ExceptionDescribe();
        string msg = "helper::catchAndThrow()\n" \
                "An error occurred while trying to call getName() on the class of the thrown exception.";
        throw JNIException( msg );
    }

    // Fallback: throw a JNIException with the type name and message of the Throwable.

    exceptionTypeString = asString( env, exceptionType );
    jmethodID getMessage = env->GetMethodID(static_cast<jclass>(exceptionClass), "getMessage", "()Ljava/lang/String;");
    if ( !getMessage) {
        throw JNIException("Could not find Throwable.getMessage() method");
    }

    jstring jmessage = static_cast<jstring>( env->CallObjectMethod(jexception, getMessage));
    if ( !jmessage) {
        // No message specified (or some error in the JVM), so just print the Exception type.
        throw JNIException(string("Exception occurred in JVM: ") + exceptionTypeString);
    }
    const char *msg = env->GetStringUTFChars(jmessage, NULL);
    throw JNIException( exceptionTypeString + ": " + msg );
}
コード例 #17
0
ファイル: JNIHelper.cpp プロジェクト: alaineman/PowerGrid
void createVm( const VmLoader& loader,
               const OptionList& options,
               bool ignoreUnrecognized ) {

    globalLoader = std::unique_ptr<VmLoader>( loader.clone() );
    globalLoader->loadVm();

    JavaVM* vm;
    JNIEnv* env;

    JavaVMInitArgs vm_args;
    JavaVMOption* jniOptions = options.createJniOptions();

    vm_args.version = globalLoader->version();
    vm_args.options = jniOptions;
    vm_args.nOptions = jint( options.size() );

    vm_args.ignoreUnrecognized = ignoreUnrecognized;

    jint rc = globalLoader->createJavaVM( &vm, reinterpret_cast<void**>( &env ), &vm_args );

    options.destroyJniOptions( jniOptions );

    if ( rc != 0 ) {
        string msg = "Unable to create the virtual machine. The error was " + std::to_string( rc );
        throw JNIException( msg );
    }

    jclass runtimeClass = env->FindClass( "java/lang/Runtime" );
    if ( ! runtimeClass ) {
        string msg = "Assert failed: Unable to find the class, java.lang.Runtime.";
        throw JNIException( msg );
    }

    jmethodID runtimeGetRuntime = env->GetStaticMethodID( runtimeClass, "getRuntime", "()Ljava/lang/Runtime;" );
    if ( ! runtimeGetRuntime ) {
        deleteLocalRef( env, runtimeClass );
        string msg = "Assert failed: Unable to find the method, Runtime.getRuntime().";
        throw JNIException( msg );
    }

    jobject runtimeObject = env->CallStaticObjectMethod( runtimeClass, runtimeGetRuntime );
    if ( ! runtimeObject )	{
        deleteLocalRef( env, runtimeClass );
        string msg = "Unable to invoke Runtime.getRuntime()";
        try
        {
            helper::catchAndThrow();
        }
        catch (JNIException& e)
        {
            msg.append("\ncaused by:\n");
            msg.append(e.what());
        }
        throw JNIException( msg );
    }

    jmethodID runtimeAddShutdownHook = env->GetMethodID( runtimeClass, "addShutdownHook", "(Ljava/lang/Thread;)V" );
    if ( ! runtimeAddShutdownHook ) {
        deleteLocalRef( env, runtimeObject );
        deleteLocalRef( env, runtimeClass );
        string msg = "Assert failed: Unable to find the method, Runtime.addShutdownHook().";
        throw JNIException( msg );
    }

    jclass shutdownHookClass = env->FindClass( "jace/util/ShutdownHook" );
    if ( ! shutdownHookClass ) {
        deleteLocalRef( env, runtimeObject );
        deleteLocalRef( env, runtimeClass );
        string msg = "Assert failed: Unable to find the class, jace.util.ShutdownHook. Did you forget to include "
                "jace-runtime.jar in your classpath at runtime?";
        throw JNIException( msg );
    }

    // Register the native method jace.util.ShutdownHook.signalVMShutdown
    JNINativeMethod signalVMShutdown;
    signalVMShutdown.fnPtr     = reinterpret_cast<void*>(Java_jace_util_ShutdownHook_signalVMShutdown);
    signalVMShutdown.name      = const_cast<char*>("signalVMShutdown");
    signalVMShutdown.signature = const_cast<char*>("()V");

    jint result = env->RegisterNatives(shutdownHookClass, &signalVMShutdown, 1);
    if (result != JNI_OK) {
        deleteLocalRef( env, runtimeObject );
        deleteLocalRef( env, runtimeClass );
        deleteLocalRef( env, shutdownHookClass );
        string msg = "Assert failed: Unable to register native method, jace.util.ShutdownHook.signalVMShutdown()";
        throw JNIException( msg );
    }

    jmethodID shutdownHookGetInstance = env->GetStaticMethodID( shutdownHookClass, "getInstance", "()Ljace/util/ShutdownHook;" );
    if ( ! shutdownHookGetInstance ) {
        deleteLocalRef( env, runtimeObject );
        deleteLocalRef( env, runtimeClass );
        deleteLocalRef( env, shutdownHookClass );
        string msg = "Assert failed: Unable to find the method, jace.util.ShutdownHook.getInstance()";
        throw JNIException( msg );
    }

    jobject shutdownHookObject = env->CallStaticObjectMethod( shutdownHookClass, shutdownHookGetInstance );
    if ( ! shutdownHookObject ) {
        deleteLocalRef( env, runtimeObject );
        deleteLocalRef( env, runtimeClass );
        deleteLocalRef( env, shutdownHookClass );
        string msg = "Unable to invoke jace.util.ShutdownHook.getInstance()";
        try
        {
            helper::catchAndThrow();
        }
        catch (JNIException& e)
        {
            msg.append("\ncaused by:\n");
            msg.append(e.what());
        }
        throw JNIException( msg );
    }

    env->CallVoidMethod( runtimeObject, runtimeAddShutdownHook, shutdownHookObject );
    try
    {
        helper::catchAndThrow();
    }
    catch (JNIException& e)
    {
        string msg = "Exception thrown invoking Runtime.addShutdownHook(shutdownHook)";
        msg.append("\ncaused by:\n");
        msg.append(e.what());
        throw JNIException( msg );
    }
    deleteLocalRef( env, runtimeObject );
    deleteLocalRef( env, runtimeClass );
    deleteLocalRef( env, shutdownHookClass );
}