javaruntimes/starterutils/src.linux/jvmstarterjni.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 27 Apr 2010 16:30:29 +0300
branchRCL_3
changeset 14 04becd199f91
permissions -rw-r--r--
Revision: v2.1.22 Kit: 201017

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  This class is meant for starting the Linux JVM.
*
*/

#include <string>
#include <algorithm>

#include "jvmstarterjni.h"

#include "jvmargsmodifier.h"

#include "runtimeexception.h"
#include "logger.h"
#include "javaoslayer.h"
#include "dynamiclibloader.h"
#include "javacommonutils.h"

using namespace java::runtime;
using namespace java::util;

extern const wchar_t CLASS_PATH_SEPARATOR = L':';
extern const char PATH_SEPARATOR_FROM  = '\\';
extern const char PATH_SEPARATOR_TO    = '/';

JvmStarter*
JvmStarter::getJvmStarterInstance(const Configuration configuration,
                                  const std::wstring& indetifier)
{
    JELOG2(EJavaRuntime);
    return  new JvmStarterJni(configuration, indetifier);
}

JvmStarter*
JvmStarter::getJvmStarterInstance()
{
    JELOG2(EJavaRuntime);
    return new JvmStarterJni();
}

JvmStarterJni::JvmStarterJni()
{
    JELOG2(EJavaRuntime);
}

JvmStarterJni::JvmStarterJni(const Configuration configuration,
                             const std::wstring& indetifier)
{
    JELOG2(EJavaRuntime);
    mConfiguration = configuration;
    mIdentifier = indetifier;

    // In Linux the binary root varies depending on the user.
    std::string rootStr;
    JavaOsLayer::getResRoot(rootStr, false);
    rootStr += "jsr/classes/common/";
    mExtensionPath.assign(rootStr.begin(), rootStr.end());
}


JvmStarterJni::~JvmStarterJni()
{
    JELOG2(EJavaRuntime);
}

void JvmStarterJni::overrideOldHeapSize(int /*heapSize*/)
{
    // Not supported by the JVM
    JELOG2(EJavaRuntime);
}

void JvmStarterJni::overrideNewHeapSize(int /*heapSize*/)
{
    // Not supported by the JVM
    JELOG2(EJavaRuntime);
}

void JvmStarterJni::overrideNativeStackSize(int /*stackSize*/)
{
    // Not supported by the JVM
    JELOG2(EJavaRuntime);
}

void JvmStarterJni::overrideJavaStackSize(int stackSize)
{
    JELOG2(EJavaRuntime);
    std::wstring stackSizeStr = L"-Xss";
    stackSizeStr += JavaCommonUtils::intToWstring(stackSize);
    stackSizeStr += L"K";
    mJvmArgs.push_back(stackSizeStr);
}

int JvmStarterJni::startJvm()
{
    JELOG2(EJavaRuntime);

    // Set mJvmArgs container to contain all the JVM args and set mAppAndArgs
    // to contain the main class and the arguments.
    completeArgumentContainers();

    // Give arguments to modifyJvmArguments for modification. Args
    // are modified if the default empty implementation has been overridden
    // by eclipsing the modifyJvmArguments dll.
    modifyJvmArguments(mIdentifier, mJvmArgs, mAppAndArgs);

    // Allocate space for the raw JVM args.
    int rawJvmArgumentCount = mJvmArgs.size();
    ScopedCharPointerArray rawJvmArgs(rawJvmArgumentCount);

    // Adding the JVM args. Main class and applcation arguments are handled
    // later.
    int ind = 0;
    for (JvmArgs_t::iterator jvmArgsIter = mJvmArgs.begin();
            jvmArgsIter!= mJvmArgs.end();
            ++jvmArgsIter)
    {
        // Do character conversion while adding the arguments.
        rawJvmArgs.get()[ind++] = JavaCommonUtils::wstringToUtf8(*jvmArgsIter);
    }
    return startJvmInSeparateThread(rawJvmArgumentCount, rawJvmArgs.get());
}

