diff -r f5050f1da672 -r 04becd199f91 javaruntimes/starterutils/src.linux/jvmstarterjni.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaruntimes/starterutils/src.linux/jvmstarterjni.cpp Tue Apr 27 16:30:29 2010 +0300 @@ -0,0 +1,447 @@ +/* +* 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 +#include + +#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(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(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(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 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(&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; +} + +