/******************************************************************************* * Copyright (c) 2006, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Andrew Niefer *******************************************************************************/ #include "eclipseJNI.h" #include "eclipseCommon.h" #include "eclipseOS.h" #include "eclipseShm.h" #include #include static _TCHAR* failedToLoadLibrary = _T_ECLIPSE("Failed to load the JNI shared library \"%s\".\n"); static _TCHAR* createVMSymbolNotFound = _T_ECLIPSE("The JVM shared library \"%s\"\ndoes not contain the JNI_CreateJavaVM symbol.\n"); static _TCHAR* failedCreateVM = _T_ECLIPSE("Failed to create the Java Virtual Machine.\n"); static _TCHAR* internalExpectedVMArgs = _T_ECLIPSE("Internal Error, the JVM argument list is empty.\n"); static _TCHAR* mainClassNotFound = _T_ECLIPSE("Failed to find a Main Class in \"%s\".\n"); static JNINativeMethod natives[] = {{"_update_splash", "()V", (void *)&update_splash}, {"_get_splash_handle", "()J", (void *)&get_splash_handle}, {"_set_exit_data", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)&set_exit_data}, {"_set_launcher_info", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)&set_launcher_info}, {"_show_splash", "(Ljava/lang/String;)V", (void *)&show_splash}, {"_takedown_splash", "()V", (void *)&takedown_splash}}; /* local methods */ static jstring newJavaString(JNIEnv *env, _TCHAR * str); static void registerNatives(JNIEnv *env); static int shouldShutdown(JNIEnv *env); static void JNI_ReleaseStringChars(JNIEnv *env, jstring s, const _TCHAR* data); static const _TCHAR* JNI_GetStringChars(JNIEnv *env, jstring str); static char * getMainClass(JNIEnv *env, _TCHAR * jarFile); static void setLibraryLocation(JNIEnv *env, jobject obj); static JavaVM * jvm = 0; static JNIEnv *env = 0; /* cache String class and methods to avoid looking them up all the time */ static jclass string_class = NULL; #if !defined(UNICODE) && !defined(MACOSX) static jmethodID string_getBytesMethod = NULL; static jmethodID string_ctor = NULL; #endif /* JNI Callback methods */ JNIEXPORT void JNICALL set_exit_data(JNIEnv * env, jobject obj, jstring id, jstring s){ const _TCHAR* data = NULL; const _TCHAR* sharedId = NULL; size_t length; if(s != NULL) { length = (*env)->GetStringLength(env, s); if(!(*env)->ExceptionOccurred(env)) { data = JNI_GetStringChars(env, s); if (data != NULL) { if(id != NULL) { sharedId = JNI_GetStringChars(env, id); if(sharedId != NULL) { setSharedData(sharedId, data); JNI_ReleaseStringChars(env, id, sharedId); } } else { exitData = malloc((length + 1) * sizeof(_TCHAR*)); _tcsncpy( exitData, data, length); exitData[length] = _T_ECLIPSE('\0'); } JNI_ReleaseStringChars(env, s, data); } } if(data == NULL && sharedId == NULL) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } } JNIEXPORT void JNICALL set_launcher_info(JNIEnv * env, jobject obj, jstring launcher, jstring name){ const _TCHAR* launcherPath = NULL; const _TCHAR* launcherName = NULL; if (launcher != NULL) { launcherPath = JNI_GetStringChars(env, launcher); if (launcherPath != NULL) { setProgramPath(_tcsdup(launcherPath)); JNI_ReleaseStringChars(env, launcher, launcherPath); } } if (name != NULL) { launcherName = JNI_GetStringChars(env, name); if (launcherName != NULL) { setOfficialName(_tcsdup(launcherName)); JNI_ReleaseStringChars(env, name, launcherName); } } } JNIEXPORT void JNICALL update_splash(JNIEnv * env, jobject obj){ dispatchMessages(); } JNIEXPORT jlong JNICALL get_splash_handle(JNIEnv * env, jobject obj){ return getSplashHandle(); } JNIEXPORT void JNICALL show_splash(JNIEnv * env, jobject obj, jstring s){ const _TCHAR* data = NULL; setLibraryLocation(env, obj); if(s != NULL) { data = JNI_GetStringChars(env, s); if(data != NULL) { showSplash(data); JNI_ReleaseStringChars(env, s, data); } else { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } } JNIEXPORT void JNICALL takedown_splash(JNIEnv * env, jobject obj){ takeDownSplash(); } /* * On AIX we need the location of the eclipse shared library so that we * can find the libeclipse-motif.so library. Reach into the JNIBridge * object to get the "library" field. */ static void setLibraryLocation(JNIEnv * env, jobject obj) { jclass bridge = (*env)->FindClass(env, "org/eclipse/equinox/launcher/JNIBridge"); if (bridge != NULL) { jfieldID libraryField = (*env)->GetFieldID(env, bridge, "library", "Ljava/lang/String;"); if (libraryField != NULL) { jstring stringObject = (jstring) (*env)->GetObjectField(env, obj, libraryField); if (stringObject != NULL) { const _TCHAR * str = JNI_GetStringChars(env, stringObject); eclipseLibrary = _tcsdup(str); JNI_ReleaseStringChars(env, stringObject, str); } } } if( (*env)->ExceptionOccurred(env) != 0 ){ (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } static void registerNatives(JNIEnv *env) { jclass bridge = (*env)->FindClass(env, "org/eclipse/equinox/launcher/JNIBridge"); if(bridge != NULL) { int numNatives = sizeof(natives) / sizeof(natives[0]); (*env)->RegisterNatives(env, bridge, natives, numNatives); } if( (*env)->ExceptionOccurred(env) != 0 ){ (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } /* Get a _TCHAR* from a jstring, string should be released later with JNI_ReleaseStringChars */ static const _TCHAR * JNI_GetStringChars(JNIEnv *env, jstring str) { const _TCHAR * result = NULL; #ifdef UNICODE /* GetStringChars is not null terminated, make a copy */ const _TCHAR * stringChars = (*env)->GetStringChars(env, str, 0); int length = (*env)->GetStringLength(env, str); _TCHAR * copy = malloc( (length + 1) * sizeof(_TCHAR)); _tcsncpy(copy, stringChars, length); copy[length] = _T_ECLIPSE('\0'); (*env)->ReleaseStringChars(env, str, stringChars); result = copy; #elif MACOSX /* Use UTF on the Mac */ result = (*env)->GetStringUTFChars(env, str, 0); #else /* Other platforms, use java's default encoding */ _TCHAR* buffer = NULL; if (string_class == NULL) string_class = (*env)->FindClass(env, "java/lang/String"); if (string_class != NULL) { if (string_getBytesMethod == NULL) string_getBytesMethod = (*env)->GetMethodID(env, string_class, "getBytes", "()[B"); if (string_getBytesMethod != NULL) { jbyteArray bytes = (*env)->CallObjectMethod(env, str, string_getBytesMethod); if (!(*env)->ExceptionOccurred(env)) { jsize length = (*env)->GetArrayLength(env, bytes); buffer = malloc( (length + 1) * sizeof(_TCHAR*)); (*env)->GetByteArrayRegion(env, bytes, 0, length, (jbyte*)buffer); buffer[length] = 0; } (*env)->DeleteLocalRef(env, bytes); } } if(buffer == NULL) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } result = buffer; #endif return result; } /* Release the string that was obtained using JNI_GetStringChars */ static void JNI_ReleaseStringChars(JNIEnv *env, jstring s, const _TCHAR* data) { #ifdef UNICODE free((_TCHAR*)data); #elif MACOSX (*env)->ReleaseStringUTFChars(env, s, data); #else free((_TCHAR*)data); #endif } static jstring newJavaString(JNIEnv *env, _TCHAR * str) { jstring newString = NULL; #ifdef UNICODE size_t length = _tcslen(str); newString = (*env)->NewString(env, str, length); #elif MACOSX newString = (*env)->NewStringUTF(env, str); #else size_t length = _tcslen(str); jbyteArray bytes = (*env)->NewByteArray(env, length); if(bytes != NULL) { (*env)->SetByteArrayRegion(env, bytes, 0, length, (jbyte *)str); if (!(*env)->ExceptionOccurred(env)) { if (string_class == NULL) string_class = (*env)->FindClass(env, "java/lang/String"); if(string_class != NULL) { if (string_ctor == NULL) string_ctor = (*env)->GetMethodID(env, string_class, "", "([B)V"); if(string_ctor != NULL) { newString = (*env)->NewObject(env, string_class, string_ctor, bytes); } } } (*env)->DeleteLocalRef(env, bytes); } #endif if(newString == NULL) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } return newString; } static jobjectArray createRunArgs( JNIEnv *env, _TCHAR * args[] ) { int index = 0, length = -1; jobjectArray stringArray = NULL; jstring string; /*count the number of elements first*/ while(args[++length] != NULL); if (string_class == NULL) string_class = (*env)->FindClass(env, "java/lang/String"); if(string_class != NULL) { stringArray = (*env)->NewObjectArray(env, length, string_class, 0); if(stringArray != NULL) { for( index = 0; index < length; index++) { string = newJavaString(env, args[index]); if(string != NULL) { (*env)->SetObjectArrayElement(env, stringArray, index, string); (*env)->DeleteLocalRef(env, string); } else { (*env)->DeleteLocalRef(env, stringArray); (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); return NULL; } } } } if(stringArray == NULL) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } return stringArray; } JavaResults * startJavaJNI( _TCHAR* libPath, _TCHAR* vmArgs[], _TCHAR* progArgs[], _TCHAR* jarFile ) { int i; int numVMArgs = -1; void * jniLibrary; JNI_createJavaVM createJavaVM; JavaVMInitArgs init_args; JavaVMOption * options; char * mainClassName = NULL; JavaResults * results = NULL; /* JNI reflection */ jclass mainClass = NULL; /* The Main class to load */ jmethodID mainConstructor = NULL; /* Main's default constructor Main() */ jobject mainObject = NULL; /* An instantiation of the main class */ jmethodID runMethod = NULL; /* Main.run(String[]) */ jobjectArray methodArgs = NULL; /* Arguments to pass to run */ results = malloc(sizeof(JavaResults)); memset(results, 0, sizeof(JavaResults)); jniLibrary = loadLibrary(libPath); if(jniLibrary == NULL) { results->launchResult = -1; results->errorMessage = malloc((_tcslen(failedToLoadLibrary) + _tcslen(libPath) + 1) * sizeof(_TCHAR)); _stprintf(results->errorMessage, failedToLoadLibrary, libPath); return results; /*error*/ } createJavaVM = (JNI_createJavaVM)findSymbol(jniLibrary, _T_ECLIPSE("JNI_CreateJavaVM")); if(createJavaVM == NULL) { results->launchResult = -2; results->errorMessage = malloc((_tcslen(createVMSymbolNotFound) + _tcslen(libPath) + 1) * sizeof(_TCHAR)); _stprintf(results->errorMessage, createVMSymbolNotFound, libPath); return results; /*error*/ } /* count the vm args */ while(vmArgs[++numVMArgs] != NULL) {} if(numVMArgs <= 0) { /*error, we expect at least the required vm arg */ results->launchResult = -3; results->errorMessage = _tcsdup(internalExpectedVMArgs); return results; } options = malloc(numVMArgs * sizeof(JavaVMOption)); for(i = 0; i < numVMArgs; i++){ options[i].optionString = toNarrow(vmArgs[i]); options[i].extraInfo = 0; } #ifdef MACOSX init_args.version = JNI_VERSION_1_4; #else init_args.version = JNI_VERSION_1_2; #endif init_args.options = options; init_args.nOptions = numVMArgs; init_args.ignoreUnrecognized = JNI_TRUE; if( createJavaVM(&jvm, &env, &init_args) == 0 ) { registerNatives(env); mainClassName = getMainClass(env, jarFile); if (mainClassName != NULL) { mainClass = (*env)->FindClass(env, mainClassName); free(mainClassName); } if (mainClass == NULL) { if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } mainClass = (*env)->FindClass(env, "org/eclipse/equinox/launcher/Main"); } if(mainClass != NULL) { results->launchResult = -6; /* this will be reset to 0 below on success */ mainConstructor = (*env)->GetMethodID(env, mainClass, "", "()V"); if(mainConstructor != NULL) { mainObject = (*env)->NewObject(env, mainClass, mainConstructor); if(mainObject != NULL) { runMethod = (*env)->GetMethodID(env, mainClass, "run", "([Ljava/lang/String;)I"); if(runMethod != NULL) { methodArgs = createRunArgs(env, progArgs); if(methodArgs != NULL) { results->launchResult = 0; results->runResult = (*env)->CallIntMethod(env, mainObject, runMethod, methodArgs); (*env)->DeleteLocalRef(env, methodArgs); } } (*env)->DeleteLocalRef(env, mainObject); } } } else { results->launchResult = -5; results->errorMessage = malloc((_tcslen(mainClassNotFound) + _tcslen(jarFile) + 1) * sizeof(_TCHAR)); _stprintf(results->errorMessage, mainClassNotFound, jarFile); } if((*env)->ExceptionOccurred(env)){ (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } else { results->launchResult = -4; results->errorMessage = _tcsdup(failedCreateVM); } /* toNarrow allocated new strings, free them */ for(i = 0; i < numVMArgs; i++){ free( options[i].optionString ); } free(options); return results; } static char * getMainClass(JNIEnv *env, _TCHAR * jarFile) { jclass jarFileClass = NULL, manifestClass = NULL, attributesClass = NULL; jmethodID jarFileConstructor = NULL, getManifestMethod = NULL, getMainAttributesMethod = NULL, closeJarMethod = NULL, getValueMethod = NULL; jobject jarFileObject, manifest, attributes; jstring mainClassString = NULL; jstring jarFileString, headerString; const _TCHAR *mainClass; /* get the classes we need */ jarFileClass = (*env)->FindClass(env, "java/util/jar/JarFile"); if (jarFileClass != NULL) { manifestClass = (*env)->FindClass(env, "java/util/jar/Manifest"); if (manifestClass != NULL) { attributesClass = (*env)->FindClass(env, "java/util/jar/Attributes"); } } if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } if (attributesClass == NULL) return NULL; /* find the methods */ jarFileConstructor = (*env)->GetMethodID(env, jarFileClass, "", "(Ljava/lang/String;Z)V"); if(jarFileConstructor != NULL) { getManifestMethod = (*env)->GetMethodID(env, jarFileClass, "getManifest", "()Ljava/util/jar/Manifest;"); if(getManifestMethod != NULL) { closeJarMethod = (*env)->GetMethodID(env, jarFileClass, "close", "()V"); if (closeJarMethod != NULL) { getMainAttributesMethod = (*env)->GetMethodID(env, manifestClass, "getMainAttributes", "()Ljava/util/jar/Attributes;"); if (getMainAttributesMethod != NULL) { getValueMethod = (*env)->GetMethodID(env, attributesClass, "getValue", "(Ljava/lang/String;)Ljava/lang/String;"); } } } } if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } if (getValueMethod == NULL) return NULL; /* jarFileString = new String(jarFile); */ jarFileString = newJavaString(env, jarFile); /* headerString = new String("Main-Class"); */ headerString = newJavaString(env, _T_ECLIPSE("Main-Class")); if (jarFileString != NULL && headerString != NULL) { /* jarfileObject = new JarFile(jarFileString, false); */ jarFileObject = (*env)->NewObject(env, jarFileClass, jarFileConstructor, jarFileString, JNI_FALSE); if (jarFileObject != NULL) { /* manifest = jarFileObject.getManifest(); */ manifest = (*env)->CallObjectMethod(env, jarFileObject, getManifestMethod); if (manifest != NULL) { /*jarFileObject.close() */ (*env)->CallVoidMethod(env, jarFileObject, closeJarMethod); if (!(*env)->ExceptionOccurred(env)) { /* attributes = manifest.getMainAttributes(); */ attributes = (*env)->CallObjectMethod(env, manifest, getMainAttributesMethod); if (attributes != NULL) { /* mainClassString = attributes.getValue(headerString); */ mainClassString = (*env)->CallObjectMethod(env, attributes, getValueMethod, headerString); } } } (*env)->DeleteLocalRef(env, jarFileObject); } } if (jarFileString != NULL) (*env)->DeleteLocalRef(env, jarFileString); if (headerString != NULL) (*env)->DeleteLocalRef(env, headerString); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } if (mainClassString == NULL) return NULL; mainClass = JNI_GetStringChars(env, mainClassString); if(mainClass != NULL) { int i = -1; char *result = toNarrow(mainClass); JNI_ReleaseStringChars(env, mainClassString, mainClass); /* replace all the '.' with '/' */ while(result[++i] != '\0') { if(result[i] == '.') result[i] = '/'; } return result; } return NULL; } void cleanupVM(int exitCode) { JNIEnv * localEnv = env; if (jvm == 0) return; if (secondThread) (*jvm)->AttachCurrentThread(jvm, (void**)&localEnv, NULL); else localEnv = env; if (localEnv == 0) return; /* we call System.exit() unless osgi.noShutdown is set */ if (shouldShutdown(env)) { jclass systemClass = NULL; jmethodID exitMethod = NULL; systemClass = (*env)->FindClass(env, "java/lang/System"); if (systemClass != NULL) { exitMethod = (*env)->GetStaticMethodID(env, systemClass, "exit", "(I)V"); if (exitMethod != NULL) { (*env)->CallStaticVoidMethod(env, systemClass, exitMethod, exitCode); } } if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } (*jvm)->DestroyJavaVM(jvm); } static int shouldShutdown(JNIEnv * env) { jclass booleanClass = NULL; jmethodID method = NULL; jstring arg = NULL; jboolean result = 0; booleanClass = (*env)->FindClass(env, "java/lang/Boolean"); if (booleanClass != NULL) { method = (*env)->GetStaticMethodID(env, booleanClass, "getBoolean", "(Ljava/lang/String;)Z"); if (method != NULL) { arg = newJavaString(env, _T_ECLIPSE("osgi.noShutdown")); result = (*env)->CallStaticBooleanMethod(env, booleanClass, method, arg); (*env)->DeleteLocalRef(env, arg); } } if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } return (result == 0); }