int JvmStarterJni::startJvm(int argc, char** argv)
{
    JELOG2(EJavaRuntime);
    // Allocate space for the raw JVM args. This will contain only
    // JVM arguments. Main class and application arguments are handled
    // differently.
    ScopedCharPointerArray rawJvmArgs(argc);

    // Assuming that in the beginning of the list there are JVM arguments and
    // they start with '-'.
    bool handlingArguments = true;

    int argCount = 0;
    for (int i = 0; i < argc; i++)
    {
        if (argv[i][0] != '-') // codescanner::accessArrayElementWithoutCheck2
        {
            // First non JVM argument was found.
            handlingArguments = false;
        }
        if (handlingArguments)
        {
            if ((strcmp(argv[i], "-cp") == 0 || // codescanner::accessArrayElementWithoutCheck2
                    strcmp(argv[i], "-classpath") == 0) && // codescanner::accessArrayElementWithoutCheck2
                    (i + 1) < argc)
            {
                // There is class path available.
                std::string cp("-Djava.class.path=");
                cp += argv[i+1];
                i++;
                rawJvmArgs.get()[argCount] = strdup(cp.c_str());
            }
            else
            {
                // Normal JVM argument.
                rawJvmArgs.get()[argCount] = strdup(argv[i]); // codescanner::accessArrayElementWithoutCheck2
            }
            argCount++;
        }
        else
        {
            // The first non JVM argument can be considered as main class.
            // The rest of the arguments are arguments for the Java app.
            mAppAndArgs.push_back(JavaCommonUtils::utf8ToWstring(argv[i])); // codescanner::accessArrayElementWithoutCheck2
        }
    }
    return startJvmInSeparateThread(argCount, rawJvmArgs.get());
}


void* JvmStarterJni::javaThreadMain(void* arg)
{
    JELOG2(EJavaRuntime);
    int result = -1;
    JvmStarterJni* jniStarter = (reinterpret_cast<JvmStarterJni*>(arg));
    try
    {
        result = jniStarter->startJvmImpl();
    }
    catch (RuntimeException& e)
    {
        ELOG1(EJavaRuntime, "JvmStarterJni::javaThreadMain() RuntimeException "
              "catched in VM thread. %s", e.toString().c_str());
    }
    catch (java::util::ExceptionBase& e)
    {
        ELOG1(EJavaRuntime, "JvmStarterJni::javaThreadMain() ExceptionBase "
              "catched in VM thread. %s", e.toString().c_str());
    }
    catch (std::exception& e)
    {
        ELOG1(EJavaRuntime, "JvmStarterJni::javaThreadMain() std::Exception "
              "catched in VM thread. %s", e.what());
    }
    return reinterpret_cast<void*>(result);
}

int JvmStarterJni::startJvmInSeparateThread(int argc, char** argv)
{
    JELOG2(EJavaRuntime);

    // Store the argc and argv into member variables in order be avaliable
    // for the JVM starter thread.
    mArgCount = argc;
    mArgs = argv;
    // return startJvmImpl(); This could be used to start the JVM into same
    // thread.
    pthread_t threadId;
    void*     result;

    // Create the JVM thread.
    pthread_create(&threadId, 0, javaThreadMain, this);

    // Wait until the thread has died.
    pthread_join(threadId, &result);
    return reinterpret_cast<int>(result);
}


