Avinash
Avinash

Reputation: 13257

Creating JVM using JNI in C++ does not return

I am trying to launch jvm for my server java program. Server java program internally launches another java process and the class/method which I am calling has system.exist(0) My program works fine , it launches the required java process but program control does not come back to c++. It just exist i.e. parent process dies. Here is my complete code.

    #include <stdexcept>
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <include/jni.h>

#include <string>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <signal.h>


class JVMLauncherException : public std::runtime_error {
public:
    JVMLauncherException(const std::string& message) : std::runtime_error(message) 
    { 
    };
};

class Server_JVMLauncher {
public:
    Server_JVMLauncher();
    void addJars(std::string inJar);
    void LaunchJVM();
    void StartServer_Server();
    void AddServerArguments(std::string);
    void StopServer_Server();
private:
    typedef jint (JNICALL *CreateJavaVM)(JavaVM **pvm, void **penv, void *args);

    HINSTANCE     m_hDllInstance;

    std::string   m_JavaHome;
    std::string   m_ProductLibDir;
    std::string   m_JvmDllLocation;

    CreateJavaVM  m_JVMInstance;
    jclass        m_CacheServerLauncherClass;
    jmethodID     m_MainMethodID;
    JNIEnv       *m_JVMEnv;
    JavaVM       *m_RunningJVMInstance;    

    std::vector<std::string> m_listOfJars;
    std::vector<std::string> m_ServerArguments;

    void CheckForJNIException();
protected:
};

Server_JVMLauncher::Server_JVMLauncher() {
    // Check for JAVA_HOME
    char *pValue;
    size_t len;
    errno_t err = _dupenv_s( &pValue, &len, "JAVA_HOME" );
    if ( err ) {
        throw JVMLauncherException("JAVA_HOME not defined");
    }    
    m_JavaHome       = pValue;
    m_JvmDllLocation = m_JavaHome + "\\jre\\bin\\client\\jvm.dll";

    err = _dupenv_s( &pValue, &len, "Server_" );
    if ( err ) {
        throw JVMLauncherException("Server_ not defined");
    }
    m_ProductLibDir = pValue;

    m_listOfJars.push_back("dep1.jar");
    m_listOfJars.push_back("dep2.jar");
    m_listOfJars.push_back("dep3.jar");
    m_listOfJars.push_back("dep4.jar");
    m_listOfJars.push_back("dep5.jar");    
}
void Server_JVMLauncher::AddServerArguments(std::string inParam) {
    m_ServerArguments.push_back(inParam);
}
void Server_JVMLauncher::LaunchJVM() {
    // Construct the product specific class path.
    std::string strJavaClassPath = "-Djava.class.path=";
    for ( std::size_t idx = 0; idx < m_listOfJars.size() - 1 ; idx++) {
        strJavaClassPath += m_ProductLibDir + "\\lib\\" + m_listOfJars[idx] + ";";
    }
    strJavaClassPath += m_ProductLibDir + "\\lib\\" + m_listOfJars[m_listOfJars.size() - 1] ;

    // consruct java.library.path
    std::string strJavaLibraryPath =  "-Djava.library.path=";
    strJavaLibraryPath             += m_JavaHome + "\\lib" + "," + m_JavaHome + "\\jre\\lib";


    // try loading jvm dll
    m_hDllInstance = LoadLibraryA(m_JvmDllLocation.c_str());
    if( m_hDllInstance == 0) {
        throw JVMLauncherException("Cannot load jvm.dll");
    }
    m_JVMInstance = (CreateJavaVM)GetProcAddress(m_hDllInstance, "JNI_CreateJavaVM");
    if ( m_JVMInstance == NULL )  {
        throw JVMLauncherException("Cannot load jvm.dll");
    }

    JavaVMOption options[2];
    options[0].optionString  = const_cast<char*>(strJavaClassPath.c_str());
    options[1].optionString  = const_cast<char*>(strJavaLibraryPath.c_str());

    JavaVMInitArgs vm_args;
    vm_args.version            = JNI_VERSION_1_6; //JNI Version 1.4 and above
    vm_args.options            = options;    
    vm_args.nOptions           = 2;       
    vm_args.ignoreUnrecognized = JNI_TRUE;

    //Create the JVM

    jint res = m_JVMInstance(&m_RunningJVMInstance, (void **)&m_JVMEnv, &vm_args);
    if (res < 0)  {
        throw JVMLauncherException("Could not launch the JVM");
    }
    //m_RunningJVMInstance->AttachCurrentThread((void **)&m_JVMEnv, &vm_args);

    m_CacheServerLauncherClass = m_JVMEnv->FindClass("com/Server/internal/ServerLauncher");    
    CheckForJNIException();

    m_MainMethodID = m_JVMEnv->GetStaticMethodID(m_CacheServerLauncherClass, "main", "([Ljava/lang/String;)V");
    CheckForJNIException();
}

