4

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;
}
Avinash
  • 12,851
  • 32
  • 116
  • 186

1 Answers1

3

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().

hmjd
  • 120,187
  • 20
  • 207
  • 252
  • unfortunately, I do not have access to server code. But even if the code return , I get the same result, i.e. this time I am not calling system.exit but calling "return;" – Avinash Jul 09 '12 at 19:25
  • Where do you call `return` from ? – hmjd Jul 09 '12 at 19:26
  • from Java main method. I have option to specify a flag, which says if the flag is true then do not call "System.exit(0)" but just "return;" – Avinash Jul 09 '12 at 19:31
  • @Avinash, this worked for me as I have done this before. Sorry can't be of more help but the order of the `m_RunningJVMInstance->DestroyJavaVM();` then `CheckForJNIException()` looks odd – hmjd Jul 09 '12 at 19:42
  • Thanks, but my Java Main method launches another JVM internally, which is getting started but somehow, C++ process gets exit after CallStaticVoidMethod – Avinash Jul 09 '12 at 19:48