int JvmStarterJni::startJvmImpl()
{
    JELOG2(EJavaRuntime);

    JavaVM* jvm;            // Denotes a Java VM.
    JNIEnv* env;            // Pointer to native method interface.
    JavaVMInitArgs vmArgs;  // VM initialization arguments.
    JavaVMOption vmOption;
    vmOption.extraInfo = 0;

    LOG(EJavaRuntime, EInfo, "VM args:");
    JvmOptionArgs_t jvmOptions;
    for (int i = 0; i < mArgCount; i++)
    {
        vmOption.optionString = mArgs[i];
        jvmOptions.push_back(vmOption);
        LOG1(EJavaRuntime, EInfo, " %s",mArgs[i]); // codescanner::accessArrayElementWithoutCheck2
    }
    vmArgs.version = JNI_VERSION_1_4;

    // Initializing JavaVMInitArgs.
    // Contiguity for std::vector<T> is mandated by the standard as long
    // as T is not bool. [See 23.2.4./1].
    vmArgs.options = &((jvmOptions)[0]); // codescanner::accessArrayElementWithoutCheck2
    vmArgs.nOptions = jvmOptions.size();
    vmArgs.ignoreUnrecognized = JNI_FALSE;

    JavaOsLayer::startUpTrace("Starting VM()", -1, -1);

    // Creating the JVM.
    int res = JNI_CreateJavaVM(&jvm, reinterpret_cast<void**>(&env), &vmArgs);
    LOG1(EJavaRuntime, EInfo, "JNI_CreateJavaVM() returned. st = %d", res);
    if (res == 0)
    {
        // Converting the '.' to '/' in the main class
        // (com.nokia.Foo -> com/nokia/Foo)
        std::wstring& appMain = mAppAndArgs.front();
        std::replace(appMain.begin(), appMain.end(), '.', '/');

        // Convert the main class to UTF-8.
        ScopedCharPointer main(JavaCommonUtils::wstringToUtf8(appMain));

        // Find the main class.
        jclass mainClass = env->FindClass(main.get());
        LOG2(EJavaRuntime, EInfo, " mainClass (%s): %p", main.get(), mainClass);
        if (mainClass != 0)
        {
            // Find method static void main(String[] args) from the main class.
            jmethodID mainMethod = env->GetStaticMethodID(mainClass, "main",
                                   "([Ljava/lang/String;)V");
            LOG1(EJavaRuntime, EInfo, " mainMethod: %p", mainMethod);
            if (mainMethod != 0)
            {
                // Call the method static void main().
                env->CallStaticVoidMethod(mainClass, mainMethod,
                                          getApplicationArguments(env));
                LOG(EJavaRuntime, EInfo, " CallStaticVoidMethod returned");
            }
            else
            {
                std::string errorStr("Not able to find main() method.");
                throw RuntimeException(errorStr,
                                       __FILE__, __FUNCTION__, __LINE__);
            }
        }
        else
        {
            std::string errorStr("Main class was not found.");
            throw RuntimeException(errorStr, __FILE__, __FUNCTION__, __LINE__);
        }
        jvm->DestroyJavaVM();
    }
    else
    {
        std::string errorStr("JNI_CreateJavaVM failed. Reason = .");
        errorStr += JavaCommonUtils::intToString(res);
        throw RuntimeException(errorStr, __FILE__, __FUNCTION__, __LINE__);
    }
    return res;
}