void Server_JVMLauncher::StartServer_Server() {

    if ( m_RunningJVMInstance->AttachCurrentThread((LPVOID *)&m_JVMEnv, NULL) ) {
        std::cout << "Fail to attach the current thread " << std::endl;
    }

    jclass StringClass = m_JVMEnv->FindClass("java/lang/String");
    int numOfArguments = (int)m_ServerArguments.size() + 2 ;
    int argumentIndex = 0;

    jobjectArray jargs = m_JVMEnv->NewObjectArray(numOfArguments, StringClass, NULL);    

    m_JVMEnv->SetObjectArrayElement(jargs, argumentIndex++, m_JVMEnv->NewStringUTF("start"));

    std::string strJavaClassPath = "-classpath=";
    strJavaClassPath             += "\"";
    for ( std::size_t idx = 0; idx < m_listOfJars.size() - 1 ; idx++) {
        strJavaClassPath += m_ProductLibDir + "\\lib\\" + m_listOfJars[idx] + ";";
    }
    strJavaClassPath      += m_ProductLibDir + "\\lib\\" + m_listOfJars[m_listOfJars.size() - 1] ;
    strJavaClassPath      += "\"";

    m_JVMEnv->SetObjectArrayElement(jargs, argumentIndex++, m_JVMEnv->NewStringUTF(strJavaClassPath.c_str()));

    for ( std::vector<std::string>::iterator iter = m_ServerArguments.begin(); iter != m_ServerArguments.end(); ++iter) {
        std::string argument = *iter;
        m_JVMEnv->SetObjectArrayElement(jargs, argumentIndex++, m_JVMEnv->NewStringUTF(argument.c_str()));
    }

    m_JVMEnv->CallStaticVoidMethod(m_CacheServerLauncherClass, m_MainMethodID, jargs);

    m_RunningJVMInstance->DestroyJavaVM();

    std::cout << "I am done with launching java program" << std::endl;
    CheckForJNIException();
}
void Server_JVMLauncher::StopServer_Server() {
    jclass StringClass = m_JVMEnv->FindClass("java/lang/String");
    int numOfArguments = 2 ;

    jobjectArray jargs = m_JVMEnv->NewObjectArray(numOfArguments, StringClass, NULL);    

    m_JVMEnv->SetObjectArrayElement(jargs, 0, m_JVMEnv->NewStringUTF("stop"));
    m_JVMEnv->SetObjectArrayElement(jargs, 1, m_JVMEnv->NewStringUTF("-dir=E:\\Avinash\\personal\\work\\CreateJvm\\Debug\\gfecs"));    

    m_JVMEnv->CallStaticVoidMethod(m_CacheServerLauncherClass, m_MainMethodID, jargs);
    CheckForJNIException();

}
void Server_JVMLauncher::CheckForJNIException() {
    jthrowable expt = m_JVMEnv->ExceptionOccurred();
    if (expt != NULL) {
        m_JVMEnv->ExceptionClear();
        jmethodID toString = m_JVMEnv->GetMethodID(m_JVMEnv->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;");
        jstring estring = (jstring) m_JVMEnv->CallObjectMethod(expt, toString);
        jboolean isCopy;
        std::string message = m_JVMEnv->GetStringUTFChars(estring, &isCopy);
        throw JVMLauncherException(message);
    }
}

int main( int argc, char **argv) {
    try {
        Server_JVMLauncher instanceServer_;
      instanceServer_.LaunchJVM();
        instanceServer_.StartServer_Server();
    } catch (JVMLauncherException &excp ) {
        std::cout << excp.what() << std::endl;
    }    
    return 0;
}

Upvotes: 4

Views: 4237

Answers (1)

hmjd
hmjd

Reputation: 121971

When you call CallStaticVoidMethod() it does not create a child process but runs the Java main() method in the calling thread, blocking the caller until it completes. If the Java program calls System.exit() the process will exit immediately and will not return to the caller: don't call System.exit().

Upvotes: 3

Related Questions