void JvmStarterJni::completeArgumentContainers()
{
    JELOG2(EJavaRuntime);

    // Set the used porting layer.
    mJvmArgs.push_back(L"-Dcom.nokia.jvm.port=sun.JvmPortJ2se");

    // Disable JIT, if requested.
    if (mJitDisabled)
    {
        mJvmArgs.push_back(L"-Xint");
    }

    // Add the classpath.
    if (mClassPath.length() > 0)
    {
        mClassPath.insert(0, L"-Djava.class.path=");
        mJvmArgs.push_front(mClassPath);
        LOG1(EJavaRuntime, EInfo, " mClassPath = %S", mClassPath.c_str());
    }

    // Add the extension classpath.
    if (mExtensionPath.length() > 0)
    {
        mExtensionPath.insert(0, L"-Djava.ext.dirs=");
        mJvmArgs.push_front(mExtensionPath);
        LOG1(EJavaRuntime, EInfo, " mExtensionPath = %S",
             mExtensionPath.c_str());
    }

    // Add the prepending boot classpath if set.
    if (mBootClassPathPrepend.length() > 0)
    {
        std::wstring bcpp(L"-Xbootclasspath/p:");
        bcpp += mBootClassPathPrepend;
        mJvmArgs.push_front(bcpp);
        LOG1(EJavaRuntime, EInfo, " bcpp = %S", bcpp.c_str());
    }

    // Add the appending boot classpath if set.
    if (mBootClassPathAppend.length() > 0)
    {
        std::wstring bcpa(L"-Xbootclasspath/a:");
        bcpa += mBootClassPathAppend;
        mJvmArgs.push_front(bcpa);
        LOG1(EJavaRuntime, EInfo, " bcpa = %S", bcpa.c_str());
    }

    std::wstring javaBinRoot;
    std::string binRoot;
    JavaOsLayer::getBinRoot(binRoot, false);
    javaBinRoot.assign(binRoot.begin(), binRoot.end());

    // Setting the java.library.path.
    std::wstring jlp(L"-Djava.library.path=");
    jlp += javaBinRoot;
    jlp +=  L"lib";
    mJvmArgs.push_front(jlp);
    LOG1(EJavaRuntime, EInfo, " jlp = %S", jlp.c_str());

    // Setting the java.home.
    std::wstring jh = L"-Djava.home=";
    const char* javaHome = getenv("JAVA_VM_HOME");
    if (javaHome == 0)
    {
        throw RuntimeException("JAVA_VM_HOME not defined",
                               __FILE__, __FUNCTION__, __LINE__);
    }
    std::string jhs(javaHome);
    jh.append(jhs.begin(), jhs.end());
    mJvmArgs.push_front(jh);
    LOG1(EJavaRuntime, EInfo, " jh = %S", jh.c_str());

    // Define emma.properties to point to emma.properties file
    // which is used when Java code coverage is measured.
    std::wstring emma(L"-Demma.properties=");
    emma += javaBinRoot;
    emma +=  L"emma.properties";
    mJvmArgs.push_front(emma);
    LOG1(EJavaRuntime, EInfo, " emma = %S", emma.c_str());

    // Add the main class.
    mAppAndArgs.push_front(mMainClass);
    LOG1(EJavaRuntime, EInfo, " mMainClass = %S", mMainClass.c_str());
}

jobjectArray JvmStarterJni::getApplicationArguments(JNIEnv* env)
{
    JELOG2(EJavaRuntime);
    // When using JNI_CreateJavaVM to start the JVM the arguments for the
    // Java application must be handled so that a jobjectArray is created
    // to contain Java Strings created using jni APIs.

    if (env == 0)
    {
        throw RuntimeException("JNIEnv was null",
                               __FILE__, __FUNCTION__, __LINE__);
    }

    // Create the jobjectArray. mAppAndArgs container contains the main
    // class which must be taken into account.
    jobjectArray array =
        (jobjectArray)env->NewObjectArray(mAppAndArgs.size() - 1,
                                          env->FindClass("java/lang/String"),
                                          env->NewStringUTF(""));
    LOG(EJavaRuntime, EInfo, "  Application arguments:");
    if (array != 0)
    {
        JvmArgs_t::iterator appAndArgsIter = mAppAndArgs.begin();
        // Skip the main class.
        ++appAndArgsIter;

        int i = 0;

        // Fill the object array with application arguments.
        for (; appAndArgsIter!= mAppAndArgs.end(); ++appAndArgsIter)
        {
            ScopedCharPointer appArg(JavaCommonUtils::wstringToUtf8(*appAndArgsIter));
            LOG1(EJavaRuntime, EInfo, "   %s", appArg.get());
            env->SetObjectArrayElement(array, i++,
                                       env->NewStringUTF(appArg.get()));
        }
    }
    else
    {
        throw RuntimeException("jobjectArray was null",
                               __FILE__, __FUNCTION__, __LINE__);
    }
    return array;
}